3d_canvas.cpp 18.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 1992-2014 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
 */

24 25 26
/**
 * @file 3d_canvas.cpp
*/
27 28
#include <fctsys.h>
#include <trigo.h>
plyatov's avatar
plyatov committed
29

30
#include <wx/image.h>
plyatov's avatar
plyatov committed
31 32 33 34 35

#if !wxUSE_GLCANVAS
#error Please set wxUSE_GLCANVAS to 1 in setup.h.
#endif

36 37
#include <wx/dataobj.h>
#include <wx/clipbrd.h>
38
#include <wx/wupdlock.h>
plyatov's avatar
plyatov committed
39

40
#include <gestfich.h>
plyatov's avatar
plyatov committed
41

42 43 44
#ifdef __WINDOWS__
#include <GL/glew.h>        // must be included before gl.h
#endif
45

46
#include <3d_viewer.h>
47
#include <3d_canvas.h>
48
#include <info3d_visu.h>
49
#include <trackball.h>
50
#include <3d_viewer_id.h>
plyatov's avatar
plyatov committed
51

52 53
#include <textures/text_silk.h>
#include <textures/text_pcb.h>
plyatov's avatar
plyatov committed
54

55

plyatov's avatar
plyatov committed
56
/*
57
 * EDA_3D_CANVAS implementation
plyatov's avatar
plyatov committed
58 59
 */

60 61
BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
    EVT_PAINT( EDA_3D_CANVAS::OnPaint )
62

63
    // key event:
64
    EVT_CHAR( EDA_3D_CANVAS::OnChar )
65

66
    // mouse events
67 68
    EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightClick )
    EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
69 70 71
#ifdef USE_OSX_MAGNIFY_EVENT
    EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )    
#endif
72
    EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
73

74
    // other events
75 76
    EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
    EVT_MENU_RANGE( ID_POPUP_3D_VIEW_START, ID_POPUP_3D_VIEW_END, EDA_3D_CANVAS::OnPopUpMenu )
plyatov's avatar
plyatov committed
77 78
END_EVENT_TABLE()

79

80
EDA_3D_CANVAS::EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList ) :
81
    wxGLCanvas( parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
82
                wxFULL_REPAINT_ON_RESIZE )
plyatov's avatar
plyatov committed
83
{
84
    m_init   = false;
85
    m_shadow_init = false;
86 87 88 89 90

    // Clear all gl list identifiers:
    for( int ii = GL_ID_BEGIN; ii < GL_ID_END; ii++ )
        m_glLists[ii] = 0;

91
    // Explicitly create a new rendering context instance for this canvas.
92
    m_glRC = new wxGLContext( this );
93

94
    DisplayStatus();
plyatov's avatar
plyatov committed
95 96 97
}


98
EDA_3D_CANVAS::~EDA_3D_CANVAS()
plyatov's avatar
plyatov committed
99
{
100
    ClearLists();
101
    m_init = false;
102
    delete m_glRC;
plyatov's avatar
plyatov committed
103 104
}

105

106
void EDA_3D_CANVAS::ClearLists( int aGlList )
plyatov's avatar
plyatov committed
107
{
108 109 110 111
    if( aGlList )
    {
        if( m_glLists[aGlList] > 0 )
            glDeleteLists( m_glLists[aGlList], 1 );
112

113 114 115 116 117 118 119 120 121 122 123 124
        m_glLists[aGlList] = 0;

        return;
    }

    for( int ii = GL_ID_BEGIN; ii < GL_ID_END; ii++ )
    {
        if( m_glLists[ii] > 0 )
            glDeleteLists( m_glLists[ii], 1 );

        m_glLists[ii] = 0;
    }
plyatov's avatar
plyatov committed
125 126
}

127

128
void EDA_3D_CANVAS::OnChar( wxKeyEvent& event )
plyatov's avatar
plyatov committed
129
{
130 131
    SetView3D( event.GetKeyCode() );
    event.Skip();
plyatov's avatar
plyatov committed
132 133
}

134

135
void EDA_3D_CANVAS::SetView3D( int keycode )
plyatov's avatar
plyatov committed
136
{
137
    int    ii;
138
    double delta_move = 0.7 * GetPrm3DVisu().m_Zoom;
139 140 141 142

    switch( keycode )
    {
    case WXK_LEFT:
143
        m_draw3dOffset.x -= delta_move;
144 145 146
        break;

    case WXK_RIGHT:
147
        m_draw3dOffset.x += delta_move;
148 149 150
        break;

    case WXK_UP:
151
        m_draw3dOffset.y += delta_move;
152 153 154
        break;

    case WXK_DOWN:
155
        m_draw3dOffset.y -= delta_move;
156 157 158
        break;

    case WXK_HOME:
159
        GetPrm3DVisu().m_Zoom = 1.0;
160
        m_draw3dOffset.x = m_draw3dOffset.y = 0;
161
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
162 163 164 165 166 167
        break;

    case WXK_END:
        break;

    case WXK_F1:
168 169 170
        GetPrm3DVisu().m_Zoom /= 1.4;
        if( GetPrm3DVisu().m_Zoom <= 0.01 )
            GetPrm3DVisu().m_Zoom = 0.01;
171 172 173
        break;

    case WXK_F2:
174
        GetPrm3DVisu().m_Zoom *= 1.4;
175 176 177 178 179 180 181 182 183 184
        break;

    case '+':
        break;

    case '-':
        break;

    case 'r':
    case 'R':
185
        m_draw3dOffset.x = m_draw3dOffset.y = 0;
186
        for( ii = 0; ii < 4; ii++ )
187
            GetPrm3DVisu().m_Rot[ii] = 0.0;
188

189
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
190 191 192 193
        break;

    case 'x':
        for( ii = 0; ii < 4; ii++ )
194
            GetPrm3DVisu().m_Rot[ii] = 0.0;
195

196 197 198
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTZ = -90;
        GetPrm3DVisu().m_ROTX = -90;
199 200 201 202
        break;

    case 'X':
        for( ii = 0; ii < 4; ii++ )
203
            GetPrm3DVisu().m_Rot[ii] = 0.0;
204

205 206 207
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTZ = 90;
        GetPrm3DVisu().m_ROTX = -90;
208 209 210 211
        break;

    case 'y':
        for( ii = 0; ii < 4; ii++ )
212
            GetPrm3DVisu().m_Rot[ii] = 0.0;
213

214 215
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTX = -90;
216 217 218 219
        break;

    case 'Y':
        for( ii = 0; ii < 4; ii++ )
220
            GetPrm3DVisu().m_Rot[ii] = 0.0;
221

222 223 224
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTX = -90;
        GetPrm3DVisu().m_ROTZ = -180;
225 226 227 228
        break;

    case 'z':
        for( ii = 0; ii < 4; ii++ )
229
            GetPrm3DVisu().m_Rot[ii] = 0.0;
230

231
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
232 233 234 235
        break;

    case 'Z':
        for( ii = 0; ii < 4; ii++ )
236
            GetPrm3DVisu().m_Rot[ii] = 0.0;
237

238 239
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTX = -180;
240 241 242 243 244 245 246
        break;

    default:
        return;
    }

    DisplayStatus();
247
    Refresh( false );
plyatov's avatar
plyatov committed
248 249
}

250

251
void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
plyatov's avatar
plyatov committed
252
{
253 254 255
    double delta = 0.05 * GetPrm3DVisu().m_Zoom * event.GetWheelRotation();

    if( event.ShiftDown() || true )
256
    {
257 258
        if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL )
            m_draw3dOffset.x -= delta;
259
        else
260
            m_draw3dOffset.y -= delta;
261
    }
262
    else if( event.ControlDown() )
263
    {
264
        m_draw3dOffset.y -= delta;
265 266 267 268
    }
    else
    {
        if( event.GetWheelRotation() > 0 )
269
        {
270
            GetPrm3DVisu().m_Zoom /= 1.4;
271

272 273
            if( GetPrm3DVisu().m_Zoom <= 0.01 )
                GetPrm3DVisu().m_Zoom = 0.01;
274
        }
275
        else
276
            GetPrm3DVisu().m_Zoom *= 1.4;
277

278
    }
plyatov's avatar
plyatov committed
279

280 281
    DisplayStatus();
    Refresh( false );
282 283
    GetPrm3DVisu().m_Beginx = event.GetX();
    GetPrm3DVisu().m_Beginy = event.GetY();
284 285
}

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
#ifdef USE_OSX_MAGNIFY_EVENT
void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
{
    double magnification = (event.GetMagnification() + 1.0f);

    GetPrm3DVisu().m_Zoom /= magnification;

    if( GetPrm3DVisu().m_Zoom <= 0.01 ) {
        GetPrm3DVisu().m_Zoom = 0.01;
    }

    DisplayStatus();
    Refresh( false );
}
#endif
301

302
void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
303 304 305 306
{
    wxSize size( GetClientSize() );
    double spin_quat[4];

307
    if( event.Dragging() )
plyatov's avatar
plyatov committed
308
    {
309 310 311 312
        if( event.LeftIsDown() )
        {
            /* drag in progress, simulate trackball */
            trackball( spin_quat,
313 314
                       (2.0 * GetPrm3DVisu().m_Beginx - size.x) / size.x,
                       (size.y - 2.0 * GetPrm3DVisu().m_Beginy) / size.y,
315 316 317
                       (     2.0 * event.GetX() - size.x) / size.x,
                       ( size.y - 2.0 * event.GetY() ) / size.y );

318
            add_quats( spin_quat, GetPrm3DVisu().m_Quat, GetPrm3DVisu().m_Quat );
319 320 321 322
        }
        else if( event.MiddleIsDown() )
        {
            /* middle button drag -> pan */
323 324 325

            /* Current zoom and an additional factor are taken into account
             * for the amount of panning. */
326
            const double PAN_FACTOR = 8.0 * GetPrm3DVisu().m_Zoom;
327
            m_draw3dOffset.x -= PAN_FACTOR *
328
                           ( GetPrm3DVisu().m_Beginx - event.GetX() ) / size.x;
329
            m_draw3dOffset.y -= PAN_FACTOR *
330
                           (event.GetY() - GetPrm3DVisu().m_Beginy) / size.y;
331
        }
plyatov's avatar
plyatov committed
332 333

        /* orientation has changed, redraw mesh */
334
        DisplayStatus();
335
        Refresh( false );
plyatov's avatar
plyatov committed
336 337
    }

338 339
    GetPrm3DVisu().m_Beginx = event.GetX();
    GetPrm3DVisu().m_Beginy = event.GetY();
plyatov's avatar
plyatov committed
340 341
}

342

343
/* Construct and display a popup menu when the right button is clicked.
344
 */
345
void EDA_3D_CANVAS::OnRightClick( wxMouseEvent& event )
plyatov's avatar
plyatov committed
346
{
347 348 349
    wxPoint     pos;
    wxMenu      PopUpMenu;

350 351 352 353
    pos.x = event.GetX();
    pos.y = event.GetY();

    wxMenuItem* item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN, _( "Zoom +" ) );
354
    item->SetBitmap( KiBitmap( zoom_in_xpm ));
355 356
    PopUpMenu.Append( item );

357
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT, _( "Zoom -" ) );
358
    item->SetBitmap( KiBitmap( zoom_out_xpm ));
359 360 361
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
362
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS, _( "Top View" ) );
363
    item->SetBitmap( KiBitmap( axis3d_top_xpm ));
364 365
    PopUpMenu.Append( item );

366
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG, _( "Bottom View" ) );
367
    item->SetBitmap( KiBitmap( axis3d_bottom_xpm ));
368 369 370
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
371
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS, _( "Right View" ) );
372
    item->SetBitmap( KiBitmap( axis3d_right_xpm ));
373 374
    PopUpMenu.Append( item );

375
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG, _( "Left View" ) );
376
    item->SetBitmap( KiBitmap( axis3d_left_xpm ));
377 378 379
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
380
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS, _( "Front View" ) );
381
    item->SetBitmap( KiBitmap( axis3d_front_xpm ));
382 383
    PopUpMenu.Append( item );

384
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG, _( "Back View" ) );
385
    item->SetBitmap( KiBitmap( axis3d_back_xpm ));
386 387 388
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
389
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT, _( "Move left <-" ) );
390
    item->SetBitmap( KiBitmap( left_xpm ));
391 392
    PopUpMenu.Append( item );

393
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT, _( "Move right ->" ) );
394
    item->SetBitmap( KiBitmap( right_xpm ));
395 396
    PopUpMenu.Append( item );

397
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP, _( "Move Up ^" ) );
398
    item->SetBitmap( KiBitmap( up_xpm ));
399 400
    PopUpMenu.Append( item );

401
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN, _( "Move Down" ) );
402
    item->SetBitmap( KiBitmap( down_xpm ));
403 404 405
    PopUpMenu.Append( item );

    PopupMenu( &PopUpMenu, pos );
plyatov's avatar
plyatov committed
406 407
}

408

409
void EDA_3D_CANVAS::OnPopUpMenu( wxCommandEvent& event )
plyatov's avatar
plyatov committed
410
{
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
    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 );
plyatov's avatar
plyatov committed
468 469 470
}


471
void EDA_3D_CANVAS::DisplayStatus()
plyatov's avatar
plyatov committed
472
{
473 474
    wxString msg;

475 476
    msg.Printf( wxT( "dx %3.2f" ), m_draw3dOffset.x );
    Parent()->SetStatusText( msg, 1 );
plyatov's avatar
plyatov committed
477

478 479
    msg.Printf( wxT( "dy %3.2f" ), m_draw3dOffset.y );
    Parent()->SetStatusText( msg, 2 );
plyatov's avatar
plyatov committed
480

481
    msg.Printf( wxT( "View: %3.1f" ), 45 * GetPrm3DVisu().m_Zoom );
482
    Parent()->SetStatusText( msg, 3 );
plyatov's avatar
plyatov committed
483 484 485
}


486
void EDA_3D_CANVAS::OnPaint( wxPaintEvent& event )
plyatov's avatar
plyatov committed
487
{
488
    wxPaintDC dc( this );
489

490 491
    Redraw();
    event.Skip();
plyatov's avatar
plyatov committed
492 493 494
}


495
void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
plyatov's avatar
plyatov committed
496
{
497
    // Do nothing, to avoid flashing.
plyatov's avatar
plyatov committed
498 499
}

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
typedef struct s_sImage
{
  unsigned int   width;
  unsigned int   height;
  unsigned int   bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */
  unsigned char  pixel_data[64 * 64 * 4 + 1];
}tsImage;


GLuint load_and_generate_texture( tsImage *image )
{

    GLuint texture;
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei (GL_PACK_ALIGNMENT, 1);

    glGenTextures( 1, &texture );
    glBindTexture( GL_TEXTURE_2D, texture );
518 519
    gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGBA, image->width, image->height,
                       GL_RGBA, GL_UNSIGNED_BYTE, image->pixel_data );
520 521 522

    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
523 524
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
525 526 527 528

    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
    return texture;
}
plyatov's avatar
plyatov committed
529

530
/* Initialize broad parameters for OpenGL */
531
void EDA_3D_CANVAS::InitGL()
plyatov's avatar
plyatov committed
532
{
533 534
    if( !m_init )
    {
535
        m_init = true;
536 537 538 539 540


        m_text_pcb = load_and_generate_texture( (tsImage *)&text_pcb  );
        m_text_silk = load_and_generate_texture( (tsImage *)&text_silk );

541
        GetPrm3DVisu().m_Zoom = 1.0;
542 543
        m_ZBottom = 1.0;
        m_ZTop = 10.0;
544 545 546

        glDisable( GL_CULL_FACE );      // show back faces
        glEnable( GL_DEPTH_TEST );      // Enable z-buferring
547
        glEnable( GL_ALPHA_TEST );
548
        glEnable( GL_LINE_SMOOTH );
549
//        glEnable(GL_POLYGON_SMOOTH);  // creates issues with some graphic cards
550
        glEnable( GL_NORMALIZE );
551 552 553
        glEnable( GL_COLOR_MATERIAL );
        glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );

554
        // speedups
555
        //glEnable( GL_DITHER );
556 557
        glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE );
        glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
558
        glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
559

560
        // Initialize alpha blending function.
561 562
        glEnable( GL_BLEND );
        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
563
    }
plyatov's avatar
plyatov committed
564 565 566
}


567
/* Initialize OpenGL light sources. */
568
void EDA_3D_CANVAS::SetLights()
plyatov's avatar
plyatov committed
569
{
570 571
    // activate light. the source is above the xy plane, at source_pos
    GLfloat source_pos[4]    = { 0.0, 0.0, 30.0, 0.0 };
572 573 574 575
    GLfloat light_color[4];     // color of lights (RGBA values)
    light_color[3] = 1.0;

    // Light above the xy plane
576 577 578
    light_color[0] = light_color[1] = light_color[2] = 0.1;
    glLightfv( GL_LIGHT0, GL_AMBIENT, light_color );

579
    light_color[0] = light_color[1] = light_color[2] = 1.0;
580
    glLightfv( GL_LIGHT0, GL_DIFFUSE, light_color );
581

582
    light_color[0] = light_color[1] = light_color[2] = 1.0;
583
    glLightfv( GL_LIGHT0, GL_SPECULAR, light_color );
584

585
    glLightfv( GL_LIGHT0, GL_POSITION, source_pos );
586 587 588 589

    light_color[0] = light_color[1] = light_color[2] = 0.1;
    glLightModelfv( GL_LIGHT_MODEL_AMBIENT, light_color );

590
    glEnable( GL_LIGHT0 );      // White spot on Z axis ( top )
591
    glEnable( GL_LIGHTING );
plyatov's avatar
plyatov committed
592 593 594 595
}


/* Create a Screenshot of the current 3D view.
596
 *  Output file format is png or jpeg, or image is copied to the clipboard
597
 */
598
void EDA_3D_CANVAS::TakeScreenshot( wxCommandEvent& event )
plyatov's avatar
plyatov committed
599
{
600
    wxFileName fn( Parent()->GetDefaultFileName() );
601 602
    wxString   FullFileName;
    wxString   file_ext, mask;
603
    bool       fmt_is_jpeg = false;
604 605

    if( event.GetId() == ID_MENU_SCREENCOPY_JPEG )
606 607
        fmt_is_jpeg = true;

608 609
    if( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD )
    {
610 611
        file_ext     = fmt_is_jpeg ? wxT( "jpg" ) : wxT( "png" );
        mask         = wxT( "*." ) + file_ext;
612
        FullFileName = Parent()->GetDefaultFileName();
613
        fn.SetExt( file_ext );
614

615 616 617
        FullFileName = EDA_FileSelector( _( "3D Image filename:" ), wxEmptyString,
                                         fn.GetFullName(), file_ext, mask, this,
                                         wxFD_SAVE, true );
618

619 620
        if( FullFileName.IsEmpty() )
            return;
621 622 623 624 625

        // Be sure the screen area destroyed by the file dialog is redrawn before making
        // a screen copy.
        // Without this call, under Linux the screen refresh is made to late.
        wxYield();
626 627
    }

628
    struct viewport_params
629 630 631 632 633 634
    {
        GLint originx;
        GLint originy;
        GLint x;
        GLint y;
    } viewport;
635

636 637 638 639
    // Be sure we have the latest 3D view (remember 3D view is buffered)
    Refresh();
    wxYield();

640
    // Build image from the 3D buffer
641 642 643 644 645 646 647
    wxWindowUpdateLocker noUpdates( this );
    glGetIntegerv( GL_VIEWPORT, (GLint*) &viewport );

    unsigned char*       pixelbuffer = (unsigned char*) malloc( viewport.x * viewport.y * 3 );
    unsigned char*       alphabuffer = (unsigned char*) malloc( viewport.x * viewport.y );
    wxImage image( viewport.x, viewport.y );

charras's avatar
charras committed
648
    glPixelStorei( GL_PACK_ALIGNMENT, 1 );
649
    glReadBuffer( GL_BACK_LEFT );
650 651 652 653 654 655
    glReadPixels( viewport.originx, viewport.originy,
                  viewport.x, viewport.y,
                  GL_RGB, GL_UNSIGNED_BYTE, pixelbuffer );
    glReadPixels( viewport.originx, viewport.originy,
                  viewport.x, viewport.y,
                  GL_ALPHA, GL_UNSIGNED_BYTE, alphabuffer );
656 657 658 659 660

    image.SetData( pixelbuffer );
    image.SetAlpha( alphabuffer );
    image = image.Mirror( false );
    wxBitmap bitmap( image );
plyatov's avatar
plyatov committed
661

662 663 664 665
    if( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD )
    {
        wxBitmapDataObject* dobjBmp = new wxBitmapDataObject;
        dobjBmp->SetBitmap( bitmap );
666

667 668 669
        if( wxTheClipboard->Open() )
        {
            if( !wxTheClipboard->SetData( dobjBmp ) )
670 671
                wxMessageBox( _( "Failed to copy image to clipboard" ) );

672 673 674
            wxTheClipboard->Flush();    /* the data in clipboard will stay
                                         * available after the
                                         * application exits */
675 676 677 678 679
            wxTheClipboard->Close();
        }
    }
    else
    {
680 681
        wxImage image = bitmap.ConvertToImage();

682
        if( !image.SaveFile( FullFileName,
683
                             fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
684
            wxMessageBox( _( "Can't save file" ) );
plyatov's avatar
plyatov committed
685

686 687 688
        image.Destroy();
    }
}