GLViewer.cpp 73.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
/*
 *	(c) LSIIT, UMR CNRS/UdS
 *	Authors: O. Gnevaux, F. Larue.
 *
 *	See licence.txt for additional information.
 */


#include "GLViewer.h"
10
#include "UIMainWindow.h"
11
12
13
14
15
16
17
18
19
20
21
22
23
24

#include <fstream>
#include <iostream>
#include <sstream>
#include "InfoBarManager.h"

#include <SelectionManager.h>
#include <SelectionToolBox.h>
#include <SelectionToolDisk.h>
#include <SelectionToolLasso.h>




25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
QVector3D GLViewer::BgTopColor;
QVector3D GLViewer::BgBottomColor;

QVector3D GLViewer::CoolColor( 0.5f, 0.0f, 1.0f );
QVector3D GLViewer::WarmColor( 1.0f, 0.8f, 0.0f );

QVector3D GLViewer::LightColor( 1.0f, 1.0f, 1.0f );
QVector3D GLViewer::AmbientColor;
QVector3D GLViewer::SpecularColor;

float     GLViewer::Ambient   = 0.1f;
float     GLViewer::Specular  = 0.5f;
float     GLViewer::Shininess = 80.0f;




42
void GLViewer::DisplayableInfo::Initialize( DisplayableInterface *displayable,
43
                                            DisplayableList::iterator priorityPos,
44
45
46
47
48
49
50
51
52
53
54
55
                                            UIParamSet &params )
{
    this->displayable = displayable;
    this->posInPriorityList = priorityPos;

    displayable->initialize( params );

    displayable->transformation( this->userTransform );
    this->packingTransform.setToIdentity();
    this->fullTransform = this->userTransform * this->packingTransform;

    displayable->boundingBox( this->boundingBox );
56

57
58
59
60
    this->selectionMgr.clear();
    displayable->selectionManagers( this->selectionMgr );

    currentSelectionMgr = NULL;
61
62
63
64
65
}




66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const char* GLViewer::s_PickingVPG =
    "#version 330\n"

    "uniform mat4 projectionMatrix;"
    "uniform mat4 viewMatrix;"

    "in vec3 vertexPosition;"

    "out float depth;"
    "out vec3 vertex;"

    "void main()"
    "{"
    "   depth  = length( (viewMatrix * vec4(vertexPosition,1.0)).xyz );"
    "   vertex = vertexPosition;"

    "   gl_Position = projectionMatrix * viewMatrix * vec4(vertexPosition,1.0);"
    "}";

const char* GLViewer::s_PickingFPG =
    "uniform int u_ObjectId;"

    "in float depth;"
    "in vec3 vertex;"

    "void main()"
    "{"
    "   gl_FragData[0].x = float( u_ObjectId      & 255) / 255.0;"
    "   gl_FragData[0].y = float((u_ObjectId>> 8) & 255) / 255.0;"
    "   gl_FragData[0].z = float((u_ObjectId>>16) & 255) / 255.0;"
    "   gl_FragData[0].w = float((u_ObjectId>>24) & 255) / 255.0;"
    "   gl_FragData[1] = vec4( vertex, depth );"
    "}";


const char* GLViewer::s_ZBufferVPG =
    "#version 330\n"

    "uniform mat4 fullTransformMatrix;"

    "in vec3 vertexPosition;"

    "void main()"
    "{"
    "   gl_Position = fullTransformMatrix * vec4(vertexPosition,1.0);"
    "}";

const char* GLViewer::s_ZBufferFPG =
    "void main()"
    "{"
    "   gl_FragData[0] = vec4( 0.0, 0.0, 0.0, 1.0 );"
    "}";


GLViewer::GLViewer( DisplayDoF dof,
                    QWidget *parent,
Frédéric Larue's avatar
Frédéric Larue committed
122
123
124
125
                    Qt::WindowFlags f ) :
    QOpenGLWidget( parent, f ),
    m_DisplayOptionsLayout( NULL ),
    m_InfoBarManager( NULL )
126
{
127
    initMetricGrids();
128
129
130
131
132
133
134
    init( dof );
}


GLViewer::GLViewer( DisplayDoF dof,
                    const QSurfaceFormat &format,
                    QWidget *parent,
Frédéric Larue's avatar
Frédéric Larue committed
135
136
137
138
                    Qt::WindowFlags f ) :
    QOpenGLWidget( parent, f ),
    m_DisplayOptionsLayout( NULL ),
    m_InfoBarManager( NULL )
139
140
{
	setFormat( format );
141
    initMetricGrids();
142
143
144
145
146
147
    init( dof );
}


GLViewer::~GLViewer()
{
148
    removeAllDisplayables();
149

150
151
    clearNavigationControlStack();

152
153
154
155
156
157
158
    for( std::vector<SelectionTool*>::iterator tool=m_SelectionTools.begin(); tool!=m_SelectionTools.end(); ++tool )
        delete *tool;

	delete m_InfoBarManager;
}


159
160
161
162
163
164
165
166
167
168
void GLViewer::initMetricGrids()
{
    m_View.metricGrid2D.disableAxis( MetricGrid::ALL );
    m_View.metricGrid2D.disableGrid( MetricGrid::ALL );

    m_View.metricGrid3D.enableAxis( MetricGrid::ALL );
    m_View.metricGrid3D.enableGrid( MetricGrid::ZX );
}


169
170
void GLViewer::init( DisplayDoF dof )
{
171
172
173
    m_BelowCursor = NULL;


174
175
176
    m_MustUpdateFocal            = true;
    m_MustUpdateProjectionMatrix = true;
    m_MustUpdateViewMatrix       = true;
177

178
    setSpeedFactor( 1.0f, 4.0f, 0.25f );
179

180
    m_View.focusPoint = QVector3D(-1.0f,-1.0f,-1.0f);
181
    setFocusPoint( QVector3D(0.0f,0.0f,0.0f) );
182

183
    m_View.DoF = dof;
184
    QMatrix4x4 rot;
185
    if( dof == DISPLAY_DOF_2D )
186
        rot.setToIdentity();
187
188
    else
    {
189
190
        rot.rotate(  30.0f, 1,0,0 );
        rot.rotate( -30.0f, 0,1,0 );
191
    }
192
193
    setViewRotationMatrix( rot );

194
    m_View.isOrthographic = false;
195
    m_View.fovY = 0.0f;
196
197
    setFovY( 65.0f );

198
199
200
201
202
    m_ChangeNearPlaneMode = false;
    m_ChangeFarPlaneMode  = false;

    m_View.nearPlaneFactor  = 0.01f;
    m_View.farPlaneFactor   = 5.0f;
203
	m_View.distToFocusPoint = 0.0f;
204
    setDistToFocusPoint( 400.0f );
205

206
207
    m_View.pointSize = 2;
	m_View.isCullingEnabled = false;
208

Frédéric Larue's avatar
Frédéric Larue committed
209
210
211
212
213
    if( !m_InfoBarManager )
    {
        m_InfoBarManager = new InfoBarManager( this );
        m_InfoBarManager->setInfoBar( new InfoBar(), InfoBar::BOTTOM );
    }
214
215


216
217
218
219
220
221
222
223
224
225
    if( m_NavigationCtrl.empty() )
    {
        m_NavigationCtrl.push_front( new NavigationControl(this) );
    }
    else
    {
        while( m_NavigationCtrl.size() > 1 )
            popNavigationControl();
    }

226
227
    // Initialize selection tools.

228
    m_IsSelectionAllowed = true;
229
230
231
232
233
234
235
236
237
238
239
240
241
242
    m_SelectionEnabled = false;

    if( m_SelectionTools.empty() )
    {
        m_SelectionTools.resize( SELECTION_TOOL_END );
        m_SelectionTools[SELECTION_TOOL_BOX  ] = new SelectionToolBox  ( *this, m_SelectionContext );
        m_SelectionTools[SELECTION_TOOL_DISK ] = new SelectionToolDisk ( *this, m_SelectionContext );
        m_SelectionTools[SELECTION_TOOL_LASSO] = new SelectionToolLasso( *this, m_SelectionContext );
    }

    m_SelectionCurrentToolName = SELECTION_TOOL_DISK;
    m_SelectionCurrentTool = m_SelectionTools[ m_SelectionCurrentToolName ];


243
    m_View.lightRotationMatrix.setToIdentity();
244

245
246
247
    m_View.isLightTrackingEnabled = true;
    m_View.isLightingEnabled      = true;
    m_View.isWireframeEnabled     = false;
248
249
250


    m_AnimationsRunningAtPreviousFrame = false;
251
	m_PerformanceCounterEnabled = false;
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273


    // Initialize the layout that will contains options for all active displayables.

    if( !m_DisplayOptionsLayout )
    {
        m_DisplayOptionsLayout = new QVBoxLayout();
        m_DisplayOptionsLayout->setAlignment( Qt::AlignTop );

        QWidget *w = new QWidget( this );
        w->setLayout( m_DisplayOptionsLayout );

        QGridLayout *viewerLayout = new QGridLayout();
        viewerLayout->setRowStretch( 1, 10 );
        viewerLayout->setColumnStretch( 0, 10 );
        viewerLayout->addWidget( w, 0,1 );

        this->setLayout( viewerLayout );
    }
}


274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
void GLViewer::pushNavigationControl( NavigationControl* ctrl )
{
    assert( ctrl != NULL );
    m_NavigationCtrl.push_front(ctrl);
    m_MustUpdateFocal = true;
    m_MustUpdateViewMatrix = true;
    update();
}


void GLViewer::popNavigationControl()
{
    if( !m_NavigationCtrl.empty() )
    {
        delete m_NavigationCtrl.front();
        m_NavigationCtrl.pop_front();
        m_MustUpdateFocal = true;
        m_MustUpdateViewMatrix = true;
        update();
    }
}


void GLViewer::clearNavigationControlStack()
{
    while( !m_NavigationCtrl.empty() )
    {
        delete m_NavigationCtrl.front();
        m_NavigationCtrl.pop_front();
    }
}


307
308
309
310
311
312
313
314
315
316
317
318
void GLViewer::setRenderState( const RenderState& state )
{
    m_View = state;

    m_MustUpdateFocal            = true;
    m_MustUpdateProjectionMatrix = true;
    m_MustUpdateViewMatrix       = true;

    update();
}


319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
void GLViewer::populateDisplayerFactories( DisplayableFactoryInterface *plugin )
{
    const unsigned int NB_ROWS = 3;

    QVector<QString> types;
    plugin->acceptedDataTypes( types );

    for( auto &type : types )
        if( m_Factories.find(type) == m_Factories.end()  &&  (plugin->viewerDoF() & dof()) )
            m_Factories[type] = plugin;
}


void GLViewer::populateDisplayerFactories( QList<DisplayableFactoryInterface*> &plugins )
{
    foreach( DisplayableFactoryInterface *plugin, plugins )
        populateDisplayerFactories( plugin );
}


339
DisplayableInterface* GLViewer::addDisplayable( GenericUIData *m )
340
341
342
343
344
345
346
{
    // If a displayable already exists for this manageable, remove it before creating a new one.
    removeDisplayable( m );

    // Recovers the displayable factory for the corresponding datatype.
    FactoryMap::iterator factory = m_Factories.find( m->GetTypeString() );
    if( factory == m_Factories.end() )
347
        return NULL;
348
349
350
351
352
353
354
355
356
357
358
359

    // Asks the factory to create a displayable of the correct type. It is then
    // initialized and inserted in the list of all displayables.
    DisplayableInterface *displayable = factory->second->newDisplayer( m, this );
    DisplayableInfo *dispInfo = NULL;

    if( displayable )
    {
        makeCurrent();

        if( !m->GetDisplayOptions() )
        {
360
            UIParamSet *params = new UIParamSet( m->GetBaseName(), NULL );
361
362
363
            displayable->declareParameters( *params );

            if( displayable->isAnimated() )
364
                params->Add( new UIParamAnimation() );
365
366
367
368
369

            params->updateChildrenLists( m );

            m->SetDisplayOptions( params );
        }
370

371
372
373
374
375
        connect( m->GetDisplayOptions(), SIGNAL(parameterUpdated(UIParam*)), this, SLOT(update()) );
        connect( m->GetDisplayOptions(), SIGNAL(parameterUpdated(UIParam*)), displayable, SLOT(updateParameter(UIParam*)) );

        if( displayable->isAnimated() )
        {
376
            UIParamAnimation *animCtrl = m->GetDisplayOptions()->GetAnimationCtrl();
377
378
379
380
381
382
383
384
385
386
387
            connect( animCtrl, SIGNAL(animationStartRequired()), displayable, SLOT(startAnimation()) );
            connect( animCtrl, SIGNAL(animationPauseRequired()), displayable, SLOT(pauseAnimation()) );
            connect( animCtrl, SIGNAL(animationStopRequired ()), displayable, SLOT(stopAnimation ()) );
            connect( animCtrl, SIGNAL(animationStepForwardRequired ()), displayable, SLOT(animationStepForward ()) );
            connect( animCtrl, SIGNAL(animationStepBackwardRequired()), displayable, SLOT(animationStepBackward()) );

            connect( displayable, SIGNAL(registerAnimation  (DisplayableInterface*)), this, SLOT(registerAnimation  (DisplayableInterface*)) );
            connect( displayable, SIGNAL(unregisterAnimation(DisplayableInterface*)), this, SLOT(unregisterAnimation(DisplayableInterface*)) );
            connect( displayable, SIGNAL(displayNeedUpdate()), this, SLOT(update()) );
        }

388
389
390
391
392
393
394
395
        DisplayableList &priorityList = m_DisplayablesByPriority[ factory->second->displayPriority() ];

        dispInfo = &m_Displayables[ m ];
        dispInfo->Initialize(
            displayable,
            priorityList.insert( priorityList.end(), dispInfo ),
            *m->GetDisplayOptions()
        );
396

397
        if( !m->GetDisplayOptions()->IsEmpty() )
398
399
400
        {
            m_DisplayOptionsLayout->addWidget( m->GetDisplayOptions() );
            m->GetDisplayOptions()->show();
401
402
            if( m->GetDisplayOptions()->AreAllHidden() )
                m->GetDisplayOptions()->hide();
403
404
        }

405
406
407
408
409
410
411
412
413
414
        if( isSelectionEnabled() )
        {
            for( auto mngr : dispInfo->selectionMgr )
                if( mngr->selectableEntity() == m_SelectionCurrentTool->entityToSelect() )
                {
                    dispInfo->currentSelectionMgr = mngr;
                    mngr->onRecompilingSelectionShader( m_SelectionCurrentTool );
                    break;
                }
        }
415
416
417
418
419
420
421
422
423
424
425
426

        update();
    }


    if( dof() == DISPLAY_DOF_2D  &&  displayable )
    {
        m_AddingOrder.push_back( dispInfo );
        repackDisplayables();
    }


427
	return displayable;
428
429
430
}


431
432
433
434
435
436
//void GLViewer::setTransform( GenericUIData *m, const QMatrix4x4 &xf )
//{
//    DisplayableMap::iterator d = m_Displayables.find( m );
//    if( d != m_Displayables.end() )
//        d->second.packingTransform = xf;
//}
437
438


439
void GLViewer::updateDisplayable( GenericUIData *m, bool resetSelectionManagers )
440
441
442
443
444
445
446
447
448
449
450
451
{
    DisplayableMap::iterator d = m_Displayables.find( m );
    if( d != m_Displayables.end() )
    {
        // Recovers the info and the factory related to the given manageable.
        DisplayableFactoryInterface *factory = m_Factories[ m->GetTypeString() ];
        DisplayableInfo *dinfo = &d->second;

        // Re-initializes the displayable.
        dinfo->displayable->release( *m->GetDisplayOptions() );
        dinfo->displayable->initialize( *m->GetDisplayOptions() );

452
453
454
455
456
457
458
459
        // Update the displayable bounding box.
        dinfo->displayable->boundingBox( dinfo->boundingBox );

        // Update the displayable transform.
        QMatrix4x4 tr;
        dinfo->displayable->transformation( tr );
        dinfo->SetUserTransform( tr );

460
        // Re-generate the selection managers.
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
		if( resetSelectionManagers )
		{
			dinfo->currentSelectionMgr = NULL;
			for( auto mngr : dinfo->selectionMgr )
				delete mngr;
			dinfo->selectionMgr.clear();
			dinfo->displayable->selectionManagers( dinfo->selectionMgr );

			if( isSelectionEnabled() )
			{
				for( auto mngr : dinfo->selectionMgr )
					if( mngr->selectableEntity() == m_SelectionCurrentTool->entityToSelect() )
					{
						dinfo->currentSelectionMgr = mngr;
						mngr->onRecompilingSelectionShader( m_SelectionCurrentTool );
						break;
					}
			}
		}
480
481
482
483
484
485
486
487
488
489
490

        update();
    }
}


void GLViewer::frameBox( Box3f &box, const QMatrix4x4 *xf )
{
    if( !box.IsNull() )
    {
        double boxHalfDiag = box.Dimensions().length() * 0.5;
491
492
        double depth = ( 2.0*zFar()*zNear() - distToFocusPoint()*(zFar()+zNear()) ) / (zNear()-zFar());
        double projectedRadius = boxHalfDiag * focal() / depth;
493
494
495
496
497
498
499

        if( xf )
            setFocusPoint( xf->map(box.Center()) );
        else
            setFocusPoint( box.Center() );

        if( projectedRadius > 0.000001 )
500
            setDistToFocusPoint( distToFocusPoint() * (width()<height()? projectedRadius*height()/width() : projectedRadius) );
501
502
503
504
505
506
507
508
    }
}


void GLViewer::frameItem( GenericUIData* item )
{
    DisplayableMap::iterator dispInfo = m_Displayables.find( item );
    if( dispInfo != m_Displayables.end() )
509
        frameBox( dispInfo->second.boundingBox, &dispInfo->second.GetTransform() );
510
511
512
}


513
void GLViewer::frameItems( QList<GenericUIData*> &items )
514
515
516
517
518
519
520
{
    Box3f box;

    for( auto m=items.begin(); m!=items.end(); ++m )
    {
        DisplayableMap::iterator dispInfo = m_Displayables.find( *m );
        if( dispInfo != m_Displayables.end() )
521
            if( !dispInfo->second.boundingBox.IsNull() )
522
523
            {
                Box3f::CornerSet objectBoxCorners;
524
                dispInfo->second.boundingBox.Corners( objectBoxCorners );
525
526

                for( auto c=objectBoxCorners.begin(); c!=objectBoxCorners.end(); ++c )
527
                    box.Add( dispInfo->second.GetTransform().map(*c) );
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
            }
    }

    frameBox( box );
}


void GLViewer::frameAll()
{
    if( !m_Displayables.empty() )
    {
        Box3f box;

        for( DisplayableMap::iterator dispInfo=m_Displayables.begin(); dispInfo!=m_Displayables.end(); ++dispInfo )
        {
            Box3f objectBox;
            dispInfo->second.displayable->boundingBox( objectBox );

            if( !objectBox.IsNull() )
            {
                Box3f::CornerSet objectBoxCorners;
                objectBox.Corners( objectBoxCorners );

                for( auto c=objectBoxCorners.begin(); c!=objectBoxCorners.end(); ++c )
552
                    box.Add( dispInfo->second.GetTransform().map(*c) );
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
            }
        }

        frameBox( box );
    }
}


// All displayables are arranged along a grid layout. All grid row have the same height (displayables are scaled so as to fit it)
// but each column has a different width, that corresponds to the width of its largest element. Displayables are horizontaly centered
// in their respective grid cells.
void GLViewer::repackDisplayables()
{
    if( m_AddingOrder.empty() )
        return;


    // Computes the number of columns of the grid.
    int nCols = (int) std::ceil( std::sqrt( (float)m_AddingOrder.size() ) );


    // Recovers the bounding boxes of all items.
    std::vector<Box3f> boxes( m_AddingOrder.size() );

    auto d = m_AddingOrder.begin();
    for( int i=0; i<boxes.size(); ++i, ++d )
        (*d)->displayable->boundingBox( boxes[i] );


    // Initializes the row height by looking at the bounding box height of the first displayable in the list.
    float rowHeight = boxes[0].Dimensions().y();
    float gridSpacing = 0.05f * rowHeight;


    // The width of each column is determined by looking for the largest rescaled width of all the elements it contains.
    std::vector<int> columnWidth( nCols );
    for( int c=0; c<nCols; ++c )
        columnWidth[c] = 0;

    for( int i=0; i<boxes.size(); ++i )
    {
        int c = i % nCols;
        float rescaledWidth = boxes[i].Dimensions().x() * rowHeight / boxes[i].Dimensions().y();
        if( rescaledWidth > columnWidth[c] )
            columnWidth[c] = rescaledWidth;
    }


    // The scaling factor to be applied to each displayable so as to match the grid row height is computed,
    // and the layout arrangement is finally performed by setting up the translation part of each displayable transform.
    float xRowBeginning = (boxes[0].Min().x() + 0.5f*boxes[0].Dimensions().x()) * rowHeight / boxes[0].Dimensions().y() - 0.5f*columnWidth[0];
    float x = xRowBeginning;
    float y = boxes[0].Min().y() * rowHeight / boxes[0].Dimensions().y();

    d = m_AddingOrder.begin();
    for( int i=0, c=0; i<boxes.size(); ++i, ++d )
    {
        float scalingFactor = rowHeight / boxes[i].Dimensions().y();
        float rescaledWidth = boxes[i].Dimensions().x() * scalingFactor;

613
614
615
616
617
618
619
        QMatrix4x4 packTr;
        packTr.setToIdentity();
        packTr(0,0) = scalingFactor;
        packTr(1,1) = scalingFactor;
        packTr(0,3) = x - boxes[i].Min().x()*scalingFactor + 0.5f*(columnWidth[c]-rescaledWidth);
        packTr(1,3) = y - boxes[i].Min().y()*scalingFactor;
        (*d)->SetPackingTransform( packTr );
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649

        x += columnWidth[c] + gridSpacing;

        if( !(c = (c+1) % nCols) )
        {
            x = xRowBeginning;
            y -= rowHeight + gridSpacing;
        }
    }
}


GLViewer::DisplayableMap::iterator GLViewer::removeDisplayable_Internal( DisplayableMap::iterator &dmapIt )
{
    const GenericUIData *item  = dmapIt->first;
    DisplayableInfo     &dinfo = dmapIt->second;

    if( dinfo.displayable->isAnimated() )
    {
        auto foundAnim = m_ActiveAnimations.find( dinfo.displayable );
        if( foundAnim != m_ActiveAnimations.end() )
        {
            m_ActiveAnimations.erase( foundAnim );
            dinfo.displayable->onAnimationStop();
        }
    }

    disconnect( item->GetDisplayOptions(), SIGNAL(parameterUpdated(UIParam*)), this, SLOT(update()) );
    disconnect( item->GetDisplayOptions(), SIGNAL(parameterUpdated(UIParam*)), dinfo.displayable, SLOT(updateParameter(UIParam*)) );

650
    if( !item->GetDisplayOptions()->IsEmpty() )
651
652
653
    {
        item->GetDisplayOptions()->hide();
        m_DisplayOptionsLayout->removeWidget( item->GetDisplayOptions() );
654
        item->GetDisplayOptions()->setParent( NULL );
655
656
    }

657
658
659
660
    for( auto mngr : dinfo.selectionMgr )
        delete mngr;
    dinfo.selectionMgr.clear();

661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
    dinfo.displayable->release( *item->GetDisplayOptions() );
    delete dinfo.displayable;
    m_DisplayablesByPriority[ m_Factories[item->GetTypeString()]->displayPriority() ].erase( dinfo.posInPriorityList );

    return m_Displayables.erase( dmapIt );
}


void GLViewer::removeDisplayable( GenericUIData *m )
{
    DisplayableMap::iterator foundDisp = m_Displayables.find( m );
    if( foundDisp != m_Displayables.end() )
    {
        makeCurrent();

        if( dof() == DISPLAY_DOF_2D )
        {
            m_AddingOrder.remove( &foundDisp->second );
            removeDisplayable_Internal( foundDisp );
            repackDisplayables();
            frameAll();
        }
        else
            removeDisplayable_Internal( foundDisp );

        update();
    }
}


void GLViewer::removeAllDisplayables()
{
    makeCurrent();

	DisplayableMap::iterator disp = m_Displayables.begin();
    while( disp != m_Displayables.end() )
        disp = removeDisplayable_Internal( disp );

    m_AddingOrder.clear();

    update();
}


void GLViewer::initializeGL()
{
    makeCurrent();
    glewInit();

    setFocusPolicy( Qt::ClickFocus );

    resizeGL( width(), height() );

    update();
}


718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
void GLViewer::pixelRay( int px, int py, QVector3D &rayOrig, QVector3D &rayDir ) const
{
    if( isProjectionOrthographic() )
    {
        QVector4D clipCoord( 2.0f*px/width() - 1.0f, 2.0f*py/height() - 1.0f, -1.0f, 1.0f );
        rayOrig = viewMatrixInverse().map( projectionMatrixInverse().map( clipCoord ) ).toVector3D();
        rayDir  = frontAxis();
    }
    else
    {
        QVector4D clipCoord( 2.0f*px/width() - 1.0f, 2.0f*py/height() - 1.0f, 0.0f, 1.0f );
        rayOrig = viewpointLocation();
        rayDir  = viewRotationMatrixInverse().map( projectionMatrixInverse().map( clipCoord ) ).toVector3D();
    }
}


735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
QMatrix4x4 GLViewer::viewportMatrix( bool flipY ) const
{
    const float xOff = 0.5f*width(), yOff = 0.5f*height();
    const float xAmp = xOff, yAmp = flipY? 1.0f - yOff : yOff;

    return QMatrix4x4( xAmp, 0.0f, 0.0f, xOff,
                       0.0f, yAmp, 0.0f, yOff,
                       0.0f, 0.0f, 1.0f, 0.0f,
                       0.0f, 0.0f, 0.0f, 1.0f );
}


QMatrix4x4 GLViewer::modelMatrix( const DisplayableInterface *d ) const
{
    auto dispInfo = m_Displayables.find(d->getSource());
750
751
    assert( dispInfo != m_Displayables.end() );
    return dispInfo->second.GetTransform();
752
753
754
755
756
757
758
759
760
761
762
763
764
765
}


void GLViewer::manageAnimations()
{
    // Check if animated objects waiting to be started are pending.

    while( !m_AnimationStartingQueue.isEmpty() )
    {
        auto anim = m_AnimationStartingQueue.front();
        m_AnimationStartingQueue.pop_front();

        if( !m_ActiveAnimations.contains(anim) )
        {
766
            m_ActiveAnimations[anim] = 0.0;
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
            anim->onAnimationStart();
        }
    }


    // Check if animated objects waiting to be stopped are pending.

    while( !m_AnimationStoppingQueue.isEmpty() )
    {
        auto anim = m_AnimationStoppingQueue.front();
        m_AnimationStoppingQueue.pop_front();

        auto foundAnim = m_ActiveAnimations.find( anim );
        if( foundAnim != m_ActiveAnimations.end() )
        {
            m_ActiveAnimations.erase( foundAnim );
            anim->onAnimationStop();
        }
    }


    // Check if at least one of the current animations is not paused.

    bool animationsRunning = false;

    for( auto anim=m_ActiveAnimations.begin(); anim!=m_ActiveAnimations.end(); ++anim )
        if( !anim.key()->isAnimationPaused() )
        {
            animationsRunning = true;
            break;
        }


800
801
    // Accurate measurement of the time elapsed since the last animation step.

Frédéric Larue's avatar
Frédéric Larue committed
802
803
804
    TimeMeasurement now = std::chrono::high_resolution_clock::now();
    double dt = 0.000001 * std::chrono::duration_cast< std::chrono::microseconds >( now - m_AnimationFrameTime ).count();
    m_AnimationFrameTime = now;
805
	m_AnimationFrameRate = float( 1.0 / dt );
806

807
808

    // Make all active animated objects to execute a new animation step.
Frédéric Larue's avatar
Frédéric Larue committed
809

810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
    if( animationsRunning )
    {
        if( m_AnimationsRunningAtPreviousFrame )
            for( auto anim=m_ActiveAnimations.begin(); anim!=m_ActiveAnimations.end(); ++anim )
                if( !anim.key()->isAnimationPaused() )
                {
                    if( anim.key()->animationTimeStepValue() != 0.0f )
                    {
                        anim.value() += dt;
                        if( anim.value() >= anim.key()->animationTimeStepValue() )
                        {
                            anim.key()->onAnimationStep( anim.value() );
                            anim.value() -= anim.key()->animationTimeStepValue();
                        }
                    }
                    else
                        anim.key()->onAnimationStep( dt );
                }

        update();
    }


    m_AnimationsRunningAtPreviousFrame = animationsRunning;
}


void GLViewer::paintGL()
{
    manageAnimations();


    // Setting up the OpenGL viewport.
843

844
845
846
847
	glViewport( 0, 0, width(), height() );


    // Clear the frame buffer.
848

849
850
851
852
853
854
855
856
857
858
859
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    glDisable( GL_LIGHTING );
    glDisable( GL_DEPTH_TEST );
    glDepthMask( GL_FALSE );

    glBegin( GL_QUADS );
860
        glColor3fv( &GLViewer::BgBottomColor[0] );
861
862
        glVertex2i( -1, -1 );
        glVertex2i(  1, -1 );
863
        glColor3fv( &GLViewer::BgTopColor[0] );
864
865
866
867
868
869
870
871
872
        glVertex2i(  1,  1 );
        glVertex2i( -1,  1 );
    glEnd();

    glDepthMask( GL_TRUE );

	glClear( GL_DEPTH_BUFFER_BIT );


873
874
875
876
877
878
879
880
881
882
883
884
885
886
    // Update the projection and view matrices, if needed.

    if( m_MustUpdateFocal )
    {
        m_Focal = 1.0 / std::tan( fovY()*M_PI/360.0 );

        m_MustUpdateFocal = false;
        m_MustUpdateProjectionMatrix = true;
    }

    bool mustUpdateViewProjectionMatrix = false;

    if( m_MustUpdateProjectionMatrix )
    {
887
        navigationControl()->updateProjectionMatrix( m_ProjectionMatrix );
888
889
890
891
892
893
894
895
896

        m_ProjectionMatrixInverse = m_ProjectionMatrix.inverted();

        m_MustUpdateProjectionMatrix = false;
        mustUpdateViewProjectionMatrix = true;
    }

    if( m_MustUpdateViewMatrix )
    {
897
        navigationControl()->updateViewMatrix( m_ViewMatrix );
898
899
900

        m_ViewMatrixInverse = m_ViewMatrix.inverted();

901
902
        m_ViewRotationMatrixInverse = viewRotationMatrix().inverted();

903
904
905
906
907
908
909
910
        m_MustUpdateViewMatrix = false;
        mustUpdateViewProjectionMatrix = true;
    }

    if( mustUpdateViewProjectionMatrix )
    {
        m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
        m_ViewProjectionMatrixInverse = m_ViewMatrixInverse * m_ProjectionMatrixInverse;
911

912
        m_Frustum.UpdateFrustumPlanes( m_ViewMatrix, m_ProjectionMatrix );
913
914
915
916
917
    }

    // Setup the OpenGL transforms and viewport.

    glMatrixMode( GL_PROJECTION );
918
919
920
921
922
    glLoadMatrixf( projectionMatrix().data() );

    glMatrixMode( GL_MODELVIEW );
    glLoadMatrixf( viewMatrix().data() );

923
    glPointSize( (GLfloat) m_View.pointSize );
924
925
926
927
928
929
930
931
    glColor3ub( 255, 255, 255 );

    setupScene();


    // Display the scene content by increasing priority.
    for( PriorityMap::iterator dlist=m_DisplayablesByPriority.begin(); dlist!=m_DisplayablesByPriority.end(); ++dlist )
        for( DisplayableList::iterator dinfo=dlist->second.begin(); dinfo!=dlist->second.end(); ++dinfo )
932
933
934
935
936
937
            if( !m_Frustum.IsOutside( (*dinfo)->boundingBox, (*dinfo)->GetTransform() ) )
            {
                glPushMatrix();
                glMultMatrixf( (*dinfo)->GetTransform().data() );
                glPushAttrib( GL_ALL_ATTRIB_BITS );
                (*dinfo)->displayable->onDisplay( *(*dinfo)->displayable->getSource()->GetDisplayOptions() );
938
                //displayLabel( (*dinfo)->displayable );
939
940
                if( (*dinfo)->currentSelectionMgr )
                    (*dinfo)->currentSelectionMgr->onDisplay( this, (*dinfo)->GetTransform() );
941
942
943
944
                glPopAttrib();
                glPopMatrix();
            }

945
946
947
948
949

    // Finalize the rendering.
    releaseScene();

    // Display the metric grid.
950
951
952
953
954
955
956
957
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
    glDepthMask( GL_FALSE );
    glEnable( GL_DEPTH_TEST );
    if( dof() == DISPLAY_DOF_2D )
        m_View.metricGrid2D.display( this );
    else
        m_View.metricGrid3D.display( this );
    glDepthMask( GL_TRUE );
958

959
    // Display the label of the object under the mouse cursor.
960
    if( /*dof() == DISPLAY_DOF_3D  &&*/  m_BelowCursor )
961
    {
962
        double textX, textY;
963
964
965
966
967
        //QVector3D c = m_BelowCursor->GetTransform().map( m_BelowCursor->boundingBox.Center() );
        //renderCoords( c.x(), c.y(), c.z(), &textX, &textY );
        QPoint c = mapFromGlobal( cursor().pos() );
        textX = c.x();
        textY = c.y();
968
969

        QString label = m_BelowCursor->displayable->label();
970
971
972
        QFont f = QApplication::font();
        f.setPointSize( 10 );
        f.setWeight( QFont::Bold );
973

974
        Qt::Alignment align = static_cast<Qt::Alignment>( Qt::AlignHCenter | Qt::AlignBottom );
975
976

        glColor3ub( 220, 220, 220 );
977
978
979
980
        renderText( textX-1, textY-1, label, f, align );
        renderText( textX-1, textY+1, label, f, align );
        renderText( textX+1, textY-1, label, f, align );
        renderText( textX+1, textY+1, label, f, align );
981
        glColor3ub( 64, 64, 64 );
982
        renderText( textX, textY, label, f, align );
983
    }
984
985

    // Display the selection tool, if selection mode is enabled.
986
    if( isSelectionEnabled()  &&  m_IsCursorInside )
987
988
989
990
991
992
    {
        m_SelectionCurrentTool->displaySelectionState();
        m_SelectionCurrentTool->display();
    }


993
994
995
996
997
998
999
1000
1001
	if( m_PerformanceCounterEnabled )
	{
		QString label = QString::number( m_AnimationFrameRate, 'f', 2 ) + " FPS";
		glColor3ub( 0, 0, 0 );
		renderText( 10, 10, label, QFont( font().family(), 20, QFont::Bold ), static_cast<Qt::Alignment>( Qt::AlignLeft | Qt::AlignTop ) );
		update();
	}


1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
    // Display the information bar.
    m_InfoBarManager->display();
}


void GLViewer::setupScene()
{
    // Setup the OpenGL states.

    if( dof() == DISPLAY_DOF_2D )
    {
        glDisable( GL_DEPTH_TEST );
        glEnable( GL_MULTISAMPLE );
        glDisable( GL_LIGHTING );
    }
    else
    {
        // Place the light into the scene.
        glMatrixMode( GL_MODELVIEW );
        glPushMatrix();

1023
        if( m_View.isLightTrackingEnabled )
1024
            glLoadIdentity();
1025
        glMultMatrixf( m_View.lightRotationMatrix.data() );
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036

        GLfloat lightPos[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
        glLightfv( GL_LIGHT0, GL_POSITION, lightPos );

        glPopMatrix();


        // Setup the OpenGL states.
	    glEnable( GL_DEPTH_TEST );
        glEnable( GL_MULTISAMPLE );

1037
        if( m_View.isLightingEnabled )
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
        {
            glEnable( GL_LIGHTING );
            glEnable( GL_LIGHT0 );
            glEnable( GL_COLOR_MATERIAL );
        }
        else
        {
            glDisable( GL_LIGHTING );
            glDisable( GL_LIGHT0 );
            glDisable( GL_COLOR_MATERIAL );
        }

1050
        glPolygonMode( GL_FRONT_AND_BACK, m_View.isWireframeEnabled && !m_SelectionEnabled? GL_LINE : GL_FILL );
1051

1052
        if( m_View.isCullingEnabled )
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
        {
            glEnable( GL_CULL_FACE );
            glCullFace( GL_BACK );
        }
        else
            glDisable( GL_CULL_FACE );
    }
}


void GLViewer::displayLabel( DisplayableInterface *d )
{
    if( dof() == DISPLAY_DOF_2D )
    {
        Box3f box;
        d->boundingBox( box );


        // Display the black border.

        glColor3ub( 0, 0, 0 );

        glBegin( GL_LINE_LOOP );
            glVertex2f( box.Min().x(), box.Min().y() );
            glVertex2f( box.Min().x(), box.Max().y() );
            glVertex2f( box.Max().x(), box.Max().y() );
            glVertex2f( box.Max().x(), box.Min().y() );
        glEnd();


        // Display the name of the image on overlay in the bottom-left corner, by constraining it to the displayed image rectangle.

1085
        QMatrix4x4 toViewportCoord = viewportMatrix(true) * MVPMatrix(d);
1086
1087
1088
        QVector3D bottomLeft = toViewportCoord.map( QVector3D( box.Min().x(), box.Min().y(), 0.0f ) );
        QVector3D topRight   = toViewportCoord.map( QVector3D( box.Max().x(), box.Max().y(), 0.0f ) );

1089
1090
        QFont f( font().family(), 10, QFont::Bold );
        QFontMetrics fm( f );
1091
1092
1093
1094
1095
1096

        QString imgName;
        int imgNameWidth;

        if( fm.height() < bottomLeft.y()-topRight.y()-4 )
        {
1097
            imgName = d->label();
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
            imgNameWidth = fm.width( imgName );

            if( imgNameWidth > topRight.x()-bottomLeft.x()-8 )
            {
                int imgNameLength = imgName.length();
                do
                {
                    -- imgNameLength;
                    imgNameWidth -= fm.width( imgName.at(imgNameLength) );
                } while( imgNameWidth > topRight.x()-bottomLeft.x()-8 );
                imgName = imgName.left( imgNameLength );
            }
        }

        if( imgName.length() > 0 )
        {
            int textX = bottomLeft.x() + 4;
1115
            int textY = bottomLeft.y() - 2;
1116
1117

            glColor3ub( 220, 220, 220 );
1118
1119
1120
1121
            renderText( textX-1, textY-1, imgName, f, Qt::AlignBottom );
            renderText( textX-1, textY+1, imgName, f, Qt::AlignBottom );
            renderText( textX+1, textY-1, imgName, f, Qt::AlignBottom );
            renderText( textX+1, textY+1, imgName, f, Qt::AlignBottom );
1122
            glColor3ub( 64, 64, 64 );
1123
            renderText( textX, textY, imgName, f, Qt::AlignBottom );
1124
1125
1126
1127
1128
1129
1130
        }
    }
}


void GLViewer::resizeGL( int width, int height )
{
1131
1132
    m_MustUpdateProjectionMatrix = true;

1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
	glViewport( 0, 0, width, height );

    m_InfoBarManager->onReshape();

    for( DisplayableMap::iterator d=m_Displayables.begin(); d!=m_Displayables.end(); ++d )
        d->second.displayable->onReshape( *d->first->GetDisplayOptions() );

    if( isSelectionEnabled() )
        m_SelectionCurrentTool->onResize();

    updateSelectionZBuffer();
}


void GLViewer::updateSelection( SelectionTool &tool )
{
    makeCurrent();

    // Initializes the GL transformation so as to match the current view.

	glMatrixMode( GL_PROJECTION );
    glLoadMatrixf( projectionMatrix().data() );

    glMatrixMode( GL_MODELVIEW );
    glLoadMatrixf( viewMatrix().data() );


    // Initializes the selection context.

1162
    tool.setDepthBuffer( m_SelectionDepthTexture );
1163
    tool.selectBackFacingGeometry( m_View.isCullingEnabled? GL_FALSE : GL_TRUE );
1164
1165
1166
1167
1168
1169
    tool.preSelectionUpdate();


    // Checks every item that can be selected.

    for( DisplayableMap::iterator d=m_Displayables.begin(); d!=m_Displayables.end(); ++d )
1170
        if( d->second.currentSelectionMgr )
1171
1172
        {
            glPushMatrix();
1173
            glMultMatrixf( d->second.GetTransform().data() );
1174
            d->second.currentSelectionMgr->onUpdatingSelection( &tool );
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
            glPopMatrix();
        }


    tool.postSelectionUpdate();
}


void GLViewer::updateSelectionMode( const Qt::KeyboardModifiers &modifiers )
{
    if( isSelectionEnabled() )
    {
        SelectionTool::SelectionMode newMode;

        if( modifiers & Qt::ShiftModifier )
            newMode = SelectionTool::SELECTION_MODE_SET;
        else if( modifiers & Qt::ControlModifier )
            newMode = SelectionTool::SELECTION_MODE_REMOVE;
        else
            newMode = SelectionTool::SELECTION_MODE_ADD;

        if( m_SelectionCurrentTool->selectionMode() != newMode )
        {
            m_SelectionCurrentTool->setSelectionMode( newMode );
            update();
        }
    }
}


void GLViewer::updateSelectionZBuffer()
{
    if( !isSelectionEnabled() )
        return;

    makeCurrent();


    // Creates the shader that makes it possible to recover the scene Z-buffer.

    if( !m_ZBufferShader.IsCreated() )
    {
        std::string logs;
        if( !GPU::CreateShaderFromSources( m_ZBufferShader, s_ZBufferVPG, s_ZBufferFPG, &logs ) )
        {
            std::cout << "Z-buffer shader compilation error:" << std::endl << logs << std::endl;
            return;
        }
    }


    // Recovers the depth buffer matching the current view for depth test during selection.

1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
    if( !m_SelectionDepthTexture.IsInstantiated() )
    {
        m_SelectionDepthTexture.Create( GL_DEPTH_COMPONENT, width(), height(), GL_DEPTH_COMPONENT, GL_INT, NULL );
        m_SelectionDepthTexture.SetFiltering( GL_LINEAR );
        m_SelectionDepthTexture.SetBaseAndMaxLevels( 0 );
        m_SelectionDepthTexture.SetCompareMode( GL_COMPARE_R_TO_TEXTURE );
        m_SelectionDepthTexture.SetCompareFunc( GL_LEQUAL );
        m_SelectionDepthTexture.SetDepthTextureMode( GL_INTENSITY );

        m_SelectionDepthBuffer.Create( width(), height() );
        m_SelectionDepthBuffer.Attach( GL_DEPTH_ATTACHMENT, m_SelectionDepthTexture );
    }
1240

1241
    m_SelectionDepthBuffer.Bind();
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253

    glClear( GL_DEPTH_BUFFER_BIT );


    // Setting up the OpenGL transforms and viewport.

	glMatrixMode( GL_PROJECTION );
    glLoadMatrixf( projectionMatrix().data() );

    glMatrixMode( GL_MODELVIEW );
    glLoadMatrixf( viewMatrix().data() );

1254
    glPointSize( (GLfloat) m_View.pointSize );
1255
1256
1257
1258
1259
1260
1261
1262

    setupScene();


    // Display the scene content by increasing priority.

    for( PriorityMap::iterator dlist=m_DisplayablesByPriority.begin(); dlist!=m_DisplayablesByPriority.end(); ++dlist )
        for( DisplayableList::iterator dinfo=dlist->second.begin(); dinfo!=dlist->second.end(); ++dinfo )
1263
1264
1265
1266
1267
1268
1269
            if( !m_Frustum.IsOutside( (*dinfo)->boundingBox, (*dinfo)->GetTransform() ) )
            {
                m_ZBufferShader.SetUniform( "fullTransformMatrix", (viewProjectionMatrix() * (*dinfo)->GetTransform()).data() );
                glPushAttrib( GL_ALL_ATTRIB_BITS );
                (*dinfo)->displayable->onPicking( *(*dinfo)->displayable->getSource()->GetDisplayOptions(), m_ZBufferShader, "vertexPosition" );
                glPopAttrib();
            }
1270
1271
1272
1273
1274
1275


    // Finalize the rendering.

    releaseScene();

1276
    m_SelectionDepthBuffer.Unbind();
1277
1278
1279
1280
1281
}


void GLViewer::processSelection( BaseSelectionProcessor &proc )
{
1282
1283
    QList<BaseSelectionProcessor*> procList;
    procList << &proc;
1284
1285
1286
1287
    processSelection( procList );
}


1288
void GLViewer::processSelection( QList<BaseSelectionProcessor*> &procList )
1289
{
1290
    QMap< QString, QList<BaseSelectionProcessor*> > processorByType;
1291

1292
    for( BaseSelectionProcessor* p : procList )
1293
    {
1294
1295
        QStringList acceptedTypes;
        p->acceptedDataType( acceptedTypes );
1296

1297
        for( QString& type : acceptedTypes )
1298
            processorByType[type].push_back( p );
1299
1300
    }

1301
    for( auto& d : m_Displayables )
1302
    {
1303
        auto processorFound = processorByType.find( d.first->GetTypeString() );
1304
        if( processorFound != processorByType.end() )
1305
1306
            for( auto p : processorFound.value() )
            processSelection( d.second, *p );
1307
1308
1309
1310
1311
1312
1313
    }
}


void GLViewer::processSelection( GenericUIData *m, BaseSelectionProcessor &proc )
{
    DisplayableMap::iterator displayableFound = m_Displayables.find( m );
1314
1315
1316
    if( displayableFound != m_Displayables.end() )
        processSelection( displayableFound->second, proc );
}
1317
1318


1319
1320
1321
1322
1323
1324
void GLViewer::processSelection( DisplayableInterface *d, BaseSelectionProcessor &proc )
{
    processSelection( d->getSource(), proc );
}


1325
1326
1327
1328
void GLViewer::processSelection( DisplayableInfo &dInfo, BaseSelectionProcessor &proc )
{
    DisplayableInterface *d   = dInfo.displayable;
    GenericUIData        *m   = d->getSource();
1329
    SelectionManager     *mgr = dInfo.currentSelectionMgr;
1330

1331
    if( !mgr  ||  mgr->selectableEntity() != proc.entityToProcess() )
1332
        return;
1333

1334
    QStringList acceptedTypes;
1335
1336
    proc.acceptedDataType( acceptedTypes );

1337
1338
1339
1340
1341
    if( acceptedTypes.contains(m->GetTypeString()) )
    {
        proc.init( m );
        d->processSelection( mgr, proc );
        proc.release( m );
1342

1343
1344
        if( proc.isDisplayableUpdateRequired() )
        {
1345
            updateDisplayable( m, proc.isSelectionResetRequired() );
1346
            updateSelectionZBuffer();
1347
        }
1348
1349
		else if( proc.isSelectionResetRequired() )
			mgr->clearSelection();
1350
    }
1351
1352
1353
}


1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
void GLViewer::renderCoords( double x, double y, double z, double *screenX, double *screenY )
{
    // Identify x and y locations to render text within widget

    QMatrix4x4 model, proj;
    GLint viewport[4];
    glGetFloatv( GL_MODELVIEW_MATRIX, model.data() );
    glGetFloatv( GL_PROJECTION_MATRIX, proj.data() );
    glGetIntegerv( GL_VIEWPORT, viewport );

    *screenX = *screenY = 0.0;
    QVector4D out = proj.map( model.map( QVector4D(x,y,z,1.0f) ) );

    if( out.w() != 0.0f )
    {
        float wInv = 1.0f / out.w();
        out.setX( out.x() * wInv );
        out.setY( out.y() * wInv );

        *screenX = viewport[0] + (1 + out.x()) * 0.5 * viewport[2];
        *screenY = viewport[1] + (1 + out.y()) * 0.5 * viewport[3];
    }

    *screenY = this->height() - *screenY; // y is inverted
}


1381
void GLViewer::renderText( double x, double y, const QString &text, const QFont &font, Qt::Alignment align )
1382
{
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
    QFontMetrics fm( font );

    // Extract lines from the input strings.

    QStringList textLines;

    int beg = 0, end;
    while( (end = text.indexOf('\n',beg)) >= 0 )
    {
        textLines.push_back( text.mid(beg,end-beg) );
        beg = end + 1;
    }

    textLines.push_back( (beg==0)? text : text.mid(beg) );

1398
1399
1400
    // Save OpenGL states.

    GLint buffer;
1401
	GLboolean blend, depthTest, multisample;
1402
1403
	GLint blendSrcRGB, blendSrcAlpha;
	GLint blendDstRGB, blendDstAlpha;
1404
1405
    GLint alignment;
    GLfloat glColor[4];
1406
1407

    glGetIntegerv( GL_FRAMEBUFFER_BINDING, &buffer );
1408
    glGetBooleanv( GL_DEPTH_TEST, &depthTest );
1409
	glGetBooleanv( GL_BLEND, &blend );
1410
    glGetBooleanv( GL_MULTISAMPLE, &multisample );
1411
1412
1413
1414
	glGetIntegerv( GL_BLEND_SRC_RGB  , &blendSrcRGB   );
	glGetIntegerv( GL_BLEND_SRC_ALPHA, &blendSrcAlpha );
	glGetIntegerv( GL_BLEND_DST_RGB  , &blendDstRGB   );
	glGetIntegerv( GL_BLEND_DST_ALPHA, &blendDstAlpha );
1415
1416
    glGetIntegerv( GL_UNPACK_ALIGNMENT, &alignment );
    glGetFloatv( GL_CURRENT_COLOR, glColor );
1417
1418
1419
1420
1421
1422
1423

    // Retrieve last OpenGL color to use as a font color.

    QColor fontColor = QColor( 255.0f*glColor[0], 255.0f*glColor[1], 255.0f*glColor[2], 255.0f*glColor[3] );

	// Paint text.

1424
1425
1426
1427
1428
1429
    if( align & Qt::AlignBottom )
        y -= (textLines.size()-1) * fm.lineSpacing() + fm.descent();
    else if( align & Qt::AlignVCenter )
        y -= ((textLines.size()) * fm.lineSpacing())/2 - fm.ascent();
	else if( align & Qt::AlignTop )
		y += fm.ascent();
1430

1431
    glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );    // Mandatory to avoid font corruption while using painter.drawText(...)!
1432
1433
1434
1435
    QPainter painter;
    painter.begin( this );
	    painter.setPen( fontColor );
	    painter.setFont( font );
1436
        //painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing );
1437
1438
1439
1440
1441
1442
1443
1444
        for( auto &l : textLines )
        {
            if( align & Qt::AlignRight )
        		painter.drawText( x - fm.width(l), y, l );
            else if( align & Qt::AlignHCenter )
        		painter.drawText( x - fm.width(l)/2, y, l );
            else
        		painter.drawText( x, y, l );
1445
            y += fm.lineSpacing();
1446
        }
1447
1448
1449
1450
    painter.end();

	// Restore OpenGL states.

1451
    glPixelStorei( GL_UNPACK_ALIGNMENT, alignment );
1452
1453
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, buffer );
    blend?  glEnable( GL_BLEND )  :  glDisable( GL_BLEND );
1454
    depthTest?  glEnable( GL_DEPTH_TEST )  :  glDisable( GL_DEPTH_TEST );
1455
    multisample?  glEnable( GL_MULTISAMPLE )  :  glDisable( GL_MULTISAMPLE );
1456
    glBlendFuncSeparateEXT( blendSrcRGB, blendDstRGB, blendSrcAlpha, blendDstAlpha );
1457
1458
1459
1460
    glColor4fv( glColor );
}


1461
void GLViewer::renderText( double x, double y, double z, const QString &text, const QFont &font, Qt::Alignment align )
1462
{
1463
1464
    double screenX, screenY;
    renderCoords( x, y, z, &screenX, &screenY );
1465
    renderText( screenX, screenY, text, font, align );
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
}


bool GLViewer::imageOfCurrentDisplay( const GenericUIData *m, QImage &renderResult )
{
    // Check if the provided item is currently displayed.

    DisplayableInterface *d = getDisplayable( m );

    if( !d )
        return false;


    // Recover its dimensions so as to determine the size of the rendering output buffer.

    Box3f dBox;
    d->boundingBox( dBox );

    int w = (int) dBox.Dimensions().x();
    int h = (int) dBox.Dimensions().y();


    // Perform off-screen rendering.

    makeCurrent();

    GPU::FrameBuffer fb( w, h );
    fb.Attach( GL_COLOR_ATTACHMENT0, GL_RGBA );
    fb.Bind();


    glMatrixMode( GL_PROJECTION );
    glPushMatrix();
    glLoadIdentity();
    glOrtho( dBox.Min().x(), dBox.Max().x(), dBox.Max().y(), dBox.Min().y(), -1.0f, 1.0f );
1501
    glMultMatrixf( viewMatrixInverse().data() );
1502
1503
1504
1505
    glMultMatrixf( modelMatrixInverse(d).data() );

    QMatrix4x4 projBackup = m_ProjectionMatrix;
    glGetFloatv( GL_PROJECTION_MATRIX, m_ProjectionMatrix.data() );
1506
1507
1508

    glMatrixMode( GL_MODELVIEW );
    glPushMatrix();
1509
    glLoadMatrixf( viewMatrix().data() );
1510
    glMultMatrixf( modelMatrix(d).data() );
1511
1512
1513

    d->onDisplay( *m->GetDisplayOptions() );

1514
    glMatrixMode( GL_MODELVIEW );
1515
1516
1517
1518
    glPopMatrix();
    glMatrixMode( GL_PROJECTION );
    glPopMatrix();

1519
1520
    m_ProjectionMatrix = projBackup;

1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
    fb.Unbind();


    // Recover the render result and store it to the provided image object.

    GLubyte *fbData = new GLubyte [ 4*w*h ];
    fb.DumpTo( GL_COLOR_ATTACHMENT0, fbData, GL_RGBA, GL_UNSIGNED_BYTE );

    renderResult = QImage( w, h, QImage::Format_RGBA8888 );
    for( int y=0, n=0; y<h; ++y )
    {
        unsigned char *lineData = renderResult.scanLine( y );
        for( int x=0; x<4*w; ++x, ++n )
            lineData[x] = fbData[n];
    }

1537
    delete[] fbData;
1538
1539
1540
1541
1542
1543


    return true;
}


1544
1545
1546
void GLViewer::enterEvent( QEvent* evt )
{
    setFocus();
1547
1548
    m_IsCursorInside = true;
    update();
1549
1550
1551
1552
    QOpenGLWidget::enterEvent( evt );
}


1553
1554
1555
1556
1557
1558
1559
1560
void GLViewer::leaveEvent( QEvent* evt )
{
    m_IsCursorInside = false;
    update();
    QOpenGLWidget::leaveEvent( evt );
}


1561
1562
1563
1564
1565
1566
void GLViewer::mousePressEvent( QMouseEvent *evt )
{
    if( dof() == DISPLAY_DOF_2D )
    {
        PickedPoint p;

1567
        if( !isSelectionEnabled()  &&  evt->button() == Qt::RightButton  &&  getPickedPoint( evt->pos().x(), height()-evt->pos().y(), p, true ) )
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
        {
            QMenu ctxMenu;
            ctxMenu.addAction( "Save current render" );

            if( ctxMenu.exec( evt->globalPos() ) )
            {
		        QStringList filters;
			    QString allImages;
                foreach( QByteArray ext, QImageWriter::supportedImageFormats() )
			    {
		            filters << QString(ext).toUpper() + " image files (*." + ext + ")";
				    allImages += " *." + ext;
			    }
			    filters.push_front( "All image files (" + allImages + " )" );

		        QFileDialog dlg( this, "Save image" );
		        dlg.setAcceptMode( QFileDialog::AcceptSave );
                dlg.setFileMode( QFileDialog::AnyFile );
                dlg.setNameFilters( filters );

			    if( dlg.exec() )
			    {
				    QImage img;
				    imageOfCurrentDisplay( p.srcObject, img );
				    img.save( dlg.selectedFiles().front() );
			    }
            }
        }
    }


    if( isSelectionEnabled() )
    {
        if( evt->button() == Qt::RightButton )
            toggleSelection();
        else
        {
            makeCurrent();
1606
1607
            if( m_SelectionZBufferUpdateMode & SelectionZBufferUpdateMode::ON_MOUSE_PRESSED )
                updateSelectionZBuffer();
1608
1609
1610
1611
            m_SelectionCurrentTool->mousePressEvent( evt );
        }
    }