opengl_gal.cpp 33.1 KB
Newer Older
1 2 3 4 5
/*
 * This program source code file is part of KICAD, a free EDA CAD application.
 *
 * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
 * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors.
6 7
 * Copyright (C) 2013 CERN
 * @author Maciej Suminski <maciej.suminski@cern.ch>
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * Graphics Abstraction Layer (GAL) for OpenGL
 *
 * 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
 */

#include <gal/opengl/opengl_gal.h>
#include <gal/definitions.h>

#include <wx/log.h>
33
#include <macros.h>
34
#include <confirm.h>
35 36 37
#ifdef __WXDEBUG__
#include <profile.h>
#endif /* __WXDEBUG__ */
38

39 40
#include <limits>

41
using namespace KIGFX;
42 43 44 45 46 47

// Prototypes
void InitTesselatorCallbacks( GLUtesselator* aTesselator );

const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0 };

48 49
wxGLContext* OPENGL_GAL::glContext = NULL;

50
OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
51
                        wxEvtHandler* aPaintListener, const wxString& aName ) :
52
    wxGLCanvas( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize,
53
                wxEXPAND, aName ),
54
    cachedManager( true ),
55 56
    nonCachedManager( false ),
    overlayManager( false )
57
{
58
    // Create the OpenGL-Context
59 60 61
    if( glContext == NULL )
        glContext = new wxGLContext( this );

62 63 64 65 66 67
    parentWindow    = aParent;
    mouseListener   = aMouseListener;
    paintListener   = aPaintListener;

    // Initialize the flags
    isGlewInitialized        = false;
68
    isFramebufferInitialized = false;
69
    isShaderInitialized      = false;
70
    isGrouping               = false;
71
    groupCounter             = 0;
72

73
    // Connecting the event handlers
74
    Connect( wxEVT_PAINT,       wxPaintEventHandler( OPENGL_GAL::onPaint ) );
75 76

    // Mouse events are skipped to the parent
77 78 79 80 81 82 83 84 85 86
    Connect( wxEVT_MOTION,          wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_LEFT_DOWN,       wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_LEFT_UP,         wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_LEFT_DCLICK,     wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_MIDDLE_DOWN,     wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_MIDDLE_UP,       wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_MIDDLE_DCLICK,   wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_RIGHT_DOWN,      wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_RIGHT_UP,        wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
    Connect( wxEVT_RIGHT_DCLICK,    wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
87
    Connect( wxEVT_MOUSEWHEEL,      wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
88
#if defined _WIN32 || defined _WIN64
89
    Connect( wxEVT_ENTER_WINDOW,    wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
90
#endif
91

92
    SetSize( aParent->GetSize() );
93
    screenSize = VECTOR2I( aParent->GetSize() );
94

Maciej Suminski's avatar
Maciej Suminski committed
95 96 97
    // Grid color settings are different in Cairo and OpenGL
    SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );

98 99 100
    // Tesselator initialization
    tesselator = gluNewTess();
    InitTesselatorCallbacks( tesselator );
101

102 103
    if( tesselator == NULL )
    {
104 105
        DisplayError( parentWindow, wxT( "Could not create the tesselator" ) );
        exit( 1 );
106
    }
107

108
    gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
109 110

    currentManager = &nonCachedManager;
111 112 113 114 115 116 117
}


OPENGL_GAL::~OPENGL_GAL()
{
    glFlush();

118
    gluDeleteTess( tesselator );
119
    ClearCache();
120 121 122 123 124 125 126
}


void OPENGL_GAL::BeginDrawing()
{
    SetCurrent( *glContext );

127
    clientDC = new wxClientDC( this );
128

129
    // Initialize GLEW, FBOs & VBOs
130 131 132
    if( !isGlewInitialized )
        initGlew();

133 134 135 136
    // Set up the view port
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glViewport( 0, 0, (GLsizei) screenSize.x, (GLsizei) screenSize.y );
137

138 139
    // Create the screen transformation
    glOrtho( 0, (GLint) screenSize.x, 0, (GLsizei) screenSize.y, -depthRange.x, -depthRange.y );
140

141 142
    if( !isFramebufferInitialized )
    {
143 144
        // Prepare rendering target buffers
        compositor.Initialize();
145 146
        mainBuffer = compositor.CreateBuffer();
        overlayBuffer = compositor.CreateBuffer();
147 148 149

        isFramebufferInitialized = true;
    }
150 151

    // Compile the shaders
152
    if( !isShaderInitialized )
153
    {
154
        if( !shader.LoadBuiltinShader( 0, SHADER_TYPE_VERTEX ) )
155 156 157 158
        {
            DisplayError( parentWindow, wxT( "Cannot compile vertex shader!" ) );
            exit( 1 );
        }
159

160
        if( !shader.LoadBuiltinShader( 1, SHADER_TYPE_FRAGMENT ) )
161 162 163 164
        {
            DisplayError( parentWindow, wxT( "Cannot compile fragment shader!" ) );
            exit( 1 );
        }
165

Maciej Suminski's avatar
Maciej Suminski committed
166
        if( !shader.Link() )
167 168 169 170
        {
            DisplayError( parentWindow, wxT( "Cannot link the shaders!" ) );
            exit( 1 );
        }
171

172 173 174
        // Make VBOs use shaders
        cachedManager.SetShader( shader );
        nonCachedManager.SetShader( shader );
175
        overlayManager.SetShader( shader );
176 177 178 179 180 181 182 183 184 185

        isShaderInitialized = true;
    }

    // Disable 2D Textures
    glDisable( GL_TEXTURE_2D );

    // Enable the depth buffer
    glEnable( GL_DEPTH_TEST );
    glDepthFunc( GL_LESS );
186

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
    // Setup blending, required for transparent objects
    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

    glMatrixMode( GL_MODELVIEW );

    // Set up the world <-> screen transformation
    ComputeWorldScreenMatrix();
    GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
    matrixData[0]   = worldScreenMatrix.m_data[0][0];
    matrixData[1]   = worldScreenMatrix.m_data[1][0];
    matrixData[2]   = worldScreenMatrix.m_data[2][0];
    matrixData[4]   = worldScreenMatrix.m_data[0][1];
    matrixData[5]   = worldScreenMatrix.m_data[1][1];
    matrixData[6]   = worldScreenMatrix.m_data[2][1];
    matrixData[12]  = worldScreenMatrix.m_data[0][2];
    matrixData[13]  = worldScreenMatrix.m_data[1][2];
    matrixData[14]  = worldScreenMatrix.m_data[2][2];
    glLoadMatrixd( matrixData );

    // Set defaults
    SetFillColor( fillColor );
    SetStrokeColor( strokeColor );
210

211 212
    // Unbind buffers - set compositor for direct drawing
    compositor.SetBuffer( OPENGL_COMPOSITOR::DIRECT_RENDERING );
213

214
    // Remove all previously stored items
215
    nonCachedManager.Clear();
216
    overlayManager.Clear();
217

218 219
    cachedManager.BeginDrawing();
    nonCachedManager.BeginDrawing();
220
    overlayManager.BeginDrawing();
221 222 223 224 225
}


void OPENGL_GAL::EndDrawing()
{
226 227
    // Cached & non-cached containers are rendered to the same buffer
    compositor.SetBuffer( mainBuffer );
228
    nonCachedManager.EndDrawing();
229
    cachedManager.EndDrawing();
230

231 232 233 234
    // Overlay container is rendered to a different buffer
    compositor.SetBuffer( overlayBuffer );
    overlayManager.EndDrawing();

235 236 237
    // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
    glColor4d( 1.0, 1.0, 1.0, 1.0 );

238
    // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
239 240
    compositor.DrawBuffer( mainBuffer );
    compositor.DrawBuffer( overlayBuffer );
241 242 243
    blitCursor();

    glFlush();
244 245 246 247 248 249
    SwapBuffers();

    delete clientDC;
}


Maciej Suminski's avatar
Maciej Suminski committed
250
void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
251
{
Maciej Suminski's avatar
Maciej Suminski committed
252 253
    const VECTOR2D  startEndVector = aEndPoint - aStartPoint;
    double          lineAngle = startEndVector.Angle();
254

255 256
    currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );

Maciej Suminski's avatar
Maciej Suminski committed
257
    drawLineQuad( aStartPoint, aEndPoint );
258

Maciej Suminski's avatar
Maciej Suminski committed
259
    // Line caps
260 261 262 263 264
    if( lineWidth > 1.0 )
    {
        drawFilledSemiCircle( aStartPoint, lineWidth / 2, lineAngle + M_PI / 2 );
        drawFilledSemiCircle( aEndPoint,   lineWidth / 2, lineAngle - M_PI / 2 );
    }
265 266 267
}


268 269
void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
                              double aWidth )
270 271
{
    VECTOR2D startEndVector = aEndPoint - aStartPoint;
272
    double   lineAngle      = startEndVector.Angle();
273

274
    if( isFillEnabled )
275
    {
Maciej Suminski's avatar
Maciej Suminski committed
276
        // Filled tracks
277
        currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
278

279 280
        SetLineWidth( aWidth );
        drawLineQuad( aStartPoint, aEndPoint );
281 282 283 284

        // Draw line caps
        drawFilledSemiCircle( aStartPoint, aWidth / 2, lineAngle + M_PI / 2 );
        drawFilledSemiCircle( aEndPoint,   aWidth / 2, lineAngle - M_PI / 2 );
285
    }
286
    else
287
    {
Maciej Suminski's avatar
Maciej Suminski committed
288
        // Outlined tracks
289
        double lineLength = startEndVector.EuclideanNorm();
290

291
        currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
292

293
        Save();
294

295 296
        currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
        currentManager->Rotate( lineAngle, 0.0f, 0.0f, 1.0f );
297

298 299
        drawLineQuad( VECTOR2D( 0.0,         aWidth / 2.0 ),
                      VECTOR2D( lineLength,  aWidth / 2.0 ) );
300

301 302
        drawLineQuad( VECTOR2D( 0.0,        -aWidth / 2.0 ),
                      VECTOR2D( lineLength, -aWidth / 2.0 ) );
303

304
        // Draw line caps
305 306
        drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2 );
        drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2 );
307

308
        Restore();
309 310 311 312
    }
}


Maciej Suminski's avatar
Maciej Suminski committed
313
void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
314
{
Maciej Suminski's avatar
Maciej Suminski committed
315
    if( isFillEnabled )
316
    {
Maciej Suminski's avatar
Maciej Suminski committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
        currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );

        /* Draw a triangle that contains the circle, then shade it leaving only the circle.
         *  Parameters given to setShader are indices of the triangle's vertices
         *  (if you want to understand more, check the vertex shader source [shader.vert]).
         *  Shader uses this coordinates to determine if fragments are inside the circle or not.
         *       v2
         *       /\
         *      //\\
         *  v0 /_\/_\ v1
         */
        currentManager->Shader( SHADER_FILLED_CIRCLE, 1.0 );
        currentManager->Vertex( aCenterPoint.x - aRadius * sqrt( 3.0f ),            // v0
                                aCenterPoint.y - aRadius, layerDepth );

        currentManager->Shader( SHADER_FILLED_CIRCLE, 2.0 );
        currentManager->Vertex( aCenterPoint.x + aRadius * sqrt( 3.0f ),             // v1
                                aCenterPoint.y - aRadius, layerDepth );

        currentManager->Shader( SHADER_FILLED_CIRCLE, 3.0 );
        currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f,    // v2
                                layerDepth );
339 340
    }

Maciej Suminski's avatar
Maciej Suminski committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    if( isStrokeEnabled )
    {
        currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );

        /* Draw a triangle that contains the circle, then shade it leaving only the circle.
         *  Parameters given to setShader are indices of the triangle's vertices
         *  (if you want to understand more, check the vertex shader source [shader.vert]).
         *  and the line width. Shader uses this coordinates to determine if fragments are
         *  inside the circle or not.
         *       v2
         *       /\
         *      //\\
         *  v0 /_\/_\ v1
         */
        double outerRadius = aRadius + ( lineWidth / 2 );
        currentManager->Shader( SHADER_STROKED_CIRCLE, 1.0, aRadius, lineWidth );
        currentManager->Vertex( aCenterPoint.x - outerRadius * sqrt( 3.0f ),            // v0
                                aCenterPoint.y - outerRadius, layerDepth );

        currentManager->Shader( SHADER_STROKED_CIRCLE, 2.0, aRadius, lineWidth );
        currentManager->Vertex( aCenterPoint.x + outerRadius * sqrt( 3.0f ),            // v1
                                aCenterPoint.y - outerRadius, layerDepth );

        currentManager->Shader( SHADER_STROKED_CIRCLE, 3.0, aRadius, lineWidth );
        currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + outerRadius * 2.0f,    // v2
                                layerDepth );
    }
368 369 370
}


Maciej Suminski's avatar
Maciej Suminski committed
371 372
void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle,
                          double aEndAngle )
373
{
Maciej Suminski's avatar
Maciej Suminski committed
374 375
    if( aRadius <= 0 )
        return;
Maciej Suminski's avatar
Maciej Suminski committed
376

Maciej Suminski's avatar
Maciej Suminski committed
377 378
    // Swap the angles, if start angle is greater than end angle
    SWAP( aStartAngle, >, aEndAngle );
Maciej Suminski's avatar
Maciej Suminski committed
379

Maciej Suminski's avatar
Maciej Suminski committed
380 381
    Save();
    currentManager->Translate( aCenterPoint.x, aCenterPoint.y, layerDepth );
382

Maciej Suminski's avatar
Maciej Suminski committed
383 384
    if( isStrokeEnabled )
    {
385
        const double alphaIncrement = 2.0 * M_PI / CIRCLE_POINTS;
Maciej Suminski's avatar
Maciej Suminski committed
386
        currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
Maciej Suminski's avatar
Maciej Suminski committed
387

Maciej Suminski's avatar
Maciej Suminski committed
388
        VECTOR2D p( cos( aStartAngle ) * aRadius, sin( aStartAngle ) * aRadius );
389 390
        double alpha;

Maciej Suminski's avatar
Maciej Suminski committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
        for( alpha = aStartAngle + alphaIncrement; alpha < aEndAngle; alpha += alphaIncrement )
        {
            VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
            DrawLine( p, p_next );

            p = p_next;
        }

        // Draw the last missing part
        if( alpha != aEndAngle )
        {
            VECTOR2D p_last( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
            DrawLine( p, p_last );
        }
    }

    if( isFillEnabled )
408
    {
409
        const double alphaIncrement = 2 * M_PI / CIRCLE_POINTS;
Maciej Suminski's avatar
Maciej Suminski committed
410 411
        double alpha;
        currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
412
        currentManager->Shader( SHADER_NONE );
413

414
        // Triangle fan
Maciej Suminski's avatar
Maciej Suminski committed
415 416
        for( alpha = aStartAngle; ( alpha + alphaIncrement ) < aEndAngle; )
        {
417 418
            currentManager->Vertex( 0.0, 0.0, 0.0 );
            currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 );
Maciej Suminski's avatar
Maciej Suminski committed
419
            alpha += alphaIncrement;
420
            currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 );
Maciej Suminski's avatar
Maciej Suminski committed
421
        }
422

423 424 425 426
        // The last missing triangle
        const VECTOR2D endPoint( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
        currentManager->Vertex( 0.0, 0.0, 0.0 );
        currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 );
Maciej Suminski's avatar
Maciej Suminski committed
427
        currentManager->Vertex( endPoint.x,    endPoint.y,     0.0 );
428
    }
429

Maciej Suminski's avatar
Maciej Suminski committed
430
    Restore();
431 432 433
}


434
void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
435 436 437 438 439 440 441 442
{
    // Compute the diagonal points of the rectangle
    VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
    VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );

    // Stroke the outline
    if( isStrokeEnabled )
    {
443
        currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
Maciej Suminski's avatar
Maciej Suminski committed
444

445 446 447 448 449 450 451 452 453 454 455 456
        std::deque<VECTOR2D> pointList;
        pointList.push_back( aStartPoint );
        pointList.push_back( diagonalPointA );
        pointList.push_back( aEndPoint );
        pointList.push_back( diagonalPointB );
        pointList.push_back( aStartPoint );
        DrawPolyline( pointList );
    }

    // Fill the rectangle
    if( isFillEnabled )
    {
457 458
        currentManager->Shader( SHADER_NONE );
        currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
459

460 461 462
        currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
        currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, layerDepth );
        currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
463

464 465 466
        currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
        currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
        currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, layerDepth );
467 468 469 470
    }
}


Maciej Suminski's avatar
Maciej Suminski committed
471
void OPENGL_GAL::DrawPolyline( std::deque<VECTOR2D>& aPointList )
472
{
473 474 475
    if( aPointList.empty() )
        return;

476 477
    currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );

Maciej Suminski's avatar
Maciej Suminski committed
478 479 480
    std::deque<VECTOR2D>::const_iterator it = aPointList.begin();

    // Start from the second point
481
    for( ++it; it != aPointList.end(); ++it )
482
    {
Maciej Suminski's avatar
Maciej Suminski committed
483 484
        const VECTOR2D startEndVector = ( *it - *( it - 1 ) );
        double lineAngle = startEndVector.Angle();
Maciej Suminski's avatar
Maciej Suminski committed
485

Maciej Suminski's avatar
Maciej Suminski committed
486
        drawLineQuad( *( it - 1 ), *it );
487

Maciej Suminski's avatar
Maciej Suminski committed
488 489
        // There is no need to draw line caps on both ends of polyline's segments
        drawFilledSemiCircle( *( it - 1 ), lineWidth / 2, lineAngle + M_PI / 2 );
490 491
    }

Maciej Suminski's avatar
Maciej Suminski committed
492 493 494 495
    // ..and now - draw the ending cap
    const VECTOR2D startEndVector = ( *( it - 1 ) - *( it - 2 ) );
    double lineAngle = startEndVector.Angle();
    drawFilledSemiCircle( *( it - 1 ), lineWidth / 2, lineAngle - M_PI / 2 );
496 497 498 499 500 501 502
}


void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
{
    // Any non convex polygon needs to be tesselated
    // for this purpose the GLU standard functions are used
503
    currentManager->Shader( SHADER_NONE );
504
    currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
Maciej Suminski's avatar
Maciej Suminski committed
505

506
    TessParams params = { currentManager, tessIntersects };
507
    gluTessBeginPolygon( tesselator, &params );
508 509
    gluTessBeginContour( tesselator );

510 511 512
    boost::shared_array<GLdouble> points( new GLdouble[3 * aPointList.size()] );
    int v = 0;
    for( std::deque<VECTOR2D>::const_iterator it = aPointList.begin(); it != aPointList.end(); it++ )
513
    {
514 515 516 517 518
        points[v]     = it->x;
        points[v + 1] = it->y;
        points[v + 2] = layerDepth;
        gluTessVertex( tesselator, &points[v], &points[v] );
        v += 3;
519 520 521 522 523
    }

    gluTessEndContour( tesselator );
    gluTessEndPolygon( tesselator );

524 525
    // Free allocated intersecting points
    tessIntersects.clear();
526 527 528 529 530

    // vertexList destroyed here
}


531 532
void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
                            const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint )
533 534 535 536 537 538 539 540
{
    // FIXME The drawing quality needs to be improved
    // FIXME Perhaps choose a quad/triangle strip instead?
    // FIXME Brute force method, use a better (recursive?) algorithm

    std::deque<VECTOR2D> pointList;

    double t  = 0.0;
541
    double dt = 1.0 / (double) CURVE_POINTS;
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562

    for( int i = 0; i <= CURVE_POINTS; i++ )
    {
        double omt  = 1.0 - t;
        double omt2 = omt * omt;
        double omt3 = omt * omt2;
        double t2   = t * t;
        double t3   = t * t2;

        VECTOR2D vertex = omt3 * aStartPoint + 3.0 * t * omt2 * aControlPointA
                          + 3.0 * t2 * omt * aControlPointB + t3 * aEndPoint;

        pointList.push_back( vertex );

        t += dt;
    }

    DrawPolyline( pointList );
}


Maciej Suminski's avatar
Maciej Suminski committed
563
void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
564
{
565
    screenSize = VECTOR2I( aWidth, aHeight );
566

Maciej Suminski's avatar
Maciej Suminski committed
567 568 569 570 571
    // Resize framebuffers
    compositor.Resize( aWidth, aHeight );
    isFramebufferInitialized = false;

    wxGLCanvas::SetSize( aWidth, aHeight );
572 573 574
}


Maciej Suminski's avatar
Maciej Suminski committed
575
bool OPENGL_GAL::Show( bool aShow )
576
{
Maciej Suminski's avatar
Maciej Suminski committed
577
    bool s = wxGLCanvas::Show( aShow );
578

Maciej Suminski's avatar
Maciej Suminski committed
579 580
    if( aShow )
        wxGLCanvas::Raise();
581

Maciej Suminski's avatar
Maciej Suminski committed
582
    return s;
583 584 585
}


Maciej Suminski's avatar
Maciej Suminski committed
586
void OPENGL_GAL::Flush()
587
{
Maciej Suminski's avatar
Maciej Suminski committed
588
    glFlush();
589 590 591
}


592
void OPENGL_GAL::ClearScreen( const COLOR4D& aColor )
593 594
{
    // Clear screen
595
    glClearColor( aColor.r, aColor.g, aColor.b, aColor.a );
596
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
597 598 599
}


600
void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
{
    GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };

    matrixData[0]   = aTransformation.m_data[0][0];
    matrixData[1]   = aTransformation.m_data[1][0];
    matrixData[2]   = aTransformation.m_data[2][0];
    matrixData[4]   = aTransformation.m_data[0][1];
    matrixData[5]   = aTransformation.m_data[1][1];
    matrixData[6]   = aTransformation.m_data[2][1];
    matrixData[12]  = aTransformation.m_data[0][2];
    matrixData[13]  = aTransformation.m_data[1][2];
    matrixData[14]  = aTransformation.m_data[2][2];

    glMultMatrixd( matrixData );
}


void OPENGL_GAL::Rotate( double aAngle )
{
620
    currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
621 622 623
}


624
void OPENGL_GAL::Translate( const VECTOR2D& aVector )
625
{
626
    currentManager->Translate( aVector.x, aVector.y, 0.0f );
627 628 629
}


630
void OPENGL_GAL::Scale( const VECTOR2D& aScale )
631
{
632
    currentManager->Scale( aScale.x, aScale.y, 0.0f );
633 634 635 636 637
}


void OPENGL_GAL::Save()
{
638
    currentManager->PushMatrix();
639 640 641 642 643
}


void OPENGL_GAL::Restore()
{
644
    currentManager->PopMatrix();
645 646 647 648 649
}


int OPENGL_GAL::BeginGroup()
{
650
    isGrouping = true;
651

652 653 654
    boost::shared_ptr<VERTEX_ITEM> newItem( new VERTEX_ITEM( cachedManager ) );
    int groupNumber = getNewGroupNumber();
    groups.insert( std::make_pair( groupNumber, newItem ) );
655

656
    return groupNumber;
657 658 659 660 661
}


void OPENGL_GAL::EndGroup()
{
662
    cachedManager.FinishItem();
663
    isGrouping = false;
664 665 666 667 668
}


void OPENGL_GAL::DrawGroup( int aGroupNumber )
{
669
    cachedManager.DrawItem( *groups[aGroupNumber] );
670 671 672
}


673 674
void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
{
675
    cachedManager.ChangeItemColor( *groups[aGroupNumber], aNewColor );
676 677 678
}


679 680
void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
{
681
    cachedManager.ChangeItemDepth( *groups[aGroupNumber], aDepth );
682 683 684
}


Maciej Suminski's avatar
Maciej Suminski committed
685
void OPENGL_GAL::DeleteGroup( int aGroupNumber )
686
{
687
    // Frees memory in the container as well
Maciej Suminski's avatar
Maciej Suminski committed
688 689
    groups.erase( aGroupNumber );
}
690

Maciej Suminski's avatar
Maciej Suminski committed
691 692 693 694 695

void OPENGL_GAL::ClearCache()
{
    groups.clear();
    cachedManager.Clear();
696 697 698
}


Maciej Suminski's avatar
Maciej Suminski committed
699
void OPENGL_GAL::SaveScreen()
700
{
Maciej Suminski's avatar
Maciej Suminski committed
701 702
    wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
}
703 704


Maciej Suminski's avatar
Maciej Suminski committed
705 706 707 708
void OPENGL_GAL::RestoreScreen()
{
    wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
}
709 710


Maciej Suminski's avatar
Maciej Suminski committed
711
void OPENGL_GAL::SetTarget( RENDER_TARGET aTarget )
Maciej Suminski's avatar
Maciej Suminski committed
712 713 714 715 716 717 718
{
    switch( aTarget )
    {
    default:
    case TARGET_CACHED:
        currentManager = &cachedManager;
        break;
719

Maciej Suminski's avatar
Maciej Suminski committed
720 721 722
    case TARGET_NONCACHED:
        currentManager = &nonCachedManager;
        break;
723

Maciej Suminski's avatar
Maciej Suminski committed
724 725 726 727
    case TARGET_OVERLAY:
        currentManager = &overlayManager;
        break;
    }
728 729 730 731 732

    currentTarget = aTarget;
}


Maciej Suminski's avatar
Maciej Suminski committed
733
RENDER_TARGET OPENGL_GAL::GetTarget() const
734 735
{
    return currentTarget;
736 737 738
}


Maciej Suminski's avatar
Maciej Suminski committed
739
void OPENGL_GAL::ClearTarget( RENDER_TARGET aTarget )
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
{
    // Save the current state
    unsigned int oldTarget = compositor.GetBuffer();

    switch( aTarget )
    {
    // Cached and noncached items are rendered to the same buffer
    default:
    case TARGET_CACHED:
    case TARGET_NONCACHED:
        compositor.SetBuffer( mainBuffer );
        break;

    case TARGET_OVERLAY:
        compositor.SetBuffer( overlayBuffer );
        break;
    }
757

758 759 760 761 762 763 764
    compositor.ClearBuffer();

    // Restore the previous state
    compositor.SetBuffer( oldTarget );
}


765
void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
Maciej Suminski's avatar
Maciej Suminski committed
766
{
767 768
    // Now we should only store the position of the mouse cursor
    // The real drawing routines are in blitCursor()
769 770
    VECTOR2D screenCursor = worldScreenMatrix * aCursorPosition;
    cursorPosition = screenWorldMatrix * VECTOR2D( screenCursor.x, screenSize.y - screenCursor.y );
771 772 773
}


Maciej Suminski's avatar
Maciej Suminski committed
774
void OPENGL_GAL::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
775
{
776
    compositor.SetBuffer( mainBuffer );
777

778 779 780
    // We do not need a very precise comparison here (the lineWidth is set by GAL::DrawGrid())
    if( fabs( lineWidth - 2.0 * gridLineWidth / worldScale ) < 0.1 )
        glLineWidth( 1.0 );
781
    else
782
        glLineWidth( 2.0 );
783

784
    glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
785

786 787 788 789
    glBegin( GL_LINES );
    glVertex3d( aStartPoint.x, aStartPoint.y, layerDepth );
    glVertex3d( aEndPoint.x, aEndPoint.y, layerDepth );
    glEnd();
790

791
    // Restore the default color, so textures will be drawn properly
792
    glColor4d( 1.0, 1.0, 1.0, 1.0 );
793 794 795
}


796
void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
797
{
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
    /* Helper drawing:                   ____--- v3       ^
     *                           ____---- ...   \          \
     *                   ____----      ...       \   end    \
     *     v1    ____----           ...    ____----          \ width
     *       ----                ...___----        \          \
     *       \             ___...--                 \          v
     *        \    ____----...                ____---- v2
     *         ----     ...           ____----
     *  start   \    ...      ____----
     *           \... ____----
     *            ----
     *            v0
     * dots mark triangles' hypotenuses
     */

Maciej Suminski's avatar
Maciej Suminski committed
813 814 815
    VECTOR2D startEndVector = aEndPoint - aStartPoint;
    double   lineLength     = startEndVector.EuclideanNorm();
    double   scale          = 0.5 * lineWidth / lineLength;
816

Maciej Suminski's avatar
Maciej Suminski committed
817 818
    if( lineLength <= 0.0 )
        return;
819

Maciej Suminski's avatar
Maciej Suminski committed
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
    // The perpendicular vector also needs transformations
    glm::vec4 vector = currentManager->GetTransformation() *
                       glm::vec4( -startEndVector.y * scale, startEndVector.x * scale, 0.0, 0.0 );

    // Line width is maintained by the vertex shader
    currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth );
    currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );    // v0

    currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth );
    currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );    // v1

    currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth );
    currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );        // v3

    currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth );
    currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );    // v0

    currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth );
    currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );        // v3

    currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth );
    currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );        // v2
}


void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
{
    if( isFillEnabled )
    {
        currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
        drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
    }

    if( isStrokeEnabled )
    {
        currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
        drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
    }
}


void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
                                       double aAngle )
{
    Save();
    currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
    currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );

    /* Draw a triangle that contains the semicircle, then shade it to leave only
     * the semicircle. Parameters given to setShader are indices of the triangle's vertices
870 871
     * (if you want to understand more, check the vertex shader source [shader.vert]).
     * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
Maciej Suminski's avatar
Maciej Suminski committed
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
     *       v2
     *       /\
     *      /__\
     *  v0 //__\\ v1
     */
    currentManager->Shader( SHADER_FILLED_CIRCLE, 4.0f );
    currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );     // v0

    currentManager->Shader( SHADER_FILLED_CIRCLE, 5.0f );
    currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );      // v1

    currentManager->Shader( SHADER_FILLED_CIRCLE, 6.0f );
    currentManager->Vertex( 0.0f, aRadius * 2.0f, layerDepth );                     // v2

    Restore();
}


void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
                                        double aAngle )
{
    double outerRadius = aRadius + ( lineWidth / 2 );

    Save();
    currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
    currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );

    /* Draw a triangle that contains the semicircle, then shade it to leave only
     * the semicircle. Parameters given to setShader are indices of the triangle's vertices
901 902 903
     * (if you want to understand more, check the vertex shader source [shader.vert]), the
     * radius and the line width. Shader uses these coordinates to determine if fragments are
     * inside the semicircle or not.
Maciej Suminski's avatar
Maciej Suminski committed
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
     *       v2
     *       /\
     *      /__\
     *  v0 //__\\ v1
     */
    currentManager->Shader( SHADER_STROKED_CIRCLE, 4.0f, aRadius, lineWidth );
    currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );     // v0

    currentManager->Shader( SHADER_STROKED_CIRCLE, 5.0f, aRadius, lineWidth );
    currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );      // v1

    currentManager->Shader( SHADER_STROKED_CIRCLE, 6.0f, aRadius, lineWidth );
    currentManager->Vertex( 0.0f, outerRadius * 2.0f, layerDepth );                     // v2

    Restore();
}


922
void OPENGL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
Maciej Suminski's avatar
Maciej Suminski committed
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
{
    PostPaint();
}


void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
{
    // Post the mouse event to the event listener registered in constructor, if any
    if( mouseListener )
        wxPostEvent( mouseListener, aEvent );
}


void OPENGL_GAL::initGlew()
{
    // Initialize GLEW library
    GLenum err = glewInit();

    if( GLEW_OK != err )
    {
943
        DisplayError( parentWindow, wxString::FromUTF8( (char*) glewGetErrorString( err ) ) );
Maciej Suminski's avatar
Maciej Suminski committed
944 945 946 947 948 949 950 951 952 953 954
        exit( 1 );
    }
    else
    {
        wxLogDebug( wxString( wxT( "Status: Using GLEW " ) ) +
                    FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
    }

    // Check the OpenGL version (minimum 2.1 is required)
    if( GLEW_VERSION_2_1 )
    {
955
        wxLogInfo( wxT( "OpenGL 2.1 supported." ) );
Maciej Suminski's avatar
Maciej Suminski committed
956 957 958
    }
    else
    {
959
        DisplayError( parentWindow, wxT( "OpenGL 2.1 or higher is required!" ) );
Maciej Suminski's avatar
Maciej Suminski committed
960 961 962 963
        exit( 1 );
    }

    // Framebuffers have to be supported
964
    if( !GLEW_EXT_framebuffer_object )
Maciej Suminski's avatar
Maciej Suminski committed
965
    {
966
        DisplayError( parentWindow, wxT( "Framebuffer objects are not supported!" ) );
Maciej Suminski's avatar
Maciej Suminski committed
967 968 969
        exit( 1 );
    }

970
    // Vertex buffer has to be supported
Maciej Suminski's avatar
Maciej Suminski committed
971 972
    if( !GLEW_ARB_vertex_buffer_object )
    {
973
        DisplayError( parentWindow, wxT( "Vertex buffer objects are not supported!" ) );
Maciej Suminski's avatar
Maciej Suminski committed
974 975 976 977 978 979 980
        exit( 1 );
    }

    isGlewInitialized = true;
}


981 982 983 984 985 986 987
void OPENGL_GAL::blitCursor()
{
    if( !isCursorEnabled )
        return;

    compositor.SetBuffer( OPENGL_COMPOSITOR::DIRECT_RENDERING );

988 989 990
    VECTOR2D cursorBegin  = cursorPosition - cursorSize / ( 2 * worldScale );
    VECTOR2D cursorEnd    = cursorPosition + cursorSize / ( 2 * worldScale );
    VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005

    glDisable( GL_TEXTURE_2D );
    glLineWidth( 1.0 );
    glColor4d( cursorColor.r, cursorColor.g, cursorColor.b, cursorColor.a );

    glBegin( GL_LINES );
    glVertex2d( cursorCenter.x, cursorBegin.y );
    glVertex2d( cursorCenter.x, cursorEnd.y );

    glVertex2d( cursorBegin.x, cursorCenter.y );
    glVertex2d( cursorEnd.x, cursorCenter.y );
    glEnd();
}


Maciej Suminski's avatar
Maciej Suminski committed
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
unsigned int OPENGL_GAL::getNewGroupNumber()
{
    wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(),
                  wxT( "There are no free slots to store a group" ) );

    while( groups.find( groupCounter ) != groups.end() )
    {
        groupCounter++;
    }

    return groupCounter++;
}


// -------------------------------------
// Callback functions for the tesselator
// -------------------------------------

// Compare Redbook Chapter 11
void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
{
    GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
    OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
    VERTEX_MANAGER* vboManager = param->vboManager;

    if( vboManager )
        vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
}


void CALLBACK CombineCallback( GLdouble coords[3],
                               GLdouble* vertex_data[4],
                               GLfloat weight[4], GLdouble** dataOut, void* aData )
{
    GLdouble* vertex = new GLdouble[3];
    OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );

    // Save the pointer so we can delete it later
1044
    param->intersectPoints.push_back( boost::shared_array<GLdouble>( vertex ) );
Maciej Suminski's avatar
Maciej Suminski committed
1045 1046 1047 1048 1049 1050 1051

    memcpy( vertex, coords, 3 * sizeof(GLdouble) );

    *dataOut = vertex;
}


1052
void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
Maciej Suminski's avatar
Maciej Suminski committed
1053 1054 1055 1056 1057 1058 1059
{
    // This callback is needed to force GLU tesselator to use triangles only
}


void CALLBACK ErrorCallback( GLenum aErrorCode )
{
1060 1061
    const GLubyte* eString = gluErrorString( aErrorCode );

1062 1063
    DisplayError( NULL, wxT( "Tessellation error: " ) +
                        wxString( (const char*)( eString ), wxConvUTF8 ) );
Maciej Suminski's avatar
Maciej Suminski committed
1064

1065
    exit( 1 );
Maciej Suminski's avatar
Maciej Suminski committed
1066 1067 1068 1069 1070 1071 1072 1073
}


void InitTesselatorCallbacks( GLUtesselator* aTesselator )
{
    gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA,  ( void (CALLBACK*)() )VertexCallback );
    gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback );
    gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG,    ( void (CALLBACK*)() )EdgeCallback );
1074
    gluTessCallback( aTesselator, GLU_TESS_ERROR,        ( void (CALLBACK*)() )ErrorCallback );
1075
}