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 <[email protected]>
5 * Copyright (C) 1992-2022 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#include <gal/opengl/kiglew.h> // Must be included first
26#include <gl_utils.h>
27#include <wx/tokenzr.h>
28
29#include "../common_ogl/ogl_utils.h"
30#include "eda_3d_canvas.h"
31#include <eda_3d_viewer_frame.h>
34#include <3d_viewer_id.h>
35#include <board.h>
36#include <reporter.h>
37#include <gl_context_mgr.h>
38#include <profile.h> // To use GetRunningMicroSecs or another profiling utility
39#include <bitmaps.h>
40#include <macros.h>
41#include <menus_helpers.h>
42#include <pgm_base.h>
45
47
48
56const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
57
58
59// A custom event, used to call DoRePaint during an idle time
60wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent );
61
62
63BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
64 EVT_PAINT( EDA_3D_CANVAS::OnPaint )
65
66 // mouse events
67 EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
68 EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
69 EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
71 EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
72 EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
73
74#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
75 EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
76#endif
77
78 // other events
79 EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
80 EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
81
83 EVT_SIZE( EDA_3D_CANVAS::OnResize )
84END_EVENT_TABLE()
85
86
87EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList,
88 BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
89 S3D_CACHE* a3DCachePointer ) :
90 HIDPI_GL_3D_CANVAS( aCamera, aParent, wxID_ANY, aAttribList, wxDefaultPosition,
91 wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
92 m_eventDispatcher( nullptr ),
93 m_parentStatusBar( nullptr ),
94 m_parentInfoBar( nullptr ),
95 m_glRC( nullptr ),
96 m_is_opengl_initialized( false ),
97 m_is_opengl_version_supported( true ),
98 m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
99 m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
100 m_render_pivot( false ),
101 m_camera_moving_speed( 1.0f ),
102 m_strtime_camera_movement( 0 ),
103 m_animation_enabled( true ),
104 m_moving_speed_multiplier( 3 ),
105 m_boardAdapter( aBoardAdapter ),
106 m_3d_render( nullptr ),
107 m_opengl_supports_raytracing( true ),
108 m_render_raytracing_was_requested( false ),
109 m_accelerator3DShapes( nullptr ),
110 m_currentRollOverItem( nullptr )
111{
112 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) );
113
114 m_editing_timeout_timer.SetOwner( this );
115 Connect( m_editing_timeout_timer.GetId(), wxEVT_TIMER,
116 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ), nullptr, this );
117
118 m_redraw_trigger_timer.SetOwner( this );
119 Connect( m_redraw_trigger_timer.GetId(), wxEVT_TIMER,
120 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ), nullptr, this );
121
122 m_is_currently_painting.clear();
123
124 m_3d_render_raytracing = new RENDER_3D_RAYTRACE( this, m_boardAdapter, m_camera );
125 m_3d_render_opengl = new RENDER_3D_OPENGL( this, m_boardAdapter, m_camera );
126
127 wxASSERT( m_3d_render_raytracing != nullptr );
128 wxASSERT( m_3d_render_opengl != nullptr );
129
130 auto busy_indicator_factory =
131 []()
132 {
133 return std::make_unique<WX_BUSY_INDICATOR>();
134 };
135
136 m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
137 m_3d_render_opengl->SetBusyIndicatorFactory( busy_indicator_factory );
138
139 // We always start with the opengl engine (raytracing is avoided due to very
140 // long calculation time)
141 m_3d_render = m_3d_render_opengl;
142
143 m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
144
145 wxASSERT( a3DCachePointer != nullptr );
146 m_boardAdapter.Set3dCacheManager( a3DCachePointer );
147
148 const wxEventType events[] =
149 {
150 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
151 // especially special key like arrow keys, are handled by the GAL event dispatcher,
152 // and not sent to GUI without filtering, because they have a default action (scroll)
153 // that must not be called.
154 wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
155 wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
156 wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
157 wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
158#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
159 wxEVT_MAGNIFY,
160#endif
161 wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
162 };
163
164 for( wxEventType eventType : events )
165 Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
166}
167
168
170{
171 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::~EDA_3D_CANVAS" ) );
172
174 m_accelerator3DShapes = nullptr;
175
177}
178
179
181{
182 if( m_glRC )
183 {
185
187 m_3d_render_raytracing = nullptr;
188
189 delete m_3d_render_opengl;
190 m_3d_render_opengl = nullptr;
191
192 // This is just a copy of a pointer, can safely be set to NULL.
193 m_3d_render = nullptr;
194
197 m_glRC = nullptr;
198 }
199}
200
201
202void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
203{
205
206 event.Skip();
207}
208
209
210void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
211{
213}
214
215
217{
218 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) );
219
220 const GLenum err = glewInit();
221
222 if( GLEW_OK != err )
223 {
224 const wxString msgError = (const char*) glewGetErrorString( err );
225
226 wxLogMessage( msgError );
227
228 return false;
229 }
230 else
231 {
232 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s" ),
233 FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
234 }
235
236 wxString version = FROM_UTF8( (char *) glGetString( GL_VERSION ) );
237
238 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL version string %s." ),
239 __WXFUNCTION__, version );
240
241 // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
242 // getting the OpenGL major and minor version as integers didn't exist.
243 wxString tmp;
244
245 wxStringTokenizer tokenizer( version );
246
247 if( tokenizer.HasMoreTokens() )
248 {
249 long major = 0;
250 long minor = 0;
251
252 tmp = tokenizer.GetNextToken();
253
254 tokenizer.SetString( tmp, wxString( wxT( "." ) ) );
255
256 if( tokenizer.HasMoreTokens() )
257 tokenizer.GetNextToken().ToLong( &major );
258
259 if( tokenizer.HasMoreTokens() )
260 tokenizer.GetNextToken().ToLong( &minor );
261
262 if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
263 {
264 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL ray tracing not supported." ),
265 __WXFUNCTION__ );
266
267 if( GetParent() )
268 {
269 wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
270 GetParent()->ProcessWindowEvent( evt );
271 }
272
274 }
275
276 if( ( major == 1 ) && ( minor < 5 ) )
277 {
278 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL not supported." ),
279 __WXFUNCTION__ );
280
282 }
283 }
284
287
288 return true;
289}
290
291
292void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
293{
294 OglGetScreenshot( aDstImage );
295}
296
297
298void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
299{
300 if( aCachePointer != nullptr )
301 m_boardAdapter.Set3dCacheManager( aCachePointer );
302
303 if( aBoard != nullptr )
304 m_boardAdapter.SetBoard( aBoard );
305
306 m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
307
308 if( m_3d_render )
310}
311
312
314{
316
317 if( m_3d_render )
319
321
323}
324
325
327{
329 {
330 wxString msg;
331
332 msg.Printf( wxT( "dx %3.2f" ), m_camera.GetCameraPos().x );
333 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
334
335 msg.Printf( wxT( "dy %3.2f" ), m_camera.GetCameraPos().y );
336 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
337 }
338}
339
340
341void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
342{
343 // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
344 DoRePaint();
345}
346
347
349{
350 if( m_is_currently_painting.test_and_set() )
351 return;
352
353 // SwapBuffer requires the window to be shown before calling
354 if( !IsShownOnScreen() )
355 {
356 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::DoRePaint !IsShown" ) );
358 return;
359 }
360
361 // Because the board to draw is handled by the parent viewer frame,
362 // ensure this parent is still alive. When it is closed before the viewer
363 // frame, a paint event can be generated after the parent is closed,
364 // therefore with invalid board.
365 // This is dependent of the platform.
366 // Especially on OSX, but also on Windows, it frequently happens
367 if( !GetParent()->GetParent()->IsShown() )
368 return; // The parent board editor frame is no more alive
369
370 wxString err_messages;
371
372 // !TODO: implement error reporter
373 INFOBAR_REPORTER warningReporter( m_parentInfoBar );
375
376 unsigned strtime = GetRunningMicroSecs();
377
378 // "Makes the OpenGL state that is represented by the OpenGL rendering
379 // context context current, i.e. it will be used by all subsequent OpenGL calls.
380 // This function may only be called when the window is shown on screen"
381
382 // Explicitly create a new rendering context instance for this canvas.
383 if( m_glRC == nullptr )
385
387
388 // Set the OpenGL viewport according to the client size of this canvas.
389 // This is done here rather than in a wxSizeEvent handler because our
390 // OpenGL rendering context (and thus viewport setting) is used with
391 // multiple canvases: If we updated the viewport in the wxSizeEvent
392 // handler, changing the size of one canvas causes a viewport setting that
393 // is wrong when next another canvas is repainted.
394 wxSize clientSize = GetNativePixelSize();
395
396 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
397
398 // Initialize openGL if need
400 {
401 if( !initializeOpenGL() )
402 {
405
406 return;
407 }
408
410 {
411 warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
412 "is 1.5." ), RPT_SEVERITY_ERROR );
413
414 warningReporter.Finalize();
415 }
416 }
417
419 {
420 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
421 glClear( GL_COLOR_BUFFER_BIT );
422
423 SwapBuffers();
424
427
428 return;
429 }
430
431 // Don't attend to ray trace if OpenGL doesn't support it.
433 {
437 }
438
439 // Check if a raytacing was requested and need to switch to raytracing mode
441 {
442 const bool was_camera_changed = m_camera.ParametersChanged();
443
444 // It reverts back to OpenGL mode if it was requested a raytracing
445 // render of the current scene. AND the mouse / camera is moving
446 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
447 || windows_size_changed )
449 {
452 }
453 }
454
455 float curtime_delta_s = 0.0f;
456
458 {
459 const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
460 curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
461 m_camera.Interpolate( curtime_delta_s );
462
463 if( curtime_delta_s > 1.0f )
464 {
465 m_render_pivot = false;
466 m_camera_is_moving = false;
467 m_mouse_was_moved = true;
468
471 }
472 else
473 {
475 }
476 }
477
478 // It will return true if the render request a new redraw
479 bool requested_redraw = false;
480
481 if( m_3d_render )
482 {
483 try
484 {
485 m_3d_render->SetCurWindowSize( clientSize );
486
487 bool reloadRaytracingForCalculations = false;
488
491 {
492 reloadRaytracingForCalculations = true;
493 }
494
496 &activityReporter, &warningReporter );
497
498 // Raytracer renderer is responsible for some features also used by the OpenGL
499 // renderer.
500 // FIXME: Presumably because raytracing renderer reload is called only after current
501 // renderer redraw, the old zoom value stays for a single frame. This is ugly, but only
502 // cosmetic, so I'm not fixing that for now: I don't know how to do this without
503 // reloading twice (maybe it's not too bad of an idea?) or doing a complicated
504 // refactor.
505 if( reloadRaytracingForCalculations )
506 m_3d_render_raytracing->Reload( nullptr, nullptr, true );
507 }
508 catch( std::runtime_error& )
509 {
515 return;
516 }
517 }
518
519 if( m_render_pivot )
520 {
521 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
522 render_pivot( curtime_delta_s, scale );
523 }
524
525#if defined( KICAD_USE_3DCONNEXION )
526 if( m_render3dmousePivot )
527 {
528 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
529 render3dmousePivot( scale );
530 }
531#endif
532
533 // "Swaps the double-buffer of this window, making the back-buffer the
534 // front-buffer and vice versa, so that the output of the previous OpenGL
535 // commands is displayed on the window."
536 SwapBuffers();
537
539
540 if( !activityReporter.HasMessage() )
541 {
543 {
544 // Calculation time in milliseconds
545 const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
546
547 activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
548 calculation_time ) );
549 }
550 }
551
552 // This will reset the flag of camera parameters changed
554
555 warningReporter.Finalize();
556
557 if( !err_messages.IsEmpty() )
558 wxLogMessage( err_messages );
559
560 if( ( !m_camera_is_moving ) && requested_redraw )
561 {
562 m_mouse_was_moved = false;
563 Request_refresh( false );
564 }
565
567}
568
569
571{
572 m_eventDispatcher = aEventDispatcher;
573}
574
575
576void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
577{
578 if( !m_eventDispatcher )
579 aEvent.Skip();
580 else
582
583 Refresh();
584}
585
586
587void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
588{
589 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) );
590 // Do nothing, to avoid flashing.
591}
592
593
594void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
595{
596 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) );
597
599
601 {
605 }
606}
607
608
609#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
610void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
611{
612 SetFocus();
613
615 return;
616
617 //m_is_moving_mouse = true;
619
620 float magnification = ( event.GetMagnification() + 1.0f );
621
622 m_camera.Zoom( magnification );
623
626}
627#endif
628
629
630void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
631{
633 return;
634
635 OnMouseMoveCamera( event );
636
638 {
641 // *Do not* reactivate the timer here during the mouse move command:
642 // OnMiddleUp() will do it at the end of mouse drag/move command
643 }
644
645 if( !event.Dragging() && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
646 {
649 BOARD_ITEM* rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
650
651 auto printNetInfo =
652 []( BOARD_CONNECTED_ITEM* aItem )
653 {
654 return wxString::Format( _( "Net %s\tNet class %s" ),
655 aItem->GetNet()->GetNetname(),
656 aItem->GetNet()->GetNetClass()->GetName() );
657 };
658
659 if( rollOverItem )
660 {
661 wxString msg;
662
663 if( rollOverItem != m_currentRollOverItem )
664 {
666 m_currentRollOverItem = rollOverItem;
667
669 }
670
671 switch( rollOverItem->Type() )
672 {
673 case PCB_PAD_T:
674 {
675 PAD* pad = static_cast<PAD*>( rollOverItem );
676
677 if( !pad->GetNumber().IsEmpty() )
678 msg += wxString::Format( _( "Pad %s\t" ), pad->GetNumber() );
679
680 if( pad->IsOnCopperLayer() )
681 msg += printNetInfo( pad );
682
683 break;
684 }
685
686 case PCB_FOOTPRINT_T:
687 {
688 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( rollOverItem );
689 msg += footprint->GetReference();
690 break;
691 }
692
693 case PCB_TRACE_T:
694 case PCB_VIA_T:
695 case PCB_ARC_T:
696 {
697 PCB_TRACK* track = static_cast<PCB_TRACK*>( rollOverItem );
698 msg += printNetInfo( track );
699 break;
700 }
701
702 case PCB_ZONE_T:
703 {
704 ZONE* zone = static_cast<ZONE*>( rollOverItem );
705
706 if( !zone->GetZoneName().IsEmpty() )
707 {
708 if( zone->GetIsRuleArea() )
709 msg += wxString::Format( _( "Rule area %s\t" ), zone->GetZoneName() );
710 else
711 msg += wxString::Format( _( "Zone %s\t" ), zone->GetZoneName() );
712 }
713
714 if( zone->IsOnCopperLayer() )
715 msg += printNetInfo( zone );
716
717 break;
718 }
719
720 default:
721 break;
722 }
723
724 reporter.Report( msg );
725 }
726 else
727 {
730 {
733
734 reporter.Report( wxEmptyString );
735 }
736
737 m_currentRollOverItem = nullptr;
738 }
739 }
740}
741
742
743void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
744{
745 SetFocus();
747
748 if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
749 {
751
752 BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
753
754 // !TODO: send a selection item to pcbnew, eg: via kiway?
755 }
756}
757
758
759void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
760{
762 return;
763
765 {
766 m_mouse_is_moving = false;
768 }
769}
770
771
772void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
773{
774 SetFocus();
776}
777
778
779void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
780{
782 return;
783
785 {
786 m_mouse_is_moving = false;
788 }
789 else
790 {
792 }
793}
794
795
796void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& aEvent )
797{
798 if( aEvent.GetId() != m_editing_timeout_timer.GetId() )
799 {
800 aEvent.Skip();
801 return;
802 }
803
804 m_mouse_is_moving = false;
805 m_mouse_was_moved = false;
806
808}
809
810
812{
814}
815
816
818{
819 if( m_3d_render )
821}
822
823
824void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& aEvent )
825{
826 if( aEvent.GetId() != m_redraw_trigger_timer.GetId() )
827 {
828 aEvent.Skip();
829 return;
830 }
831
832 Request_refresh( true );
833}
834
835
836void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
837{
838 DoRePaint();
839}
840
841
842void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
843{
844 if( aRedrawImmediately )
845 {
846 // Just calling Refresh() does not work always
847 // Using an event to call DoRepaint ensure the repaint code will be executed,
848 // and PostEvent will take priority to other events like mouse movements, keys, etc.
849 // and is executed during the next idle time
850 wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
851 wxPostEvent( this, redrawEvent );
852 }
853 else
854 {
855 // Schedule a timed redraw
856 m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
857 }
858}
859
860
861void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
862{
863 wxASSERT( aMovingSpeed > FLT_EPSILON );
864
865 // Fast forward the animation if the animation is disabled
867 {
868 m_camera.Interpolate( 1.0f );
871 return;
872 }
873
874 // Map speed multiplier option to actual multiplier value
875 // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
876 aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
877
878 m_render_pivot = aRenderPivot;
879 m_camera_moving_speed = aMovingSpeed;
880
882
885
886 m_camera_is_moving = true;
887
889}
890
891
893{
895
896 float hit_t;
897
898 // Test it with the board bounding box
899 if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
900 {
903 m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
905
907 }
908}
909
910
911bool EDA_3D_CANVAS::SetView3D( int aKeycode )
912{
914 return false;
915
916 const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
917 const float arrow_moving_time_speed = 8.0f;
918 bool handled = false;
919
920 switch( aKeycode )
921 {
922 case WXK_SPACE:
924 return true;
925
926 case WXK_LEFT:
929 m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
930 request_start_moving_camera( arrow_moving_time_speed, false );
931 return true;
932
933 case WXK_RIGHT:
936 m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
937 request_start_moving_camera( arrow_moving_time_speed, false );
938 return true;
939
940 case WXK_UP:
943 m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
944 request_start_moving_camera( arrow_moving_time_speed, false );
945 return true;
946
947 case WXK_DOWN:
950 m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
951 request_start_moving_camera( arrow_moving_time_speed, false );
952 return true;
953
954 case WXK_HOME:
958 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
959 return true;
960
961 case WXK_END:
962 break;
963
964 case WXK_TAB:
967 m_camera.RotateZ_T1( glm::radians( 45.0f ) );
969 handled = true;
970 break;
971
972 case WXK_F1:
975
976 if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
978
979 return true;
980
981 case WXK_F2:
984
985 if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
987
988 return true;
989
990 case ID_VIEW3D_RESET:
994 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
995 return true;
996
997 case ID_VIEW3D_RIGHT:
1001 m_camera.RotateZ_T1( glm::radians( -90.0f ) );
1002 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1004 return true;
1005
1006 case ID_VIEW3D_LEFT:
1010 m_camera.RotateZ_T1( glm::radians( 90.0f ) );
1011 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1013 return true;
1014
1015 case ID_VIEW3D_FRONT:
1019 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1021 return true;
1022
1023 case ID_VIEW3D_BACK:
1027 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1028
1029 // The rotation angle should be 180.
1030 // We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
1031 // using 180 deg if the previous rotated position was already 180 deg
1032 m_camera.RotateZ_T1( glm::radians( 179.999f ) );
1034 return true;
1035
1036 case ID_VIEW3D_TOP:
1040 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1041 return true;
1042
1043 case ID_VIEW3D_BOTTOM:
1047 m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
1048 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1049 return true;
1050
1051 case ID_VIEW3D_FLIP:
1054 m_camera.RotateY_T1( glm::radians( 179.999f ) );
1056 return true;
1057
1058 default:
1059 return false;
1060 }
1061
1062 m_mouse_was_moved = true;
1063
1065
1066 DisplayStatus();
1068
1069 return handled;
1070}
1071
1072
1074{
1075 SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
1077
1078 switch( cfg->m_Render.engine )
1079 {
1082 default: m_3d_render = nullptr; break;
1083 }
1084
1085 if( m_3d_render )
1087
1088 m_mouse_was_moved = false;
1089
1091}
1092
1093
1095{
1096 SFVEC3F rayOrigin;
1097 SFVEC3F rayDir;
1098
1099 // Generate a ray origin and direction based on current mouser position and camera
1100 m_camera.MakeRayAtCurrentMousePosition( rayOrigin, rayDir );
1101
1102 RAY mouseRay;
1103 mouseRay.Init( rayOrigin, rayDir );
1104
1105 return mouseRay;
1106}
@ ID_CUSTOM_EVENT_1
Definition: 3d_viewer_id.h:44
@ ID_DISABLE_RAY_TRACING
Definition: 3d_viewer_id.h:42
@ ID_VIEW3D_BACK
Definition: 3d_viewer_id.h:26
@ ID_VIEW3D_RESET
Definition: 3d_viewer_id.h:27
@ ID_VIEW3D_RIGHT
Definition: 3d_viewer_id.h:24
@ ID_VIEW3D_FRONT
Definition: 3d_viewer_id.h:25
@ ID_VIEW3D_TOP
Definition: 3d_viewer_id.h:21
@ ID_VIEW3D_LEFT
Definition: 3d_viewer_id.h:23
@ ID_VIEW3D_FLIP
Definition: 3d_viewer_id.h:28
@ ID_VIEW3D_BOTTOM
Definition: 3d_viewer_id.h:22
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:69
const BBOX_3D & GetBBox() const noexcept
Get the board outling bounding box.
void SetBoard(BOARD *aBoard) noexcept
Set current board to be rendered.
EDA_3D_VIEWER_SETTINGS * m_Cfg
bool m_MousewheelPanning
void Set3dCacheManager(S3D_CACHE *aCacheMgr) noexcept
Update the cache manager pointer.
Definition: board_adapter.h:80
void SetColorSettings(COLOR_SETTINGS *aSettings) noexcept
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
A class used to derive camera objects from.
Definition: camera.h:78
bool Zoom(float aFactor)
Definition: camera.cpp:539
void RotateY_T1(float aAngleInRadians)
Definition: camera.cpp:619
bool Zoom_T1(float aFactor)
Definition: camera.cpp:570
virtual void Reset_T1()
Definition: camera.cpp:103
virtual void Pan_T1(const SFVEC3F &aDeltaOffsetInc)=0
virtual void Interpolate(float t)
It will update the matrix to interpolate between T0 and T1 values.
Definition: camera.cpp:645
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:512
void SetInterpolateMode(CAMERA_INTERPOLATION aInterpolateMode)
Definition: camera.h:228
float GetZoom() const
Definition: camera.h:192
void ResetXYpos_T1()
Definition: camera.cpp:444
virtual void SetT0_and_T1_current_T()
This will set T0 and T1 with the current values.
Definition: camera.cpp:631
void MakeRayAtCurrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
Make a ray based on the latest mouse position.
Definition: camera.cpp:403
void RotateX_T1(float aAngleInRadians)
Definition: camera.cpp:613
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition: camera.h:133
const SFVEC3F & GetCameraPos() const
Definition: camera.h:140
void RotateZ_T1(float aAngleInRadians)
Definition: camera.cpp:625
bool ParametersChanged()
Definition: camera.cpp:663
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:49
RENDER_3D_OPENGL * m_3d_render_opengl
TOOL_DISPATCHER * m_eventDispatcher
void OnEvent(wxEvent &aEvent)
Used to forward events to the canvas from popups, etc.
unsigned m_strtime_camera_movement
RENDER_3D_RAYTRACE * m_3d_render_raytracing
void restart_editingTimeOut_Timer()
Reset the editing timer.
BOARD_ITEM * m_currentRollOverItem
RENDER_3D_BASE * m_3d_render
bool m_is_opengl_initialized
void OnResize(wxSizeEvent &event)
void OnMouseWheel(wxMouseEvent &event)
WX_INFOBAR * m_parentInfoBar
wxTimer m_editing_timeout_timer
void OnLeftDown(wxMouseEvent &event)
wxGLContext * m_glRC
bool initializeOpenGL()
ACCELERATOR_3D * m_accelerator3DShapes
void OnTimerTimeout_Redraw(wxTimerEvent &event)
wxStatusBar * m_parentStatusBar
void DoRePaint()
The actual function to repaint the canvas.
int m_moving_speed_multiplier
bool SetView3D(int aKeycode)
Helper function to call view commands.
bool m_is_opengl_version_supported
wxTimer m_redraw_trigger_timer
BOARD_ADAPTER & m_boardAdapter
void DisplayStatus()
Update the status bar with the position information.
void OnPaint(wxPaintEvent &aEvent)
Called by a wxPaintEvent event.
void RenderRaytracingRequest()
Request to render the current view in Raytracing mode.
void SetEventDispatcher(TOOL_DISPATCHER *aEventDispatcher)
Set a dispatcher that processes events and forwards them to tools.
bool m_render_raytracing_was_requested
float m_camera_moving_speed
bool m_animation_enabled
void OnLeftUp(wxMouseEvent &event)
void ReloadRequest(BOARD *aBoard=nullptr, S3D_CACHE *aCachePointer=nullptr)
RAY getRayAtCurrentMousePosition()
void OnCloseWindow(wxCloseEvent &event)
void OnMiddleDown(wxMouseEvent &event)
void GetScreenshot(wxImage &aDstImage)
Request a screenshot and output it to the aDstImage.
void OnMiddleUp(wxMouseEvent &event)
void render_pivot(float t, float aScale)
Render the pivot cursor.
void request_start_moving_camera(float aMovingSpeed=2.0f, bool aRenderPivot=true)
Start a camera movement.
void RenderEngineChanged()
Notify that the render engine was changed.
std::atomic_flag m_is_currently_painting
void OnRefreshRequest(wxEvent &aEvent)
void OnEraseBackground(wxEraseEvent &event)
void releaseOpenGL()
Free created targets and openGL context.
void Request_refresh(bool aRedrawImmediately=true)
Schedule a refresh update of the canvas.
bool m_opengl_supports_raytracing
void OnMouseMove(wxMouseEvent &event)
void move_pivot_based_on_cur_mouse_position()
This function hits a ray to the board and start a movement.
void OnTimerTimeout_Editing(wxTimerEvent &event)
void stop_editingTimeOut_Timer()
Stop the editing time so it will not timeout.
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
const wxString & GetReference() const
Definition: footprint.h:519
void UnlockCtx(wxGLContext *aContext)
Allow other canvases to bind an OpenGL context.
void DestroyCtx(wxGLContext *aContext)
Destroy a managed OpenGL context.
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
static GL_CONTEXT_MANAGER & Get()
Return the GL_CONTEXT_MANAGER instance (singleton).
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:44
Provides basic 3D controls ( zoom, rotate, translate, ... )
static const float m_delta_move_step_factor
void OnMouseWheelCamera(wxMouseEvent &event, bool aPan)
void OnMouseMoveCamera(wxMouseEvent &event)
virtual wxSize GetNativePixelSize() const
A wrapper for reporting to a WX_INFOBAR UI element.
Definition: reporter.h:316
void Finalize()
Update the infobar with the reported text.
Definition: reporter.cpp:227
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:211
Definition: pad.h:59
virtual bool Redraw(bool aIsMoving, REPORTER *aStatusReporter=nullptr, REPORTER *aWarningReporter=nullptr)=0
Redraw the view.
virtual int GetWaitForEditingTimeOut()=0
Give the interface the time (in ms) that it should wait for editing or movements before (this works f...
void ReloadRequest()
virtual void SetCurWindowSize(const wxSize &aSize)=0
Before each render, the canvas will tell the render what is the size of its windows,...
bool IsReloadRequestPending() const
Query if there is a pending reload request.
Object to render the board using openGL.
void SetCurrentRollOverItem(BOARD_ITEM *aRollOverItem)
BOARD_ITEM * IntersectBoardItem(const RAY &aRay)
void Reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter, bool aOnlyLoadCopperAndShapes)
Cache for storing the 3D shapes.
Definition: 3d_cache.h:53
T * GetAppSettings(bool aLoadNow=true)
Returns a handle to the a given settings by type If the settings have already been loaded,...
A wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:287
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:188
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:197
virtual void DispatchWxEvent(wxEvent &aEvent)
Process wxEvents (mostly UI events), translate them to TOOL_EVENTs, and make tools handle those.
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:697
wxString GetZoneName() const
Definition: zone.h:124
bool IsOnCopperLayer() const override
Definition: zone.cpp:251
#define _(s)
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent)
@ ACTIVITY
@ HOVERED_ITEM
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
This file contains miscellaneous commonly used macros and functions.
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
EVT_MIDDLE_DOWN(mpWindow::OnMouseMiddleDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1586
Macros and inline functions to create menus items in menubars or popup menus.
void OglGetScreenshot(wxImage &aDstImage)
Get the pixel data of current OpenGL image.
Definition: ogl_utils.cpp:37
SETTINGS_MANAGER * GetSettingsManager()
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
see class PGM_BASE
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ RPT_SEVERITY_ERROR
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
const int scale
bool Intersect(const RAY &aRay, float *t) const
Definition: bbox_3d_ray.cpp:46
Definition: ray.h:63
void Init(const SFVEC3F &o, const SFVEC3F &d)
Definition: ray.cpp:35
SFVEC3F at(float t) const
Definition: ray.h:84
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:102
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:103
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44