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, 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 <kicad_gl/kiglu.h> // Must be included first
26#include <kicad_gl/gl_utils.h>
28
29#include <wx/tokenzr.h>
30
31#include <fmt/format.h>
32
34#include "eda_3d_canvas.h"
35#include <eda_3d_viewer_frame.h>
38#include <3d_viewer_id.h>
39#include <advanced_config.h>
40#include <build_version.h>
42#include <board.h>
43#include <footprint.h>
44#include <pad.h>
45#include <pcb_field.h>
46#include <pcb_track.h>
47#include <reporter.h>
48#include <widgets/wx_infobar.h>
49#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
50#include <bitmaps.h>
51#include <kiway_holder.h>
52#include <kiway.h>
53#include <macros.h>
54#include <pgm_base.h>
57#include <string_utils.h>
58#include <mail_type.h>
59#include <kiway_mail.h>
61#include <zone.h>
62
63
71const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
72
73
74// A custom event, used to call DoRePaint during an idle time
75wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent );
76
77
78BEGIN_EVENT_TABLE( EDA_3D_CANVAS, HIDPI_GL_3D_CANVAS )
79 EVT_PAINT( EDA_3D_CANVAS::OnPaint )
80
81 // mouse events
82 EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
83 EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
84 EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
85 EVT_MIDDLE_DOWN( EDA_3D_CANVAS::OnMiddleDown)
86 EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightDown )
87 EVT_RIGHT_UP( EDA_3D_CANVAS::OnRightUp )
88 EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
89 EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
90 EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
91
92 // touch gesture events
93 EVT_GESTURE_ZOOM( wxID_ANY, EDA_3D_CANVAS::OnZoomGesture )
94 EVT_GESTURE_PAN( wxID_ANY, EDA_3D_CANVAS::OnPanGesture )
95 EVT_GESTURE_ROTATE( wxID_ANY, EDA_3D_CANVAS::OnRotateGesture )
96
97 // other events
98 EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
99 EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
100
102 EVT_SIZE( EDA_3D_CANVAS::OnResize )
103END_EVENT_TABLE()
104
105
106EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
107 BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
108 S3D_CACHE* a3DCachePointer ) :
109 HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
110 EDA_3D_CANVAS_ID, wxDefaultPosition,
111 wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
112 m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
113 m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
114 m_boardAdapter( aBoardAdapter )
115{
116 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) );
117
118 m_editing_timeout_timer.SetOwner( this );
119 Connect( m_editing_timeout_timer.GetId(), wxEVT_TIMER,
120 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ), nullptr, this );
121
122 m_redraw_trigger_timer.SetOwner( this );
123 Connect( m_redraw_trigger_timer.GetId(), wxEVT_TIMER,
124 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ), nullptr, this );
125
127
130
131 wxASSERT( m_3d_render_raytracing != nullptr );
132 wxASSERT( m_3d_render_opengl != nullptr );
133
134 auto busy_indicator_factory =
135 []()
136 {
137 return std::make_unique<WX_BUSY_INDICATOR>();
138 };
139
140 m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
141 m_3d_render_opengl->SetBusyIndicatorFactory( busy_indicator_factory );
142
143 // We always start with the opengl engine (raytracing is avoided due to very
144 // long calculation time)
146
147 m_boardAdapter.ReloadColorSettings();
148
149 wxASSERT( a3DCachePointer != nullptr );
150 m_boardAdapter.Set3dCacheManager( a3DCachePointer );
151
152#if defined( __WXMSW__ )
153 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE | wxTOUCH_PAN_GESTURES );
154#elif defined( __WXGTK__ )
155 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE );
156#endif
157
158 const wxEventType events[] =
159 {
160 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
161 // especially special key like arrow keys, are handled by the GAL event dispatcher,
162 // and not sent to GUI without filtering, because they have a default action (scroll)
163 // that must not be called.
164 wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
165 wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
166 wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
167 wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
168 wxEVT_MAGNIFY,
169 wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
170 };
171
172 for( wxEventType eventType : events )
173 Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
174}
175
176
178{
179 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::~EDA_3D_CANVAS" ) );
180
182 m_accelerator3DShapes = nullptr;
183
185}
186
187
189{
190 if( m_glRC )
191 {
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 m_glRC = nullptr;
207 }
208}
209
210
211void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
212{
214
215 event.Skip();
216}
217
218
219void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
220{
222}
223
224
226{
227 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) );
228
230
231 const int glVersion = gladLoaderLoadGL();
232
233 if( glVersion == 0 )
234 {
235 wxLogMessage( wxT( "Failed to load OpenGL via loader" ) );
236
237 return false;
238 }
239 else
240 {
241 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL Using OpenGL version %s" ),
242 From_UTF8( (char*) glGetString( GL_VERSION ) ) );
243 }
244
245 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
246 (const char*) glGetString( GL_VERSION ) );
247
248 wxString version = From_UTF8( (char *) glGetString( GL_VERSION ) );
249
250 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL version string %s." ),
251 __WXFUNCTION__, version );
252
253 // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
254 // getting the OpenGL major and minor version as integers didn't exist.
255 wxString tmp;
256
257 wxStringTokenizer tokenizer( version, " \t\r\n" );
258
259 if( tokenizer.HasMoreTokens() )
260 {
261 long major = 0;
262 long minor = 0;
263
264 tmp = tokenizer.GetNextToken();
265
266 tokenizer.SetString( tmp, wxString( wxT( "." ) ) );
267
268 if( tokenizer.HasMoreTokens() )
269 tokenizer.GetNextToken().ToLong( &major );
270
271 if( tokenizer.HasMoreTokens() )
272 tokenizer.GetNextToken().ToLong( &minor );
273
274 if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
275 {
276 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL ray tracing not supported." ),
277 __WXFUNCTION__ );
278
279 if( GetParent() )
280 {
281 wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
282 GetParent()->ProcessWindowEvent( evt );
283 }
284
286 }
287
288 if( ( major == 1 ) && ( minor < 5 ) )
289 {
290 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL not supported." ),
291 __WXFUNCTION__ );
292
294 }
295 }
296
297#if wxCHECK_VERSION( 3, 3, 3 )
298 wxGLCanvas::SetSwapInterval( -1 );
299#else
300 GL_UTILS::SetSwapInterval( this, -1 );
301#endif
302
304
305 return true;
306}
307
308
309void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
310{
311 OglGetScreenshot( aDstImage );
312}
313
314
315void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
316{
317 if( aCachePointer != nullptr )
318 m_boardAdapter.Set3dCacheManager( aCachePointer );
319
320 if( aBoard != nullptr )
321 m_boardAdapter.SetBoard( aBoard );
322
323 m_boardAdapter.ReloadColorSettings();
324
325 if( m_3d_render )
326 m_3d_render->ReloadRequest();
327}
328
329
341
342
344{
346 {
347 wxString msg;
348
349 msg.Printf( wxT( "dx %3.2f" ), m_camera.GetCameraPos().x );
350 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
351
352 msg.Printf( wxT( "dy %3.2f" ), m_camera.GetCameraPos().y );
353 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
354
355 msg.Printf( wxT( "zoom %3.2f" ), 1 / m_camera.GetZoom() );
356 m_parentStatusBar->SetStatusText( msg,
357 static_cast<int>( EDA_3D_VIEWER_STATUSBAR::ZOOM_LEVEL ) );
358 }
359}
360
361
362void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
363{
364 // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
365 DoRePaint();
366}
367
368
370{
371 if( m_is_currently_painting.test_and_set() )
372 return;
373
374 // SwapBuffer requires the window to be shown before calling
375 if( !IsShownOnScreen() )
376 {
377 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::DoRePaint !IsShown" ) );
379 return;
380 }
381
382 // Because the board to draw is handled by the parent viewer frame,
383 // ensure this parent is still alive. When it is closed before the viewer
384 // frame, a paint event can be generated after the parent is closed,
385 // therefore with invalid board.
386 // This is dependent of the platform.
387 // Especially on OSX, but also on Windows, it frequently happens
388 if( !GetParent()->GetParent()->IsShownOnScreen() )
389 return; // The parent board editor frame is no more alive
390
391 wxString err_messages;
392 INFOBAR_REPORTER warningReporter( m_parentInfoBar );
394 int64_t start_time = GetRunningMicroSecs();
396
397 // "Makes the OpenGL state that is represented by the OpenGL rendering
398 // context context current, i.e. it will be used by all subsequent OpenGL calls.
399 // This function may only be called when the window is shown on screen"
400
401 // Explicitly create a new rendering context instance for this canvas.
402 if( m_glRC == nullptr )
403 m_glRC = gl_mgr->CreateCtx( this );
404
405 // CreateCtx could and does fail per sentry crash events, lets be graceful
406 if( m_glRC == nullptr )
407 {
408 warningReporter.Report( _( "OpenGL context creation error" ), RPT_SEVERITY_ERROR );
409 warningReporter.Finalize();
411 return;
412 }
413
414 gl_mgr->LockCtx( m_glRC, this );
415
416 // Set the OpenGL viewport according to the client size of this canvas.
417 // This is done here rather than in a wxSizeEvent handler because our
418 // OpenGL rendering context (and thus viewport setting) is used with
419 // multiple canvases: If we updated the viewport in the wxSizeEvent
420 // handler, changing the size of one canvas causes a viewport setting that
421 // is wrong when next another canvas is repainted.
422 wxSize clientSize = GetNativePixelSize();
423
424 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
425
426 // Initialize openGL if need
428 {
429 if( !initializeOpenGL() )
430 {
431 gl_mgr->UnlockCtx( m_glRC );
433
434 return;
435 }
436
438 {
439 warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
440 "is 1.5." ), RPT_SEVERITY_ERROR );
441
442 warningReporter.Finalize();
443 }
444 }
445
447 {
448 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
449 glClear( GL_COLOR_BUFFER_BIT );
450
451 SwapBuffers();
452
453 gl_mgr->UnlockCtx( m_glRC );
455
456 return;
457 }
458
459 // Don't attempt to ray trace if OpenGL doesn't support it.
461 {
464 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
465 }
466
467 // Check if a raytracing was requested and need to switch to raytracing mode
468 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
469 {
470 const bool was_camera_changed = m_camera.ParametersChanged();
471
472 // It reverts back to OpenGL mode if it was requested a raytracing
473 // render of the current scene. AND the mouse / camera is moving
474 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
475 || windows_size_changed )
477 {
480 }
481 }
482
483 float curtime_delta_s = 0.0f;
484
486 {
487 const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
488 // Convert microseconds to seconds as float and apply speed multiplier
489 curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
491 m_camera.Interpolate( curtime_delta_s );
492
493 if( curtime_delta_s > 1.0f )
494 {
495 m_render_pivot = false;
496 m_camera_is_moving = false;
497 m_mouse_was_moved = true;
498
501 }
502 else
503 {
505 }
506 }
507
508 // It will return true if the render request a new redraw
509 bool requested_redraw = false;
510
511 if( m_3d_render )
512 {
513 try
514 {
515 m_3d_render->SetCurWindowSize( clientSize );
516
517 bool reloadRaytracingForCalculations = false;
518
519 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL
520 && m_3d_render_opengl->IsReloadRequestPending() )
521 {
522 reloadRaytracingForCalculations = true;
523 }
524
525 requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
526 &activityReporter, &warningReporter );
527
528 // Raytracer renderer is responsible for some features also used by the OpenGL
529 // renderer.
530 // FIXME: Presumably because raytracing renderer reload is called only after current
531 // renderer redraw, the old zoom value stays for a single frame. This is ugly, but only
532 // cosmetic, so I'm not fixing that for now: I don't know how to do this without
533 // reloading twice (maybe it's not too bad of an idea?) or doing a complicated
534 // refactor.
535 if( reloadRaytracingForCalculations )
536 m_3d_render_raytracing->Reload( nullptr, nullptr, true );
537 }
538 catch( std::runtime_error& )
539 {
543 gl_mgr->UnlockCtx( m_glRC );
545 return;
546 }
547 }
548
549 if( m_render_pivot )
550 {
551 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
552 render_pivot( curtime_delta_s, scale );
553 }
554
555 // This will only be enabled by the 3d mouse plugin, so we can leave
556 // it as a simple if statement
558 {
559 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
561 }
562
563 // "Swaps the double-buffer of this window, making the back-buffer the
564 // front-buffer and vice versa, so that the output of the previous OpenGL
565 // commands is displayed on the window."
566 SwapBuffers();
567
568 gl_mgr->UnlockCtx( m_glRC );
569
571 {
572 // Calculation time in milliseconds
573 const double calculation_time = (double)( GetRunningMicroSecs() - start_time ) / 1e3;
574
575 activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
576 calculation_time ) );
577 }
578
579 // This will reset the flag of camera parameters changed
580 m_camera.ParametersChanged();
581
582 warningReporter.Finalize();
583
584 if( !err_messages.IsEmpty() )
585 wxLogMessage( err_messages );
586
587 if( ( !m_camera_is_moving ) && requested_redraw )
588 {
589 m_mouse_was_moved = false;
590 Request_refresh( false );
591 }
592
593 static constexpr std::array<VIEW3D_TYPE, static_cast<int>( SPHERES_GIZMO::GizmoSphereSelection::Count )>
596
597 SPHERES_GIZMO::GizmoSphereSelection selectedGizmoSphere = m_3d_render_opengl->getSelectedGizmoSphere();
598 int index = static_cast<int>( selectedGizmoSphere );
599 if( index >= 0 && index < static_cast<int>( viewTable.size() ) )
600 {
601 SetView3D( viewTable[index] );
602 }
603
604 m_3d_render_opengl->resetSelectedGizmoSphere();
605
607}
608
609
610void EDA_3D_CANVAS::RenderToFrameBuffer( unsigned char* buffer, int width, int height )
611{
612 if( m_is_currently_painting.test_and_set() )
613 return;
614
615 // Validate input parameters
616 if( !buffer || width <= 0 || height <= 0 )
617 {
619 return;
620 }
621
622 // Because the board to draw is handled by the parent viewer frame,
623 // ensure this parent is still alive
624 if( !GetParent() || !GetParent()->GetParent() || !GetParent()->GetParent()->IsShownOnScreen() )
625 {
627 return;
628 }
629
630 wxString err_messages;
631 int64_t start_time = GetRunningMicroSecs();
633
634 // Create OpenGL context if needed
635 if( m_glRC == nullptr )
636 m_glRC = gl_mgr->CreateCtx( this );
637
638 if( m_glRC == nullptr )
639 {
640 wxLogError( _( "OpenGL context creation error" ) );
642 return;
643 }
644
645 gl_mgr->LockCtx( m_glRC, this );
646
647 // Set up framebuffer objects for off-screen rendering
648 GLuint framebuffer = 0;
649 GLuint colorTexture = 0;
650 GLuint depthStencilBuffer = 0;
651 GLint oldFramebuffer = 0;
652 GLint oldViewport[4];
653
654 // Save current state
655 glGetIntegerv( GL_FRAMEBUFFER_BINDING, &oldFramebuffer );
656 glGetIntegerv( GL_VIEWPORT, oldViewport );
657
658 // Create and bind framebuffer
659 glGenFramebuffers( 1, &framebuffer );
660 glBindFramebuffer( GL_FRAMEBUFFER, framebuffer );
661
662 // Create color texture attachment
663 glGenTextures( 1, &colorTexture );
664 glBindTexture( GL_TEXTURE_2D, colorTexture );
665 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
666 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
667 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
668 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
669 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
670 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0 );
671
672 // Create combined depth+stencil renderbuffer attachment. The stencil buffer is required
673 // because the OpenGL renderer uses stencil operations to cut holes in copper layers and
674 // the board body (see OPENGL_RENDER_LIST::DrawCulled).
675 glGenRenderbuffers( 1, &depthStencilBuffer );
676 glBindRenderbuffer( GL_RENDERBUFFER, depthStencilBuffer );
677 glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height );
678 glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
679 depthStencilBuffer );
680
681 auto resetState = std::unique_ptr<void, std::function<void(void*)>>(
682 reinterpret_cast<void*>(1),
683 [&](void*) {
684 glBindFramebuffer( GL_FRAMEBUFFER, oldFramebuffer );
685 glViewport( oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3] );
686 glDeleteFramebuffers( 1, &framebuffer );
687 glDeleteTextures( 1, &colorTexture );
688 glDeleteRenderbuffers( 1, &depthStencilBuffer );
689 gl_mgr->UnlockCtx( m_glRC );
691 }
692 );
693
694 // Check framebuffer completeness
695 GLenum framebufferStatus = glCheckFramebufferStatus( GL_FRAMEBUFFER );
696
697 if( framebufferStatus != GL_FRAMEBUFFER_COMPLETE )
698 {
699 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer Framebuffer incomplete: 0x%04X" ),
700 framebufferStatus );
701
702 return;
703 }
704
705 // Set viewport for off-screen rendering
706 glViewport( 0, 0, width, height );
707
708 // Set window size for camera and rendering
709 wxSize clientSize( width, height );
710 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
711
712 // Initialize OpenGL if needed
714 {
715 if( !initializeOpenGL() )
716 {
717 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL initialization failed." ) );
718 return;
719 }
720
722 {
723 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL version not supported." ) );
724 }
725 }
726
728 {
729 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
730 glClear( GL_COLOR_BUFFER_BIT );
731
732 // Read black screen to buffer
733 glReadPixels( 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer );
734 return;
735 }
736
737 // Handle raytracing/OpenGL renderer selection
739 {
742 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
743 }
744
745 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
746 {
747 const bool was_camera_changed = m_camera.ParametersChanged();
748
749 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed || windows_size_changed )
751 {
754 }
755 }
756
757 // Handle camera animation (simplified for off-screen rendering)
758 float curtime_delta_s = 0.0f;
760 {
761 const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
762 curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
764 m_camera.Interpolate( curtime_delta_s );
765
766 if( curtime_delta_s > 1.0f )
767 {
768 m_render_pivot = false;
769 m_camera_is_moving = false;
770 m_mouse_was_moved = true;
771 }
772 }
773
774 // Perform the actual rendering
775 bool requested_redraw = false;
776 if( m_3d_render )
777 {
778 try
779 {
780 m_3d_render->SetCurWindowSize( clientSize );
781
782 bool reloadRaytracingForCalculations = false;
783 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL
784 && m_3d_render_opengl->IsReloadRequestPending() )
785 {
786 reloadRaytracingForCalculations = true;
787 }
788
789 requested_redraw = m_3d_render->Redraw( false, nullptr, nullptr );
790
791 if( reloadRaytracingForCalculations )
792 m_3d_render_raytracing->Reload( nullptr, nullptr, true );
793 }
794 catch( std::runtime_error& )
795 {
799 return;
800 }
801 }
802
803 // Read pixels from framebuffer to the provided buffer
804 // Note: This reads RGB format. Adjust format as needed.
805 glReadPixels( 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer );
806
807 // Check for OpenGL errors
808 GLenum error = glGetError();
809 if( error != GL_NO_ERROR )
810 {
811 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL error: 0x%04X" ), error );
812 err_messages += wxString::Format( _( "OpenGL error during off-screen rendering: 0x%04X\n" ), error );
813 }
814
815 // Reset camera parameters changed flag
816 m_camera.ParametersChanged();
817
818 if( !err_messages.IsEmpty() )
819 wxLogMessage( err_messages );
820}
821
822
824{
825 m_eventDispatcher = aEventDispatcher;
826}
827
828
829void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
830{
831 if( !m_eventDispatcher )
832 aEvent.Skip();
833 else
834 m_eventDispatcher->DispatchWxEvent( aEvent );
835
836 Refresh();
837}
838
839
840void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
841{
842 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) );
843 // Do nothing, to avoid flashing.
844}
845
846
847void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
848{
849 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) );
850
851 OnMouseWheelCamera( event, m_boardAdapter.m_MousewheelPanning );
852
854 {
858 }
859}
860
861
862void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
863{
864 SetFocus();
865
867 return;
868
869 //m_is_moving_mouse = true;
871
872 float magnification = ( event.GetMagnification() + 1.0f );
873
874 m_camera.Zoom( magnification );
875
878}
879
880
881void EDA_3D_CANVAS::OnZoomGesture( wxZoomGestureEvent& aEvent )
882{
883 SetFocus();
884
885 if( aEvent.IsGestureStart() )
886 {
888 m_camera.SetCurMousePosition( aEvent.GetPosition() );
889 }
890
892 return;
893
895
896 m_camera.Pan( aEvent.GetPosition() );
897 m_camera.SetCurMousePosition( aEvent.GetPosition() );
898
899 m_camera.Zoom( static_cast<float>( aEvent.GetZoomFactor() / m_gestureLastZoomFactor ) );
900
901 m_gestureLastZoomFactor = aEvent.GetZoomFactor();
902
905}
906
907
908void EDA_3D_CANVAS::OnPanGesture( wxPanGestureEvent& aEvent )
909{
910 SetFocus();
911
912 if( aEvent.IsGestureStart() )
913 m_camera.SetCurMousePosition( aEvent.GetPosition() );
914
916 return;
917
918 m_camera.Pan( aEvent.GetPosition() );
919 m_camera.SetCurMousePosition( aEvent.GetPosition() );
920
923}
924
925
926void EDA_3D_CANVAS::OnRotateGesture( wxRotateGestureEvent& aEvent )
927{
928 SetFocus();
929
930 if( aEvent.IsGestureStart() )
931 {
933 m_camera.SetCurMousePosition( aEvent.GetPosition() );
934
935 // We don't want to process the first angle
936 return;
937 }
938
940 return;
941
942 m_camera.RotateScreen( static_cast<float>( m_gestureLastAngle - aEvent.GetRotationAngle() ) );
943 m_gestureLastAngle = aEvent.GetRotationAngle();
944
947}
948
949
950void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
951{
952 if( m_3d_render && m_3d_render->IsReloadRequestPending() )
953 return; // Prevents using invalid m_3d_render_raytracing data
954
956 return;
957
958 OnMouseMoveCamera( event );
959
961 {
964 // *Do not* reactivate the timer here during the mouse move command:
965 // OnMiddleUp() will do it at the end of mouse drag/move command
966 }
967
968 if( !event.Dragging() && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
969 {
972 BOARD_ITEM* rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
973
974 auto printNetInfo =
975 []( BOARD_CONNECTED_ITEM* aItem )
976 {
977 return wxString::Format( _( "Net %s\tNet class %s" ), aItem->GetNet()->GetNetname(),
978 aItem->GetNet()->GetNetClass()->GetHumanReadableName() );
979 };
980
981 if( rollOverItem )
982 {
983 wxString msg;
984
985 if( rollOverItem != m_currentRollOverItem )
986 {
987 m_3d_render_opengl->SetCurrentRollOverItem( rollOverItem );
988 m_currentRollOverItem = rollOverItem;
989
991 }
992
993 switch( rollOverItem->Type() )
994 {
995 case PCB_PAD_T:
996 {
997 PAD* pad = static_cast<PAD*>( rollOverItem );
998
999 if( !pad->GetNumber().IsEmpty() )
1000 msg += wxString::Format( _( "Pad %s\t" ), pad->GetNumber() );
1001
1002 if( pad->IsOnCopperLayer() )
1003 msg += printNetInfo( pad );
1004
1005 break;
1006 }
1007
1008 case PCB_FOOTPRINT_T:
1009 {
1010 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( rollOverItem );
1011 msg += footprint->GetReference() + wxT( " " ) + footprint->GetValue();
1012 break;
1013 }
1014
1015 case PCB_TRACE_T:
1016 case PCB_VIA_T:
1017 case PCB_ARC_T:
1018 {
1019 PCB_TRACK* track = static_cast<PCB_TRACK*>( rollOverItem );
1020 msg += printNetInfo( track );
1021 break;
1022 }
1023
1024 case PCB_ZONE_T:
1025 {
1026 ZONE* zone = static_cast<ZONE*>( rollOverItem );
1027
1028 if( !zone->GetZoneName().IsEmpty() )
1029 {
1030 if( zone->GetIsRuleArea() )
1031 msg += wxString::Format( _( "Rule area %s\t" ), zone->GetZoneName() );
1032 else
1033 msg += wxString::Format( _( "Zone %s\t" ), zone->GetZoneName() );
1034 }
1035
1036 if( zone->IsOnCopperLayer() )
1037 msg += printNetInfo( zone );
1038
1039 break;
1040 }
1041
1042 default:
1043 break;
1044 }
1045
1046 reporter.Report( msg );
1047 }
1048 else
1049 {
1051 && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
1052 {
1053 m_3d_render_opengl->SetCurrentRollOverItem( nullptr );
1055
1056 reporter.Report( wxEmptyString );
1057 }
1058
1059 m_currentRollOverItem = nullptr;
1060 }
1061 }
1062}
1063
1064
1065void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
1066{
1067 SetFocus();
1069
1070 // Ensure m_camera.m_lastPosition (current mouse position) is up to date for
1071 // future drag events (can be not the case when left clicking after
1072 // opening a context menu)
1073 OnMouseMoveCamera( event );
1074
1075 if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
1076 {
1077 RAY mouseRay = getRayAtCurrentMousePosition();
1078
1079 BOARD_ITEM* intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
1080
1081 if( intersectedBoardItem )
1082 {
1083 FOOTPRINT* footprint = nullptr;
1084
1085 switch( intersectedBoardItem->Type() )
1086 {
1087 case PCB_FOOTPRINT_T:
1088 footprint = static_cast<FOOTPRINT*>( intersectedBoardItem );
1089 break;
1090
1091 case PCB_PAD_T:
1092 footprint = static_cast<PAD*>( intersectedBoardItem )->GetParentFootprint();
1093 break;
1094
1095 case PCB_FIELD_T:
1096 footprint = static_cast<PCB_FIELD*>( intersectedBoardItem )->GetParentFootprint();
1097 break;
1098
1099 default:
1100 break;
1101 }
1102
1103 if( footprint )
1104 {
1105 // We send a message (by ExpressMail) to the board and schematic editor, but only
1106 // if the manager of this canvas is a EDA_3D_VIEWER_FRAME, because only this
1107 // kind of frame has ExpressMail stuff
1108 EDA_3D_VIEWER_FRAME* frame = dynamic_cast<EDA_3D_VIEWER_FRAME*>( GetParent() );
1109
1110 if( frame )
1111 {
1112 std::string command = fmt::format( "$SELECT: 0,F{}",
1113 EscapeString( footprint->GetReference(), CTX_IPC ).ToStdString() );
1114
1115 frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_SELECTION, command, frame );
1116 frame->Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, command, frame );
1117 }
1118 }
1119 }
1120 }
1121}
1122
1123
1124void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
1125{
1126 if( m_camera_is_moving )
1127 return;
1128
1129 if( m_mouse_is_moving )
1130 {
1131 m_mouse_is_moving = false;
1133 }
1134
1135 wxSize logicalSize = GetClientSize();
1136 int logicalW = logicalSize.GetWidth();
1137 int logicalH = logicalSize.GetHeight();
1138
1139 int gizmo_x = 0, gizmo_y = 0, gizmo_width = 0, gizmo_height = 0;
1140 std::tie( gizmo_x, gizmo_y, gizmo_width, gizmo_height ) = m_3d_render_opengl->getGizmoViewport();
1141
1142 float scaleX = static_cast<float>( static_cast<double>( gizmo_width ) / static_cast<double>( logicalW ) );
1143 float scaleY = static_cast<float>( static_cast<double>( gizmo_height ) / static_cast<double>( logicalH ) );
1144
1145 int scaledMouseX = static_cast<int>( static_cast<float>( event.GetX() ) * scaleX );
1146 int scaledMouseY = static_cast<int>( static_cast<float>( logicalH - event.GetY() ) * scaleY );
1147
1148 m_3d_render_opengl->handleGizmoMouseInput( scaledMouseX, scaledMouseY );
1149 Refresh();
1150}
1151
1152
1153void EDA_3D_CANVAS::OnRightDown( wxMouseEvent& event )
1154{
1155 SetFocus();
1157
1158 // Ensure m_camera.m_lastPosition is up to date for future drag events.
1159 OnMouseMoveCamera( event );
1160}
1161
1162
1163void EDA_3D_CANVAS::OnRightUp( wxMouseEvent& event )
1164{
1165 if( m_camera_is_moving )
1166 return;
1167
1168 if( m_mouse_is_moving )
1169 {
1170 m_mouse_is_moving = false;
1172 }
1173}
1174
1175
1176void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
1177{
1178 SetFocus();
1180}
1181
1182
1183void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
1184{
1185 if( m_camera_is_moving )
1186 return;
1187
1188 if( m_mouse_is_moving )
1189 {
1190 m_mouse_is_moving = false;
1192 }
1193 else
1194 {
1196 }
1197}
1198
1199
1200void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& aEvent )
1201{
1202 if( aEvent.GetId() != m_editing_timeout_timer.GetId() )
1203 {
1204 aEvent.Skip();
1205 return;
1206 }
1207
1208 m_mouse_is_moving = false;
1209 m_mouse_was_moved = false;
1210
1212}
1213
1214
1219
1220
1222{
1223 if( m_3d_render )
1224 m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
1225}
1226
1227
1228void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& aEvent )
1229{
1230 if( aEvent.GetId() != m_redraw_trigger_timer.GetId() )
1231 {
1232 aEvent.Skip();
1233 return;
1234 }
1235
1236 Request_refresh( true );
1237}
1238
1239
1240void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
1241{
1242 Refresh();
1243}
1244
1245
1246void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
1247{
1248 if( aRedrawImmediately )
1249 {
1250 // Just calling Refresh() does not work always
1251 // Using an event to call DoRepaint ensure the repaint code will be executed,
1252 // and PostEvent will take priority to other events like mouse movements, keys, etc.
1253 // and is executed during the next idle time
1254 wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
1255 wxPostEvent( this, redrawEvent );
1256 }
1257 else
1258 {
1259 // Schedule a timed redraw
1260 m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
1261 }
1262}
1263
1264
1265void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
1266{
1267 wxASSERT( aMovingSpeed > FLT_EPSILON );
1268
1269 // Fast forward the animation if the animation is disabled
1270 if( !m_animation_enabled )
1271 {
1272 m_camera.Interpolate( 1.0f );
1273 DisplayStatus();
1275 return;
1276 }
1277
1278 // Map speed multiplier option to actual multiplier value
1279 // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
1280 aMovingSpeed *= static_cast<float>( ( 1 << m_moving_speed_multiplier ) ) / 8.0f;
1281
1282 m_render_pivot = aRenderPivot;
1283 m_camera_moving_speed = aMovingSpeed;
1284
1286
1287 DisplayStatus();
1289
1290 m_camera_is_moving = true;
1291
1293}
1294
1295
1297{
1298 RAY mouseRay = getRayAtCurrentMousePosition();
1299
1300 float hit_t = 0.0f;
1301
1302 // Test it with the board bounding box
1303 if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
1304 {
1305 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1306 m_camera.SetT0_and_T1_current_T();
1307 m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
1308 m_camera.ResetXYpos_T1();
1309
1311 }
1312}
1313
1314
1316{
1317 if( m_camera_is_moving )
1318 return false;
1319
1320 const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
1321 const float arrow_moving_time_speed = 8.0f;
1322
1323 switch( aRequestedView )
1324 {
1327 return true;
1328
1330 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1331 m_camera.SetT0_and_T1_current_T();
1332 m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
1333 request_start_moving_camera( arrow_moving_time_speed, false );
1334 return true;
1335
1337 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1338 m_camera.SetT0_and_T1_current_T();
1339 m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
1340 request_start_moving_camera( arrow_moving_time_speed, false );
1341 return true;
1342
1344 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1345 m_camera.SetT0_and_T1_current_T();
1346 m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
1347 request_start_moving_camera( arrow_moving_time_speed, false );
1348 return true;
1349
1351 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1352 m_camera.SetT0_and_T1_current_T();
1353 m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
1354 request_start_moving_camera( arrow_moving_time_speed, false );
1355 return true;
1356
1358 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1359 m_camera.SetT0_and_T1_current_T();
1360 m_camera.Reset_T1();
1361 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
1362 return true;
1363
1365 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1366 m_camera.SetT0_and_T1_current_T();
1367
1368 if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1370
1371 return true;
1372
1374 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1375 m_camera.SetT0_and_T1_current_T();
1376
1377 if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1379
1380 return true;
1381
1387 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1388 m_camera.SetT0_and_T1_current_T();
1389 m_camera.ViewCommand_T1( aRequestedView );
1391 return true;
1392
1395 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1396 m_camera.SetT0_and_T1_current_T();
1397 m_camera.ViewCommand_T1( aRequestedView );
1398 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1399 return true;
1400
1401 default:
1402 return false;
1403 }
1404}
1405
1406
1408{
1410 {
1411 switch( cfg->m_Render.engine )
1412 {
1415 default: m_3d_render = nullptr; break;
1416 }
1417 }
1418
1419 if( m_3d_render )
1420 m_3d_render->ReloadRequest();
1421
1422 m_mouse_was_moved = false;
1423
1425}
1426
1427
1429{
1430 SFVEC3F rayOrigin;
1431 SFVEC3F rayDir;
1432
1433 // Generate a ray origin and direction based on current mouser position and camera
1434 m_camera.MakeRayAtCurrentMousePosition( rayOrigin, rayDir );
1435
1436 RAY mouseRay;
1437 mouseRay.Init( rayOrigin, rayDir );
1438
1439 return mouseRay;
1440}
VIEW3D_TYPE
Definition 3d_enums.h:78
@ VIEW3D_ZOOM_OUT
Definition 3d_enums.h:94
@ VIEW3D_PAN_LEFT
Definition 3d_enums.h:91
@ VIEW3D_FIT_SCREEN
Definition 3d_enums.h:98
@ VIEW3D_ZOOM_IN
Definition 3d_enums.h:93
@ VIEW3D_PIVOT_CENTER
Definition 3d_enums.h:95
@ VIEW3D_BOTTOM
Definition 3d_enums.h:81
@ VIEW3D_PAN_UP
Definition 3d_enums.h:89
@ VIEW3D_PAN_DOWN
Definition 3d_enums.h:90
@ VIEW3D_PAN_RIGHT
Definition 3d_enums.h:92
@ 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:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
A class used to derive camera objects from.
Definition camera.h:103
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:111
const wxString & GetValue() const
Definition footprint.h:773
const wxString & GetReference() const
Definition footprint.h:751
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:82
static wxString DetectGLBackend(wxGLCanvas *aCanvas)
Definition gl_utils.cpp:54
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:505
Definition pad.h:55
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition pgm_base.h:121
Object to render the board using openGL.
Cache for storing the 3D shapes.
Definition 3d_cache.h:55
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:359
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:283
Handle a list of polygons defining a copper zone.
Definition zone.h:73
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:719
const wxString & GetZoneName() const
Definition zone.h:163
bool IsOnCopperLayer() const override
Definition zone.cpp:543
#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:42
@ FRAME_SCH
Definition frame_type.h:34
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:40
void OglGetScreenshot(wxImage &aDstImage)
Get the pixel data of current OpenGL image.
Definition ogl_utils.cpp:36
Define generic OpenGL functions that are common to any OpenGL target.
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
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: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:97
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:108
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:90
@ 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:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
glm::vec3 SFVEC3F
Definition xv3d_types.h:44