KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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 <advanced_config.h>
36#include <board.h>
37#include <reporter.h>
38#include <gl_context_mgr.h>
39#include <profile.h> // To use GetRunningMicroSecs or another profiling utility
40#include <bitmaps.h>
41#include <macros.h>
42#include <menus_helpers.h>
43#include <pgm_base.h>
46
48
49
57const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
58
59
60// A custom event, used to call DoRePaint during an idle time
61wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent );
62
63
64BEGIN_EVENT_TABLE( EDA_3D_CANVAS, HIDPI_GL_3D_CANVAS )
65 EVT_PAINT( EDA_3D_CANVAS::OnPaint )
66
67 // mouse events
68 EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
69 EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
70 EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
71 EVT_MIDDLE_DOWN( EDA_3D_CANVAS::OnMiddleDown)
72 EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
73 EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
74
75#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
76 EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
77#endif
78
79 // other events
80 EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
81 EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
82
84 EVT_SIZE( EDA_3D_CANVAS::OnResize )
85END_EVENT_TABLE()
86
87
88EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList,
89 BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
90 S3D_CACHE* a3DCachePointer ) :
91 HIDPI_GL_3D_CANVAS( aCamera, aParent, wxID_ANY, aAttribList, wxDefaultPosition,
92 wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
93 m_eventDispatcher( nullptr ),
94 m_parentStatusBar( nullptr ),
95 m_parentInfoBar( nullptr ),
96 m_glRC( nullptr ),
97 m_is_opengl_initialized( false ),
98 m_is_opengl_version_supported( true ),
99 m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
100 m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
101 m_render_pivot( false ),
102 m_camera_moving_speed( 1.0f ),
103 m_strtime_camera_movement( 0 ),
104 m_animation_enabled( true ),
105 m_moving_speed_multiplier( 3 ),
106 m_boardAdapter( aBoardAdapter ),
107 m_3d_render( nullptr ),
108 m_opengl_supports_raytracing( true ),
109 m_render_raytracing_was_requested( false ),
110 m_accelerator3DShapes( nullptr ),
111 m_currentRollOverItem( nullptr )
112{
113 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) );
114
115 m_editing_timeout_timer.SetOwner( this );
116 Connect( m_editing_timeout_timer.GetId(), wxEVT_TIMER,
117 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ), nullptr, this );
118
119 m_redraw_trigger_timer.SetOwner( this );
120 Connect( m_redraw_trigger_timer.GetId(), wxEVT_TIMER,
121 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ), nullptr, this );
122
123 m_is_currently_painting.clear();
124
125 m_3d_render_raytracing = new RENDER_3D_RAYTRACE( this, m_boardAdapter, m_camera );
126 m_3d_render_opengl = new RENDER_3D_OPENGL( this, m_boardAdapter, m_camera );
127
128 wxASSERT( m_3d_render_raytracing != nullptr );
129 wxASSERT( m_3d_render_opengl != nullptr );
130
131 auto busy_indicator_factory =
132 []()
133 {
134 return std::make_unique<WX_BUSY_INDICATOR>();
135 };
136
137 m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
138 m_3d_render_opengl->SetBusyIndicatorFactory( busy_indicator_factory );
139
140 // We always start with the opengl engine (raytracing is avoided due to very
141 // long calculation time)
142 m_3d_render = m_3d_render_opengl;
143
144 m_boardAdapter.ReloadColorSettings();
145
146 wxASSERT( a3DCachePointer != nullptr );
147 m_boardAdapter.Set3dCacheManager( a3DCachePointer );
148
149 const wxEventType events[] =
150 {
151 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
152 // especially special key like arrow keys, are handled by the GAL event dispatcher,
153 // and not sent to GUI without filtering, because they have a default action (scroll)
154 // that must not be called.
155 wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
156 wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
157 wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
158 wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
159#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
160 wxEVT_MAGNIFY,
161#endif
162 wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
163 };
164
165 for( wxEventType eventType : events )
166 Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
167}
168
169
171{
172 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::~EDA_3D_CANVAS" ) );
173
175 m_accelerator3DShapes = nullptr;
176
178}
179
180
182{
183 if( m_glRC )
184 {
186
188 m_3d_render_raytracing = nullptr;
189
190 delete m_3d_render_opengl;
191 m_3d_render_opengl = nullptr;
192
193 // This is just a copy of a pointer, can safely be set to NULL.
194 m_3d_render = nullptr;
195
198 m_glRC = nullptr;
199 }
200}
201
202
203void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
204{
206
207 event.Skip();
208}
209
210
211void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
212{
214}
215
216
218{
219 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) );
220
221 const GLenum err = glewInit();
222
223 if( GLEW_OK != err )
224 {
225 const wxString msgError = (const char*) glewGetErrorString( err );
226
227 wxLogMessage( msgError );
228
229 return false;
230 }
231 else
232 {
233 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s" ),
234 FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
235 }
236
237 wxString version = FROM_UTF8( (char *) glGetString( GL_VERSION ) );
238
239 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL version string %s." ),
240 __WXFUNCTION__, version );
241
242 // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
243 // getting the OpenGL major and minor version as integers didn't exist.
244 wxString tmp;
245
246 wxStringTokenizer tokenizer( version );
247
248 if( tokenizer.HasMoreTokens() )
249 {
250 long major = 0;
251 long minor = 0;
252
253 tmp = tokenizer.GetNextToken();
254
255 tokenizer.SetString( tmp, wxString( wxT( "." ) ) );
256
257 if( tokenizer.HasMoreTokens() )
258 tokenizer.GetNextToken().ToLong( &major );
259
260 if( tokenizer.HasMoreTokens() )
261 tokenizer.GetNextToken().ToLong( &minor );
262
263 if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
264 {
265 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL ray tracing not supported." ),
266 __WXFUNCTION__ );
267
268 if( GetParent() )
269 {
270 wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
271 GetParent()->ProcessWindowEvent( evt );
272 }
273
275 }
276
277 if( ( major == 1 ) && ( minor < 5 ) )
278 {
279 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL not supported." ),
280 __WXFUNCTION__ );
281
283 }
284 }
285
288
289 return true;
290}
291
292
293void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
294{
295 OglGetScreenshot( aDstImage );
296}
297
298
299void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
300{
301 if( aCachePointer != nullptr )
302 m_boardAdapter.Set3dCacheManager( aCachePointer );
303
304 if( aBoard != nullptr )
305 m_boardAdapter.SetBoard( aBoard );
306
308
309 if( m_3d_render )
311}
312
313
315{
317
318 if( m_3d_render )
320
322
324}
325
326
328{
330 {
331 wxString msg;
332
333 msg.Printf( wxT( "dx %3.2f" ), m_camera.GetCameraPos().x );
334 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
335
336 msg.Printf( wxT( "dy %3.2f" ), m_camera.GetCameraPos().y );
337 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
338 }
339}
340
341
342void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
343{
344 // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
345 DoRePaint();
346}
347
348
350{
351 if( m_is_currently_painting.test_and_set() )
352 return;
353
354 // SwapBuffer requires the window to be shown before calling
355 if( !IsShownOnScreen() )
356 {
357 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::DoRePaint !IsShown" ) );
359 return;
360 }
361
362 // Because the board to draw is handled by the parent viewer frame,
363 // ensure this parent is still alive. When it is closed before the viewer
364 // frame, a paint event can be generated after the parent is closed,
365 // therefore with invalid board.
366 // This is dependent of the platform.
367 // Especially on OSX, but also on Windows, it frequently happens
368 if( !GetParent()->GetParent()->IsShown() )
369 return; // The parent board editor frame is no more alive
370
371 wxString err_messages;
372
373 // !TODO: implement error reporter
374 INFOBAR_REPORTER warningReporter( m_parentInfoBar );
375 STATUSBAR_REPORTER activityReporter( m_parentStatusBar, EDA_3D_VIEWER_STATUSBAR::ACTIVITY );
376
377 unsigned strtime = GetRunningMicroSecs();
378
379 // "Makes the OpenGL state that is represented by the OpenGL rendering
380 // context context current, i.e. it will be used by all subsequent OpenGL calls.
381 // This function may only be called when the window is shown on screen"
382
383 // Explicitly create a new rendering context instance for this canvas.
384 if( m_glRC == nullptr )
386
387 // CreateCtx could and does fail per sentry crash events, lets be graceful
388 if( m_glRC == nullptr )
389 {
390 warningReporter.Report( _( "OpenGL context creation error" ), RPT_SEVERITY_ERROR );
391 warningReporter.Finalize();
393 return;
394 }
395
397
398 // Set the OpenGL viewport according to the client size of this canvas.
399 // This is done here rather than in a wxSizeEvent handler because our
400 // OpenGL rendering context (and thus viewport setting) is used with
401 // multiple canvases: If we updated the viewport in the wxSizeEvent
402 // handler, changing the size of one canvas causes a viewport setting that
403 // is wrong when next another canvas is repainted.
404 wxSize clientSize = GetNativePixelSize();
405
406 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
407
408 // Initialize openGL if need
410 {
411 if( !initializeOpenGL() )
412 {
415
416 return;
417 }
418
420 {
421 warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
422 "is 1.5." ), RPT_SEVERITY_ERROR );
423
424 warningReporter.Finalize();
425 }
426 }
427
429 {
430 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
431 glClear( GL_COLOR_BUFFER_BIT );
432
433 SwapBuffers();
434
437
438 return;
439 }
440
441 // Don't attend to ray trace if OpenGL doesn't support it.
443 {
446 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
447 }
448
449 // Check if a raytacing was requested and need to switch to raytracing mode
450 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
451 {
452 const bool was_camera_changed = m_camera.ParametersChanged();
453
454 // It reverts back to OpenGL mode if it was requested a raytracing
455 // render of the current scene. AND the mouse / camera is moving
456 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
457 || windows_size_changed )
459 {
462 }
463 }
464
465 float curtime_delta_s = 0.0f;
466
468 {
469 const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
470 curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
471 m_camera.Interpolate( curtime_delta_s );
472
473 if( curtime_delta_s > 1.0f )
474 {
475 m_render_pivot = false;
476 m_camera_is_moving = false;
477 m_mouse_was_moved = true;
478
481 }
482 else
483 {
485 }
486 }
487
488 // It will return true if the render request a new redraw
489 bool requested_redraw = false;
490
491 if( m_3d_render )
492 {
493 try
494 {
495 m_3d_render->SetCurWindowSize( clientSize );
496
497 bool reloadRaytracingForCalculations = false;
498
499 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL
501 {
502 reloadRaytracingForCalculations = true;
503 }
504
506 &activityReporter, &warningReporter );
507
508 // Raytracer renderer is responsible for some features also used by the OpenGL
509 // renderer.
510 // FIXME: Presumably because raytracing renderer reload is called only after current
511 // renderer redraw, the old zoom value stays for a single frame. This is ugly, but only
512 // cosmetic, so I'm not fixing that for now: I don't know how to do this without
513 // reloading twice (maybe it's not too bad of an idea?) or doing a complicated
514 // refactor.
515 if( reloadRaytracingForCalculations )
516 m_3d_render_raytracing->Reload( nullptr, nullptr, true );
517 }
518 catch( std::runtime_error& )
519 {
525 return;
526 }
527 }
528
529 if( m_render_pivot )
530 {
531 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
532 render_pivot( curtime_delta_s, scale );
533 }
534
535 if( m_render3dmousePivot && ADVANCED_CFG::GetCfg().m_Use3DConnexionDriver )
536 {
537 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
539 }
540
541 // "Swaps the double-buffer of this window, making the back-buffer the
542 // front-buffer and vice versa, so that the output of the previous OpenGL
543 // commands is displayed on the window."
544 SwapBuffers();
545
547
548 if( !activityReporter.HasMessage() )
549 {
551 {
552 // Calculation time in milliseconds
553 const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
554
555 activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
556 calculation_time ) );
557 }
558 }
559
560 // This will reset the flag of camera parameters changed
562
563 warningReporter.Finalize();
564
565 if( !err_messages.IsEmpty() )
566 wxLogMessage( err_messages );
567
568 if( ( !m_camera_is_moving ) && requested_redraw )
569 {
570 m_mouse_was_moved = false;
571 Request_refresh( false );
572 }
573
575}
576
577
579{
580 m_eventDispatcher = aEventDispatcher;
581}
582
583
584void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
585{
586 if( !m_eventDispatcher )
587 aEvent.Skip();
588 else
590
591 Refresh();
592}
593
594
595void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
596{
597 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) );
598 // Do nothing, to avoid flashing.
599}
600
601
602void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
603{
604 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) );
605
607
609 {
613 }
614}
615
616
617#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
618void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
619{
620 SetFocus();
621
623 return;
624
625 //m_is_moving_mouse = true;
627
628 float magnification = ( event.GetMagnification() + 1.0f );
629
630 m_camera.Zoom( magnification );
631
634}
635#endif
636
637
638void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
639{
641 return;
642
643 OnMouseMoveCamera( event );
644
646 {
649 // *Do not* reactivate the timer here during the mouse move command:
650 // OnMiddleUp() will do it at the end of mouse drag/move command
651 }
652
653 if( !event.Dragging() && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
654 {
655 STATUSBAR_REPORTER reporter( m_parentStatusBar, EDA_3D_VIEWER_STATUSBAR::HOVERED_ITEM );
657 BOARD_ITEM* rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
658
659 auto printNetInfo =
660 []( BOARD_CONNECTED_ITEM* aItem )
661 {
662 return wxString::Format( _( "Net %s\tNet class %s" ),
663 aItem->GetNet()->GetNetname(),
664 aItem->GetNet()->GetNetClass()->GetName() );
665 };
666
667 if( rollOverItem )
668 {
669 wxString msg;
670
671 if( rollOverItem != m_currentRollOverItem )
672 {
674 m_currentRollOverItem = rollOverItem;
675
677 }
678
679 switch( rollOverItem->Type() )
680 {
681 case PCB_PAD_T:
682 {
683 PAD* pad = static_cast<PAD*>( rollOverItem );
684
685 if( !pad->GetNumber().IsEmpty() )
686 msg += wxString::Format( _( "Pad %s\t" ), pad->GetNumber() );
687
688 if( pad->IsOnCopperLayer() )
689 msg += printNetInfo( pad );
690
691 break;
692 }
693
694 case PCB_FOOTPRINT_T:
695 {
696 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( rollOverItem );
697 msg += footprint->GetReference();
698 break;
699 }
700
701 case PCB_TRACE_T:
702 case PCB_VIA_T:
703 case PCB_ARC_T:
704 {
705 PCB_TRACK* track = static_cast<PCB_TRACK*>( rollOverItem );
706 msg += printNetInfo( track );
707 break;
708 }
709
710 case PCB_ZONE_T:
711 {
712 ZONE* zone = static_cast<ZONE*>( rollOverItem );
713
714 if( !zone->GetZoneName().IsEmpty() )
715 {
716 if( zone->GetIsRuleArea() )
717 msg += wxString::Format( _( "Rule area %s\t" ), zone->GetZoneName() );
718 else
719 msg += wxString::Format( _( "Zone %s\t" ), zone->GetZoneName() );
720 }
721
722 if( zone->IsOnCopperLayer() )
723 msg += printNetInfo( zone );
724
725 break;
726 }
727
728 default:
729 break;
730 }
731
732 reporter.Report( msg );
733 }
734 else
735 {
737 && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
738 {
741
742 reporter.Report( wxEmptyString );
743 }
744
745 m_currentRollOverItem = nullptr;
746 }
747 }
748}
749
750
751void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
752{
753 SetFocus();
755
756 if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
757 {
759
760 BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
761
762 // !TODO: send a selection item to pcbnew, eg: via kiway?
763 }
764}
765
766
767void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
768{
770 return;
771
773 {
774 m_mouse_is_moving = false;
776 }
777}
778
779
780void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
781{
782 SetFocus();
784}
785
786
787void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
788{
790 return;
791
793 {
794 m_mouse_is_moving = false;
796 }
797 else
798 {
800 }
801}
802
803
804void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& aEvent )
805{
806 if( aEvent.GetId() != m_editing_timeout_timer.GetId() )
807 {
808 aEvent.Skip();
809 return;
810 }
811
812 m_mouse_is_moving = false;
813 m_mouse_was_moved = false;
814
816}
817
818
820{
822}
823
824
826{
827 if( m_3d_render )
829}
830
831
832void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& aEvent )
833{
834 if( aEvent.GetId() != m_redraw_trigger_timer.GetId() )
835 {
836 aEvent.Skip();
837 return;
838 }
839
840 Request_refresh( true );
841}
842
843
844void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
845{
846 DoRePaint();
847}
848
849
850void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
851{
852 if( aRedrawImmediately )
853 {
854 // Just calling Refresh() does not work always
855 // Using an event to call DoRepaint ensure the repaint code will be executed,
856 // and PostEvent will take priority to other events like mouse movements, keys, etc.
857 // and is executed during the next idle time
858 wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
859 wxPostEvent( this, redrawEvent );
860 }
861 else
862 {
863 // Schedule a timed redraw
864 m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
865 }
866}
867
868
869void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
870{
871 wxASSERT( aMovingSpeed > FLT_EPSILON );
872
873 // Fast forward the animation if the animation is disabled
875 {
876 m_camera.Interpolate( 1.0f );
879 return;
880 }
881
882 // Map speed multiplier option to actual multiplier value
883 // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
884 aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
885
886 m_render_pivot = aRenderPivot;
887 m_camera_moving_speed = aMovingSpeed;
888
890
893
894 m_camera_is_moving = true;
895
897}
898
899
901{
903
904 float hit_t;
905
906 // Test it with the board bounding box
907 if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
908 {
909 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
911 m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
913
915 }
916}
917
918
919bool EDA_3D_CANVAS::SetView3D( int aKeycode )
920{
922 return false;
923
924 const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
925 const float arrow_moving_time_speed = 8.0f;
926 bool handled = false;
927
928 switch( aKeycode )
929 {
930 case WXK_SPACE:
932 return true;
933
934 case WXK_LEFT:
935 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
937 m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
938 request_start_moving_camera( arrow_moving_time_speed, false );
939 return true;
940
941 case WXK_RIGHT:
942 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
944 m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
945 request_start_moving_camera( arrow_moving_time_speed, false );
946 return true;
947
948 case WXK_UP:
949 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
951 m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
952 request_start_moving_camera( arrow_moving_time_speed, false );
953 return true;
954
955 case WXK_DOWN:
956 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
958 m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
959 request_start_moving_camera( arrow_moving_time_speed, false );
960 return true;
961
962 case WXK_HOME:
963 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
966 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
967 return true;
968
969 case WXK_END:
970 break;
971
972 case WXK_TAB:
973 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::EASING_IN_OUT );
975 m_camera.RotateZ_T1( glm::radians( 45.0f ) );
977 handled = true;
978 break;
979
980 case WXK_F1:
981 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
983
984 if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
986
987 return true;
988
989 case WXK_F2:
990 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
992
993 if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
995
996 return true;
997
998 case ID_VIEW3D_RESET:
999 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1002 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1003 return true;
1004
1005 case ID_VIEW3D_RIGHT:
1006 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1009 m_camera.RotateZ_T1( glm::radians( -90.0f ) );
1010 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1012 return true;
1013
1014 case ID_VIEW3D_LEFT:
1015 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1018 m_camera.RotateZ_T1( glm::radians( 90.0f ) );
1019 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1021 return true;
1022
1023 case ID_VIEW3D_FRONT:
1024 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1027 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1029 return true;
1030
1031 case ID_VIEW3D_BACK:
1032 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1035 m_camera.RotateX_T1( glm::radians( -90.0f ) );
1036
1037 // The rotation angle should be 180.
1038 // We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
1039 // using 180 deg if the previous rotated position was already 180 deg
1040 m_camera.RotateZ_T1( glm::radians( 179.999f ) );
1042 return true;
1043
1044 case ID_VIEW3D_TOP:
1045 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1048 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1049 return true;
1050
1051 case ID_VIEW3D_BOTTOM:
1052 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1055 m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
1056 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1057 return true;
1058
1059 case ID_VIEW3D_FLIP:
1060 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1062 m_camera.RotateY_T1( glm::radians( 179.999f ) );
1064 return true;
1065
1066 default:
1067 return false;
1068 }
1069
1070 m_mouse_was_moved = true;
1071
1073
1074 DisplayStatus();
1076
1077 return handled;
1078}
1079
1080
1082{
1083 SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
1085
1086 switch( cfg->m_Render.engine )
1087 {
1088 case RENDER_ENGINE::OPENGL: m_3d_render = m_3d_render_opengl; break;
1089 case RENDER_ENGINE::RAYTRACING: m_3d_render = m_3d_render_raytracing; break;
1090 default: m_3d_render = nullptr; break;
1091 }
1092
1093 if( m_3d_render )
1095
1096 m_mouse_was_moved = false;
1097
1099}
1100
1101
1103{
1104 SFVEC3F rayOrigin;
1105 SFVEC3F rayDir;
1106
1107 // Generate a ray origin and direction based on current mouser position and camera
1108 m_camera.MakeRayAtCurrentMousePosition( rayOrigin, rayDir );
1109
1110 RAY mouseRay;
1111 mouseRay.Init( rayOrigin, rayDir );
1112
1113 return mouseRay;
1114}
@ 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
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
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 ReloadColorSettings() noexcept
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
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:71
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:270
A class used to derive camera objects from.
Definition: camera.h:78
bool Zoom(float aFactor)
Definition: camera.cpp:538
void RotateY_T1(float aAngleInRadians)
Definition: camera.cpp:618
bool Zoom_T1(float aFactor)
Definition: camera.cpp:569
virtual void Reset_T1()
Definition: camera.cpp:102
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:644
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:511
void SetInterpolateMode(CAMERA_INTERPOLATION aInterpolateMode)
Definition: camera.h:231
float GetZoom() const
Definition: camera.h:195
void ResetXYpos_T1()
Definition: camera.cpp:443
virtual void SetT0_and_T1_current_T()
This will set T0 and T1 with the current values.
Definition: camera.cpp:630
void MakeRayAtCurrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
Make a ray based on the latest mouse position.
Definition: camera.cpp:402
void RotateX_T1(float aAngleInRadians)
Definition: camera.cpp:612
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition: camera.h:136
const SFVEC3F & GetCameraPos() const
Definition: camera.h:143
void RotateZ_T1(float aAngleInRadians)
Definition: camera.cpp:624
bool ParametersChanged()
Definition: camera.cpp:662
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)
bool m_render3dmousePivot
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 render3dmousePivot(float aScale)
Render the 3dmouse pivot cursor.
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:521
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:49
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()
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:72
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:710
wxString GetZoneName() const
Definition: zone.h:131
bool IsOnCopperLayer() const override
Definition: zone.cpp:263
#define _(s)
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent)
Declaration of the eda_3d_viewer class.
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
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
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.
@ RPT_SEVERITY_ERROR
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:115
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:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:103
@ 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:94
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:92
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44