kicad/3d-viewer/3d_canvas.cpp

673 lines
18 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: 3d_canvas.cpp
/////////////////////////////////////////////////////////////////////////////
#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif
#include "fctsys.h"
#include "trigo.h"
#include "wx/image.h"
#if !wxUSE_GLCANVAS
#error Please set wxUSE_GLCANVAS to 1 in setup.h.
#endif
#include "wx/dataobj.h"
#include "wx/clipbrd.h"
#include "id.h"
#include "gestfich.h"
#include "3d_viewer.h"
#include "trackball.h"
/* Tool and button Bitmaps */
#include "bitmaps.h"
enum onrclick_id {
ID_POPUP_3D_VIEW_START = 2000,
ID_POPUP_ZOOMIN,
ID_POPUP_ZOOMOUT,
ID_POPUP_VIEW_XPOS,
ID_POPUP_VIEW_XNEG,
ID_POPUP_VIEW_YPOS,
ID_POPUP_VIEW_YNEG,
ID_POPUP_VIEW_ZPOS,
ID_POPUP_VIEW_ZNEG,
ID_POPUP_MOVE3D_LEFT,
ID_POPUP_MOVE3D_RIGHT,
ID_POPUP_MOVE3D_UP,
ID_POPUP_MOVE3D_DOWN,
ID_POPUP_3D_VIEW_END
};
/*
* Pcb3D_GLCanvas implementation
*/
BEGIN_EVENT_TABLE( Pcb3D_GLCanvas, wxGLCanvas )
EVT_PAINT( Pcb3D_GLCanvas::OnPaint )
EVT_CHAR( Pcb3D_GLCanvas::OnChar )
EVT_MOUSE_EVENTS( Pcb3D_GLCanvas::OnMouseEvent )
EVT_ERASE_BACKGROUND( Pcb3D_GLCanvas::OnEraseBackground )
EVT_MENU_RANGE( ID_POPUP_3D_VIEW_START, ID_POPUP_3D_VIEW_END,
Pcb3D_GLCanvas::OnPopUpMenu )
END_EVENT_TABLE()
/*************************************************************************/
Pcb3D_GLCanvas::Pcb3D_GLCanvas( WinEDA3D_DrawFrame* parent ) :
wxGLCanvas( parent, -1, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE )
/*************************************************************************/
{
m_init = FALSE;
m_gllist = 0;
m_Parent = parent;
DisplayStatus();
}
/*************************************/
Pcb3D_GLCanvas::~Pcb3D_GLCanvas()
/*************************************/
{
ClearLists();
m_init = FALSE;
}
/*************************************/
void Pcb3D_GLCanvas::ClearLists()
/*************************************/
{
if( m_gllist > 0 )
glDeleteLists( m_gllist, 1 );
m_gllist = 0;
}
/*********************************************/
void Pcb3D_GLCanvas::OnChar( wxKeyEvent& event )
/*********************************************/
{
SetView3D( event.GetKeyCode() );
event.Skip();
}
/*********************************************/
void Pcb3D_GLCanvas::SetView3D( int keycode )
/*********************************************/
{
int ii;
double delta_move = 0.7 * g_Parm_3D_Visu.m_Zoom;
switch( keycode )
{
case WXK_LEFT:
g_Draw3d_dx -= delta_move;
break;
case WXK_RIGHT:
g_Draw3d_dx += delta_move;
break;
case WXK_UP:
g_Draw3d_dy += delta_move;
break;
case WXK_DOWN:
g_Draw3d_dy -= delta_move;
break;
case WXK_HOME:
g_Parm_3D_Visu.m_Zoom = 1.0;
g_Draw3d_dx = g_Draw3d_dy = 0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
break;
case WXK_END:
break;
case WXK_F1:
g_Parm_3D_Visu.m_Zoom /= 1.4;
if( g_Parm_3D_Visu.m_Zoom <= 0.01 )
g_Parm_3D_Visu.m_Zoom = 0.01;
break;
case WXK_F2:
g_Parm_3D_Visu.m_Zoom *= 1.4;
break;
case '+':
break;
case '-':
break;
case 'r':
case 'R':
g_Draw3d_dx = g_Draw3d_dy = 0;
for( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
break;
case 'x':
for( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTZ = -90;
g_Parm_3D_Visu.m_ROTX = -90;
break;
case 'X':
for( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTZ = 90;
g_Parm_3D_Visu.m_ROTX = -90;
break;
case 'y':
for( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTX = -90;
break;
case 'Y':
for( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTX = -90;
g_Parm_3D_Visu.m_ROTZ = -180;
break;
case 'z':
for( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
break;
case 'Z':
for( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTX = -180;
break;
default:
return;
}
DisplayStatus();
Refresh( FALSE );
}
/********************************************************/
void Pcb3D_GLCanvas::OnMouseEvent( wxMouseEvent& event )
/********************************************************/
{
wxSize size( GetClientSize() );
double spin_quat[4];
if( event.RightDown() )
{
OnRightClick( event ); return;
}
if( event.m_wheelRotation )
{
if( event.ShiftDown() )
{
if( event.GetWheelRotation() < 0 )
{
/* up */
SetView3D( WXK_UP );
}
else
{
/* down */
SetView3D( WXK_DOWN );
}
}
else if( event.ControlDown() )
{
if( event.GetWheelRotation() > 0 )
{
/* right */
SetView3D( WXK_RIGHT );
}
else
{
/* left */
SetView3D( WXK_LEFT );
}
}
else
{
if( event.GetWheelRotation() > 0 )
{
g_Parm_3D_Visu.m_Zoom /= 1.4;
if( g_Parm_3D_Visu.m_Zoom <= 0.01 )
g_Parm_3D_Visu.m_Zoom = 0.01;
}
else
g_Parm_3D_Visu.m_Zoom *= 1.4;
DisplayStatus();
Refresh( FALSE );
}
}
if( event.Dragging() )
{
if( event.LeftIsDown() )
{
/* drag in progress, simulate trackball */
trackball( spin_quat,
(2.0 * g_Parm_3D_Visu.m_Beginx - size.x) / size.x,
(size.y - 2.0 * g_Parm_3D_Visu.m_Beginy) / size.y,
( 2.0 * event.GetX() - size.x) / size.x,
( size.y - 2.0 * event.GetY() ) / size.y );
add_quats( spin_quat, g_Parm_3D_Visu.m_Quat, g_Parm_3D_Visu.m_Quat );
}
else if( event.MiddleIsDown() )
{
/* middle button drag -> pan */
/* Current zoom and an additional factor are taken into account for the amount of panning. */
const double PAN_FACTOR = 8.0 * g_Parm_3D_Visu.m_Zoom;
g_Draw3d_dx -= PAN_FACTOR * ( g_Parm_3D_Visu.m_Beginx - event.GetX() ) / size.x;
g_Draw3d_dy -= PAN_FACTOR * (event.GetY() - g_Parm_3D_Visu.m_Beginy) / size.y;
}
/* orientation has changed, redraw mesh */
DisplayStatus();
Refresh( FALSE );
}
g_Parm_3D_Visu.m_Beginx = event.GetX();
g_Parm_3D_Visu.m_Beginy = event.GetY();
}
/*******************************************************/
void Pcb3D_GLCanvas::OnRightClick( wxMouseEvent& event )
/*******************************************************/
/* Construit et affiche un menu Popup lorsque on actionne le bouton droit
* de la souris
*/
{
wxPoint pos;
wxMenu PopUpMenu;
pos.x = event.GetX(); pos.y = event.GetY();
wxMenuItem* item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN,
_( "Zoom +" ) );
item->SetBitmap( zoom_in_xpm );
PopUpMenu.Append( item );
item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT,
_( "Zoom -" ) );
item->SetBitmap( zoom_out_xpm );
PopUpMenu.Append( item );
PopUpMenu.AppendSeparator();
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS,
_( "Top View" ) );
item->SetBitmap( axis3d_top_xpm );
PopUpMenu.Append( item );
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG,
_( "Bottom View" ) );
item->SetBitmap( axis3d_bottom_xpm );
PopUpMenu.Append( item );
PopUpMenu.AppendSeparator();
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS,
_( "Right View" ) );
item->SetBitmap( axis3d_right_xpm );
PopUpMenu.Append( item );
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG,
_( "Left View" ) );
item->SetBitmap( axis3d_left_xpm );
PopUpMenu.Append( item );
PopUpMenu.AppendSeparator();
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS,
_( "Front View" ) );
item->SetBitmap( axis3d_front_xpm );
PopUpMenu.Append( item );
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG,
_( "Back View" ) );
item->SetBitmap( axis3d_back_xpm );
PopUpMenu.Append( item );
PopUpMenu.AppendSeparator();
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT,
_( "Move left <-" ) );
item->SetBitmap( left_xpm );
PopUpMenu.Append( item );
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT,
_( "Move right ->" ) );
item->SetBitmap( right_xpm );
PopUpMenu.Append( item );
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP,
_( "Move Up ^" ) );
item->SetBitmap( up_xpm );
PopUpMenu.Append( item );
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN,
_( "Move Down" ) );
item->SetBitmap( down_xpm );
PopUpMenu.Append( item );
PopupMenu( &PopUpMenu, pos );
}
/*******************************************************/
void Pcb3D_GLCanvas::OnPopUpMenu( wxCommandEvent& event )
/*******************************************************/
{
int key = 0;
switch( event.GetId() )
{
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 = 'X';
break;
case ID_POPUP_VIEW_YPOS:
key = 'y';
break;
case ID_POPUP_VIEW_YNEG:
key = 'Y';
break;
case ID_POPUP_VIEW_ZPOS:
key = 'z';
break;
case ID_POPUP_VIEW_ZNEG:
key = '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 Pcb3D_GLCanvas::DisplayStatus()
/***************************************/
{
wxString msg;
msg.Printf( wxT( "dx %3.2f" ), g_Draw3d_dx );
m_Parent->SetStatusText( msg, 1 );
msg.Printf( wxT( "dy %3.2f" ), g_Draw3d_dy );
m_Parent->SetStatusText( msg, 2 );
msg.Printf( wxT( "View: %3.1f" ), 45 * g_Parm_3D_Visu.m_Zoom );
m_Parent->SetStatusText( msg, 3 );
}
/*************************************************/
void Pcb3D_GLCanvas::OnPaint( wxPaintEvent& event )
/*************************************************/
{
wxPaintDC dc( 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.
const wxSize ClientSize = GetClientSize();
glViewport( 0, 0, ClientSize.x, ClientSize.y );
Redraw();
event.Skip();
}
/***********************************************************/
void Pcb3D_GLCanvas::OnEraseBackground( wxEraseEvent& event )
/***********************************************************/
{
// Do nothing, to avoid flashing.
}
/****************************/
void Pcb3D_GLCanvas::InitGL()
/****************************/
/* Int parametres generaux pour OPENGL
*/
{
wxSize size = GetClientSize();
if( !m_init )
{
m_init = TRUE;
g_Parm_3D_Visu.m_Zoom = 1.0;
ZBottom = 1.0; ZTop = 10.0;
}
/* set viewing projection */
double ratio_HV = (double) size.x / size.y; // Ratio largeur /hauteur de la fenetre d'affichage
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
#define MAX_VIEW_ANGLE 160.0 / 45.0
if( g_Parm_3D_Visu.m_Zoom > MAX_VIEW_ANGLE )
g_Parm_3D_Visu.m_Zoom = MAX_VIEW_ANGLE;
gluPerspective( 45.0 * g_Parm_3D_Visu.m_Zoom, ratio_HV, 1, 10 );
// glFrustum(-1., 1.1F, -1.1F, 1.1F, ZBottom, ZTop);
/* position viewer */
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0.0F, 0.0F, -( ZBottom + ZTop) / 2 );
/* clear color and depth buffers */
glClearColor( g_Parm_3D_Visu.m_BgColor.m_Red,
g_Parm_3D_Visu.m_BgColor.m_Green,
g_Parm_3D_Visu.m_BgColor.m_Blue, 1 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
/* Setup light souces: */
SetLights();
glDisable( GL_CULL_FACE ); // show back faces
glEnable( GL_DEPTH_TEST ); // Enable z-buferring
glEnable( GL_LINE_SMOOTH );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
/* speedups */
glEnable( GL_DITHER );
glShadeModel( GL_SMOOTH );
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST );
glHint( GL_POLYGON_SMOOTH_HINT, GL_FASTEST );
/* blend */
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
/***********************************/
void Pcb3D_GLCanvas::SetLights()
/***********************************/
/* Init sources lumineuses pour OPENGL
*/
{
double light;
GLfloat light_color[4];
/* set viewing projection */
light_color[3] = 1.0;
GLfloat Z_axis_pos[4] = { 0.0, 0.0, 3.0, 0.0 };
GLfloat lowZ_axis_pos[4] = { 0.0, 0.0, -3.0, 0.5 };
/* activate light */
light = 1.0;
light_color[0] = light_color[1] = light_color[2] = light;
glLightfv( GL_LIGHT0, GL_POSITION, Z_axis_pos );
glLightfv( GL_LIGHT0, GL_DIFFUSE, light_color );
light = 0.3;
light_color[0] = light_color[1] = light_color[2] = light;
glLightfv( GL_LIGHT1, GL_POSITION, lowZ_axis_pos );
glLightfv( GL_LIGHT1, GL_DIFFUSE, light_color );
glEnable( GL_LIGHT0 ); // White spot on Z axis
glEnable( GL_LIGHT1 ); // White spot on Z axis ( bottom)
glEnable( GL_LIGHTING );
}
/**********************************************************/
void Pcb3D_GLCanvas::TakeScreenshot( wxCommandEvent& event )
/**********************************************************/
/* Create a Screenshot of the current 3D view.
* Output file format is png or jpeg, or image is copied on clipboard
*/
{
wxFileName fn( m_Parent->m_Parent->GetScreen()->m_FileName );
wxString FullFileName;
wxString file_ext, mask;
bool fmt_is_jpeg = FALSE;
if( event.GetId() == ID_MENU_SCREENCOPY_JPEG )
fmt_is_jpeg = TRUE;
if( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD )
{
file_ext = fmt_is_jpeg ? wxT( "jpg" ) : wxT( "png"; )
mask = wxT( "*." ) + file_ext;
FullFileName = m_Parent->m_Parent->GetScreen()->m_FileName;
fn.SetExt( file_ext );
FullFileName =
EDA_FileSelector( _( "3D Image filename:" ),
wxEmptyString, /* Chemin par defaut */
fn.GetFullName(), /* nom fichier par defaut */
file_ext, /* extension par defaut */
mask, /* Masque d'affichage */
this,
wxFD_SAVE,
TRUE
);
if( FullFileName.IsEmpty() )
return;
}
Redraw( true );
struct vieport_params
{
GLint originx;
GLint originy;
GLint x;
GLint y;
} viewport;
glGetIntegerv( GL_VIEWPORT, (GLint*) &viewport );
unsigned char* pixelbuffer = (unsigned char*) malloc( 3 * viewport.x * viewport.y );
glReadPixels( 0, 0, viewport.x, viewport.y, GL_RGB, GL_UNSIGNED_BYTE, pixelbuffer );
wxImage image( viewport.x, viewport.y );
image.SetData( pixelbuffer );
image = image.Mirror();
image = image.Rotate90().Rotate90();
wxBitmap bitmap( image, -1 );
if( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD )
{
wxBitmapDataObject* dobjBmp = new wxBitmapDataObject;
dobjBmp->SetBitmap( bitmap );
if( wxTheClipboard->Open() )
{
if( !wxTheClipboard->SetData( dobjBmp ) )
wxLogError( _T( "Failed to copy image to clipboard" ) );
wxTheClipboard->Flush(); /* the data on clipboard
* will stay available after the application exits */
wxTheClipboard->Close();
}
}
else
{
if( !image.SaveFile( FullFileName,
fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
wxLogError( wxT( "Can't save file" ) );
image.Destroy();
}
}