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 The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <kicad_gl/kiglu.h> // Must be included first
22#include <kicad_gl/gl_utils.h>
24
25#include <wx/tokenzr.h>
26
27#include <fmt/format.h>
28
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 <build_version.h>
38#include <board.h>
39#include <footprint.h>
40#include <pad.h>
41#include <pcb_field.h>
42#include <pcb_track.h>
43#include <reporter.h>
44#include <widgets/wx_infobar.h>
45#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
46#include <bitmaps.h>
47#include <kiway_holder.h>
48#include <kiway.h>
49#include <macros.h>
50#include <pgm_base.h>
53#include <string_utils.h>
54#include <mail_type.h>
55#include <kiway_mail.h>
57#include <zone.h>
58
59
67const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
68
69
70// A custom event, used to call DoRePaint during an idle time
71wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent );
72
73
74BEGIN_EVENT_TABLE( EDA_3D_CANVAS, HIDPI_GL_3D_CANVAS )
75 EVT_PAINT( EDA_3D_CANVAS::OnPaint )
76
77 // mouse events
78 EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
79 EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
80 EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
81 EVT_MIDDLE_DOWN( EDA_3D_CANVAS::OnMiddleDown)
82 EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightDown )
83 EVT_RIGHT_UP( EDA_3D_CANVAS::OnRightUp )
84 EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
85 EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
86 EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
87
88 // touch gesture events
89 EVT_GESTURE_ZOOM( wxID_ANY, EDA_3D_CANVAS::OnZoomGesture )
90 EVT_GESTURE_PAN( wxID_ANY, EDA_3D_CANVAS::OnPanGesture )
91 EVT_GESTURE_ROTATE( wxID_ANY, EDA_3D_CANVAS::OnRotateGesture )
92
93 // other events
94 EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
95 EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
96
98 EVT_SIZE( EDA_3D_CANVAS::OnResize )
99END_EVENT_TABLE()
100
101
102EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
103 BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
104 S3D_CACHE* a3DCachePointer ) :
105 HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
106 EDA_3D_CANVAS_ID, wxDefaultPosition,
107 wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
108 m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
109 m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
110 m_boardAdapter( aBoardAdapter )
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
123
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)
142
143 m_boardAdapter.ReloadColorSettings();
144
145 wxASSERT( a3DCachePointer != nullptr );
146 m_boardAdapter.Set3dCacheManager( a3DCachePointer );
147
148#if defined( __WXMSW__ )
149 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE | wxTOUCH_PAN_GESTURES );
150#elif defined( __WXGTK__ )
151 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE );
152#endif
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 wxEVT_MAGNIFY,
165 wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
166 };
167
168 for( wxEventType eventType : events )
169 Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
170}
171
172
174{
175 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::~EDA_3D_CANVAS" ) );
176
178 m_accelerator3DShapes = nullptr;
179
181}
182
183
185{
186 if( m_glRC )
187 {
189 wxASSERT( gl_mgr );
190
191 if( gl_mgr )
192 {
193 gl_mgr->LockCtx( m_glRC, this );
194
196 m_3d_render_raytracing = nullptr;
197
198 delete m_3d_render_opengl;
199 m_3d_render_opengl = nullptr;
200
201 // This is just a copy of a pointer, can safely be set to NULL.
202 m_3d_render = nullptr;
203
204 gl_mgr->UnlockCtx( m_glRC );
205 gl_mgr->DestroyCtx( m_glRC );
206 }
207
208 m_glRC = nullptr;
209 }
210}
211
212
213void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
214{
216
217 event.Skip();
218}
219
220
221void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
222{
224}
225
226
228{
229 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) );
230
232
233 const int glVersion = gladLoaderLoadGL();
234
235 if( glVersion == 0 )
236 {
237 wxLogMessage( wxT( "Failed to load OpenGL via loader" ) );
238
239 return false;
240 }
241 else
242 {
243 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL Using OpenGL version %s" ),
244 From_UTF8( (char*) glGetString( GL_VERSION ) ) );
245 }
246
247 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
248 (const char*) glGetString( GL_VERSION ) );
249
250 wxString version = From_UTF8( (char *) glGetString( GL_VERSION ) );
251
252 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL version string %s." ),
253 __WXFUNCTION__, version );
254
255 // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
256 // getting the OpenGL major and minor version as integers didn't exist.
257 wxString tmp;
258
259 wxStringTokenizer tokenizer( version, " \t\r\n" );
260
261 if( tokenizer.HasMoreTokens() )
262 {
263 long major = 0;
264 long minor = 0;
265
266 tmp = tokenizer.GetNextToken();
267
268 tokenizer.SetString( tmp, wxString( wxT( "." ) ) );
269
270 if( tokenizer.HasMoreTokens() )
271 tokenizer.GetNextToken().ToLong( &major );
272
273 if( tokenizer.HasMoreTokens() )
274 tokenizer.GetNextToken().ToLong( &minor );
275
276 if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
277 {
278 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL ray tracing not supported." ),
279 __WXFUNCTION__ );
280
281 if( GetParent() )
282 {
283 wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
284 GetParent()->ProcessWindowEvent( evt );
285 }
286
288 }
289
290 if( ( major == 1 ) && ( minor < 5 ) )
291 {
292 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL not supported." ),
293 __WXFUNCTION__ );
294
296 }
297 }
298
299#if wxCHECK_VERSION( 3, 3, 3 )
300 wxGLCanvas::SetSwapInterval( -1 );
301#else
302 GL_UTILS::SetSwapInterval( this, -1 );
303#endif
304
306
307 return true;
308}
309
310
311void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
312{
313 OglGetScreenshot( aDstImage );
314}
315
316
317void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
318{
319 if( aCachePointer != nullptr )
320 m_boardAdapter.Set3dCacheManager( aCachePointer );
321
322 if( aBoard != nullptr )
323 m_boardAdapter.SetBoard( aBoard );
324
325 m_boardAdapter.ReloadColorSettings();
326
327 if( m_3d_render )
328 m_3d_render->ReloadRequest();
329}
330
331
343
344
346{
348 {
349 wxString msg;
350
351 msg.Printf( wxT( "dx %3.2f" ), m_camera.GetCameraPos().x );
352 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
353
354 msg.Printf( wxT( "dy %3.2f" ), m_camera.GetCameraPos().y );
355 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
356
357 msg.Printf( wxT( "zoom %3.2f" ), 1 / m_camera.GetZoom() );
358 m_parentStatusBar->SetStatusText( msg,
359 static_cast<int>( EDA_3D_VIEWER_STATUSBAR::ZOOM_LEVEL ) );
360 }
361}
362
363
364void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
365{
366 // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
367 DoRePaint();
368}
369
370
372{
373 if( m_is_currently_painting.test_and_set() )
374 return;
375
376 // SwapBuffer requires the window to be shown before calling
377 if( !IsShownOnScreen() )
378 {
379 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::DoRePaint !IsShown" ) );
381 return;
382 }
383
384 // Because the board to draw is handled by the parent viewer frame,
385 // ensure this parent is still alive. When it is closed before the viewer
386 // frame, a paint event can be generated after the parent is closed,
387 // therefore with invalid board.
388 // This is dependent of the platform.
389 // Especially on OSX, but also on Windows, it frequently happens
390 if( !GetParent()->GetParent()->IsShownOnScreen() )
391 return; // The parent board editor frame is no more alive
392
393 wxString err_messages;
394 INFOBAR_REPORTER warningReporter( m_parentInfoBar );
396 int64_t start_time = GetRunningMicroSecs();
398
399 if( !gl_mgr )
400 {
402 return;
403 }
404
405 // "Makes the OpenGL state that is represented by the OpenGL rendering
406 // context context current, i.e. it will be used by all subsequent OpenGL calls.
407 // This function may only be called when the window is shown on screen"
408
409 // Explicitly create a new rendering context instance for this canvas.
410 if( m_glRC == nullptr )
411 m_glRC = gl_mgr->CreateCtx( this );
412
413 // CreateCtx could and does fail per sentry crash events, lets be graceful
414 if( m_glRC == nullptr )
415 {
416 warningReporter.Report( _( "OpenGL context creation error" ), RPT_SEVERITY_ERROR );
417 warningReporter.Finalize();
419 return;
420 }
421
422 gl_mgr->LockCtx( m_glRC, this );
423
424 // Set the OpenGL viewport according to the client size of this canvas.
425 // This is done here rather than in a wxSizeEvent handler because our
426 // OpenGL rendering context (and thus viewport setting) is used with
427 // multiple canvases: If we updated the viewport in the wxSizeEvent
428 // handler, changing the size of one canvas causes a viewport setting that
429 // is wrong when next another canvas is repainted.
430 wxSize clientSize = GetNativePixelSize();
431
432 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
433
434 // Initialize openGL if need
436 {
437 if( !initializeOpenGL() )
438 {
439 gl_mgr->UnlockCtx( m_glRC );
441
442 return;
443 }
444
446 {
447 warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
448 "is 1.5." ), RPT_SEVERITY_ERROR );
449
450 warningReporter.Finalize();
451 }
452 }
453
455 {
456 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
457 glClear( GL_COLOR_BUFFER_BIT );
458
459 SwapBuffers();
460
461 gl_mgr->UnlockCtx( m_glRC );
463
464 return;
465 }
466
467 // Don't attempt to ray trace if OpenGL doesn't support it.
469 {
472 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
473 }
474
475 // Check if a raytracing was requested and need to switch to raytracing mode
476 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
477 {
478 const bool was_camera_changed = m_camera.ParametersChanged();
479
480 // It reverts back to OpenGL mode if it was requested a raytracing
481 // render of the current scene. AND the mouse / camera is moving
482 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
483 || windows_size_changed )
485 {
488 }
489 }
490
491 float curtime_delta_s = 0.0f;
492
494 {
495 const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
496 // Convert microseconds to seconds as float and apply speed multiplier
497 curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
499 m_camera.Interpolate( curtime_delta_s );
500
501 if( curtime_delta_s > 1.0f )
502 {
503 m_render_pivot = false;
504 m_camera_is_moving = false;
505 m_mouse_was_moved = true;
506
509 }
510 else
511 {
513 }
514 }
515
516 // It will return true if the render request a new redraw
517 bool requested_redraw = false;
518
519 if( m_3d_render )
520 {
521 try
522 {
523 m_3d_render->SetCurWindowSize( clientSize );
524
525 bool reloadRaytracingForCalculations = false;
526
527 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL
528 && m_3d_render_opengl->IsReloadRequestPending() )
529 {
530 reloadRaytracingForCalculations = true;
531 }
532
533 requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
534 &activityReporter, &warningReporter );
535
536 // Raytracer renderer is responsible for some features also used by the OpenGL
537 // renderer.
538 // FIXME: Presumably because raytracing renderer reload is called only after current
539 // renderer redraw, the old zoom value stays for a single frame. This is ugly, but only
540 // cosmetic, so I'm not fixing that for now: I don't know how to do this without
541 // reloading twice (maybe it's not too bad of an idea?) or doing a complicated
542 // refactor.
543 if( reloadRaytracingForCalculations )
544 m_3d_render_raytracing->Reload( nullptr, nullptr, true );
545 }
546 catch( std::runtime_error& )
547 {
551 gl_mgr->UnlockCtx( m_glRC );
553 return;
554 }
555 }
556
557 if( m_render_pivot )
558 {
559 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
560 render_pivot( curtime_delta_s, scale );
561 }
562
563 // This will only be enabled by the 3d mouse plugin, so we can leave
564 // it as a simple if statement
566 {
567 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
569 }
570
571 // "Swaps the double-buffer of this window, making the back-buffer the
572 // front-buffer and vice versa, so that the output of the previous OpenGL
573 // commands is displayed on the window."
574 SwapBuffers();
575
576 gl_mgr->UnlockCtx( m_glRC );
577
579 {
580 // Calculation time in milliseconds
581 const double calculation_time = (double)( GetRunningMicroSecs() - start_time ) / 1e3;
582
583 activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
584 calculation_time ) );
585 }
586
587 // This will reset the flag of camera parameters changed
588 m_camera.ParametersChanged();
589
590 warningReporter.Finalize();
591
592 if( !err_messages.IsEmpty() )
593 wxLogMessage( err_messages );
594
595 if( ( !m_camera_is_moving ) && requested_redraw )
596 {
597 m_mouse_was_moved = false;
598 Request_refresh( false );
599 }
600
601 static constexpr std::array<VIEW3D_TYPE, static_cast<int>( SPHERES_GIZMO::GizmoSphereSelection::Count )>
604
605 SPHERES_GIZMO::GizmoSphereSelection selectedGizmoSphere = m_3d_render_opengl->getSelectedGizmoSphere();
606 int index = static_cast<int>( selectedGizmoSphere );
607 if( index >= 0 && index < static_cast<int>( viewTable.size() ) )
608 {
609 SetView3D( viewTable[index] );
610 }
611
612 m_3d_render_opengl->resetSelectedGizmoSphere();
613
615}
616
617
618void EDA_3D_CANVAS::RenderToFrameBuffer( unsigned char* buffer, int width, int height )
619{
620 if( m_is_currently_painting.test_and_set() )
621 return;
622
623 // Validate input parameters
624 if( !buffer || width <= 0 || height <= 0 )
625 {
627 return;
628 }
629
630 // Because the board to draw is handled by the parent viewer frame,
631 // ensure this parent is still alive
632 if( !GetParent() || !GetParent()->GetParent() || !GetParent()->GetParent()->IsShownOnScreen() )
633 {
635 return;
636 }
637
638 wxString err_messages;
639 int64_t start_time = GetRunningMicroSecs();
641
642 if( !gl_mgr )
643 {
645 return;
646 }
647
648 // Create OpenGL context if needed
649 if( m_glRC == nullptr )
650 m_glRC = gl_mgr->CreateCtx( this );
651
652 if( m_glRC == nullptr )
653 {
654 wxLogError( _( "OpenGL context creation error" ) );
656 return;
657 }
658
659 gl_mgr->LockCtx( m_glRC, this );
660
661 // Set up framebuffer objects for off-screen rendering
662 GLuint framebuffer = 0;
663 GLuint colorTexture = 0;
664 GLuint depthStencilBuffer = 0;
665 GLint oldFramebuffer = 0;
666 GLint oldViewport[4];
667
668 // Save current state
669 glGetIntegerv( GL_FRAMEBUFFER_BINDING, &oldFramebuffer );
670 glGetIntegerv( GL_VIEWPORT, oldViewport );
671
672 // Create and bind framebuffer
673 glGenFramebuffers( 1, &framebuffer );
674 glBindFramebuffer( GL_FRAMEBUFFER, framebuffer );
675
676 // Create color texture attachment
677 glGenTextures( 1, &colorTexture );
678 glBindTexture( GL_TEXTURE_2D, colorTexture );
679 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
680 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
681 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
682 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
683 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
684 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0 );
685
686 // Create combined depth+stencil renderbuffer attachment. The stencil buffer is required
687 // because the OpenGL renderer uses stencil operations to cut holes in copper layers and
688 // the board body (see OPENGL_RENDER_LIST::DrawCulled).
689 glGenRenderbuffers( 1, &depthStencilBuffer );
690 glBindRenderbuffer( GL_RENDERBUFFER, depthStencilBuffer );
691 glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height );
692 glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
693 depthStencilBuffer );
694
695 auto resetState = std::unique_ptr<void, std::function<void(void*)>>(
696 reinterpret_cast<void*>(1),
697 [&](void*) {
698 glBindFramebuffer( GL_FRAMEBUFFER, oldFramebuffer );
699 glViewport( oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3] );
700 glDeleteFramebuffers( 1, &framebuffer );
701 glDeleteTextures( 1, &colorTexture );
702 glDeleteRenderbuffers( 1, &depthStencilBuffer );
703 gl_mgr->UnlockCtx( m_glRC );
705 }
706 );
707
708 // Check framebuffer completeness
709 GLenum framebufferStatus = glCheckFramebufferStatus( GL_FRAMEBUFFER );
710
711 if( framebufferStatus != GL_FRAMEBUFFER_COMPLETE )
712 {
713 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer Framebuffer incomplete: 0x%04X" ),
714 framebufferStatus );
715
716 return;
717 }
718
719 // Set viewport for off-screen rendering
720 glViewport( 0, 0, width, height );
721
722 // Set window size for camera and rendering
723 wxSize clientSize( width, height );
724 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
725
726 // Initialize OpenGL if needed
728 {
729 if( !initializeOpenGL() )
730 {
731 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL initialization failed." ) );
732 return;
733 }
734
736 {
737 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL version not supported." ) );
738 }
739 }
740
742 {
743 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
744 glClear( GL_COLOR_BUFFER_BIT );
745
746 // Read black screen to buffer
747 glReadPixels( 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer );
748 return;
749 }
750
751 // Handle raytracing/OpenGL renderer selection
753 {
756 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
757 }
758
759 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
760 {
761 const bool was_camera_changed = m_camera.ParametersChanged();
762
763 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed || windows_size_changed )
765 {
768 }
769 }
770
771 // Handle camera animation (simplified for off-screen rendering)
772 float curtime_delta_s = 0.0f;
774 {
775 const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
776 curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
778 m_camera.Interpolate( curtime_delta_s );
779
780 if( curtime_delta_s > 1.0f )
781 {
782 m_render_pivot = false;
783 m_camera_is_moving = false;
784 m_mouse_was_moved = true;
785 }
786 }
787
788 // Perform the actual rendering
789 bool requested_redraw = false;
790 if( m_3d_render )
791 {
792 try
793 {
794 m_3d_render->SetCurWindowSize( clientSize );
795
796 bool reloadRaytracingForCalculations = false;
797 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL
798 && m_3d_render_opengl->IsReloadRequestPending() )
799 {
800 reloadRaytracingForCalculations = true;
801 }
802
803 requested_redraw = m_3d_render->Redraw( false, nullptr, nullptr );
804
805 if( reloadRaytracingForCalculations )
806 m_3d_render_raytracing->Reload( nullptr, nullptr, true );
807 }
808 catch( std::runtime_error& )
809 {
813 return;
814 }
815 }
816
817 // Read pixels from framebuffer to the provided buffer
818 // Note: This reads RGB format. Adjust format as needed.
819 glReadPixels( 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer );
820
821 // Check for OpenGL errors
822 GLenum error = glGetError();
823 if( error != GL_NO_ERROR )
824 {
825 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL error: 0x%04X" ), error );
826 err_messages += wxString::Format( _( "OpenGL error during off-screen rendering: 0x%04X\n" ), error );
827 }
828
829 // Reset camera parameters changed flag
830 m_camera.ParametersChanged();
831
832 if( !err_messages.IsEmpty() )
833 wxLogMessage( err_messages );
834}
835
836
838{
839 m_eventDispatcher = aEventDispatcher;
840}
841
842
843void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
844{
845 if( !m_eventDispatcher )
846 aEvent.Skip();
847 else
848 m_eventDispatcher->DispatchWxEvent( aEvent );
849
850 Refresh();
851}
852
853
854void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
855{
856 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) );
857 // Do nothing, to avoid flashing.
858}
859
860
861void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
862{
863 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) );
864
865 OnMouseWheelCamera( event, m_boardAdapter.m_MousewheelPanning );
866
868 {
872 }
873}
874
875
876void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
877{
878 SetFocus();
879
881 return;
882
883 //m_is_moving_mouse = true;
885
886 float magnification = ( event.GetMagnification() + 1.0f );
887
888 m_camera.Zoom( magnification );
889
892}
893
894
895void EDA_3D_CANVAS::OnZoomGesture( wxZoomGestureEvent& aEvent )
896{
897 SetFocus();
898
899 if( aEvent.IsGestureStart() )
900 {
902 m_camera.SetCurMousePosition( aEvent.GetPosition() );
903 }
904
906 return;
907
909
910 m_camera.Pan( aEvent.GetPosition() );
911 m_camera.SetCurMousePosition( aEvent.GetPosition() );
912
913 m_camera.Zoom( static_cast<float>( aEvent.GetZoomFactor() / m_gestureLastZoomFactor ) );
914
915 m_gestureLastZoomFactor = aEvent.GetZoomFactor();
916
919}
920
921
922void EDA_3D_CANVAS::OnPanGesture( wxPanGestureEvent& aEvent )
923{
924 SetFocus();
925
926 if( aEvent.IsGestureStart() )
927 m_camera.SetCurMousePosition( aEvent.GetPosition() );
928
930 return;
931
932 m_camera.Pan( aEvent.GetPosition() );
933 m_camera.SetCurMousePosition( aEvent.GetPosition() );
934
937}
938
939
940void EDA_3D_CANVAS::OnRotateGesture( wxRotateGestureEvent& aEvent )
941{
942 SetFocus();
943
944 if( aEvent.IsGestureStart() )
945 {
947 m_camera.SetCurMousePosition( aEvent.GetPosition() );
948
949 // We don't want to process the first angle
950 return;
951 }
952
954 return;
955
956 m_camera.RotateScreen( static_cast<float>( m_gestureLastAngle - aEvent.GetRotationAngle() ) );
957 m_gestureLastAngle = aEvent.GetRotationAngle();
958
961}
962
963
964void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
965{
966 if( m_3d_render && m_3d_render->IsReloadRequestPending() )
967 return; // Prevents using invalid m_3d_render_raytracing data
968
970 return;
971
972 OnMouseMoveCamera( event );
973
975 {
978 // *Do not* reactivate the timer here during the mouse move command:
979 // OnMiddleUp() will do it at the end of mouse drag/move command
980 }
981
982 if( !event.Dragging() && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
983 {
986 BOARD_ITEM* rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
987
988 auto printNetInfo =
989 []( BOARD_CONNECTED_ITEM* aItem )
990 {
991 return wxString::Format( _( "Net %s\tNet class %s" ), aItem->GetNet()->GetNetname(),
992 aItem->GetNet()->GetNetClass()->GetHumanReadableName() );
993 };
994
995 if( rollOverItem )
996 {
997 wxString msg;
998
999 if( rollOverItem != m_currentRollOverItem )
1000 {
1001 m_3d_render_opengl->SetCurrentRollOverItem( rollOverItem );
1002 m_currentRollOverItem = rollOverItem;
1003
1005 }
1006
1007 switch( rollOverItem->Type() )
1008 {
1009 case PCB_PAD_T:
1010 {
1011 PAD* pad = static_cast<PAD*>( rollOverItem );
1012
1013 if( !pad->GetNumber().IsEmpty() )
1014 msg += wxString::Format( _( "Pad %s\t" ), pad->GetNumber() );
1015
1016 if( pad->IsOnCopperLayer() )
1017 msg += printNetInfo( pad );
1018
1019 break;
1020 }
1021
1022 case PCB_FOOTPRINT_T:
1023 {
1024 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( rollOverItem );
1025 msg += footprint->GetReference() + wxT( " " ) + footprint->GetValue();
1026 break;
1027 }
1028
1029 case PCB_TRACE_T:
1030 case PCB_VIA_T:
1031 case PCB_ARC_T:
1032 {
1033 PCB_TRACK* track = static_cast<PCB_TRACK*>( rollOverItem );
1034 msg += printNetInfo( track );
1035 break;
1036 }
1037
1038 case PCB_ZONE_T:
1039 {
1040 ZONE* zone = static_cast<ZONE*>( rollOverItem );
1041
1042 if( !zone->GetZoneName().IsEmpty() )
1043 {
1044 if( zone->GetIsRuleArea() )
1045 msg += wxString::Format( _( "Rule area %s\t" ), zone->GetZoneName() );
1046 else
1047 msg += wxString::Format( _( "Zone %s\t" ), zone->GetZoneName() );
1048 }
1049
1050 if( zone->IsOnCopperLayer() )
1051 msg += printNetInfo( zone );
1052
1053 break;
1054 }
1055
1056 default:
1057 break;
1058 }
1059
1060 reporter.Report( msg );
1061 }
1062 else
1063 {
1065 && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
1066 {
1067 m_3d_render_opengl->SetCurrentRollOverItem( nullptr );
1069
1070 reporter.Report( wxEmptyString );
1071 }
1072
1073 m_currentRollOverItem = nullptr;
1074 }
1075 }
1076}
1077
1078
1079void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
1080{
1081 SetFocus();
1083
1084 // Ensure m_camera.m_lastPosition (current mouse position) is up to date for
1085 // future drag events (can be not the case when left clicking after
1086 // opening a context menu)
1087 OnMouseMoveCamera( event );
1088
1089 if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
1090 {
1091 RAY mouseRay = getRayAtCurrentMousePosition();
1092
1093 BOARD_ITEM* intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
1094
1095 if( intersectedBoardItem )
1096 {
1097 FOOTPRINT* footprint = nullptr;
1098
1099 switch( intersectedBoardItem->Type() )
1100 {
1101 case PCB_FOOTPRINT_T:
1102 footprint = static_cast<FOOTPRINT*>( intersectedBoardItem );
1103 break;
1104
1105 case PCB_PAD_T:
1106 footprint = static_cast<PAD*>( intersectedBoardItem )->GetParentFootprint();
1107 break;
1108
1109 case PCB_FIELD_T:
1110 footprint = static_cast<PCB_FIELD*>( intersectedBoardItem )->GetParentFootprint();
1111 break;
1112
1113 default:
1114 break;
1115 }
1116
1117 if( footprint )
1118 {
1119 // We send a message (by ExpressMail) to the board and schematic editor, but only
1120 // if the manager of this canvas is a EDA_3D_VIEWER_FRAME, because only this
1121 // kind of frame has ExpressMail stuff
1122 EDA_3D_VIEWER_FRAME* frame = dynamic_cast<EDA_3D_VIEWER_FRAME*>( GetParent() );
1123
1124 if( frame )
1125 {
1126 std::string command = fmt::format( "$SELECT: 0,F{}",
1127 EscapeString( footprint->GetReference(), CTX_IPC ).ToStdString() );
1128
1129 frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_SELECTION, command, frame );
1130 frame->Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, command, frame );
1131 }
1132 }
1133 }
1134 }
1135}
1136
1137
1138void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
1139{
1140 if( m_camera_is_moving )
1141 return;
1142
1143 if( m_mouse_is_moving )
1144 {
1145 m_mouse_is_moving = false;
1147 }
1148
1149 wxSize logicalSize = GetClientSize();
1150 int logicalW = logicalSize.GetWidth();
1151 int logicalH = logicalSize.GetHeight();
1152
1153 int gizmo_x = 0, gizmo_y = 0, gizmo_width = 0, gizmo_height = 0;
1154 std::tie( gizmo_x, gizmo_y, gizmo_width, gizmo_height ) = m_3d_render_opengl->getGizmoViewport();
1155
1156 float scaleX = static_cast<float>( static_cast<double>( gizmo_width ) / static_cast<double>( logicalW ) );
1157 float scaleY = static_cast<float>( static_cast<double>( gizmo_height ) / static_cast<double>( logicalH ) );
1158
1159 int scaledMouseX = static_cast<int>( static_cast<float>( event.GetX() ) * scaleX );
1160 int scaledMouseY = static_cast<int>( static_cast<float>( logicalH - event.GetY() ) * scaleY );
1161
1162 m_3d_render_opengl->handleGizmoMouseInput( scaledMouseX, scaledMouseY );
1163 m_3d_render_opengl->updateGizmoSelection( m_camera.GetRotationMatrix() );
1164 Refresh();
1165}
1166
1167
1168void EDA_3D_CANVAS::OnRightDown( wxMouseEvent& event )
1169{
1170 SetFocus();
1172
1173 // Ensure m_camera.m_lastPosition is up to date for future drag events.
1174 OnMouseMoveCamera( event );
1175}
1176
1177
1178void EDA_3D_CANVAS::OnRightUp( wxMouseEvent& event )
1179{
1180 if( m_camera_is_moving )
1181 return;
1182
1183 if( m_mouse_is_moving )
1184 {
1185 m_mouse_is_moving = false;
1187 }
1188}
1189
1190
1191void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
1192{
1193 SetFocus();
1195}
1196
1197
1198void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
1199{
1200 if( m_camera_is_moving )
1201 return;
1202
1203 if( m_mouse_is_moving )
1204 {
1205 m_mouse_is_moving = false;
1207 }
1208 else
1209 {
1211 }
1212}
1213
1214
1215void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& aEvent )
1216{
1217 if( aEvent.GetId() != m_editing_timeout_timer.GetId() )
1218 {
1219 aEvent.Skip();
1220 return;
1221 }
1222
1223 m_mouse_is_moving = false;
1224 m_mouse_was_moved = false;
1225
1227}
1228
1229
1234
1235
1237{
1238 if( m_3d_render )
1239 m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
1240}
1241
1242
1243void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& aEvent )
1244{
1245 if( aEvent.GetId() != m_redraw_trigger_timer.GetId() )
1246 {
1247 aEvent.Skip();
1248 return;
1249 }
1250
1251 Request_refresh( true );
1252}
1253
1254
1255void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
1256{
1257 Refresh();
1258}
1259
1260
1261void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
1262{
1263 if( aRedrawImmediately )
1264 {
1265 // Just calling Refresh() does not work always
1266 // Using an event to call DoRepaint ensure the repaint code will be executed,
1267 // and PostEvent will take priority to other events like mouse movements, keys, etc.
1268 // and is executed during the next idle time
1269 wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
1270 wxPostEvent( this, redrawEvent );
1271 }
1272 else
1273 {
1274 // Schedule a timed redraw
1275 m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
1276 }
1277}
1278
1279
1280void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
1281{
1282 wxASSERT( aMovingSpeed > FLT_EPSILON );
1283
1284 // Fast forward the animation if the animation is disabled
1285 if( !m_animation_enabled )
1286 {
1287 m_camera.Interpolate( 1.0f );
1288 DisplayStatus();
1290 return;
1291 }
1292
1293 // Map speed multiplier option to actual multiplier value
1294 // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
1295 aMovingSpeed *= static_cast<float>( ( 1 << m_moving_speed_multiplier ) ) / 8.0f;
1296
1297 m_render_pivot = aRenderPivot;
1298 m_camera_moving_speed = aMovingSpeed;
1299
1301
1302 DisplayStatus();
1304
1305 m_camera_is_moving = true;
1306
1308}
1309
1310
1312{
1313 RAY mouseRay = getRayAtCurrentMousePosition();
1314
1315 float hit_t = 0.0f;
1316
1317 // Test it with the board bounding box
1318 if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
1319 {
1320 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1321 m_camera.SetT0_and_T1_current_T();
1322 m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
1323 m_camera.ResetXYpos_T1();
1324
1326 }
1327}
1328
1329
1331{
1332 if( m_camera_is_moving )
1333 return false;
1334
1335 const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
1336 const float arrow_moving_time_speed = 8.0f;
1337
1338 switch( aRequestedView )
1339 {
1342 return true;
1343
1345 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1346 m_camera.SetT0_and_T1_current_T();
1347 m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
1348 request_start_moving_camera( arrow_moving_time_speed, false );
1349 return true;
1350
1352 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1353 m_camera.SetT0_and_T1_current_T();
1354 m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
1355 request_start_moving_camera( arrow_moving_time_speed, false );
1356 return true;
1357
1359 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1360 m_camera.SetT0_and_T1_current_T();
1361 m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
1362 request_start_moving_camera( arrow_moving_time_speed, false );
1363 return true;
1364
1366 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1367 m_camera.SetT0_and_T1_current_T();
1368 m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
1369 request_start_moving_camera( arrow_moving_time_speed, false );
1370 return true;
1371
1373 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1374 m_camera.SetT0_and_T1_current_T();
1375 m_camera.Reset_T1();
1376 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
1377 return true;
1378
1380 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1381 m_camera.SetT0_and_T1_current_T();
1382
1383 if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1385
1386 return true;
1387
1389 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1390 m_camera.SetT0_and_T1_current_T();
1391
1392 if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1394
1395 return true;
1396
1402 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1403 m_camera.SetT0_and_T1_current_T();
1404 m_camera.ViewCommand_T1( aRequestedView );
1406 return true;
1407
1410 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1411 m_camera.SetT0_and_T1_current_T();
1412 m_camera.ViewCommand_T1( aRequestedView );
1413 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1414 return true;
1415
1416 default:
1417 return false;
1418 }
1419}
1420
1421
1423{
1425 {
1426 switch( cfg->m_Render.engine )
1427 {
1430 default: m_3d_render = nullptr; break;
1431 }
1432 }
1433
1434 if( m_3d_render )
1435 m_3d_render->ReloadRequest();
1436
1437 m_mouse_was_moved = false;
1438
1440}
1441
1442
1444{
1445 SFVEC3F rayOrigin;
1446 SFVEC3F rayDir;
1447
1448 // Generate a ray origin and direction based on current mouser position and camera
1449 m_camera.MakeRayAtCurrentMousePosition( rayOrigin, rayDir );
1450
1451 RAY mouseRay;
1452 mouseRay.Init( rayOrigin, rayDir );
1453
1454 return mouseRay;
1455}
VIEW3D_TYPE
Definition 3d_enums.h:74
@ VIEW3D_ZOOM_OUT
Definition 3d_enums.h:90
@ VIEW3D_PAN_LEFT
Definition 3d_enums.h:87
@ VIEW3D_FIT_SCREEN
Definition 3d_enums.h:94
@ VIEW3D_ZOOM_IN
Definition 3d_enums.h:89
@ VIEW3D_PIVOT_CENTER
Definition 3d_enums.h:91
@ VIEW3D_BOTTOM
Definition 3d_enums.h:77
@ VIEW3D_PAN_UP
Definition 3d_enums.h:85
@ VIEW3D_PAN_DOWN
Definition 3d_enums.h:86
@ VIEW3D_PAN_RIGHT
Definition 3d_enums.h:88
@ ID_CUSTOM_EVENT_1
@ ID_DISABLE_RAY_TRACING
int index
void SetOpenGLInfo(const char *aVendor, const char *aRenderer, const char *aVersion)
A setter for OpenGL info when it's initialized.
void SetOpenGLBackendInfo(wxString aBackend)
A setter for OpenGL backend info after the canvas is created.
Helper class to handle information needed to display 3D board.
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:81
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
A class used to derive camera objects from.
Definition camera.h:99
Implement a canvas based on a wxGLCanvas.
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.
void OnMagnify(wxMouseEvent &event)
void restart_editingTimeOut_Timer()
Reset the editing timer.
void RenderToFrameBuffer(unsigned char *aBuffer, int aWidth, int aHeight)
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
int64_t m_strtime_camera_movement
void OnLeftDown(wxMouseEvent &event)
void OnPanGesture(wxPanGestureEvent &event)
void OnRightUp(wxMouseEvent &event)
wxGLContext * m_glRC
ACCELERATOR_3D * m_accelerator3DShapes
void OnTimerTimeout_Redraw(wxTimerEvent &event)
wxStatusBar * m_parentStatusBar
void DoRePaint()
The actual function to repaint the canvas.
void OnRightDown(wxMouseEvent &event)
int m_moving_speed_multiplier
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)
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
void OnLeftUp(wxMouseEvent &event)
void ReloadRequest(BOARD *aBoard=nullptr, S3D_CACHE *aCachePointer=nullptr)
RAY getRayAtCurrentMousePosition()
double m_gestureLastZoomFactor
Used to track gesture events.
void OnCloseWindow(wxCloseEvent &event)
bool SetView3D(VIEW3D_TYPE aRequestedView)
Select a specific 3D view or operation.
void OnMiddleDown(wxMouseEvent &event)
void GetScreenshot(wxImage &aDstImage)
Request a screenshot and output it to the aDstImage.
RENDER_3D_RAYTRACE_GL * m_3d_render_raytracing
void OnZoomGesture(wxZoomGestureEvent &event)
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
~EDA_3D_CANVAS() override
void OnMouseMove(wxMouseEvent &event)
void OnRotateGesture(wxRotateGestureEvent &event)
EDA_3D_CANVAS(wxWindow *aParent, const wxGLAttributes &aGLAttribs, BOARD_ADAPTER &aSettings, CAMERA &aCamera, S3D_CACHE *a3DCachePointer)
Create a new 3D Canvas with an attribute list.
void move_pivot_based_on_cur_mouse_position()
This function hits a ray to the board and start a movement.
double m_gestureLastAngle
void OnTimerTimeout_Editing(wxTimerEvent &event)
void stop_editingTimeOut_Timer()
Stop the editing time so it will not timeout.
Create and handle a window for the 3d viewer connected to a Kiway and a pcbboard.
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
const wxString & GetValue() const
Definition footprint.h:863
const wxString & GetReference() const
Definition footprint.h:841
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.
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
static int SetSwapInterval(wxGLCanvas *aCanvas, int aVal)
Attempt to set the OpenGL swap interval.
Definition gl_utils.cpp:78
static wxString DetectGLBackend(wxGLCanvas *aCanvas)
Definition gl_utils.cpp:50
Provides basic 3D controls ( zoom, rotate, translate, ... )
static const float m_delta_move_step_factor
HIDPI_GL_3D_CANVAS(const KIGFX::VC_SETTINGS &aVcSettings, CAMERA &aCamera, wxWindow *parent, const wxGLAttributes &aGLAttribs, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxGLCanvasName, const wxPalette &palette=wxNullPalette)
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 wx_infobar.h:319
void Finalize()
Update the infobar with the reported text.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr, bool aFromOtherThread=false)
Send aPayload to aDestination from aSource.
Definition kiway.cpp:496
Definition pad.h:61
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition pgm_base.h:114
Object to render the board using openGL.
Cache for storing the 3D shapes.
Definition 3d_cache.h:53
GizmoSphereSelection
Enum to indicate which sphere (direction) is selected.
@ Count
Number of selectable spheres.
A wrapper for reporting to a specific text location in a statusbar.
Definition reporter.h:357
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:308
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:811
const wxString & GetZoneName() const
Definition zone.h:160
bool IsOnCopperLayer() const override
Definition zone.cpp:574
#define _(s)
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent)
#define EDA_3D_CANVAS_ID
Declaration of the eda_3d_viewer class.
@ ZOOM_LEVEL
@ HOVERED_ITEM
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_SCH
Definition frame_type.h:30
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.
@ MAIL_SELECTION
Definition mail_type.h:36
void OglGetScreenshot(wxImage &aDstImage)
Get the pixel data of current OpenGL image.
Definition ogl_utils.cpp:32
Define generic OpenGL functions that are common to any OpenGL target.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
@ RPT_SEVERITY_ERROR
T * GetAppSettings(const char *aFilename)
const int scale
wxString From_UTF8(const char *cstring)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_IPC
Definition ray.h:59
void Init(const SFVEC3F &o, const SFVEC3F &d)
Definition ray.cpp:31
SFVEC3F at(float t) const
Definition ray.h:80
IbisParser parser & reporter
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
glm::vec3 SFVEC3F
Definition xv3d_types.h:40