3d_canvas.cpp 18.7 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 56 57
// -----------------
// helper function (from wxWidgets, opengl/cube.cpp sample
// -----------------
58
void CheckGLError(const char *aFileName, int aLineNumber)
59 60 61
{
    GLenum errLast = GL_NO_ERROR;

62
    for ( ; ; )
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
    {
        GLenum err = glGetError();
        if ( err == GL_NO_ERROR )
            return;

        // normally the error is reset by the call to glGetError() but if
        // glGetError() itself returns an error, we risk looping forever here
        // so check that we get a different error than the last time
        if ( err == errLast )
        {
            wxLogError(wxT("OpenGL error state couldn't be reset."));
            return;
        }

        errLast = err;
78 79 80

        wxLogError( wxT( "OpenGL error %d At: %s, line: %d" ), err,
                    GetChars( FROM_UTF8( aFileName ) ), aLineNumber );
81 82 83
    }
}

84

plyatov's avatar
plyatov committed
85
/*
86
 * EDA_3D_CANVAS implementation
plyatov's avatar
plyatov committed
87 88
 */

89 90
BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
    EVT_PAINT( EDA_3D_CANVAS::OnPaint )
91

92
    // key event:
93
    EVT_CHAR( EDA_3D_CANVAS::OnChar )
94

95
    // mouse events
96 97 98
    EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightClick )
    EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
    EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
99

100
    // other events
101 102
    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
103 104
END_EVENT_TABLE()

105

106
EDA_3D_CANVAS::EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList ) :
107
    wxGLCanvas( parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
108
                wxFULL_REPAINT_ON_RESIZE )
plyatov's avatar
plyatov committed
109
{
110
    m_init   = false;
111
    m_shadow_init = false;
112 113 114 115 116

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

117
    // Explicitly create a new rendering context instance for this canvas.
118
    m_glRC = new wxGLContext( this );
119

120
    DisplayStatus();
plyatov's avatar
plyatov committed
121 122 123
}


124
EDA_3D_CANVAS::~EDA_3D_CANVAS()
plyatov's avatar
plyatov committed
125
{
126
    ClearLists();
127
    m_init = false;
128
    delete m_glRC;
plyatov's avatar
plyatov committed
129 130
}

131

132
void EDA_3D_CANVAS::ClearLists( int aGlList )
plyatov's avatar
plyatov committed
133
{
134 135 136 137
    if( aGlList )
    {
        if( m_glLists[aGlList] > 0 )
            glDeleteLists( m_glLists[aGlList], 1 );
138

139 140 141 142 143 144 145 146 147 148 149 150
        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
151 152
}

153

154
void EDA_3D_CANVAS::OnChar( wxKeyEvent& event )
plyatov's avatar
plyatov committed
155
{
156 157
    SetView3D( event.GetKeyCode() );
    event.Skip();
plyatov's avatar
plyatov committed
158 159
}

160

161
void EDA_3D_CANVAS::SetView3D( int keycode )
plyatov's avatar
plyatov committed
162
{
163
    int    ii;
164
    double delta_move = 0.7 * GetPrm3DVisu().m_Zoom;
165 166 167 168

    switch( keycode )
    {
    case WXK_LEFT:
169
        m_draw3dOffset.x -= delta_move;
170 171 172
        break;

    case WXK_RIGHT:
173
        m_draw3dOffset.x += delta_move;
174 175 176
        break;

    case WXK_UP:
177
        m_draw3dOffset.y += delta_move;
178 179 180
        break;

    case WXK_DOWN:
181
        m_draw3dOffset.y -= delta_move;
182 183 184
        break;

    case WXK_HOME:
185
        GetPrm3DVisu().m_Zoom = 1.0;
186
        m_draw3dOffset.x = m_draw3dOffset.y = 0;
187
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
188 189 190 191 192 193
        break;

    case WXK_END:
        break;

    case WXK_F1:
194 195 196
        GetPrm3DVisu().m_Zoom /= 1.4;
        if( GetPrm3DVisu().m_Zoom <= 0.01 )
            GetPrm3DVisu().m_Zoom = 0.01;
197 198 199
        break;

    case WXK_F2:
200
        GetPrm3DVisu().m_Zoom *= 1.4;
201 202 203 204 205 206 207 208 209 210
        break;

    case '+':
        break;

    case '-':
        break;

    case 'r':
    case 'R':
211
        m_draw3dOffset.x = m_draw3dOffset.y = 0;
212
        for( ii = 0; ii < 4; ii++ )
213
            GetPrm3DVisu().m_Rot[ii] = 0.0;
214

215
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
216 217 218 219
        break;

    case 'x':
        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_ROTZ = -90;
        GetPrm3DVisu().m_ROTX = -90;
225 226 227 228
        break;

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

231 232 233
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTZ = 90;
        GetPrm3DVisu().m_ROTX = -90;
234 235 236 237
        break;

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

240 241
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTX = -90;
242 243 244 245
        break;

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

248 249 250
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTX = -90;
        GetPrm3DVisu().m_ROTZ = -180;
251 252 253 254
        break;

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

257
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
258 259 260 261
        break;

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

264 265
        trackball( GetPrm3DVisu().m_Quat, 0.0, 0.0, 0.0, 0.0 );
        GetPrm3DVisu().m_ROTX = -180;
266 267 268 269 270 271 272
        break;

    default:
        return;
    }

    DisplayStatus();
273
    Refresh( false );
plyatov's avatar
plyatov committed
274 275
}

276

277
void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
plyatov's avatar
plyatov committed
278
{
279
    if( event.ShiftDown() )
280
    {
281
        if( event.GetWheelRotation() < 0 )
282
            SetView3D( WXK_UP );    // move up
283
        else
284
            SetView3D( WXK_DOWN );  // move down
285
    }
286
    else if( event.ControlDown() )
287
    {
288
        if( event.GetWheelRotation() > 0 )
289
            SetView3D( WXK_RIGHT ); // move right
290
        else
291
            SetView3D( WXK_LEFT );  // move left
292 293 294 295
    }
    else
    {
        if( event.GetWheelRotation() > 0 )
296
        {
297
            GetPrm3DVisu().m_Zoom /= 1.4;
298

299 300
            if( GetPrm3DVisu().m_Zoom <= 0.01 )
                GetPrm3DVisu().m_Zoom = 0.01;
301
        }
302
        else
303
            GetPrm3DVisu().m_Zoom *= 1.4;
304

305
        DisplayStatus();
306
        Refresh( false );
307
    }
plyatov's avatar
plyatov committed
308

309 310
    GetPrm3DVisu().m_Beginx = event.GetX();
    GetPrm3DVisu().m_Beginy = event.GetY();
311 312 313
}


314
void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
315 316 317 318
{
    wxSize size( GetClientSize() );
    double spin_quat[4];

319
    if( event.Dragging() )
plyatov's avatar
plyatov committed
320
    {
321 322 323 324
        if( event.LeftIsDown() )
        {
            /* drag in progress, simulate trackball */
            trackball( spin_quat,
325 326
                       (2.0 * GetPrm3DVisu().m_Beginx - size.x) / size.x,
                       (size.y - 2.0 * GetPrm3DVisu().m_Beginy) / size.y,
327 328 329
                       (     2.0 * event.GetX() - size.x) / size.x,
                       ( size.y - 2.0 * event.GetY() ) / size.y );

330
            add_quats( spin_quat, GetPrm3DVisu().m_Quat, GetPrm3DVisu().m_Quat );
331 332 333 334
        }
        else if( event.MiddleIsDown() )
        {
            /* middle button drag -> pan */
335 336 337

            /* Current zoom and an additional factor are taken into account
             * for the amount of panning. */
338
            const double PAN_FACTOR = 8.0 * GetPrm3DVisu().m_Zoom;
339
            m_draw3dOffset.x -= PAN_FACTOR *
340
                           ( GetPrm3DVisu().m_Beginx - event.GetX() ) / size.x;
341
            m_draw3dOffset.y -= PAN_FACTOR *
342
                           (event.GetY() - GetPrm3DVisu().m_Beginy) / size.y;
343
        }
plyatov's avatar
plyatov committed
344 345

        /* orientation has changed, redraw mesh */
346
        DisplayStatus();
347
        Refresh( false );
plyatov's avatar
plyatov committed
348 349
    }

350 351
    GetPrm3DVisu().m_Beginx = event.GetX();
    GetPrm3DVisu().m_Beginy = event.GetY();
plyatov's avatar
plyatov committed
352 353
}

354

355
/* Construct and display a popup menu when the right button is clicked.
356
 */
357
void EDA_3D_CANVAS::OnRightClick( wxMouseEvent& event )
plyatov's avatar
plyatov committed
358
{
359 360 361
    wxPoint     pos;
    wxMenu      PopUpMenu;

362 363 364 365
    pos.x = event.GetX();
    pos.y = event.GetY();

    wxMenuItem* item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN, _( "Zoom +" ) );
366
    item->SetBitmap( KiBitmap( zoom_in_xpm ));
367 368
    PopUpMenu.Append( item );

369
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT, _( "Zoom -" ) );
370
    item->SetBitmap( KiBitmap( zoom_out_xpm ));
371 372 373
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
374
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS, _( "Top View" ) );
375
    item->SetBitmap( KiBitmap( axis3d_top_xpm ));
376 377
    PopUpMenu.Append( item );

378
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG, _( "Bottom View" ) );
379
    item->SetBitmap( KiBitmap( axis3d_bottom_xpm ));
380 381 382
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
383
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS, _( "Right View" ) );
384
    item->SetBitmap( KiBitmap( axis3d_right_xpm ));
385 386
    PopUpMenu.Append( item );

387
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG, _( "Left View" ) );
388
    item->SetBitmap( KiBitmap( axis3d_left_xpm ));
389 390 391
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
392
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS, _( "Front View" ) );
393
    item->SetBitmap( KiBitmap( axis3d_front_xpm ));
394 395
    PopUpMenu.Append( item );

396
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG, _( "Back View" ) );
397
    item->SetBitmap( KiBitmap( axis3d_back_xpm ));
398 399 400
    PopUpMenu.Append( item );

    PopUpMenu.AppendSeparator();
401
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT, _( "Move left <-" ) );
402
    item->SetBitmap( KiBitmap( left_xpm ));
403 404
    PopUpMenu.Append( item );

405
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT, _( "Move right ->" ) );
406
    item->SetBitmap( KiBitmap( right_xpm ));
407 408
    PopUpMenu.Append( item );

409
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP, _( "Move Up ^" ) );
410
    item->SetBitmap( KiBitmap( up_xpm ));
411 412
    PopUpMenu.Append( item );

413
    item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN, _( "Move Down" ) );
414
    item->SetBitmap( KiBitmap( down_xpm ));
415 416 417
    PopUpMenu.Append( item );

    PopupMenu( &PopUpMenu, pos );
plyatov's avatar
plyatov committed
418 419
}

420

421
void EDA_3D_CANVAS::OnPopUpMenu( wxCommandEvent& event )
plyatov's avatar
plyatov committed
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 468 469 470 471 472 473 474 475 476 477 478 479
    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
480 481 482
}


483
void EDA_3D_CANVAS::DisplayStatus()
plyatov's avatar
plyatov committed
484
{
485 486
    wxString msg;

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

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

493
    msg.Printf( wxT( "View: %3.1f" ), 45 * GetPrm3DVisu().m_Zoom );
494
    Parent()->SetStatusText( msg, 3 );
plyatov's avatar
plyatov committed
495 496 497
}


498
void EDA_3D_CANVAS::OnPaint( wxPaintEvent& event )
plyatov's avatar
plyatov committed
499
{
500
    wxPaintDC dc( this );
501

502 503
    Redraw();
    event.Skip();
plyatov's avatar
plyatov committed
504 505 506
}


507
void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
plyatov's avatar
plyatov committed
508
{
509
    // Do nothing, to avoid flashing.
plyatov's avatar
plyatov committed
510 511
}

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
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 );
530 531
    gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGBA, image->width, image->height,
                       GL_RGBA, GL_UNSIGNED_BYTE, image->pixel_data );
532 533 534

    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
535 536
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
537 538 539 540

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

542
/* Initialize broad parameters for OpenGL */
543
void EDA_3D_CANVAS::InitGL()
plyatov's avatar
plyatov committed
544
{
545 546
    if( !m_init )
    {
547
        m_init = true;
548 549 550 551 552


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

553
        GetPrm3DVisu().m_Zoom = 1.0;
554 555
        m_ZBottom = 1.0;
        m_ZTop = 10.0;
556 557 558

        glDisable( GL_CULL_FACE );      // show back faces
        glEnable( GL_DEPTH_TEST );      // Enable z-buferring
559
        glEnable( GL_ALPHA_TEST );
560
        glEnable( GL_LINE_SMOOTH );
561
//        glEnable(GL_POLYGON_SMOOTH);  // creates issues with some graphic cards
562
        glEnable( GL_NORMALIZE );
563 564 565
        glEnable( GL_COLOR_MATERIAL );
        glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );

566
        // speedups
567
        //glEnable( GL_DITHER );
568 569
        glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE );
        glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
570
        glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
571

572
        // Initialize alpha blending function.
573 574
        glEnable( GL_BLEND );
        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
575
    }
plyatov's avatar
plyatov committed
576 577 578
}


579
/* Initialize OpenGL light sources. */
580
void EDA_3D_CANVAS::SetLights()
plyatov's avatar
plyatov committed
581
{
582 583
    // activate light. the source is above the xy plane, at source_pos
    GLfloat source_pos[4]    = { 0.0, 0.0, 30.0, 0.0 };
584 585 586 587
    GLfloat light_color[4];     // color of lights (RGBA values)
    light_color[3] = 1.0;

    // Light above the xy plane
588 589 590
    light_color[0] = light_color[1] = light_color[2] = 0.1;
    glLightfv( GL_LIGHT0, GL_AMBIENT, light_color );

591
    light_color[0] = light_color[1] = light_color[2] = 1.0;
592
    glLightfv( GL_LIGHT0, GL_DIFFUSE, light_color );
593

594
    light_color[0] = light_color[1] = light_color[2] = 1.0;
595
    glLightfv( GL_LIGHT0, GL_SPECULAR, light_color );
596

597
    glLightfv( GL_LIGHT0, GL_POSITION, source_pos );
598 599 600 601

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

602
    glEnable( GL_LIGHT0 );      // White spot on Z axis ( top )
603
    glEnable( GL_LIGHTING );
plyatov's avatar
plyatov committed
604 605 606 607
}


/* Create a Screenshot of the current 3D view.
608
 *  Output file format is png or jpeg, or image is copied to the clipboard
609
 */
610
void EDA_3D_CANVAS::TakeScreenshot( wxCommandEvent& event )
plyatov's avatar
plyatov committed
611
{
612
    wxFileName fn( Parent()->GetDefaultFileName() );
613 614
    wxString   FullFileName;
    wxString   file_ext, mask;
615
    bool       fmt_is_jpeg = false;
616 617

    if( event.GetId() == ID_MENU_SCREENCOPY_JPEG )
618 619
        fmt_is_jpeg = true;

620 621
    if( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD )
    {
622 623
        file_ext     = fmt_is_jpeg ? wxT( "jpg" ) : wxT( "png" );
        mask         = wxT( "*." ) + file_ext;
624
        FullFileName = Parent()->GetDefaultFileName();
625
        fn.SetExt( file_ext );
626

627 628 629
        FullFileName = EDA_FileSelector( _( "3D Image filename:" ), wxEmptyString,
                                         fn.GetFullName(), file_ext, mask, this,
                                         wxFD_SAVE, true );
630

631 632
        if( FullFileName.IsEmpty() )
            return;
633 634 635 636 637

        // 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();
638 639
    }

640
    struct viewport_params
641 642 643 644 645 646
    {
        GLint originx;
        GLint originy;
        GLint x;
        GLint y;
    } viewport;
647

648 649 650 651
    // Be sure we have the latest 3D view (remember 3D view is buffered)
    Refresh();
    wxYield();

652
    // Build image from the 3D buffer
653 654 655 656 657 658 659
    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
660
    glPixelStorei( GL_PACK_ALIGNMENT, 1 );
661
    glReadBuffer( GL_BACK_LEFT );
662 663 664 665 666 667
    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 );
668 669 670 671 672

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

674 675 676 677
    if( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD )
    {
        wxBitmapDataObject* dobjBmp = new wxBitmapDataObject;
        dobjBmp->SetBitmap( bitmap );
678

679 680 681
        if( wxTheClipboard->Open() )
        {
            if( !wxTheClipboard->SetData( dobjBmp ) )
682 683
                wxMessageBox( _( "Failed to copy image to clipboard" ) );

684 685 686
            wxTheClipboard->Flush();    /* the data in clipboard will stay
                                         * available after the
                                         * application exits */
687 688 689 690 691
            wxTheClipboard->Close();
        }
    }
    else
    {
692 693
        wxImage image = bitmap.ConvertToImage();

694
        if( !image.SaveFile( FullFileName,
695
                             fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
696
            wxMessageBox( _( "Can't save file" ) );
plyatov's avatar
plyatov committed
697

698 699 700
        image.Destroy();
    }
}