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
29#include <api/api_utils.h>
30#include <api/common/commands/cross_probe_commands.pb.h>
31
33#include "eda_3d_canvas.h"
34#include <eda_3d_viewer_frame.h>
37#include <3d_viewer_id.h>
38#include <advanced_config.h>
39#include <build_version.h>
41#include <board.h>
42#include <footprint.h>
43#include <pad.h>
44#include <pcb_field.h>
45#include <pcb_track.h>
46#include <reporter.h>
47#include <widgets/wx_infobar.h>
48#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
49#include <bitmaps.h>
50#include <kiway_holder.h>
51#include <kiway.h>
52#include <macros.h>
53#include <pgm_base.h>
56#include <string_utils.h>
57#include <mail_type.h>
58#include <kiway_mail.h>
60#include <zone.h>
61#include <chrono>
62#include <ratio>
63
64
72const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
73
74
75// A custom event, used to call DoRePaint during an idle time
76wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxCommandEvent );
77
78
79BEGIN_EVENT_TABLE( EDA_3D_CANVAS, HIDPI_GL_3D_CANVAS )
80 EVT_PAINT( EDA_3D_CANVAS::OnPaint )
81
82 // mouse events
83 EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
84 EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
85 EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
86 EVT_MIDDLE_DOWN( EDA_3D_CANVAS::OnMiddleDown)
87 EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightDown )
88 EVT_RIGHT_UP( EDA_3D_CANVAS::OnRightUp )
89 EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
90 EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
91 EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
92
93 // touch gesture events
94 EVT_GESTURE_ZOOM( wxID_ANY, EDA_3D_CANVAS::OnZoomGesture )
95 EVT_GESTURE_PAN( wxID_ANY, EDA_3D_CANVAS::OnPanGesture )
96 EVT_GESTURE_ROTATE( wxID_ANY, EDA_3D_CANVAS::OnRotateGesture )
97
98 // other events
99 EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
100 EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
101
103 EVT_SIZE( EDA_3D_CANVAS::OnResize )
104END_EVENT_TABLE()
105
106
107EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
108 BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
109 S3D_CACHE* a3DCachePointer ) :
110 HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
111 EDA_3D_CANVAS_ID, wxDefaultPosition,
112 wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
113 m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
114 m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
115 m_boardAdapter( aBoardAdapter )
116{
117 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) );
118
119 m_editing_timeout_timer.SetOwner( this );
120 Connect( m_editing_timeout_timer.GetId(), wxEVT_TIMER,
121 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ), nullptr, this );
122
123 m_redraw_trigger_timer.SetOwner( this );
124 Connect( m_redraw_trigger_timer.GetId(), wxEVT_TIMER,
125 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ), nullptr, this );
126
128
129 m_3d_render_raytracing = std::make_unique<RENDER_3D_RAYTRACE_GL>( this, m_boardAdapter, m_camera );
130 m_3d_render_opengl = std::make_unique<RENDER_3D_OPENGL>( this, m_boardAdapter, m_camera );
131
132 auto busy_indicator_factory =
133 []()
134 {
135 return std::make_unique<WX_BUSY_INDICATOR>();
136 };
137
138 m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
139 m_3d_render_opengl->SetBusyIndicatorFactory( busy_indicator_factory );
140
141 // We always start with the opengl engine (raytracing is avoided due to very
142 // long calculation time)
144
145 m_boardAdapter.ReloadColorSettings();
146
147 wxASSERT( a3DCachePointer != nullptr );
148 m_boardAdapter.Set3dCacheManager( a3DCachePointer );
149
150#if defined( __WXMSW__ )
151 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE | wxTOUCH_PAN_GESTURES );
152#elif defined( __WXGTK__ )
153 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE );
154#endif
155
156 const wxEventType events[] =
157 {
158 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
159 // especially special key like arrow keys, are handled by the GAL event dispatcher,
160 // and not sent to GUI without filtering, because they have a default action (scroll)
161 // that must not be called.
162 wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
163 wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
164 wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
165 wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
166 wxEVT_MAGNIFY,
167 wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
168 };
169
170 for( wxEventType eventType : events )
171 Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
172}
173
174
176{
177 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::~EDA_3D_CANVAS" ) );
178
179 // Detach UI reporters before joining/destroying renderers that may still report.
181 m_activityReporterSync->SetNullReporter();
182
184 m_warningReporterSync->SetNullReporter();
185
187 m_accelerator3DShapes = nullptr;
188
190}
191
192
194{
195 // Join the bg worker before taking the GL lock or destroying renderers. The worker
196 // may still be in ReloadRaytracingForHitTesting() and must not outlive either renderer.
198 m_3d_render_opengl->StopBgWorker();
199
200 if( m_glRC )
201 {
203 wxASSERT( gl_mgr );
204
205 if( gl_mgr )
206 {
207 gl_mgr->LockCtx( m_glRC, this );
208
209 m_3d_render = nullptr;
210
211 // OpenGL dtor joins the bg worker; reset it before raytracing.
212 m_3d_render_opengl.reset();
214
215 gl_mgr->UnlockCtx( m_glRC );
216 gl_mgr->DestroyCtx( m_glRC );
217 }
218
219 m_glRC = nullptr;
220 }
221}
222
223
224void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
225{
227
228 event.Skip();
229}
230
231
232void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
233{
235}
236
237
239{
240 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) );
241
243
244 const int glVersion = gladLoaderLoadGL();
245
246 if( glVersion == 0 )
247 {
248 wxLogMessage( wxT( "Failed to load OpenGL via loader" ) );
249
250 return false;
251 }
252 else
253 {
254 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL Using OpenGL version %s" ),
255 From_UTF8( (char*) glGetString( GL_VERSION ) ) );
256 }
257
258 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
259 (const char*) glGetString( GL_VERSION ) );
260
261 wxString version = From_UTF8( (char *) glGetString( GL_VERSION ) );
262
263 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL version string %s." ),
264 __WXFUNCTION__, version );
265
266 // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
267 // getting the OpenGL major and minor version as integers didn't exist.
268 wxString tmp;
269
270 wxStringTokenizer tokenizer( version, " \t\r\n" );
271
272 if( tokenizer.HasMoreTokens() )
273 {
274 long major = 0;
275 long minor = 0;
276
277 tmp = tokenizer.GetNextToken();
278
279 tokenizer.SetString( tmp, wxString( wxT( "." ) ) );
280
281 if( tokenizer.HasMoreTokens() )
282 tokenizer.GetNextToken().ToLong( &major );
283
284 if( tokenizer.HasMoreTokens() )
285 tokenizer.GetNextToken().ToLong( &minor );
286
287 if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
288 {
289 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL ray tracing not supported." ),
290 __WXFUNCTION__ );
291
292 if( GetParent() )
293 {
294 wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
295 GetParent()->ProcessWindowEvent( evt );
296 }
297
299 }
300
301 if( ( major == 1 ) && ( minor < 5 ) )
302 {
303 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL not supported." ),
304 __WXFUNCTION__ );
305
307 }
308 }
309
310#if wxCHECK_VERSION( 3, 3, 3 )
311 wxGLCanvas::SetSwapInterval( -1 );
312#else
313 GL_UTILS::SetSwapInterval( this, -1 );
314#endif
315
317
318 return true;
319}
320
321
322void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
323{
324 OglGetScreenshot( aDstImage );
325}
326
327
329{
330 if( m_3d_render )
331 m_3d_render->JoinBgWorker();
332}
333
334
335void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
336{
338 m_3d_render_opengl->StopBgWorker();
339
340 if( aCachePointer != nullptr )
341 m_boardAdapter.Set3dCacheManager( aCachePointer );
342
343 if( aBoard != nullptr )
344 m_boardAdapter.SetBoard( aBoard );
345
346 m_boardAdapter.ReloadColorSettings();
347
348 if( m_3d_render )
349 m_3d_render->ReloadRequest();
350}
351
352
354{
356 m_3d_render_raytracing->Reload( true, aStop );
357}
358
359
365
366
368{
370
371 if( m_3d_render )
372 m_3d_render->ReloadRequest();
373
375
377}
378
379
381{
383 {
384 wxString msg;
385
386 msg.Printf( wxT( "dx %3.2f" ), m_camera.GetCameraPos().x );
387 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
388
389 msg.Printf( wxT( "dy %3.2f" ), m_camera.GetCameraPos().y );
390 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
391
392 msg.Printf( wxT( "zoom %3.2f" ), 1 / m_camera.GetZoom() );
393 m_parentStatusBar->SetStatusText( msg,
394 static_cast<int>( EDA_3D_VIEWER_STATUSBAR::ZOOM_LEVEL ) );
395 }
396}
397
398
399void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
400{
401 // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
402 DoRePaint();
403}
404
405
407{
408 if( m_is_currently_painting.test_and_set() )
409 return;
410
411 // SwapBuffer requires the window to be shown before calling
412 if( !IsShownOnScreen() )
413 {
414 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::DoRePaint !IsShown" ) );
416 return;
417 }
418
419 // Because the board to draw is handled by the parent viewer frame,
420 // ensure this parent is still alive. When it is closed before the viewer
421 // frame, a paint event can be generated after the parent is closed,
422 // therefore with invalid board.
423 // This is dependent of the platform.
424 // Especially on OSX, but also on Windows, it frequently happens
425 wxWindow* viewer = wxGetTopLevelParent( this );
426 wxWindow* owner = viewer ? viewer->GetParent() : nullptr;
427
428 if( owner && !owner->IsShownOnScreen() )
429 {
431 return; // The parent board editor frame is no more alive
432 }
433
435 {
437 std::make_unique<STATUSBAR_REPORTER>( m_parentStatusBar, EDA_3D_VIEWER_STATUSBAR::ACTIVITY );
438 m_infoBarReporter = std::make_unique<INFOBAR_REPORTER>( m_parentInfoBar );
439
440 m_activityReporterSync = std::make_shared<SYNC_REPORTER>( *m_statusBarReporter );
441 m_warningReporterSync = std::make_shared<SYNC_REPORTER>( *m_infoBarReporter );
442
445
448 }
449
450 wxString err_messages;
451 auto start_time = std::chrono::steady_clock::now();
453
454 if( !gl_mgr )
455 {
457 return;
458 }
459
460 // "Makes the OpenGL state that is represented by the OpenGL rendering
461 // context context current, i.e. it will be used by all subsequent OpenGL calls.
462 // This function may only be called when the window is shown on screen"
463
464 // Explicitly create a new rendering context instance for this canvas.
465 if( m_glRC == nullptr )
466 m_glRC = gl_mgr->CreateCtx( this );
467
468 // CreateCtx could and does fail per sentry crash events, lets be graceful
469 if( m_glRC == nullptr )
470 {
471 m_warningReporterSync->Report( _( "OpenGL context creation error" ), RPT_SEVERITY_ERROR );
472 m_warningReporterSync->Finalize();
474 return;
475 }
476
477 gl_mgr->LockCtx( m_glRC, this );
478
479 // Set the OpenGL viewport according to the client size of this canvas.
480 // This is done here rather than in a wxSizeEvent handler because our
481 // OpenGL rendering context (and thus viewport setting) is used with
482 // multiple canvases: If we updated the viewport in the wxSizeEvent
483 // handler, changing the size of one canvas causes a viewport setting that
484 // is wrong when next another canvas is repainted.
485 wxSize clientSize = GetNativePixelSize();
486
487 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
488
489 // Initialize openGL if need
491 {
492 if( !initializeOpenGL() )
493 {
494 gl_mgr->UnlockCtx( m_glRC );
496
497 return;
498 }
499
501 {
502 m_warningReporterSync->Report( _( "Your OpenGL version is not supported. Minimum required "
503 "is 1.5." ), RPT_SEVERITY_ERROR );
504
505 m_warningReporterSync->Finalize();
506 }
507 }
508
510 {
511 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
512 glClear( GL_COLOR_BUFFER_BIT );
513
514 SwapBuffers();
515
516 gl_mgr->UnlockCtx( m_glRC );
518
519 return;
520 }
521
522 // Don't attempt to ray trace if OpenGL doesn't support it.
524 {
527 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
528 }
529
530 // Check if a raytracing was requested and need to switch to raytracing mode
531 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
532 {
533 const bool was_camera_changed = m_camera.ParametersChanged();
534
535 // It reverts back to OpenGL mode if it was requested a raytracing
536 // render of the current scene. AND the mouse / camera is moving
537 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
538 || windows_size_changed )
540 {
543 }
544 }
545
546 float curtime_delta_s = 0.0f;
547
549 {
550 const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
551 // Convert microseconds to seconds as float and apply speed multiplier
552 curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
554 m_camera.Interpolate( curtime_delta_s );
555
556 if( curtime_delta_s > 1.0f )
557 {
558 m_render_pivot = false;
559 m_camera_is_moving = false;
560 m_mouse_was_moved = true;
561
564 }
565 else
566 {
568 }
569 }
570
571 // It will return true if the render request a new redraw
572 bool requested_redraw = false;
573
574 if( m_3d_render )
575 {
576 try
577 {
578 m_3d_render->SetCurWindowSize( clientSize );
579
580 requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving );
581 }
582 catch( std::runtime_error& )
583 {
587 gl_mgr->UnlockCtx( m_glRC );
589 return;
590 }
591 }
592
593 if( m_render_pivot )
594 {
595 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
596 render_pivot( curtime_delta_s, scale );
597 }
598
599 // This will only be enabled by the 3d mouse plugin, so we can leave
600 // it as a simple if statement
602 {
603 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
605 }
606
607 // "Swaps the double-buffer of this window, making the back-buffer the
608 // front-buffer and vice versa, so that the output of the previous OpenGL
609 // commands is displayed on the window."
610 SwapBuffers();
611
612 gl_mgr->UnlockCtx( m_glRC );
613
614 // Calculation time in milliseconds
615 const double calculation_time =
616 std::chrono::duration<double, std::milli>( std::chrono::steady_clock::now() - start_time ).count();
617
619 {
620 m_parentStatusBar->SetStatusText( wxString::Format( _( "Last render time %.0f ms" ), calculation_time ),
622 }
623
624 // This will reset the flag of camera parameters changed
625 m_camera.ParametersChanged();
626
628 m_warningReporterSync->Finalize();
629
630 if( !err_messages.IsEmpty() )
631 wxLogMessage( err_messages );
632
633 if( ( !m_camera_is_moving ) && requested_redraw )
634 {
635 m_mouse_was_moved = false;
636 Request_refresh( false );
637 }
638
639 static constexpr std::array<VIEW3D_TYPE, static_cast<int>( SPHERES_GIZMO::GizmoSphereSelection::Count )>
642
643 SPHERES_GIZMO::GizmoSphereSelection selectedGizmoSphere = m_3d_render_opengl->getSelectedGizmoSphere();
644 int index = static_cast<int>( selectedGizmoSphere );
645 if( index >= 0 && index < static_cast<int>( viewTable.size() ) )
646 {
647 SetView3D( viewTable[index] );
648 }
649
650 m_3d_render_opengl->resetSelectedGizmoSphere();
651
653}
654
655
656void EDA_3D_CANVAS::RenderToFrameBuffer( unsigned char* buffer, int width, int height )
657{
658 if( m_is_currently_painting.test_and_set() )
659 return;
660
661 // Validate input parameters
662 if( !buffer || width <= 0 || height <= 0 )
663 {
665 return;
666 }
667
668 // Because the board to draw is handled by the parent viewer frame,
669 // ensure this parent is still alive
670 if( !GetParent() || !GetParent()->GetParent() || !GetParent()->GetParent()->IsShownOnScreen() )
671 {
673 return;
674 }
675
676 wxString err_messages;
677 int64_t start_time = GetRunningMicroSecs();
679
680 if( !gl_mgr )
681 {
683 return;
684 }
685
686 // Create OpenGL context if needed
687 if( m_glRC == nullptr )
688 m_glRC = gl_mgr->CreateCtx( this );
689
690 if( m_glRC == nullptr )
691 {
692 wxLogError( _( "OpenGL context creation error" ) );
694 return;
695 }
696
697 gl_mgr->LockCtx( m_glRC, this );
698
699 // Set up framebuffer objects for off-screen rendering
700 GLuint framebuffer = 0;
701 GLuint colorTexture = 0;
702 GLuint depthStencilBuffer = 0;
703 GLint oldFramebuffer = 0;
704 GLint oldViewport[4];
705
706 // Save current state
707 glGetIntegerv( GL_FRAMEBUFFER_BINDING, &oldFramebuffer );
708 glGetIntegerv( GL_VIEWPORT, oldViewport );
709
710 // Create and bind framebuffer
711 glGenFramebuffers( 1, &framebuffer );
712 glBindFramebuffer( GL_FRAMEBUFFER, framebuffer );
713
714 // Create color texture attachment
715 glGenTextures( 1, &colorTexture );
716 glBindTexture( GL_TEXTURE_2D, colorTexture );
717 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
718 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
719 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
720 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
721 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
722 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0 );
723
724 // Create combined depth+stencil renderbuffer attachment. The stencil buffer is required
725 // because the OpenGL renderer uses stencil operations to cut holes in copper layers and
726 // the board body (see OPENGL_RENDER_LIST::DrawCulled).
727 glGenRenderbuffers( 1, &depthStencilBuffer );
728 glBindRenderbuffer( GL_RENDERBUFFER, depthStencilBuffer );
729 glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height );
730 glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
731 depthStencilBuffer );
732
733 auto resetState = std::unique_ptr<void, std::function<void(void*)>>(
734 reinterpret_cast<void*>(1),
735 [&](void*) {
736 glBindFramebuffer( GL_FRAMEBUFFER, oldFramebuffer );
737 glViewport( oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3] );
738 glDeleteFramebuffers( 1, &framebuffer );
739 glDeleteTextures( 1, &colorTexture );
740 glDeleteRenderbuffers( 1, &depthStencilBuffer );
741 gl_mgr->UnlockCtx( m_glRC );
743 }
744 );
745
746 // Check framebuffer completeness
747 GLenum framebufferStatus = glCheckFramebufferStatus( GL_FRAMEBUFFER );
748
749 if( framebufferStatus != GL_FRAMEBUFFER_COMPLETE )
750 {
751 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer Framebuffer incomplete: 0x%04X" ),
752 framebufferStatus );
753
754 return;
755 }
756
757 // Set viewport for off-screen rendering
758 glViewport( 0, 0, width, height );
759
760 // Set window size for camera and rendering
761 wxSize clientSize( width, height );
762 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
763
764 // Initialize OpenGL if needed
766 {
767 if( !initializeOpenGL() )
768 {
769 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL initialization failed." ) );
770 return;
771 }
772
774 {
775 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL version not supported." ) );
776 }
777 }
778
780 {
781 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
782 glClear( GL_COLOR_BUFFER_BIT );
783
784 // Read black screen to buffer
785 glReadPixels( 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer );
786 return;
787 }
788
789 // Handle raytracing/OpenGL renderer selection
791 {
794 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
795 }
796
797 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
798 {
799 const bool was_camera_changed = m_camera.ParametersChanged();
800
801 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed || windows_size_changed )
803 {
806 }
807 }
808
809 // Handle camera animation (simplified for off-screen rendering)
810 float curtime_delta_s = 0.0f;
812 {
813 const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
814 curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
816 m_camera.Interpolate( curtime_delta_s );
817
818 if( curtime_delta_s > 1.0f )
819 {
820 m_render_pivot = false;
821 m_camera_is_moving = false;
822 m_mouse_was_moved = true;
823 }
824 }
825
826 // Perform the actual rendering. The first redraw may start background loading;
827 // wait for it to finish and redraw once more before reading pixels.
828 if( m_3d_render )
829 {
830 try
831 {
832 m_3d_render->SetCurWindowSize( clientSize );
833 m_3d_render->Redraw( false );
834
835 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
836 {
837 m_3d_render->JoinBgWorker();
838 m_3d_render->Redraw( false );
839 }
840 }
841 catch( std::runtime_error& )
842 {
846 return;
847 }
848 }
849
850 glFinish();
851
852 // Read pixels from framebuffer to the provided buffer
853 glReadPixels( 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer );
854
855 // Check for OpenGL errors
856 GLenum error = glGetError();
857 if( error != GL_NO_ERROR )
858 {
859 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::RenderToFrameBuffer OpenGL error: 0x%04X" ), error );
860 err_messages += wxString::Format( _( "OpenGL error during off-screen rendering: 0x%04X\n" ), error );
861 }
862
863 // Reset camera parameters changed flag
864 m_camera.ParametersChanged();
865
866 if( !err_messages.IsEmpty() )
867 wxLogMessage( err_messages );
868}
869
870
872{
873 m_eventDispatcher = aEventDispatcher;
874}
875
876
877void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
878{
879 if( !m_eventDispatcher )
880 aEvent.Skip();
881 else
882 m_eventDispatcher->DispatchWxEvent( aEvent );
883
884 Refresh();
885}
886
887
888void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
889{
890 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) );
891 // Do nothing, to avoid flashing.
892}
893
894
895void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
896{
897 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) );
898
899 OnMouseWheelCamera( event, m_boardAdapter.m_MousewheelPanning );
900
902 {
906 }
907}
908
909
910void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
911{
912 SetFocus();
913
915 return;
916
917 //m_is_moving_mouse = true;
919
920 float magnification = ( event.GetMagnification() + 1.0f );
921
922 m_camera.Zoom( magnification );
923
926}
927
928
929void EDA_3D_CANVAS::OnZoomGesture( wxZoomGestureEvent& aEvent )
930{
931 SetFocus();
932
933 if( aEvent.IsGestureStart() )
934 {
936 m_camera.SetCurMousePosition( aEvent.GetPosition() );
937 }
938
940 return;
941
943
944 m_camera.Pan( aEvent.GetPosition() );
945 m_camera.SetCurMousePosition( aEvent.GetPosition() );
946
947 m_camera.Zoom( static_cast<float>( aEvent.GetZoomFactor() / m_gestureLastZoomFactor ) );
948
949 m_gestureLastZoomFactor = aEvent.GetZoomFactor();
950
953}
954
955
956void EDA_3D_CANVAS::OnPanGesture( wxPanGestureEvent& aEvent )
957{
958 SetFocus();
959
960 if( aEvent.IsGestureStart() )
961 m_camera.SetCurMousePosition( aEvent.GetPosition() );
962
964 return;
965
966 m_camera.Pan( aEvent.GetPosition() );
967 m_camera.SetCurMousePosition( aEvent.GetPosition() );
968
971}
972
973
974void EDA_3D_CANVAS::OnRotateGesture( wxRotateGestureEvent& aEvent )
975{
976 SetFocus();
977
978 if( aEvent.IsGestureStart() )
979 {
981 m_camera.SetCurMousePosition( aEvent.GetPosition() );
982
983 // We don't want to process the first angle
984 return;
985 }
986
988 return;
989
990 m_camera.RotateScreen( static_cast<float>( m_gestureLastAngle - aEvent.GetRotationAngle() ) );
991 m_gestureLastAngle = aEvent.GetRotationAngle();
992
995}
996
997
998void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
999{
1000 if( m_3d_render && m_3d_render->IsReloadRequestPending() )
1001 return; // Prevents using invalid m_3d_render_raytracing data
1002
1003 if( m_camera_is_moving )
1004 return;
1005
1006 OnMouseMoveCamera( event );
1007
1008 if( m_mouse_was_moved )
1009 {
1010 DisplayStatus();
1012 // *Do not* reactivate the timer here during the mouse move command:
1013 // OnMiddleUp() will do it at the end of mouse drag/move command
1014 }
1015
1016 if( !event.Dragging() && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
1017 {
1019 RAY mouseRay = getRayAtCurrentMousePosition();
1020 BOARD_ITEM* rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
1021
1022 auto printNetInfo =
1023 []( BOARD_CONNECTED_ITEM* aItem )
1024 {
1025 return wxString::Format( _( "Net %s\tNet class %s" ), aItem->GetNet()->GetNetname(),
1026 aItem->GetNet()->GetNetClass()->GetHumanReadableName() );
1027 };
1028
1029 if( rollOverItem )
1030 {
1031 wxString msg;
1032
1033 if( rollOverItem != m_currentRollOverItem )
1034 {
1035 m_3d_render_opengl->SetCurrentRollOverItem( rollOverItem );
1036 m_currentRollOverItem = rollOverItem;
1037
1039 }
1040
1041 switch( rollOverItem->Type() )
1042 {
1043 case PCB_PAD_T:
1044 {
1045 PAD* pad = static_cast<PAD*>( rollOverItem );
1046
1047 if( !pad->GetNumber().IsEmpty() )
1048 msg += wxString::Format( _( "Pad %s\t" ), pad->GetNumber() );
1049
1050 if( pad->IsOnCopperLayer() )
1051 msg += printNetInfo( pad );
1052
1053 break;
1054 }
1055
1056 case PCB_FOOTPRINT_T:
1057 {
1058 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( rollOverItem );
1059 msg += footprint->GetReference() + wxT( " " ) + footprint->GetValue();
1060 break;
1061 }
1062
1063 case PCB_TRACE_T:
1064 case PCB_VIA_T:
1065 case PCB_ARC_T:
1066 {
1067 PCB_TRACK* track = static_cast<PCB_TRACK*>( rollOverItem );
1068 msg += printNetInfo( track );
1069 break;
1070 }
1071
1072 case PCB_ZONE_T:
1073 {
1074 ZONE* zone = static_cast<ZONE*>( rollOverItem );
1075
1076 if( !zone->GetZoneName().IsEmpty() )
1077 {
1078 if( zone->GetIsRuleArea() )
1079 msg += wxString::Format( _( "Rule area %s\t" ), zone->GetZoneName() );
1080 else
1081 msg += wxString::Format( _( "Zone %s\t" ), zone->GetZoneName() );
1082 }
1083
1084 if( zone->IsOnCopperLayer() )
1085 msg += printNetInfo( zone );
1086
1087 break;
1088 }
1089
1090 default:
1091 break;
1092 }
1093
1094 reporter.Report( msg );
1095 }
1096 else
1097 {
1099 && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
1100 {
1101 m_3d_render_opengl->SetCurrentRollOverItem( nullptr );
1103
1104 reporter.Report( wxEmptyString );
1105 }
1106
1107 m_currentRollOverItem = nullptr;
1108 }
1109 }
1110}
1111
1112
1113void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
1114{
1115 SetFocus();
1117
1118 // Ensure m_camera.m_lastPosition (current mouse position) is up to date for
1119 // future drag events (can be not the case when left clicking after
1120 // opening a context menu)
1121 OnMouseMoveCamera( event );
1122
1123 // Selection/deselection is handled on button release in OnLeftUp, so a click-drag
1124 // used to rotate the view does not change the current selection
1125}
1126
1127
1128void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
1129{
1130 if( m_camera_is_moving )
1131 return;
1132
1133 bool wasRotating = m_mouse_is_moving;
1134
1135 if( m_mouse_is_moving )
1136 {
1137 m_mouse_is_moving = false;
1139 }
1140
1141 bool gizmoClicked = false;
1142
1143 if( m_boardAdapter.m_Cfg->m_Render.show_navigator
1144 && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
1145 {
1146 wxSize logicalSize = GetClientSize();
1147 int logicalW = logicalSize.GetWidth();
1148 int logicalH = logicalSize.GetHeight();
1149
1150 int gizmo_x = 0, gizmo_y = 0, gizmo_width = 0, gizmo_height = 0;
1151 std::tie( gizmo_x, gizmo_y, gizmo_width, gizmo_height ) = m_3d_render_opengl->getGizmoViewport();
1152
1153 float scaleX = static_cast<float>( static_cast<double>( gizmo_width ) / static_cast<double>( logicalW ) );
1154 float scaleY = static_cast<float>( static_cast<double>( gizmo_height ) / static_cast<double>( logicalH ) );
1155
1156 int scaledMouseX = static_cast<int>( static_cast<float>( event.GetX() ) * scaleX );
1157 int scaledMouseY = static_cast<int>( static_cast<float>( logicalH - event.GetY() ) * scaleY );
1158
1159 m_3d_render_opengl->handleGizmoMouseInput( scaledMouseX, scaledMouseY );
1160 m_3d_render_opengl->updateGizmoSelection( m_camera.GetRotationMatrix() );
1161
1162 gizmoClicked = m_3d_render_opengl->getSelectedGizmoSphere() != SPHERES_GIZMO::GizmoSphereSelection::None;
1163 }
1164
1165 // A plain click that missed the orientation gizmo: cross-probe the clicked footprint,
1166 // or clear the selection when clicking empty space. A click-drag rotation or a click
1167 // on the gizmo leaves the current selection untouched.
1168 if( !wasRotating && !gizmoClicked && m_3d_render_raytracing != nullptr )
1169 {
1170 RAY mouseRay = getRayAtCurrentMousePosition();
1171 BOARD_ITEM* intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
1172 FOOTPRINT* footprint = nullptr;
1173
1174 if( intersectedBoardItem )
1175 {
1176 switch( intersectedBoardItem->Type() )
1177 {
1178 case PCB_FOOTPRINT_T: footprint = static_cast<FOOTPRINT*>( intersectedBoardItem ); break;
1179
1180 case PCB_PAD_T: footprint = static_cast<PAD*>( intersectedBoardItem )->GetParentFootprint(); break;
1181
1182 case PCB_FIELD_T: footprint = static_cast<PCB_FIELD*>( intersectedBoardItem )->GetParentFootprint(); break;
1183
1184 default: break;
1185 }
1186 }
1187
1188 // We send a message (by ExpressMail) to the board and schematic editor, but only
1189 // if the manager of this canvas is a EDA_3D_VIEWER_FRAME, because only this
1190 // kind of frame has ExpressMail stuff
1191 if( EDA_3D_VIEWER_FRAME* frame = dynamic_cast<EDA_3D_VIEWER_FRAME*>( wxGetTopLevelParent( this ) ) )
1192 {
1193 kiapi::common::commands::SyncSelection sync;
1194
1195 if( footprint )
1196 sync.add_items()->mutable_footprint()->set_reference( footprint->GetReference().ToUTF8() );
1197
1198 sync.set_mode( kiapi::common::commands::SyncSelectionMode::SSM_ITEMS_ONLY );
1199 sync.set_context( kiapi::common::commands::SyncSelectionContext::SSC_IMPLICIT );
1200
1201 std::string payload;
1202 kiapi::common::PackKiwayApiMessage( sync, payload );
1203
1204 frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_SELECTION, payload, frame );
1205 frame->Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, payload, frame );
1206 }
1207 }
1208
1209 Refresh();
1210}
1211
1212
1213void EDA_3D_CANVAS::OnRightDown( wxMouseEvent& event )
1214{
1215 SetFocus();
1217
1218 // Ensure m_camera.m_lastPosition is up to date for future drag events.
1219 OnMouseMoveCamera( event );
1220}
1221
1222
1223void EDA_3D_CANVAS::OnRightUp( wxMouseEvent& event )
1224{
1225 if( m_camera_is_moving )
1226 return;
1227
1228 if( m_mouse_is_moving )
1229 {
1230 m_mouse_is_moving = false;
1232 }
1233}
1234
1235
1236void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
1237{
1238 SetFocus();
1240}
1241
1242
1243void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
1244{
1245 if( m_camera_is_moving )
1246 return;
1247
1248 if( m_mouse_is_moving )
1249 {
1250 m_mouse_is_moving = false;
1252 }
1253 else
1254 {
1256 }
1257}
1258
1259
1260void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& aEvent )
1261{
1262 if( aEvent.GetId() != m_editing_timeout_timer.GetId() )
1263 {
1264 aEvent.Skip();
1265 return;
1266 }
1267
1268 m_mouse_is_moving = false;
1269 m_mouse_was_moved = false;
1270
1272}
1273
1274
1279
1280
1282{
1283 if( m_3d_render )
1284 m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
1285}
1286
1287
1288void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& aEvent )
1289{
1290 if( aEvent.GetId() != m_redraw_trigger_timer.GetId() )
1291 {
1292 aEvent.Skip();
1293 return;
1294 }
1295
1296 Request_refresh( true );
1297}
1298
1299
1300void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
1301{
1302 Refresh();
1303}
1304
1305
1306void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
1307{
1308 if( aRedrawImmediately )
1309 {
1310 // Just calling Refresh() does not work always
1311 // Using an event to call DoRepaint ensure the repaint code will be executed,
1312 // and PostEvent will take priority to other events like mouse movements, keys, etc.
1313 // and is executed during the next idle time
1314 wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
1315 wxPostEvent( this, redrawEvent );
1316 }
1317 else
1318 {
1319 // Schedule a timed redraw
1320 m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
1321 }
1322}
1323
1324
1325void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
1326{
1327 wxASSERT( aMovingSpeed > FLT_EPSILON );
1328
1329 // Fast forward the animation if the animation is disabled
1330 if( !m_animation_enabled )
1331 {
1332 m_camera.Interpolate( 1.0f );
1333 DisplayStatus();
1335 return;
1336 }
1337
1338 // Map speed multiplier option to actual multiplier value
1339 // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
1340 aMovingSpeed *= static_cast<float>( ( 1 << m_moving_speed_multiplier ) ) / 8.0f;
1341
1342 m_render_pivot = aRenderPivot;
1343 m_camera_moving_speed = aMovingSpeed;
1344
1346
1347 DisplayStatus();
1349
1350 m_camera_is_moving = true;
1351
1353}
1354
1355
1357{
1358 RAY mouseRay = getRayAtCurrentMousePosition();
1359
1360 float hit_t = 0.0f;
1361
1362 // Test it with the board bounding box
1363 if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
1364 {
1365 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1366 m_camera.SetT0_and_T1_current_T();
1367 m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
1368 m_camera.ResetXYpos_T1();
1369
1371 }
1372}
1373
1374
1376{
1377 if( m_camera_is_moving )
1378 return false;
1379
1380 const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
1381 const float arrow_moving_time_speed = 8.0f;
1382
1383 switch( aRequestedView )
1384 {
1387 return true;
1388
1390 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1391 m_camera.SetT0_and_T1_current_T();
1392 m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
1393 request_start_moving_camera( arrow_moving_time_speed, false );
1394 return true;
1395
1397 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1398 m_camera.SetT0_and_T1_current_T();
1399 m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
1400 request_start_moving_camera( arrow_moving_time_speed, false );
1401 return true;
1402
1404 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1405 m_camera.SetT0_and_T1_current_T();
1406 m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
1407 request_start_moving_camera( arrow_moving_time_speed, false );
1408 return true;
1409
1411 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1412 m_camera.SetT0_and_T1_current_T();
1413 m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
1414 request_start_moving_camera( arrow_moving_time_speed, false );
1415 return true;
1416
1418 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1419 m_camera.SetT0_and_T1_current_T();
1420 m_camera.Reset_T1();
1421 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
1422 return true;
1423
1425 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1426 m_camera.SetT0_and_T1_current_T();
1427
1428 if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1430
1431 return true;
1432
1434 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1435 m_camera.SetT0_and_T1_current_T();
1436
1437 if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1439
1440 return true;
1441
1447 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1448 m_camera.SetT0_and_T1_current_T();
1449 m_camera.ViewCommand_T1( aRequestedView );
1451 return true;
1452
1455 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1456 m_camera.SetT0_and_T1_current_T();
1457 m_camera.ViewCommand_T1( aRequestedView );
1458 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1459 return true;
1460
1461 default:
1462 return false;
1463 }
1464}
1465
1466
1468{
1470 {
1471 switch( cfg->m_Render.engine )
1472 {
1475 default: m_3d_render = nullptr; break;
1476 }
1477 }
1478
1479 if( m_3d_render )
1480 m_3d_render->ReloadRequest();
1481
1482 m_mouse_was_moved = false;
1483
1485}
1486
1487
1489{
1490 SFVEC3F rayOrigin;
1491 SFVEC3F rayDir;
1492
1493 // Generate a ray origin and direction based on current mouser position and camera
1494 m_camera.MakeRayAtCurrentMousePosition( rayOrigin, rayDir );
1495
1496 RAY mouseRay;
1497 mouseRay.Init( rayOrigin, rayDir );
1498
1499 return mouseRay;
1500}
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:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
A class used to derive camera objects from.
Definition camera.h:99
Implement a canvas based on a wxGLCanvas.
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
Non-owning pointer to the active renderer (one of the two below).
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.
std::shared_ptr< SYNC_REPORTER > m_warningReporterSync
void OnRightDown(wxMouseEvent &event)
void InvalidateRaytracingHitTesting()
Invalidate the hover hit-test BVH before board layers are rebuilt.
int m_moving_speed_multiplier
void JoinBgWorker()
Block until any in-progress OpenGL background loading has finished.
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 ReloadRaytracingForHitTesting(std::stop_token aStop)
Rebuild the auxiliary raytracing BVH used for hover hit-testing in OpenGL mode.
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.
std::unique_ptr< INFOBAR_REPORTER > m_infoBarReporter
void OnZoomGesture(wxZoomGestureEvent &event)
std::unique_ptr< STATUSBAR_REPORTER > m_statusBarReporter
std::unique_ptr< RENDER_3D_OPENGL > m_3d_render_opengl
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::shared_ptr< SYNC_REPORTER > m_activityReporterSync
std::atomic_flag m_is_currently_painting
void OnRefreshRequest(wxEvent &aEvent)
void OnEraseBackground(wxEraseEvent &event)
void releaseOpenGL()
Free created targets and openGL context.
std::unique_ptr< RENDER_3D_RAYTRACE_GL > m_3d_render_raytracing
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:110
const wxString & GetValue() const
Definition footprint.h:925
const wxString & GetReference() const
Definition footprint.h:901
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
Definition pad.h:61
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition pgm_base.h:113
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:410
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:807
const wxString & GetZoneName() const
Definition zone.h:160
bool IsOnCopperLayer() const override
Definition zone.cpp:616
#define _(s)
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxCommandEvent)
#define EDA_3D_CANVAS_ID
Declaration of the eda_3d_viewer class.
@ ZOOM_LEVEL
@ HOVERED_ITEM
@ RENDER_TIME
@ 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
KICOMMON_API bool PackKiwayApiMessage(const google::protobuf::Message &aMessage, std::string &aBytes)
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)
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:89
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
glm::vec3 SFVEC3F
Definition xv3d_types.h:40