1 /**********************************************************************
2 GLWidget - general OpenGL display
4 Copyright (C) 2006,2007 Geoffrey R. Hutchison
5 Copyright (C) 2006,2007 Donald Ephraim Curtis
6 Copyright (C) 2007 Benoit Jacob
7 Copyright (C) 2007,2008 Marcus D. Hanwell
9 This file is part of the Avogadro molecular editor project.
10 For more information, see <http://avogadro.sourceforge.net/>
12 Avogadro is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 Avogadro is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 **********************************************************************/
30 #include <avogadro/glwidget.h>
31 #include <avogadro/glpainter.h>
32 #include <avogadro/painterdevice.h>
33 #include <avogadro/toolgroup.h>
35 #include "elementcolor.h"
39 #include <QPluginLoader>
42 #ifdef ENABLE_THREADED_GL
43 #include <QWaitCondition>
52 using namespace OpenBabel
;
53 using namespace Eigen
;
57 bool engineLessThan( const Engine
* lhs
, const Engine
* rhs
)
59 Engine::EngineFlags lhsFlags
= lhs
->flags();
60 Engine::EngineFlags rhsFlags
= rhs
->flags();
62 if ( !( lhsFlags
& Engine::Overlay
) && rhsFlags
& Engine::Overlay
) {
64 } else if (( lhsFlags
& Engine::Overlay
) && ( rhsFlags
& Engine::Overlay
) ) {
65 return lhs
->transparencyDepth() < rhs
->transparencyDepth();
66 } else if (( lhsFlags
& Engine::Overlay
) && !( rhsFlags
& Engine::Overlay
) ) {
68 } else if ( !( lhsFlags
& Engine::Molecules
) && rhsFlags
& Engine::Molecules
) {
70 } else if (( lhsFlags
& Engine::Molecules
) && ( rhsFlags
& Engine::Molecules
) ) {
71 return lhs
->transparencyDepth() < rhs
->transparencyDepth();
72 } else if (( lhsFlags
& Engine::Molecules
) && !( rhsFlags
& Engine::Molecules
) ) {
74 } else if ( !( lhsFlags
& Engine::Atoms
) && rhsFlags
& Engine::Atoms
) {
76 } else if (( lhsFlags
& Engine::Atoms
) && ( rhsFlags
& Engine::Atoms
) ) {
77 return lhs
->transparencyDepth() < rhs
->transparencyDepth();
78 } else if (( lhsFlags
& Engine::Atoms
) && !( rhsFlags
& Engine::Atoms
) ) {
80 } else if ( !( lhsFlags
& Engine::Bonds
) && rhsFlags
& Engine::Bonds
) {
82 } else if (( lhsFlags
& Engine::Bonds
) && ( rhsFlags
& Engine::Bonds
) ) {
83 return lhs
->transparencyDepth() < rhs
->transparencyDepth();
84 } else if (( lhsFlags
& Engine::Bonds
) && !( rhsFlags
& Engine::Bonds
) ) {
101 GLHit::GLHit() : d( new GLHitPrivate
) {}
103 GLHit::GLHit( const GLHit
&other
) : d( new GLHitPrivate
)
105 GLHitPrivate
*e
= other
.d
;
112 GLHit::GLHit( GLuint type
, GLuint name
, GLuint minZ
, GLuint maxZ
) : d( new GLHitPrivate
)
120 GLHit
&GLHit::operator=( const GLHit
&other
)
122 GLHitPrivate
*e
= other
.d
;
135 bool GLHit::operator<( const GLHit
&other
) const
137 GLHitPrivate
*e
= other
.d
;
138 return d
->minZ
< e
->minZ
;
141 bool GLHit::operator==( const GLHit
&other
) const
143 GLHitPrivate
*e
= other
.d
;
144 return (( d
->type
== e
->type
) && ( d
->name
== e
->name
) );
147 GLuint
GLHit::name() const { return d
->name
; }
148 GLuint
GLHit::type() const { return d
->type
; }
149 GLuint
GLHit::minZ() const { return d
->minZ
; }
150 GLuint
GLHit::maxZ() const { return d
->maxZ
; }
152 void GLHit::setName( GLuint name
) { d
->name
= name
; }
153 void GLHit::setType( GLuint type
) { d
->type
= type
; }
154 void GLHit::setMinZ( GLuint minZ
) { d
->minZ
= minZ
; }
155 void GLHit::setMaxZ( GLuint maxZ
) { d
->maxZ
= maxZ
; }
157 class GLPainterDevice
: public PainterDevice
160 GLPainterDevice(GLWidget
*gl
) { widget
= gl
; }
161 ~GLPainterDevice() {}
163 Painter
*painter() const { return widget
->painter(); }
164 Camera
*camera() const { return widget
->camera(); }
165 bool isSelected( const Primitive
*p
) const { return widget
->isSelected(p
); }
166 double radius( const Primitive
*p
) const { return widget
->radius(p
); }
167 const Molecule
*molecule() const { return widget
->molecule(); }
168 Color
*colorMap() const { return widget
->colorMap(); }
170 int width() { return widget
->width(); }
171 int height() { return widget
->height(); }
177 class GLWidgetPrivate
180 GLWidgetPrivate() : background( Qt::black
),
181 aCells( 1 ), bCells( 1 ), cCells( 1 ),
184 camera( new Camera
),
189 #ifdef ENABLE_THREADED_GL
192 initialized( false ),
196 defaultMap( new ElementColor
),
201 dlistQuick(0), dlistOpaque(0), dlistTransparent(0),
204 loadEngineFactories();
209 if ( selectBuf
) delete[] selectBuf
;
213 // free the display lists
215 glDeleteLists(dlistQuick
, 1);
217 glDeleteLists(dlistOpaque
, 1);
218 if (dlistTransparent
)
219 glDeleteLists(dlistTransparent
, 1);
222 void updateListQuick();
223 static void loadEngineFactories();
224 static QList
<EngineFactory
*> engineFactories
;
225 static QHash
<QString
, EngineFactory
*> engineClassFactory
;
227 QList
<Engine
*> engines
;
231 Vector3d normalVector
;
234 const Atom
*farthestAtom
;
236 //! number of unit cells in a, b, and c crystal directions
237 unsigned char aCells
;
238 unsigned char bCells
;
239 unsigned char cCells
;
248 ToolGroup
*toolGroup
;
253 PrimitiveList selectedPrimitives
;
254 PrimitiveList primitives
;
256 QUndoStack
*undoStack
;
260 #ifdef ENABLE_THREADED_GL
261 QWaitCondition paintCondition
;
270 Color
*map
; // global color map
271 Color
*defaultMap
; // default fall-back coloring (i.e., by elements)
272 bool updateCache
; // Update engine caches in quick render?
273 bool quickRender
; // Are we using quick render?
274 bool renderAxes
; // Should the x, y, z axes be rendered?
275 bool renderDebug
; // Should the debug information be shown?
279 GLuint dlistTransparent
;
282 * Member GLPainterDevice which is passed to the engines.
287 QList
<EngineFactory
*> GLWidgetPrivate::engineFactories
;
288 QHash
<QString
, EngineFactory
*> GLWidgetPrivate::engineClassFactory
;
290 void GLWidgetPrivate::loadEngineFactories()
292 static bool enginesLoaded
= false;
295 QString prefixPath
= QString( INSTALL_PREFIX
) + "/lib/avogadro/engines";
296 QStringList pluginPaths
;
297 pluginPaths
<< prefixPath
;
300 pluginPaths
<< "./engines";
303 if ( getenv( "AVOGADRO_ENGINES" ) != NULL
) {
304 pluginPaths
= QString( getenv( "AVOGADRO_ENGINES" ) ).split( ':' );
307 // load static plugins first
309 // now load plugins from paths
310 foreach( QString path
, pluginPaths
) {
312 foreach( QString fileName
, dir
.entryList( QDir::Files
) ) {
313 QPluginLoader
loader( dir
.absoluteFilePath( fileName
) );
314 QObject
*instance
= loader
.instance();
315 EngineFactory
*factory
= qobject_cast
<EngineFactory
*>( instance
);
318 engineFactories
.append(factory
);
319 engineClassFactory
[factory
->className()] = factory
;
323 enginesLoaded
= true;
327 void GLWidgetPrivate::updateListQuick()
329 // Create a display list cache
331 qDebug() << "Making new quick display lists...";
333 dlistQuick
= glGenLists(1);
335 // Don't use dynamic scaling when rendering quickly
336 painter
->setDynamicScaling(false);
338 glNewList(dlistQuick
, GL_COMPILE
);
339 foreach(Engine
*engine
, engines
)
340 if(engine
->isEnabled())
341 engine
->renderQuick(pd
);
345 painter
->setDynamicScaling(true);
350 #ifdef ENABLE_THREADED_GL
351 class GLThread
: public QThread
354 GLThread( GLWidget
*widget
, QObject
*parent
);
357 void resize( int width
, int height
);
362 QGLContext
*m_context
;
372 GLThread::GLThread( GLWidget
*widget
, QObject
*parent
) : QThread( parent
),
373 m_widget( widget
), m_running( true ), m_resize( false ), m_initialized( false )
378 GLWidgetPrivate
*d
= m_widget
->d
;
382 d
->renderMutex
.lock();
385 d
->paintCondition
.wait( &( d
->renderMutex
) );
387 d
->renderMutex
.unlock();
390 m_widget
->makeCurrent();
392 if ( !m_initialized
) {
393 m_widget
->initializeGL();
394 m_initialized
= true;
399 m_widget
->resizeGL( m_width
, m_height
);
403 m_widget
->qglClearColor(d
->background
);
405 m_widget
->swapBuffers();
406 m_widget
->doneCurrent();
407 d
->renderMutex
.unlock();
411 void GLThread::resize( int width
, int height
)
418 void GLThread::stop()
424 GLWidget::GLWidget( QWidget
*parent
)
425 : QGLWidget( parent
), d( new GLWidgetPrivate
)
430 GLWidget::GLWidget( const QGLFormat
&format
, QWidget
*parent
,
431 const GLWidget
*shareWidget
)
432 : QGLWidget( format
, parent
, shareWidget
), d( new GLWidgetPrivate
)
434 constructor(shareWidget
);
437 GLWidget::GLWidget( Molecule
*molecule
,
438 const QGLFormat
&format
, QWidget
*parent
,
439 const GLWidget
*shareWidget
)
440 : QGLWidget( format
, parent
, shareWidget
), d( new GLWidgetPrivate
)
442 constructor(shareWidget
);
443 setMolecule( molecule
);
446 GLWidget::~GLWidget()
448 if(!d
->painter
->isShared())
454 d
->painter
->decrementShare();
457 #ifdef ENABLE_THREADED_GL
458 // cleanup our thread
460 d
->paintCondition
.wakeAll();
464 // delete the engines
465 foreach( Engine
*engine
, d
->engines
) {
472 void GLWidget::constructor(const GLWidget
*shareWidget
)
474 d
->pd
= new GLPainterDevice(this);
475 if(shareWidget
&& isSharing()) {
476 // we are sharing contexts
477 d
->painter
= static_cast<GLPainter
*>(shareWidget
->painter());
481 d
->painter
= new GLPainter();
483 d
->painter
->incrementShare();
485 setSizePolicy( QSizePolicy::MinimumExpanding
,QSizePolicy::MinimumExpanding
);
486 d
->camera
->setParent( this );
487 setAutoBufferSwap( false );
488 #ifdef ENABLE_THREADED_GL
489 qDebug() << "Threaded GL enabled.";
490 d
->thread
= new GLThread( this, this );
496 GLWidget
*GLWidget::m_current
= 0;
498 GLWidget
*GLWidget::current()
503 void GLWidget::setCurrent(GLWidget
*current
)
508 void GLWidget::initializeGL()
510 qDebug() << "GLWidget initialisation...";
511 qglClearColor( d
->background
);
513 glShadeModel( GL_SMOOTH
);
514 glEnable( GL_DEPTH_TEST
);
515 glDepthFunc( GL_LEQUAL
);
516 glEnable( GL_CULL_FACE
);
517 glEnable( GL_COLOR_SUM_EXT
);
519 // Used to display semi-transparent selection rectangle
520 // glBlendFunc(GL_ONE, GL_ONE);
521 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
523 glEnable( GL_NORMALIZE
);
525 GLfloat ambientLight
[] = { 0.2, 0.2, 0.2, 1.0 };
526 GLfloat diffuseLight
[] = { 1.0, 1.0, 1.0, 1.0 };
527 GLfloat diffuseLight2
[] = { 0.3, 0.3, 0.3, 1.0 };
528 GLfloat specularLight
[] = { 1.0, 1.0, 1.0, 1.0 };
529 GLfloat specularLight2
[] = { 0.5, 0.5, 0.5, 1.0 };
530 GLfloat position
[] = { 0.8, 0.7, 1.0, 0.0 };
531 GLfloat position2
[] = { -0.8, 0.7, -0.5, 0.0 };
533 glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL_EXT
,
534 GL_SEPARATE_SPECULAR_COLOR_EXT
);
536 // Due to the bug found with Mesa 6.5.3 in the Radeon DRI driver
537 // in radeon_state.c in radeonUpdateSpecular(),
538 // it is important to set GL_SEPARATE_SPECULAR_COLOR_EXT
539 // _before_ enabling lighting
540 glEnable( GL_LIGHTING
);
542 glLightfv( GL_LIGHT0
, GL_AMBIENT
, ambientLight
);
543 glLightfv( GL_LIGHT0
, GL_DIFFUSE
, diffuseLight
);
544 glLightfv( GL_LIGHT0
, GL_SPECULAR
, specularLight
);
545 glLightfv( GL_LIGHT0
, GL_POSITION
, position
);
546 glEnable( GL_LIGHT0
);
547 // Create a second light source to illuminate those shadows a little better
548 glLightfv( GL_LIGHT1
, GL_AMBIENT
, ambientLight
);
549 glLightfv( GL_LIGHT1
, GL_DIFFUSE
, diffuseLight2
);
550 glLightfv( GL_LIGHT1
, GL_SPECULAR
, specularLight2
);
551 glLightfv( GL_LIGHT1
, GL_POSITION
, position2
);
552 glEnable( GL_LIGHT1
);
553 qDebug() << "GLWidget initialised...";
556 void GLWidget::resizeEvent( QResizeEvent
*event
)
558 //qDebug() << "resizeEvent";
559 #ifdef ENABLE_THREADED_GL
560 d
->thread
->resize( event
->size().width(), event
->size().height() );
567 d
->initialized
= true;
570 // GLXWaitX() is called by the TT resizeEvent on Linux... We may need
571 // specific functions here - need to look at Mac and Windows code.
572 resizeGL( event
->size().width(), event
->size().height() );
576 void GLWidget::resizeGL( int width
, int height
)
578 glViewport( 0, 0, width
, height
);
581 void GLWidget::setBackground( const QColor
&background
)
583 #ifdef ENABLE_THREADED_GL
584 d
->renderMutex
.lock();
586 d
->background
= background
;
587 #ifdef ENABLE_THREADED_GL
588 d
->renderMutex
.unlock();
592 QColor
GLWidget::background() const
594 return d
->background
;
597 void GLWidget::setColorMap(Color
*map
)
602 Color
*GLWidget::colorMap() const
607 return d
->defaultMap
;
610 void GLWidget::setQuality(int quality
)
612 d
->painter
->setQuality(quality
);
615 int GLWidget::quality() const
617 return d
->painter
->quality();
620 void GLWidget::setRenderAxes(bool renderAxes
)
622 d
->renderAxes
= renderAxes
;
625 bool GLWidget::renderAxes()
627 return d
->renderAxes
;
630 void GLWidget::setRenderDebug(bool renderDebug
)
632 d
->renderDebug
= renderDebug
;
635 bool GLWidget::renderDebug()
637 return d
->renderDebug
;
640 void GLWidget::render()
642 d
->painter
->begin(this);
644 // Use renderQuick if the view is being moved, otherwise full render
645 if (d
->quickRender
) {
647 d
->updateListQuick();
648 qDebug() << "Calling quick display lists...";
649 glCallList(d
->dlistQuick
);
650 if (d
->uc
) renderCrystal(d
->dlistQuick
);
654 qDebug() << "Normal rendering...";
656 // we save a display list if we're doing a crystal
657 if (d
->dlistOpaque
== 0)
658 d
->dlistOpaque
= glGenLists(1);
659 if (d
->dlistTransparent
== 0)
660 d
->dlistTransparent
= glGenLists(1);
662 if (d
->uc
) glNewList(d
->dlistOpaque
, GL_COMPILE
);
663 foreach(Engine
*engine
, d
->engines
)
664 if(engine
->isEnabled())
665 engine
->renderOpaque(d
->pd
);
666 if (d
->uc
) { // end the main list and render the opaque crystal
668 renderCrystal(d
->dlistOpaque
);
671 glDepthMask(GL_FALSE
);
672 if (d
->uc
) glNewList(d
->dlistTransparent
, GL_COMPILE
);
673 foreach(Engine
*engine
, d
->engines
)
674 if(engine
->isEnabled() && engine
->flags() & Engine::Transparent
)
675 engine
->renderTransparent(d
->pd
);
676 if (d
->uc
) { // end the main list and render the transparent bits
678 renderCrystal(d
->dlistTransparent
);
680 glDepthMask(GL_TRUE
);
683 // Render all the inactive tools
684 if ( d
->toolGroup
) {
685 QList
<Tool
*> tools
= d
->toolGroup
->tools();
686 foreach( Tool
*tool
, tools
) {
687 if ( tool
!= d
->tool
) {
693 // Render the active tool
695 d
->tool
->paint( this );
698 // If enabled draw the axes
699 if (d
->renderAxes
) renderAxesOverlay();
701 // If enabled show debug information
702 if (d
->renderDebug
) renderDebugOverlay();
707 void GLWidget::renderCrystal(GLuint displayList
)
709 std::vector
<vector3
> cellVectors
= d
->uc
->GetCellVectors();
711 for (int a
= 0; a
< d
->aCells
; a
++) {
712 for (int b
= 0; b
< d
->bCells
; b
++) {
713 for (int c
= 0; c
< d
->cCells
; c
++) {
716 cellVectors
[0].x() * a
717 + cellVectors
[1].x() * b
718 + cellVectors
[2].x() * c
,
719 cellVectors
[0].y() * a
720 + cellVectors
[1].y() * b
721 + cellVectors
[2].y() * c
,
722 cellVectors
[0].z() * a
723 + cellVectors
[1].z() * b
724 + cellVectors
[2].z() * c
);
726 glCallList(displayList
);
730 } // end of for loops
735 // Render the unit cell axes, indicating the frame of the cell
738 // / / | (0 is the "origin" for this unit cell)
739 // 3---2 6 (7 is in the back corner = cellVector[2])
740 // | | / (3 is cellVector[1])
741 // | |/ (1 is cellVector[0])
743 void GLWidget::renderCrystalAxes()
745 std::vector
<vector3
> cellVectors
= d
->uc
->GetCellVectors();
746 vector3
v0(0.0, 0.0, 0.0);
747 vector3
v1(cellVectors
[0]);
748 vector3
v3(cellVectors
[1]);
749 vector3
v7(cellVectors
[2]);
750 vector3 v2
, v4
, v5
, v6
;
756 glDisable(GL_LIGHTING
);
757 glColor4f(1.0, 1.0, 1.0, 0.7);
759 for (int a
= 0; a
< d
->aCells
; a
++) {
760 for (int b
= 0; b
< d
->bCells
; b
++) {
761 for (int c
= 0; c
< d
->cCells
; c
++) {
764 cellVectors
[0].x() * a
765 + cellVectors
[1].x() * b
766 + cellVectors
[2].x() * c
,
767 cellVectors
[0].y() * a
768 + cellVectors
[1].y() * b
769 + cellVectors
[2].y() * c
,
770 cellVectors
[0].z() * a
771 + cellVectors
[1].z() * b
772 + cellVectors
[2].z() * c
);
774 glBegin(GL_LINE_STRIP
);
775 glVertex3dv(v0
.AsArray());
776 glVertex3dv(v1
.AsArray());
779 glBegin(GL_LINE_STRIP
);
780 glVertex3dv(v0
.AsArray());
781 glVertex3dv(v3
.AsArray());
784 glBegin(GL_LINE_STRIP
);
785 glVertex3dv(v0
.AsArray());
786 glVertex3dv(v7
.AsArray());
789 glBegin(GL_LINE_STRIP
);
790 glVertex3dv(v1
.AsArray());
791 glVertex3dv(v2
.AsArray());
794 glBegin(GL_LINE_STRIP
);
795 glVertex3dv(v3
.AsArray());
796 glVertex3dv(v2
.AsArray());
799 glBegin(GL_LINE_STRIP
);
800 glVertex3dv(v3
.AsArray());
801 glVertex3dv(v4
.AsArray());
804 glBegin(GL_LINE_STRIP
);
805 glVertex3dv(v5
.AsArray());
806 glVertex3dv(v4
.AsArray());
809 glBegin(GL_LINE_STRIP
);
810 glVertex3dv(v5
.AsArray());
811 glVertex3dv(v2
.AsArray());
814 glBegin(GL_LINE_STRIP
);
815 glVertex3dv(v5
.AsArray());
816 glVertex3dv(v6
.AsArray());
819 glBegin(GL_LINE_STRIP
);
820 glVertex3dv(v1
.AsArray());
821 glVertex3dv(v6
.AsArray());
824 glBegin(GL_LINE_STRIP
);
825 glVertex3dv(v6
.AsArray());
826 glVertex3dv(v7
.AsArray());
829 glBegin(GL_LINE_STRIP
);
830 glVertex3dv(v4
.AsArray());
831 glVertex3dv(v7
.AsArray());
837 } // end of for loops
838 glEnable(GL_LIGHTING
);
841 void GLWidget::renderAxesOverlay()
843 // Render x, y, z axes as an overlay on the widget
844 // Save the opengl projection matrix and set up an orthogonal projection
845 glMatrixMode(GL_PROJECTION
);
848 // Ensure the axes are of the same length
849 double aspectRatio
= static_cast<double>(d
->pd
->width())/static_cast<double>(d
->pd
->height());
850 glOrtho(0, aspectRatio
, 0, 1, 0, 1);
851 glMatrixMode(GL_MODELVIEW
);
855 // Set the origin and calculate the positions of the axes
856 Vector3d origin
= Vector3d(0.07, 0.07, -.07);
857 MatrixP3d axisTranslation
;
858 axisTranslation
.loadTranslation(d
->pd
->camera()->transformedXAxis() * 0.04);
859 Vector3d aXa
= axisTranslation
* origin
;
860 axisTranslation
.loadTranslation(d
->pd
->camera()->transformedXAxis() * 0.06);
861 Vector3d aX
= axisTranslation
* origin
;
862 axisTranslation
.loadTranslation(d
->pd
->camera()->transformedYAxis() * 0.04);
863 Vector3d aYa
= axisTranslation
* origin
;
864 axisTranslation
.loadTranslation(d
->pd
->camera()->transformedYAxis() * 0.06);
865 Vector3d aY
= axisTranslation
* origin
;
866 axisTranslation
.loadTranslation(d
->pd
->camera()->transformedZAxis() * 0.04);
867 Vector3d aZa
= axisTranslation
* origin
;
868 axisTranslation
.loadTranslation(d
->pd
->camera()->transformedZAxis() * 0.06);
869 Vector3d aZ
= axisTranslation
* origin
;
871 // Turn off dynamic scaling in the painter (cylinders don't render correctly)
872 d
->painter
->setDynamicScaling(false);
875 painter()->setColor(1.0, 0.0, 0.0);
876 painter()->drawCylinder(origin
, aXa
, 0.005);
877 painter()->drawCone(aXa
, aX
, 0.01);
879 painter()->setColor(0.0, 1.0, 0.0);
880 painter()->drawCylinder(origin
, aYa
, 0.005);
881 painter()->drawCone(aYa
, aY
, 0.01);
883 painter()->setColor(0.0, 0.0, 1.0);
884 painter()->drawCylinder(origin
, aZa
, 0.005);
885 painter()->drawCone(aZa
, aZ
, 0.01);
887 // Turn dynamic scaling back on (default state)
888 d
->painter
->setDynamicScaling(true);
891 glMatrixMode(GL_PROJECTION
);
893 glMatrixMode(GL_MODELVIEW
);
896 void GLWidget::renderDebugOverlay()
898 QList
<Primitive
*> list
;
900 // Draw all text in while
901 d
->pd
->painter()->setColor(1.0, 1.0, 1.0);
904 y
+= d
->pd
->painter()->drawText(x
, y
, "---- " + tr("Debug Information") + " ----");
905 y
+= d
->pd
->painter()->drawText(x
, y
, tr("FPS") + ": " + QString::number(computeFramesPerSecond(), 'g', 3));
907 y
+= d
->pd
->painter()->drawText(x
, y
, tr("View Size") + ": "
908 + QString::number(d
->pd
->width())
910 + QString::number(d
->pd
->height()) );
912 list
= primitives().subList(Primitive::AtomType
);
913 y
+= d
->pd
->painter()->drawText(x
, y
, tr("Atoms") + ": " + QString::number(list
.size()));
915 list
= primitives().subList(Primitive::BondType
);
916 y
+= d
->pd
->painter()->drawText(x
, y
, tr("Bonds") + ": " + QString::number(list
.size()));
919 void GLWidget::paintGL()
921 resizeGL(width(), height()); // fix for bug #1797069. don't remove!
923 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
925 // setup the OpenGL projection matrix using the camera
926 glMatrixMode( GL_PROJECTION
);
928 d
->camera
->applyPerspective();
930 // setup the OpenGL modelview matrix using the camera
931 glMatrixMode( GL_MODELVIEW
);
933 d
->camera
->applyModelview();
938 void GLWidget::paintEvent( QPaintEvent
* )
940 //qDebug() << "paintEvent";
941 #ifdef ENABLE_THREADED_GL
942 // tell our thread to paint
943 d
->paintCondition
.wakeAll();
948 d
->initialized
= true;
951 qglClearColor(d
->background
);
957 bool GLWidget::event( QEvent
*event
)
959 if(event
->type() == QEvent::Show
)
961 GLWidget::setCurrent(this);
964 return QGLWidget::event(event
);
967 void GLWidget::mousePressEvent( QMouseEvent
* event
)
970 QUndoCommand
*command
= 0;
971 command
= d
->tool
->mousePress( this, event
);
973 if ( command
&& d
->undoStack
) {
974 d
->undoStack
->push( command
);
975 } else if ( command
) {
979 #ifdef ENABLE_THREADED_GL
980 d
->renderMutex
.lock();
982 // Use quick render while the mouse is down
983 d
->quickRender
= true;
984 #ifdef ENABLE_THREADED_GL
985 d
->renderMutex
.unlock();
989 void GLWidget::mouseReleaseEvent( QMouseEvent
* event
)
992 QUndoCommand
*command
= d
->tool
->mouseRelease( this, event
);
994 if ( command
&& d
->undoStack
) {
995 d
->undoStack
->push( command
);
998 #ifdef ENABLE_THREADED_GL
999 d
->renderMutex
.lock();
1001 // Stop using quickRender
1002 d
->quickRender
= false;
1003 #ifdef ENABLE_THREADED_GL
1004 d
->renderMutex
.unlock();
1006 // Render the scene at full quality now the mouse button has been released
1010 void GLWidget::mouseMoveEvent( QMouseEvent
* event
)
1013 QUndoCommand
*command
= d
->tool
->mouseMove( this, event
);
1014 if ( command
&& d
->undoStack
) {
1015 d
->undoStack
->push( command
);
1020 void GLWidget::wheelEvent( QWheelEvent
* event
)
1023 QUndoCommand
*command
= d
->tool
->wheel( this, event
);
1024 if ( command
&& d
->undoStack
) {
1025 d
->undoStack
->push( command
);
1030 void A_EXPORT
GLWidget::setMolecule( Molecule
*molecule
)
1032 if ( !molecule
) { return; }
1034 // disconnect from our old molecule
1035 if ( d
->molecule
) {
1036 QObject::disconnect( d
->molecule
, 0, this, 0 );
1039 d
->molecule
= molecule
;
1041 // clear our engine queues
1042 for ( int i
=0; i
< d
->engines
.size(); i
++ ) {
1043 d
->engines
.at( i
)->clearPrimitives();
1045 d
->primitives
.clear();
1047 // add the atoms to the default queue
1048 std::vector
<OpenBabel::OBNodeBase
*>::iterator i
;
1049 for ( Atom
*atom
= ( Atom
* )d
->molecule
->BeginAtom( i
);
1050 atom
; atom
= ( Atom
* )d
->molecule
->NextAtom( i
) ) {
1051 d
->primitives
.append( atom
);
1054 // add the bonds to the default queue
1055 std::vector
<OpenBabel::OBEdgeBase
*>::iterator j
;
1056 for ( Bond
*bond
= ( Bond
* )d
->molecule
->BeginBond( j
);
1057 bond
; bond
= ( Bond
* )d
->molecule
->NextBond( j
) ) {
1058 d
->primitives
.append( bond
);
1061 // add the residues to the default queue
1062 std::vector
<OpenBabel::OBResidue
*>::iterator k
;
1063 for ( Residue
*residue
= ( Residue
* )d
->molecule
->BeginResidue( k
);
1064 residue
; residue
= ( Residue
* )d
->molecule
->NextResidue( k
) ) {
1065 d
->primitives
.append( residue
);
1068 d
->primitives
.append( d
->molecule
);
1070 std::cout
<< "SetMolecule Called!" << std::endl
;
1071 // Now set the primitives for the engines
1072 for (int i
= 0; i
< d
->engines
.size(); i
++)
1073 d
->engines
.at(i
)->setPrimitives(d
->primitives
);
1075 // connect our signals so if the molecule gets updated
1076 connect( d
->molecule
, SIGNAL( primitiveAdded( Primitive
* ) ),
1077 this, SLOT( addPrimitive( Primitive
* ) ) );
1078 connect( d
->molecule
, SIGNAL( primitiveUpdated( Primitive
* ) ),
1079 this, SLOT( updatePrimitive( Primitive
* ) ) );
1080 connect( d
->molecule
, SIGNAL( primitiveRemoved( Primitive
* ) ),
1081 this, SLOT( removePrimitive( Primitive
* ) ) );
1083 // compute the molecule's geometric info
1086 // setup the camera to have a nice viewpoint on the molecule
1087 d
->camera
->initializeViewPoint();
1092 const Molecule
* GLWidget::molecule() const
1097 Molecule
* GLWidget::molecule()
1102 const Vector3d
& GLWidget::center() const
1107 const Vector3d
& GLWidget::normalVector() const
1109 return d
->normalVector
;
1112 const double & GLWidget::radius() const
1117 const Atom
*GLWidget::farthestAtom() const
1119 return d
->farthestAtom
;
1122 void GLWidget::updateGeometry()
1124 if (d
->molecule
->HasData(OBGenericDataType::UnitCell
))
1125 d
->uc
= dynamic_cast<OBUnitCell
*>(d
->molecule
->GetData(OBGenericDataType::UnitCell
));
1127 if ( !d
->uc
) { // a plain molecule, no crystal cell
1128 d
->center
= d
->molecule
->center();
1129 d
->normalVector
= d
->molecule
->normalVector();
1130 d
->radius
= d
->molecule
->radius();
1131 d
->farthestAtom
= d
->molecule
->farthestAtom();
1133 // render a crystal (so most geometry comes from the cell vectors)
1134 // Origin at 0.0, 0.0, 0.0
1138 std::vector
<vector3
> cellVectors
= d
->uc
->GetCellVectors();
1139 Vector3d
a(cellVectors
[0].AsArray());
1140 Vector3d
b(cellVectors
[1].AsArray());
1141 Vector3d
c(cellVectors
[2].AsArray());
1142 Vector3d centerOffset
= ( a
* (d
->aCells
- 1)
1143 + b
* (d
->bCells
- 1)
1144 + c
* (d
->cCells
- 1) ) / 2.0;
1145 // the center is the center of the molecule translated by centerOffset
1146 d
->center
= d
->molecule
->center() + centerOffset
;
1147 // the radius is the length of centerOffset plus the molecule radius
1148 d
->radius
= d
->molecule
->radius() + centerOffset
.norm();
1149 // for the normal vector, we just ask for the molecule's normal vector,
1150 // crossing our fingers hoping that it will give a nice viewpoint not only
1151 // with respect to the molecule but also with respect to the cells.
1152 d
->normalVector
= d
->molecule
->normalVector();
1153 // Computation of the farthest atom.
1154 // First case: the molecule is empty
1155 if(d
->molecule
->NumAtoms() == 0 ) {
1156 d
->farthestAtom
= 0;
1158 // Second case: there is no repetition of the molecule
1159 else if(d
->aCells
<= 1 && d
->bCells
<= 1 && d
->cCells
<= 1) {
1160 d
->farthestAtom
= d
->molecule
->farthestAtom();
1162 // General case: the farthest atom is the one that is located the
1163 // farthest in the direction pointed to by centerOffset.
1165 std::vector
<OBAtom
*>::iterator atom_iterator
;
1169 atom
= static_cast<Atom
*>(d
->molecule
->BeginAtom(atom_iterator
)),
1170 max_x
= centerOffset
.dot(atom
->pos()),
1171 d
->farthestAtom
= atom
;
1173 atom
= static_cast<Atom
*>(d
->molecule
->NextAtom(atom_iterator
))
1175 x
= centerOffset
.dot(atom
->pos());
1179 d
->farthestAtom
= atom
;
1186 Camera
* GLWidget::camera() const
1191 QList
<Engine
*> GLWidget::engines() const
1196 QList
<EngineFactory
*> GLWidget::engineFactories() const
1198 return d
->engineFactories
;
1201 PrimitiveList
GLWidget::primitives() const
1203 return d
->primitives
;
1206 void GLWidget::addPrimitive( Primitive
*primitive
)
1209 // add the molecule to the default queue
1210 for ( int i
=0; i
< d
->engines
.size(); i
++ ) {
1211 d
->engines
.at( i
)->addPrimitive( primitive
);
1213 d
->primitives
.append( primitive
);
1217 void GLWidget::updatePrimitive( Primitive
*primitive
)
1219 for ( int i
=0; i
< d
->engines
.size(); i
++ ) {
1220 d
->engines
.at( i
)->updatePrimitive( primitive
);
1225 void GLWidget::removePrimitive( Primitive
*primitive
)
1228 // add the molecule to the default queue
1229 for ( int i
=0; i
< d
->engines
.size(); i
++ ) {
1230 d
->engines
.at( i
)->removePrimitive( primitive
);
1232 d
->selectedPrimitives
.removeAll( primitive
);
1233 d
->primitives
.removeAll( primitive
);
1237 void GLWidget::addEngine(Engine
*engine
)
1239 connect( engine
, SIGNAL(changed()), this, SLOT(update()));
1240 connect(engine
, SIGNAL(changed()), this, SLOT(invalidateDLs()));
1241 d
->engines
.append(engine
);
1242 qSort(d
->engines
.begin(), d
->engines
.end(), engineLessThan
);
1243 emit
engineAdded(engine
);
1247 void GLWidget::removeEngine(Engine
*engine
)
1249 disconnect(engine
, SIGNAL(changed()), this, SLOT(update()));
1250 disconnect(engine
, SIGNAL(changed()), this, SLOT(invalidateDLs()));
1251 d
->engines
.removeAll(engine
);
1252 emit
engineRemoved(engine
);
1253 engine
->deleteLater();
1257 void GLWidget::setTool( Tool
*tool
)
1264 void GLWidget::setToolGroup( ToolGroup
*toolGroup
)
1266 if ( d
->toolGroup
) {
1267 disconnect( d
->toolGroup
, 0, this, 0 );
1271 d
->toolGroup
= toolGroup
;
1272 d
->tool
= toolGroup
->activeTool();
1273 connect( toolGroup
, SIGNAL( toolActivated( Tool
* ) ),
1274 this, SLOT( setTool( Tool
* ) ) );
1279 void GLWidget::setUndoStack( QUndoStack
*undoStack
)
1281 d
->undoStack
= undoStack
;
1284 QUndoStack
*GLWidget::undoStack() const
1286 return d
->undoStack
;
1289 Tool
* GLWidget::tool() const
1294 ToolGroup
*GLWidget::toolGroup() const
1296 return d
->toolGroup
;
1299 Painter
*GLWidget::painter() const
1304 QList
<GLHit
> GLWidget::hits( int x
, int y
, int w
, int h
)
1308 if ( !molecule() ) return hits
;
1311 unsigned int hit_count
;
1316 // setup the selection buffer
1317 int requiredSelectBufSize
= ( d
->molecule
->NumAtoms() + d
->molecule
->NumBonds() ) * 8;
1318 if ( requiredSelectBufSize
> d
->selectBufSize
) {
1319 //resize selection buffer
1320 if ( d
->selectBuf
) delete[] d
->selectBuf
;
1321 // add some margin so that resizing doesn't occur every time an atom is added
1322 d
->selectBufSize
= requiredSelectBufSize
+ SEL_BUF_MARGIN
;
1323 if ( d
->selectBufSize
> SEL_BUF_MAX_SIZE
) {
1324 d
->selectBufSize
= SEL_BUF_MAX_SIZE
;
1326 d
->selectBuf
= new GLuint
[d
->selectBufSize
];
1329 #ifdef ENABLE_THREADED_GL
1330 d
->renderMutex
.lock();
1335 glSelectBuffer( d
->selectBufSize
, d
->selectBuf
);
1336 glRenderMode( GL_SELECT
);
1339 // Setup a projection matrix for picking in the zone delimited by (x,y,w,h).
1340 glGetIntegerv( GL_VIEWPORT
, viewport
);
1341 glMatrixMode( GL_PROJECTION
);
1344 gluPickMatrix( cx
,viewport
[3]-cy
, w
, h
,viewport
);
1346 // now multiply that projection matrix with the perspective of the camera
1347 d
->camera
->applyPerspective();
1349 // now load the modelview matrix from the camera
1350 glMatrixMode( GL_MODELVIEW
);
1353 d
->camera
->applyModelview();
1355 // now actually render using low quality a.k.a. "quickrender"
1356 bool oldQuickRender
= d
->quickRender
;
1357 d
->quickRender
= true;
1359 d
->quickRender
= oldQuickRender
;
1361 // returning to normal rendering mode
1362 hit_count
= glRenderMode( GL_RENDER
);
1364 glMatrixMode( GL_PROJECTION
);
1366 glMatrixMode( GL_MODELVIEW
);
1369 #ifdef ENABLE_THREADED_GL
1371 d
->renderMutex
.unlock();
1374 // if no error occurred and there are hits, process them
1375 if ( hit_count
> 0 ) {
1377 GLuint names
, type
, *ptr
;
1381 //X printf ("hits = %d\n", hits);
1382 ptr
= ( GLuint
* ) d
->selectBuf
;
1383 // for all hits and not past end of buffer
1384 for ( i
= 0; i
< hit_count
&& !( ptr
> d
->selectBuf
+ d
->selectBufSize
); i
++ ) {
1386 // make sure that we won't be passing the end of bufer
1387 if ( ptr
+ names
+ 2 > d
->selectBuf
+ d
->selectBufSize
) {
1395 for ( j
= 0; j
< names
/2; j
++ ) { /* for each name */
1400 // printf ("%ld(%d) ", name,type);
1401 hits
.append( GLHit( type
,name
,minZ
,maxZ
) );
1411 Primitive
* GLWidget::computeClickedPrimitive(const QPoint
& p
)
1415 // Perform an OpenGL selection and retrieve the list of hits.
1416 chits
= hits(p
.x()-SEL_BOX_HALF_SIZE
,
1417 p
.y()-SEL_BOX_HALF_SIZE
,
1418 SEL_BOX_SIZE
, SEL_BOX_SIZE
);
1420 // Find the first atom or bond (if any) in hits - this will be the closest
1421 foreach( GLHit hit
, chits
)
1423 //qDebug() << "Hit: " << hit.name();
1424 if(hit
.type() == Primitive::AtomType
) {
1425 return static_cast<Atom
*>(molecule()->GetAtom(hit
.name()));
1426 } else if(hit
.type() == Primitive::BondType
) {
1427 return static_cast<Bond
*>(molecule()->GetBond(hit
.name()));
1433 Atom
* GLWidget::computeClickedAtom(const QPoint
& p
)
1437 // Perform an OpenGL selection and retrieve the list of hits.
1438 chits
= hits(p
.x()-SEL_BOX_HALF_SIZE
,
1439 p
.y()-SEL_BOX_HALF_SIZE
,
1440 SEL_BOX_SIZE
, SEL_BOX_SIZE
);
1442 // Find the first atom (if any) in hits - this will be the closest
1443 foreach( GLHit hit
, chits
)
1445 if(hit
.type() == Primitive::AtomType
)
1447 return static_cast<Atom
*>(molecule()->GetAtom(hit
.name()));
1453 Bond
* GLWidget::computeClickedBond(const QPoint
& p
)
1457 // Perform an OpenGL selection and retrieve the list of hits.
1458 chits
= hits(p
.x()-SEL_BOX_HALF_SIZE
,
1459 p
.y()-SEL_BOX_HALF_SIZE
,
1460 SEL_BOX_SIZE
, SEL_BOX_SIZE
);
1462 // Find the first bond (if any) in hits - this will be the closest
1463 foreach( GLHit hit
, chits
)
1465 if(hit
.type() == Primitive::BondType
)
1467 return static_cast<Bond
*>(molecule()->GetBond(hit
.name()));
1473 QSize
GLWidget::sizeHint() const
1475 return minimumSizeHint();
1478 QSize
GLWidget::minimumSizeHint() const
1480 return QSize( 200,200 );
1483 double GLWidget::radius( const Primitive
*p
) const
1485 double radius
= 0.0;
1486 foreach( Engine
*engine
, d
->engines
) {
1487 if ( engine
->isEnabled() ) {
1488 double engineRadius
= engine
->radius( d
->pd
, p
);
1489 if ( engineRadius
> radius
) {
1490 radius
= engineRadius
;
1498 bool GLWidget::isStable() const
1503 void GLWidget::setStable( bool stable
)
1508 void GLWidget::setSelected(PrimitiveList primitives
, bool select
)
1510 foreach( Primitive
*item
, primitives
)
1512 if (select
&& !d
->selectedPrimitives
.contains(item
))
1513 d
->selectedPrimitives
.append( item
);
1515 d
->selectedPrimitives
.removeAll( item
);
1516 // The engine caches must be invalidated
1517 d
->updateCache
= true;
1522 PrimitiveList
GLWidget::selectedPrimitives() const
1524 return d
->selectedPrimitives
.list();
1527 void GLWidget::toggleSelected( PrimitiveList primitives
)
1529 foreach(Primitive
*item
, primitives
)
1531 if (d
->selectedPrimitives
.contains(item
))
1532 d
->selectedPrimitives
.removeAll( item
);
1534 d
->selectedPrimitives
.append(item
);
1536 // The engine caches must be invalidated
1537 d
->updateCache
= true;
1540 void GLWidget::clearSelected()
1542 d
->selectedPrimitives
.clear();
1543 // The engine caches must be invalidated
1544 d
->updateCache
= true;
1547 bool GLWidget::isSelected( const Primitive
*p
) const
1549 // Return true if the item is selected
1550 return d
->selectedPrimitives
.contains( const_cast<Primitive
*>( p
) );
1553 void GLWidget::setUnitCells( int a
, int b
, int c
)
1559 d
->camera
->initializeViewPoint();
1563 int GLWidget::aCells()
1568 int GLWidget::bCells()
1573 int GLWidget::cCells()
1578 inline double GLWidget::computeFramesPerSecond()
1581 static bool firstTime
= true;
1582 static int old_time
, new_time
;
1590 old_time
= time
.elapsed();
1595 new_time
= time
.elapsed();
1598 if( new_time
- old_time
> 200 )
1600 fps
= 1000.0 * frames
/ double( new_time
- old_time
);
1603 old_time
= time
.elapsed();
1609 void GLWidget::writeSettings(QSettings
&settings
) const
1611 settings
.setValue("background", d
->background
);
1612 settings
.setValue("quality", d
->painter
->quality());
1613 settings
.setValue("renderAxes", d
->renderAxes
);
1614 settings
.setValue("renderDebug", d
->renderDebug
);
1616 int count
= d
->engines
.size();
1617 settings
.beginWriteArray("engines");
1618 for(int i
= 0; i
< count
; i
++)
1620 settings
.setArrayIndex(i
);
1621 Engine
*engine
= d
->engines
.at(i
);
1622 settings
.setValue("engineClass", engine
->metaObject()->className());
1623 engine
->writeSettings(settings
);
1625 settings
.endArray();
1628 void GLWidget::readSettings(QSettings
&settings
)
1630 // Make sure to provide some default values for any settings.value("", DEFAULT) call
1631 d
->painter
->setQuality(settings
.value("quality", 2).toInt());
1632 d
->background
= settings
.value("background", QColor(0,0,0)).value
<QColor
>();
1633 d
->renderAxes
= settings
.value("renderAxes", 1).value
<bool>();
1634 d
->renderDebug
= settings
.value("renderDebug", 0).value
<bool>();
1636 int count
= settings
.beginReadArray("engines");
1637 for(int i
=0; i
<count
; i
++)
1639 settings
.setArrayIndex(i
);
1640 QString engineClass
= settings
.value("engineClass", QString()).toString();
1642 if(!engineClass
.isEmpty() && d
->engineClassFactory
.contains(engineClass
))
1644 EngineFactory
*factory
= d
->engineClassFactory
.value(engineClass
);
1645 Engine
*engine
= factory
->createInstance(this);
1646 engine
->readSettings(settings
);
1648 // eventually settings will store which has what but
1649 // for now we ignore this. (will need this when we
1650 // copy the selected primitives also).
1651 if(!engine
->primitives().size())
1653 engine
->setPrimitives(primitives());
1659 settings
.endArray();
1661 if(!d
->engines
.count())
1663 loadDefaultEngines();
1667 void GLWidget::loadDefaultEngines()
1669 QList
<Engine
*> engines
= d
->engines
;
1672 foreach(Engine
*engine
, engines
)
1677 foreach(EngineFactory
*factory
, d
->engineFactories
)
1679 Engine
*engine
= factory
->createInstance(this);
1680 if ( engine
->name() == tr("Ball and Stick") ) {
1681 engine
->setEnabled( true );
1683 engine
->setPrimitives(primitives());
1688 void GLWidget::invalidateDLs()
1690 // Something changed and we need to invalidate the display lists
1691 d
->updateCache
= true;
1695 #include "glwidget.moc"