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