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