kicad-source/3d-viewer/3d_canvas/eda_3d_canvas.cpp
John Beard 18593d2dbb 3D viewer: decouple renderers from wxCursors
Provide a toolkit-agnostic interface for "busy indicators", which
allows the 3D viewer to show a busy cursor, without the canvases
having to to know how a wxCursore works.

The motivation here is to decouple the 3D renderers from the WX
GUI system, as they can then be used when when there is not an
active window (e.g. for offscreen rendering).

Otherwise, attempting to use a wxBusyCursor without a GUI
available is an instant segfault.
2019-05-12 14:19:39 +01:00

1156 lines
32 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file eda_3d_canvas.cpp
* @brief Implementation of a 3d canvas
*/
#include <GL/glew.h> // Must be included first
#include <wx/tokenzr.h>
#include "../common_ogl/openGL_includes.h"
#include "../common_ogl/ogl_utils.h"
#include "eda_3d_canvas.h"
#include "../3d_viewer_id.h"
#include "../3d_rendering/3d_render_raytracing/c3d_render_raytracing.h"
#include "../3d_viewer/eda_3d_viewer.h"
#include "../3d_rendering/test_cases.h"
#include <class_board.h>
#include "status_text_reporter.h"
#include <gl_context_mgr.h>
#include <profile.h> // To use GetRunningMicroSecs or another profiling utility
#include <bitmaps.h>
#include <hotkeys_basic.h>
#include <menus_helpers.h>
#include <widgets/wx_busy_indicator.h>
/**
* Flag to enable 3D canvas debug tracing.
*
* Use "KI_TRACE_EDA_3D_CANVAS" to enable.
*
* @ingroup trace_env_vars
*/
const wxChar * EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
const float EDA_3D_CANVAS::m_delta_move_step_factor = 0.7f;
BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
EVT_PAINT( EDA_3D_CANVAS::OnPaint )
EVT_CHAR( EDA_3D_CANVAS::OnKeyEvent )
EVT_CHAR_HOOK( EDA_3D_CANVAS::OnCharHook )
// mouse events
EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
EVT_MIDDLE_DOWN( EDA_3D_CANVAS::OnMiddleDown)
EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightClick )
EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
#endif
// other events
EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
EVT_MENU_RANGE( ID_POPUP_3D_VIEW_START,
ID_POPUP_3D_VIEW_END, EDA_3D_CANVAS::OnPopUpMenu )
EVT_CLOSE( EDA_3D_CANVAS::OnCloseWindow )
EVT_SIZE( EDA_3D_CANVAS::OnResize )
END_EVENT_TABLE()
EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow *aParent,
const int *aAttribList,
BOARD *aBoard,
CINFO3D_VISU &aSettings , S3D_CACHE *a3DCachePointer ) :
HIDPI_GL_CANVAS( aParent,
wxID_ANY,
aAttribList,
wxDefaultPosition,
wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE
),
m_settings( aSettings )
{
// Run test cases in debug mode, once.
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::EDA_3D_CANVAS" );
m_editing_timeout_timer.SetOwner( this );
Connect( m_editing_timeout_timer.GetId(),
wxEVT_TIMER,
wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ),
NULL,
this );
m_redraw_trigger_timer.SetOwner( this );
Connect( m_redraw_trigger_timer.GetId(),
wxEVT_TIMER,
wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ),
NULL,
this );
m_mouse_was_moved = false;
m_mouse_is_moving = false;
m_camera_is_moving = false;
m_render_pivot = false;
m_camera_moving_speed = 1.0f;
m_strtime_camera_movement = 0;
m_is_opengl_initialized = false;
m_render_raytracing_was_requested = false;
m_opengl_supports_raytracing = false;
m_parentStatusBar = NULL;
m_glRC = NULL;
m_3d_render = NULL;
m_3d_render_raytracing = new C3D_RENDER_RAYTRACING( aSettings );
m_3d_render_ogl_legacy = new C3D_RENDER_OGL_LEGACY( aSettings );
wxASSERT( m_3d_render_raytracing != NULL );
wxASSERT( m_3d_render_ogl_legacy != NULL );
auto busy_indicator_factory = []() { return std::make_unique<WX_BUSY_INDICATOR>(); };
m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
m_3d_render_ogl_legacy->SetBusyIndicatorFactory( busy_indicator_factory );
RenderEngineChanged();
wxASSERT( aBoard != NULL );
m_settings.SetBoard( aBoard );
wxASSERT( a3DCachePointer != NULL );
m_settings.Set3DCacheManager( a3DCachePointer );
}
EDA_3D_CANVAS::~EDA_3D_CANVAS()
{
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::~EDA_3D_CANVAS" );
releaseOpenGL();
}
void EDA_3D_CANVAS::releaseOpenGL()
{
if( m_glRC )
{
GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this );
delete m_3d_render_raytracing;
m_3d_render_raytracing = NULL;
delete m_3d_render_ogl_legacy;
m_3d_render_ogl_legacy = NULL;
// This is just a copy of a pointer, can safely be set to NULL
m_3d_render = NULL;
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
GL_CONTEXT_MANAGER::Get().DestroyCtx( m_glRC );
m_glRC = NULL;
}
}
void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent &event )
{
releaseOpenGL();
event.Skip();
}
void EDA_3D_CANVAS::OnResize( wxSizeEvent &event )
{
this->Request_refresh();
}
bool EDA_3D_CANVAS::initializeOpenGL()
{
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL" );
const GLenum err = glewInit();
if( GLEW_OK != err )
{
const wxString msgError = (const char*) glewGetErrorString( err );
wxLogMessage( msgError );
return false;
}
else
{
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s",
FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
}
wxString version = FROM_UTF8( (char *) glGetString( GL_VERSION ) );
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL version string %s.",
__WXFUNCTION__, version );
// Extract OpenGL version from string. This method is used because prior to OpenGL 2,
// getting the OpenGL major and minor version as integers didn't exist.
wxString tmp;
wxStringTokenizer tokenizer( version );
m_opengl_supports_raytracing = true;
if( tokenizer.HasMoreTokens() )
{
long major = 0;
long minor = 0;
tmp = tokenizer.GetNextToken();
tokenizer.SetString( tmp, wxString( "." ) );
if( tokenizer.HasMoreTokens() )
tokenizer.GetNextToken().ToLong( &major );
if( tokenizer.HasMoreTokens() )
tokenizer.GetNextToken().ToLong( &minor );
if( major < 2 || ( (major == 2 ) && (minor < 1) ) )
{
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL ray tracing not supported.",
__WXFUNCTION__ );
if( GetParent() )
{
wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
GetParent()->ProcessWindowEvent( evt );
}
m_opengl_supports_raytracing = false;
}
}
m_is_opengl_initialized = true;
return true;
}
void EDA_3D_CANVAS::GetScreenshot( wxImage &aDstImage )
{
OGL_GetScreenshot( aDstImage );
}
void EDA_3D_CANVAS::ReloadRequest( BOARD *aBoard , S3D_CACHE *aCachePointer )
{
if( aCachePointer != NULL )
m_settings.Set3DCacheManager( aCachePointer );
if( aBoard != NULL )
m_settings.SetBoard( aBoard );
if( m_3d_render )
m_3d_render->ReloadRequest();
}
void EDA_3D_CANVAS::RenderRaytracingRequest()
{
m_3d_render = m_3d_render_raytracing;
if( m_3d_render )
m_3d_render->ReloadRequest();
m_render_raytracing_was_requested = true;
//m_mouse_was_moved = true;
Request_refresh();
}
void EDA_3D_CANVAS::DisplayStatus()
{
if( m_parentStatusBar )
{
wxString msg;
msg.Printf( "dx %3.2f", m_settings.CameraGet().GetCameraPos().x );
m_parentStatusBar->SetStatusText( msg, 1 );
msg.Printf( "dy %3.2f", m_settings.CameraGet().GetCameraPos().y );
m_parentStatusBar->SetStatusText( msg, 2 );
}
}
void EDA_3D_CANVAS::OnPaint( wxPaintEvent &event )
{
// Please have a look at:
// https://lists.launchpad.net/kicad-developers/msg25149.html
// wxPaintDC( this );
// event.Skip( false );
// SwapBuffer requires the window to be shown before calling
if( !IsShownOnScreen() )
{
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnPaint !IsShown" );
return;
}
// Because the board to draw is handled by the parent viewer frame,
// ensure this parent is still alive. When it is closed before the viewer
// frame, a paint event can be generated after the parent is closed,
// therefore with invalid board.
// This is dependent of the platform.
// Especially on OSX, but also on Windows, it frequently happens
if( !GetParent()->GetParent()->IsShown() )
return; // The parent board editor frame is no more alive
wxString err_messages;
// !TODO: implement error reporter
//WX_STRING_REPORTER errorReporter( &err_messages );
STATUS_TEXT_REPORTER activityReporter( m_parentStatusBar, 0 );
unsigned strtime = GetRunningMicroSecs();
// "Makes the OpenGL state that is represented by the OpenGL rendering
// context context current, i.e. it will be used by all subsequent OpenGL calls.
// This function may only be called when the window is shown on screen"
// Explicitly create a new rendering context instance for this canvas.
if( m_glRC == NULL )
m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this );
GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this );
// Set the OpenGL viewport according to the client size of this canvas.
// This is done here rather than in a wxSizeEvent handler because our
// OpenGL rendering context (and thus viewport setting) is used with
// multiple canvases: If we updated the viewport in the wxSizeEvent
// handler, changing the size of one canvas causes a viewport setting that
// is wrong when next another canvas is repainted.
wxSize clientSize = GetNativePixelSize();
const bool windows_size_changed = m_settings.CameraGet().SetCurWindowSize( clientSize );
// Initialize openGL if need
// /////////////////////////////////////////////////////////////////////////
if( !m_is_opengl_initialized )
{
if( !initializeOpenGL() )
{
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
return;
}
}
// Don't attend to ray trace if OpenGL doesn't support it.
if( !m_opengl_supports_raytracing )
{
m_3d_render = m_3d_render_ogl_legacy;
m_render_raytracing_was_requested = false;
m_settings.RenderEngineSet( RENDER_ENGINE_OPENGL_LEGACY );
}
// Check if a raytacing was requested and need to switch to raytracing mode
if( m_settings.RenderEngineGet() == RENDER_ENGINE_OPENGL_LEGACY )
{
const bool was_camera_changed = m_settings.CameraGet().ParametersChanged();
// It reverts back to OpenGL mode if it was requested a raytracing
// render of the current scene. AND the mouse / camera is moving
if( ( m_mouse_is_moving ||
m_camera_is_moving ||
was_camera_changed ||
windows_size_changed ) &&
m_render_raytracing_was_requested )
{
m_render_raytracing_was_requested = false;
m_3d_render = m_3d_render_ogl_legacy;
}
}
float curtime_delta_s = 0.0f;
if( m_camera_is_moving )
{
const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
m_settings.CameraGet().Interpolate( curtime_delta_s );
if( curtime_delta_s > 1.0f )
{
m_render_pivot = false;
m_camera_is_moving = false;
m_mouse_was_moved = true;
restart_editingTimeOut_Timer();
DisplayStatus();
}
else
{
Request_refresh();
}
}
// It will return true if the render request a new redraw
bool requested_redraw = false;
if( m_3d_render )
{
m_3d_render->SetCurWindowSize( clientSize );
requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
&activityReporter );
}
if( m_render_pivot )
{
const float scale = glm::min( m_settings.CameraGet().ZoomGet(), 1.0f );
render_pivot( curtime_delta_s, scale * scale );
}
// "Swaps the double-buffer of this window, making the back-buffer the
// front-buffer and vice versa, so that the output of the previous OpenGL
// commands is displayed on the window."
SwapBuffers();
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
if( !activityReporter.HasMessage() )
{
if( m_mouse_was_moved || m_camera_is_moving )
{
// Calculation time in milliseconds
const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
activityReporter.Report( wxString::Format( _( "Render time %.0f ms ( %.1f fps)" ),
calculation_time, 1000.0 / calculation_time ) );
}
}
// This will reset the flag of camera parameters changed
m_settings.CameraGet().ParametersChanged();
if( !err_messages.IsEmpty() )
wxLogMessage( err_messages );
if( (!m_camera_is_moving) && requested_redraw )
{
m_mouse_was_moved = false;
Request_refresh( false );
}
}
void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent &event )
{
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnEraseBackground" );
// Do nothing, to avoid flashing.
}
void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent &event )
{
bool mouseActivity = false;
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnMouseWheel" );
if( m_camera_is_moving )
return;
float delta_move = m_delta_move_step_factor * m_settings.CameraGet().ZoomGet();
if( m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) )
delta_move *= (0.01f * event.GetWheelRotation());
else
if( event.GetWheelRotation() < 0 )
delta_move = -delta_move;
// mousewheel_panning enabled:
// wheel -> pan;
// wheel + shift -> horizontal scrolling;
// wheel + ctrl -> zooming;
// mousewheel_panning disabled:
// wheel + shift -> vertical scrolling;
// wheel + ctrl -> horizontal scrolling;
// wheel -> zooming.
if( m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) && !event.ControlDown() )
{
if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL || event.ShiftDown() )
m_settings.CameraGet().Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
else
m_settings.CameraGet().Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
mouseActivity = true;
}
else if( event.ShiftDown() && !m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) )
{
m_settings.CameraGet().Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
mouseActivity = true;
}
else if( event.ControlDown() && !m_settings.GetFlag( FL_MOUSEWHEEL_PANNING ) )
{
m_settings.CameraGet().Pan( SFVEC3F( delta_move, 0.0f, 0.0f ) );
mouseActivity = true;
}
else
{
mouseActivity = m_settings.CameraGet().Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
}
// If it results on a camera movement
if( mouseActivity )
{
DisplayStatus();
Request_refresh();
m_mouse_is_moving = true;
m_mouse_was_moved = true;
restart_editingTimeOut_Timer();
}
// Update the cursor current mouse position on the camera
m_settings.CameraGet().SetCurMousePosition( event.GetPosition() );
}
#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
{
SetFocus();
if( m_camera_is_moving )
return;
//m_is_moving_mouse = true;
restart_editingTimeOut_Timer();
float magnification = ( event.GetMagnification() + 1.0f );
m_settings.CameraGet().Zoom( magnification );
DisplayStatus();
Request_refresh();
}
#endif
void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent &event )
{
//wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseMove" ) );
if( m_camera_is_moving )
return;
m_settings.CameraGet().SetCurWindowSize( GetNativePixelSize() );
if( event.Dragging() )
{
if( event.LeftIsDown() ) // Drag
m_settings.CameraGet().Drag( event.GetPosition() );
else if( event.MiddleIsDown() ) // Pan
m_settings.CameraGet().Pan( event.GetPosition() );
m_mouse_is_moving = true;
m_mouse_was_moved = true;
// orientation has changed, redraw mesh
DisplayStatus();
Request_refresh();
}
const wxPoint eventPosition = event.GetPosition();
m_settings.CameraGet().SetCurMousePosition( eventPosition );
}
void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent &event )
{
SetFocus();
stop_editingTimeOut_Timer();
}
void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent &event )
{
if( m_camera_is_moving )
return;
if( m_mouse_is_moving )
{
m_mouse_is_moving = false;
restart_editingTimeOut_Timer();
}
}
void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent &event )
{
SetFocus();
stop_editingTimeOut_Timer();
}
void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent &event )
{
if( m_camera_is_moving )
return;
if( m_mouse_is_moving )
{
m_mouse_is_moving = false;
restart_editingTimeOut_Timer();
}
else
{
move_pivot_based_on_cur_mouse_position();
}
}
void EDA_3D_CANVAS::OnRightClick( wxMouseEvent &event )
{
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnRightClick" );
SetFocus();
if( m_camera_is_moving )
return;
wxPoint pos;
wxMenu PopUpMenu;
wxString msg;
pos.x = event.GetX();
pos.y = event.GetY();
msg = AddHotkeyName( _( "Zoom +" ), GetHotkeyConfig(),
ID_POPUP_ZOOMIN );
AddMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN,
msg, KiBitmap( zoom_in_xpm ) );
msg = AddHotkeyName( _( "Zoom -" ), GetHotkeyConfig(),
ID_POPUP_ZOOMOUT );
AddMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT,
msg, KiBitmap( zoom_out_xpm ) );
PopUpMenu.AppendSeparator();
msg = AddHotkeyName( _( "Top View" ), GetHotkeyConfig(),
ID_POPUP_VIEW_ZPOS );
AddMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS,
msg, KiBitmap( axis3d_top_xpm ) );
msg = AddHotkeyName( _( "Bottom View" ), GetHotkeyConfig(),
ID_POPUP_VIEW_ZNEG );
AddMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG,
msg, KiBitmap( axis3d_bottom_xpm ) );
PopUpMenu.AppendSeparator();
msg = AddHotkeyName( _( "Right View" ), GetHotkeyConfig(),
ID_POPUP_VIEW_XPOS );
AddMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS,
msg, KiBitmap( axis3d_right_xpm ) );
msg = AddHotkeyName( _( "Left View" ), GetHotkeyConfig(),
ID_POPUP_VIEW_XNEG );
AddMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG,
msg, KiBitmap( axis3d_left_xpm ) );
PopUpMenu.AppendSeparator();
msg = AddHotkeyName( _( "Front View" ), GetHotkeyConfig(),
ID_POPUP_VIEW_YPOS );
AddMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS,
msg, KiBitmap( axis3d_front_xpm ) );
msg = AddHotkeyName( _( "Back View" ), GetHotkeyConfig(),
ID_POPUP_VIEW_YNEG );
AddMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG,
msg, KiBitmap( axis3d_back_xpm ) );
PopUpMenu.AppendSeparator();
msg = AddHotkeyName( _( "Move Left <-" ), GetHotkeyConfig(),
ID_POPUP_MOVE3D_LEFT );
AddMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT,
msg, KiBitmap( left_xpm ) );
msg = AddHotkeyName( _( "Move Right ->" ), GetHotkeyConfig(),
ID_POPUP_MOVE3D_RIGHT );
AddMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT,
msg, KiBitmap( right_xpm ) );
msg = AddHotkeyName( _( "Move Up ^" ), GetHotkeyConfig(),
ID_POPUP_MOVE3D_UP );
AddMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP,
msg, KiBitmap( up_xpm ) );
msg = AddHotkeyName( _( "Move Down" ), GetHotkeyConfig(),
ID_POPUP_MOVE3D_DOWN );
AddMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN,
msg, KiBitmap( down_xpm ) );
PopupMenu( &PopUpMenu, pos );
}
void EDA_3D_CANVAS::OnPopUpMenu( wxCommandEvent &event )
{
int id = event.GetId();
wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnPopUpMenu id:%d", id );
int key = 0;
switch( id )
{
case ID_POPUP_ZOOMIN:
key = WXK_F1;
break;
case ID_POPUP_ZOOMOUT:
key = WXK_F2;
break;
case ID_POPUP_VIEW_XPOS:
key = 'X';
break;
case ID_POPUP_VIEW_XNEG:
key = GR_KB_SHIFT + 'X';
break;
case ID_POPUP_VIEW_YPOS:
key = 'Y';
break;
case ID_POPUP_VIEW_YNEG:
key = GR_KB_SHIFT + 'Y';
break;
case ID_POPUP_VIEW_ZPOS:
key = 'Z';
break;
case ID_POPUP_VIEW_ZNEG:
key = GR_KB_SHIFT + 'Z';
break;
case ID_POPUP_MOVE3D_LEFT:
key = WXK_LEFT;
break;
case ID_POPUP_MOVE3D_RIGHT:
key = WXK_RIGHT;
break;
case ID_POPUP_MOVE3D_UP:
key = WXK_UP;
break;
case ID_POPUP_MOVE3D_DOWN:
key = WXK_DOWN;
break;
default:
return;
}
SetView3D( key );
}
void EDA_3D_CANVAS::OnCharHook( wxKeyEvent &event )
{
//wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnCharHook" );
event.Skip();
}
void EDA_3D_CANVAS::OnKeyEvent( wxKeyEvent& event )
{
//wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnKeyEvent" );
int localkey = event.GetKeyCode();
// Use only upper char values in comparisons
// (the Shift modifier is a separate attribute)
if( (localkey >= 'a') && (localkey <= 'z') )
localkey += 'A' - 'a';
if( m_camera_is_moving )
return;
if( event.ShiftDown() )
localkey |= GR_KB_SHIFT;
if( event.ControlDown() )
localkey |= GR_KB_CTRL;
if( event.AltDown() )
localkey |= GR_KB_ALT;
if( !SetView3D( localkey ) )
event.Skip();
}
void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent &event )
{
(void)event;
m_mouse_is_moving = false;
m_mouse_was_moved = false;
Request_refresh();
}
void EDA_3D_CANVAS::stop_editingTimeOut_Timer()
{
m_editing_timeout_timer.Stop();
}
void EDA_3D_CANVAS::restart_editingTimeOut_Timer()
{
m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut() , wxTIMER_ONE_SHOT );
}
void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent &event )
{
(void)event;
//Refresh();
//Update();
wxPaintEvent redrawEvent;
wxPostEvent( this, redrawEvent );
}
void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
{
if( aRedrawImmediately )
{
// On some systems, just calling Refresh does not work always
// (Issue experienced on Win7 MSYS2)
//Refresh();
//Update();
// Using PostEvent will take priority to other events, like
// mouse movements, keys, etc.
wxPaintEvent redrawEvent;
wxPostEvent( this, redrawEvent );
// This behaves the same
// wxQueueEvent( this,
// From wxWidget documentation: "The heap-allocated and
// non-NULL event to queue, the function takes ownership of it."
// new wxPaintEvent()
// );
}
else
{
// Schedule a timed redraw
m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
}
}
void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
{
wxASSERT( aMovingSpeed > FLT_EPSILON );
m_render_pivot = aRenderPivot;
m_camera_moving_speed = aMovingSpeed;
stop_editingTimeOut_Timer();
DisplayStatus();
Request_refresh();
m_camera_is_moving = true;
m_strtime_camera_movement = GetRunningMicroSecs();
}
void EDA_3D_CANVAS::move_pivot_based_on_cur_mouse_position()
{
SFVEC3F rayOrigin;
SFVEC3F rayDir;
// Generate a ray origin and direction based on current mouser position and camera
m_settings.CameraGet().MakeRayAtCurrrentMousePosition( rayOrigin, rayDir );
RAY mouseRay;
mouseRay.Init( rayOrigin, rayDir );
float hit_t;
// Test it with the board bounding box
if( m_settings.GetBBox3DU().Intersect( mouseRay, &hit_t ) )
{
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().SetLookAtPos_T1( mouseRay.at( hit_t ) );
m_settings.CameraGet().ResetXYpos_T1();
request_start_moving_camera();
}
}
bool EDA_3D_CANVAS::SetView3D( int aKeycode )
{
if( m_camera_is_moving )
return false;
const float delta_move = m_delta_move_step_factor * m_settings.CameraGet().ZoomGet();
const float arrow_moving_time_speed = 8.0f;
bool handled = false;
switch( aKeycode )
{
case WXK_SPACE:
move_pivot_based_on_cur_mouse_position();
return true;
case WXK_LEFT:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
request_start_moving_camera( arrow_moving_time_speed, false );
return true;
case WXK_RIGHT:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
request_start_moving_camera( arrow_moving_time_speed, false );
return true;
case WXK_UP:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
request_start_moving_camera( arrow_moving_time_speed, false );
return true;
case WXK_DOWN:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_LINEAR );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
request_start_moving_camera( arrow_moving_time_speed, false );
return true;
case WXK_HOME:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
request_start_moving_camera( glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) );
return true;
case WXK_END:
break;
case WXK_TAB:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_EASING_IN_OUT );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().RotateZ_T1( glm::radians( 45.0f ) );
request_start_moving_camera();
handled = true;
break;
case WXK_F1:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
if( m_settings.CameraGet().Zoom_T1( 1.4f ) )
request_start_moving_camera( 3.0f );
return true;
case WXK_F2:
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
if( m_settings.CameraGet().Zoom_T1( 1/1.4f ) )
request_start_moving_camera( 3.0f );
return true;
case '+':
case '-':
break;
case 't':
case 'T':
m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL,
!m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL ) );
ReloadRequest();
handled = true;
break;
case 's':
case 'S':
m_settings.SetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT,
!m_settings.GetFlag( FL_MODULE_ATTRIBUTES_NORMAL_INSERT ) );
ReloadRequest();
handled = true;
break;
case 'v':
case 'V':
m_settings.SetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL,
!m_settings.GetFlag( FL_MODULE_ATTRIBUTES_VIRTUAL ) );
ReloadRequest();
handled = true;
break;
case 'r':
case 'R':
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
request_start_moving_camera( glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) );
return true;
case 'X':
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
m_settings.CameraGet().RotateZ_T1( glm::radians( -90.0f ) );
m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) );
request_start_moving_camera();
return true;
case GR_KB_SHIFT + 'X':
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
m_settings.CameraGet().RotateZ_T1( glm::radians( 90.0f ) );
m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) );
request_start_moving_camera();
return true;
case 'Y':
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) );
request_start_moving_camera();
return true;
case GR_KB_SHIFT + 'Y':
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
m_settings.CameraGet().RotateX_T1( glm::radians( -90.0f ) );
m_settings.CameraGet().RotateZ_T1( glm::radians( -180.0f ) );
request_start_moving_camera();
return true;
case 'Z':
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
request_start_moving_camera(
glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) );
return true;
case GR_KB_SHIFT + 'Z':
m_settings.CameraGet().SetInterpolateMode( INTERPOLATION_BEZIER );
m_settings.CameraGet().SetT0_and_T1_current_T();
m_settings.CameraGet().Reset_T1();
m_settings.CameraGet().RotateY_T1( glm::radians( 180.0f ) );
request_start_moving_camera(
glm::min( glm::max( m_settings.CameraGet().ZoomGet(), 0.5f ), 1.125f ) );
return true;
default:
return false;
}
m_mouse_was_moved = true;
restart_editingTimeOut_Timer();
DisplayStatus();
Request_refresh();
return handled;
}
void EDA_3D_CANVAS::RenderEngineChanged()
{
switch( m_settings.RenderEngineGet() )
{
case RENDER_ENGINE_OPENGL_LEGACY:
m_3d_render = m_3d_render_ogl_legacy;
break;
case RENDER_ENGINE_RAYTRACING:
m_3d_render = m_3d_render_raytracing;
break;
default:
m_3d_render = NULL;
break;
}
if( m_3d_render )
m_3d_render->ReloadRequest();
m_mouse_was_moved = false;
Request_refresh();
}