glview.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           glview.cpp  -  description
00003                              -------------------
00004     begin                : Fri Nov 5 2004
00005     copyright            : (C) 2004-2006 by Ben Swerts
00006     email                : bswerts@users.sourceforge.net
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00019 
00033 
00034 
00035 
00037 
00038 // Qt header files
00039 #include <qdatetime.h>
00040 #include <qfiledialog.h>
00041 #include <qimage.h>
00042 #include <qmessagebox.h>
00043 #include <qpoint.h>
00044 #include <qstringlist.h>
00045 #include <qtimer.h>
00046 
00047 // Xbrabo header files
00048 #include "glview.h"
00049 #include "quaternion.h"
00050 #include "vector3d.h"
00051 
00055 
00057 GLView::GLView(QWidget* parent, const char* name) : QGLWidget(parent,name),
00059   xPos(0.0f),
00060   yPos(0.0f),
00061   zPos(0.0f),
00062   //aspectRatio(1.0f),
00063   xRot(0.0f),
00064   yRot(0.0f),
00065   zRot(0.0f),
00066   animation(false),
00067   maxRadius(1.0f),
00068   currentPerspectiveProjection(baseParameters.perspectiveProjection)
00069 {
00070   setFocusPolicy(QWidget::StrongFocus); // needed to receive all keystrokes
00071   orientationQuaternion = new Quaternion<float>(0.0f, 0.0f, 0.0f);
00072   timer = new QTimer(this);
00073   connect(timer, SIGNAL(timeout()), this, SLOT(update()));
00074   setModified(false);
00075   //resetView(false); // can't call this as it indirectly calles the pure virtual boundingSphereRadius
00076                       // which is not defined yet for the dervived class which calls this constructor
00077                       // in its constructor
00078   updateIndex = staticUpdateIndex - 1; // an update is needed
00079 }
00080 
00082 GLView::~GLView()
00084 {
00085   delete orientationQuaternion;
00086 }
00087 
00089 bool GLView::isModified() const
00091 {
00092   return viewModified;
00093 }
00094 
00096 bool GLView::isAnimating() const
00098 {
00099   return animation;
00100 }
00101 
00103 unsigned int GLView::calculateFPS()
00106 {
00107   unsigned int numFPS = 0;        // the counter for the FPS
00108   const int nummSec = 5000;             // the number of milliseconds measured
00109   QTime t;                              // the time object
00110 
00112   bool oldAnimation = animation;
00113   Quaternion<float> oldOrientation = *orientationQuaternion;
00114 
00116   animation = false;
00117   t.start();
00118   while (t.elapsed() < nummSec)
00119   {
00120     xRot = 1.00;
00121     yRot = 1.25;
00122     zRot = 1.50;
00123     updateGL();
00124     numFPS++;
00125   }
00126 
00128   animation = oldAnimation;
00129   *orientationQuaternion = oldOrientation;
00130   updateGL();
00131 
00133   return numFPS*1000/nummSec;
00134 }
00135 
00137 void GLView::setParameters(GLBaseParameters params)
00140 {
00141   baseParameters = params;
00142   staticUpdateIndex++;
00143 }
00144 
00148 
00150 void GLView::setModified(const bool status)
00152 {
00153   if(!status)
00154   {
00155     viewModified = false;
00156     return;
00157   }
00158   if(!viewModified)
00159   {
00160     viewModified = true;
00161     emit modified();
00162   }
00163   emit changed();
00164 }
00165 
00167 void GLView::toggleAnimation()
00169 {
00170   animation = !animation;
00171   if(animation)
00172   {
00173     xRot = 1.0;
00174     yRot = 0.0;
00175     zRot = 0.0;
00176     updateGL();
00177   }
00178   else
00179     timer->stop();
00180 
00181   emit changed(); // don't call setModified as animation does not get saved/restored
00182 }
00183 
00185 void GLView::centerView(const bool update)
00187 {
00188   xPos = 0.0;
00189   yPos = 0.0;
00190   if(update)
00191     updateGL();
00192   setModified();
00193 }
00194 
00196 void GLView::resetOrientation(const bool update)
00198 {
00199   orientationQuaternion->eulerToQuaternion(0.0f, 0.0f, 0.0f);
00200   if(update)
00201     updateGL();
00202   setModified();
00203 }
00204 
00206 void GLView::zoomFit(const bool update)
00209 {
00211   maxRadius = boundingSphereRadius();
00213   if(baseParameters.perspectiveProjection)
00214   {
00215     if(width() > height())
00216       zPos = maxRadius/tan(fieldOfView)/1.5f;
00217     else
00218       zPos = maxRadius/tan(fieldOfView)/1.5f * static_cast<float>(height())/static_cast<float>(width());
00219     if(zPos < 0.1f)
00220       zPos = 0.1f;
00221   }
00222   else
00223   {
00224     zPos = 1.0f;
00225     resizeGL(width(), height());
00226   }
00227 
00229   updateFog(maxRadius);
00230   if(update)
00231     updateGL();
00232   setModified();
00233 }
00234 
00236 void GLView::resetView(const bool update)
00238 {
00239   centerView(false);
00240   resetOrientation(false);
00241   zoomFit(); // calls setModified()
00242   if(update)
00243     updateGL();
00244 }
00245 
00247 void GLView::saveImage()
00249 {
00251   QStringList saveFormats = QImage::outputFormatList();
00252   QStringList::Iterator it = saveFormats.begin();
00253   while(it != saveFormats.end())
00254   {
00255     if((*it) == "JPEG")
00256       *it = "JPEG (*.jpg)";
00257     else
00258       *it += " (*."+(*it).lower()+")";  // 'BMP' -> 'BMP (*.bmp)'
00259     it++;
00260   }
00261 
00263   /*
00264   QFileDialog saveDialog("", QString::null, this, 0, true);
00265   saveDialog.setFilters(saveFormats);
00266   saveDialog.setSelectedFilter("PNG (*.png)");
00267   saveDialog.setCaption(tr("Choose a filename and format"));
00268   saveDialog.setMode(QFileDialog::AnyFile);
00269   if(saveDialog.exec() != QDialog::Accepted)
00270     return;
00272   QString filename = saveDialog.selectedFile();
00273   if(filename.isEmpty())
00274     return;
00275   */
00276   // this way does not allow to set the selected filter. maybe put PNG at the top manually. now BMP is the default
00277   QString selectedFilter;
00278   QString filename = QFileDialog::getSaveFileName(0, saveFormats.join(";;"), 0, 0, tr("Choose a filename and format"), &selectedFilter);
00279   if(filename.isEmpty())
00280     return;
00281 
00282   //QString extension = saveDialog.selectedFilter();
00283   //extension = extension.mid(extension.find("."));
00284   //extension = extension.remove(")");
00285   //qDebug("extension: "+extension);
00286   QString extension = selectedFilter.mid(selectedFilter.find(".")).remove(")");
00287 
00288   if(filename.contains(extension) == 0)
00289     filename += extension;
00290 
00291   //QString format = saveDialog.selectedFilter();
00292   //format = format.left(format.find(" "));
00293   QString format = selectedFilter.left(selectedFilter.find(" "));
00294 
00295   // generate an image from the OpenGL view
00296   // -> It is possible to get a transparant image when using grabFrameBuffer(true)
00297   QImage image = grabFrameBuffer();
00298 
00299   // save it
00300   if(!image.save(filename, format))
00301     QMessageBox::warning(this, tr("Save image"), tr("An error occured. Image is not saved"));
00302 }
00303 
00307 
00309 void GLView::initializeGL()
00312 {
00313   GLfloat lightAmbient[] = {0.2f, 0.2f, 0.2f, 0.0f};
00314   GLfloat lightDiffuse[] = {0.5f, 0.5f, 0.5f, 0.0f};
00315   GLfloat lightSpecular[] = {1.0f, 1.0f, 1.0f, 0.0f};
00316 
00317   updateGLSettings();
00318 
00321   //glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);         // disable twosided lighting (default)
00322   //glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);     // disable local viewpoint (default)
00323   //glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightAmbient);     // set the ambient light (default)
00325   glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);     // enable ambient
00326   glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);     // enable diffuse
00327   glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);    // enable specular
00328   glEnable(GL_LIGHTING);                // enable lighting
00329   glEnable(GL_LIGHT0);                  // enable light 0
00330 
00331   glEnable(GL_AUTO_NORMAL);
00332   glEnable(GL_NORMALIZE); // maybe this copes with the non-uniform scaling of the bonds => nope
00333 
00335   glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
00336   glEnable(GL_COLOR_MATERIAL);
00337 
00338   glEnable(GL_DITHER);                  // enable dithering (default)
00339   glEnable(GL_DEPTH_TEST);              // use the depth buffer for hidden surface removal
00340 
00341   glCullFace(GL_BACK);                 // we will only see the outsides of objects
00342   glEnable(GL_CULL_FACE);               // => enable culling
00343 
00345   //glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);                // antialiasing method
00346   //glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);               // set the proper blending mode
00348   glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
00349   //glEnable(GL_LINE_SMOOTH);
00351   glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
00352   //glEnable(GL_POINT_SMOOTH);
00353 
00354   // transparancy setup
00355   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00356 
00358   glFogi(GL_FOG_MODE, GL_LINEAR);
00359 
00361   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);        // color interpolation method
00362 }
00363 
00365 void GLView::resizeGL(int w, int h)
00368 {
00369   glViewport(0, 0, w, h);
00370   glMatrixMode(GL_PROJECTION);
00371   glLoadIdentity();
00372   //aspectRatio = static_cast<float>(w) / static_cast<float>(h);
00373   setPerspective(); // calls gluPerspective or glOrtho depending on the prespective setting
00374   glMatrixMode(GL_MODELVIEW);
00375 }
00376 
00378 void GLView::paintGL()
00380 {
00381   if(staticUpdateIndex != updateIndex)
00382     updateGLSettings();
00383 
00385   Quaternion<float> changeQuaternion(xRot, yRot, zRot);
00387   Quaternion<float> tempQuaternion = *orientationQuaternion;
00388   *orientationQuaternion = tempQuaternion*changeQuaternion;  // no *= operator implemented yet => calling default constructor?
00390   Vector3D<float> axis;
00391   float angle;
00392   orientationQuaternion->getAxisAngle(axis, angle);
00393 
00394   if(!animation)
00395   {
00397     xRot = 0.0f;
00398     yRot = 0.0f;
00399     zRot = 0.0f;
00400   }
00401 
00402   //if(baseParameters.antialias)
00403   //{
00404   //  glClear(GL_COLOR_BUFFER_BIT);
00405   //}
00406   //else
00407   //{
00408     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00409   //}
00410 
00411   glLoadIdentity();
00412   //qDebug("GlView::xPos = %f, yPos = %f, zPos = %f", xPos, yPos, zPos);
00413   //qDebug("GlView::orientationQuaternion = (%f, %f, %f, %f)",orientationQuaternion->x(),orientationQuaternion->y(),orientationQuaternion->z(),orientationQuaternion->w());
00415   if(baseParameters.perspectiveProjection)
00416     gluLookAt(0.0f, 0.0f, zPos, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
00417   else
00418     resizeGL(width(), height());
00419   glPushMatrix();
00420   glTranslatef(xPos, yPos, 0.0f);
00421   glRotatef(angle, axis.x(), axis.y(), axis.z());
00422 
00423   drawScene(); // pure virtual
00424 
00425   glPopMatrix();
00426   glFlush(); // drawing is complete => send for execution
00427   if(animation)
00428     timer->start(redrawWait, true);
00429 }
00430 
00432 void GLView::mousePressEvent(QMouseEvent* e)
00434 {
00435   if(e->button() & Qt::LeftButton) // only track mousemoves for the left button
00436   {
00437     mousePosition = e->pos(); // set the starting position of the move
00438     startingClick = true;
00439   }
00440   else
00441     e->ignore();
00442 }
00443 
00445 void GLView::mouseMoveEvent(QMouseEvent* e)
00447 {
00448   if(e->state() & Qt::LeftButton) // only track mousemoves for the left button
00449   {
00450     QPoint newPosition = e->pos(); // the mouse moved from mousePosition to newPosition
00451 
00452     if(e->state() & Qt::ShiftButton) // shift has precedence over control
00453     {
00458       if(abs(newPosition.y() - mousePosition.y()) > abs(newPosition.x() - mousePosition.x()))
00459         translateZ(newPosition.y() - mousePosition.y());
00460       else
00461         zRot = -180.0f * static_cast<float>(newPosition.x() - mousePosition.x()) / static_cast<float>(width()); // z-rotation of entire system
00462     }
00463     else if(e->state() & Qt::ControlButton)
00464     {
00469       translateXY(newPosition.x() - mousePosition.x(), newPosition.y() - mousePosition.y());
00470     }
00471     else
00472     {
00476       yRot = 180.0f * static_cast<float>(newPosition.x() - mousePosition.x()) / static_cast<float>(width());
00477       xRot = 180.0f * static_cast<float>(newPosition.y() - mousePosition.y()) / static_cast<float>(height());
00478     }
00479     setModified();
00480     mousePosition = newPosition;
00481     updateGL();
00482     startingClick = false;
00483   }
00484   else
00485     e->ignore();
00486 }
00487 
00489 void GLView::mouseReleaseEvent(QMouseEvent* e)
00492 {
00493   if(e->button() & Qt::LeftButton) // only track mousemoves for the left button
00494   {
00495     if(startingClick)
00496     {
00498       clicked(mousePosition);
00499     }
00500   }
00501   else
00502     e->ignore(); //event will be handled by the parent widget
00503 }
00504 
00506 void GLView::keyPressEvent(QKeyEvent* e)
00522 {
00523   switch(e->key())
00524   {
00525     case Qt::Key_Left :   if(e->state() & Qt::ShiftButton)
00526                             zRot = 5.0f; // rotate counterclockwise
00527                           else if(e->state() & Qt::ControlButton)
00528                             translateXY(-5, 0);
00529                           else
00530                             yRot = -5.0f; // rotate left
00531                           break;
00532 
00533     case Qt::Key_Up     : if(e->state() & Qt::ShiftButton)
00534                             translateZ(-5);
00535                           else if(e->state() & Qt::ControlButton)
00536                             translateXY(0, -5);
00537                           else
00538                              xRot = -5.0f; // rotate up
00539                           break;
00540 
00541     case Qt::Key_Right  : if(e->state() & Qt::ShiftButton)
00542                             zRot = -5.0f; // rotate clockwise
00543                           else if(e->state() & Qt::ControlButton)
00544                             translateXY(5, 0);
00545                           else
00546                             yRot =  5.0f; // rotate right
00547                           break;
00548 
00549     case Qt::Key_Down   : if(e->state() & Qt::ShiftButton)
00550                             translateZ(5);
00551                           else if(e->state() & Qt::ControlButton)
00552                             translateXY(0, 5);
00553                           else
00554                             xRot = 5.0f; // rotate down
00555                           break;
00556 
00557     default:              e->ignore();
00558                           return;
00559   }
00560   setModified();
00561   updateGL();
00562 }
00563 
00565 void GLView::wheelEvent(QWheelEvent* e)
00568 {
00569   translateZ(-e->delta()/4); // abs(e->delta()) is always WHEELDELTA == 120
00570   setModified();
00571   updateGL();
00572   e->accept();
00573 }
00574 
00576 void GLView::translateZ(const int amount)
00579 {
00580   // a zoom over a distance of the height of the OpenGL window should translate
00581   // in half the size of the molecule
00582   if(amount != 0)
00583   {
00584     float zoomFactor = static_cast<float>(amount)/height(); // percentage
00585     if(baseParameters.perspectiveProjection)
00586       zoomFactor *= 2.0f * maxRadius;
00587     zPos += zoomFactor;
00588     if(zPos < 0.1f)
00589       zPos = 0.1f;
00590     if(!baseParameters.perspectiveProjection)
00591       resizeGL(width(), height()); // zooming for ortho projection is in fact direct scaling of the view
00592   }
00593 }
00594 
00596 void GLView::translateXY(const int amountX, const int amountY)
00599 {
00600   if(amountX != 0)
00601     xPos += amountX < 0 ? -0.1f : 0.1f;
00602   if(amountY != 0)
00603     yPos += amountY > 0 ? -0.1f : 0.1f;
00604 }
00605 
00607 void GLView::clicked(const QPoint&)
00611 {
00612 }
00613 
00615 void GLView::updateGLSettings()
00619 {
00620   updateIndex = staticUpdateIndex;
00621 
00623   GLfloat lightPosition[] = {baseParameters.lightPositionX, baseParameters.lightPositionY, baseParameters.lightPositionZ, 0.0f};
00624   GLfloat materialSpecular[] = {baseParameters.materialSpecular/100.0f, baseParameters.materialSpecular/100.0f, baseParameters.materialSpecular/100.0f, 0.0f};
00625   GLfloat materialShininess[] = {baseParameters.materialShininess};
00626 
00627   glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); // set light position for directional light
00628   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular);
00629   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, materialShininess);
00630 
00632   //qglClearColor(baseParameters.backgroundColor);
00633   qglClearColor(QColor(baseParameters.backgroundColor));
00634 
00636   if(baseParameters.smoothShading)
00637     glShadeModel(GL_SMOOTH);
00638   else
00639     glShadeModel(GL_FLAT);
00640 
00642   if(baseParameters.antialias)
00643   {
00644     //polygon antialiasing is way to slow.
00645     //glEnable(GL_BLEND);
00646     //glEnable(GL_POLYGON_SMOOTH);
00647     //glDisable(GL_DEPTH_TEST);
00648     // antialiasing for lines and points.
00649     glEnable(GL_LINE_SMOOTH);
00650     glEnable(GL_POINT_SMOOTH);
00651   }
00652   else
00653   {
00654     //glDisable(GL_BLEND);
00655     //glDisable(GL_POLYGON_SMOOTH);
00656     //glEnable(GL_DEPTH_TEST);
00657     glDisable(GL_LINE_SMOOTH);
00658     glDisable(GL_POINT_SMOOTH);
00659   }
00660 
00662   if(baseParameters.depthCue)
00663     glEnable(GL_FOG);
00664   else
00665     glDisable(GL_FOG);
00666 
00667   updateProjection(); // pure virtual which takes care of a change in projection
00668 
00670   resizeGL(width(), height());
00671 }
00672 
00674 void GLView::updateFog(const float radius)
00677 {
00678   makeCurrent();
00679   GLfloat fogStart = zPos;
00680   GLfloat fogEnd = zPos + 2.0*radius;
00681   //qDebug("setting fog from %f to %f",fogStart, fogEnd);
00682   glFogf(GL_FOG_START, fogStart);
00683   glFogf(GL_FOG_END, fogEnd);
00684 }
00685 
00687 void GLView::updateProjection()
00689 {
00690   if(currentPerspectiveProjection == baseParameters.perspectiveProjection)
00691     return;
00692 
00693   resizeGL(width(), height());
00694   zoomFit();
00695 
00696   currentPerspectiveProjection = baseParameters.perspectiveProjection;
00697 }
00698 
00700 void GLView::setPerspective()
00702 {
00703   GLfloat aspectRatio = static_cast<float>(width()) / static_cast<float>(height());
00704   if(baseParameters.perspectiveProjection)
00705     gluPerspective(fieldOfView, aspectRatio, 0.1f, 100.0f); // originally 0.01f and 100.0f but artifacts from intersecting triangles
00706                                                             // are greatly reduced. Problem was the inaccuracy of the Z-buffer
00707                                                             // best results are obtained with a ratio near/far as low as possible
00708   else
00709     glOrtho(-maxRadius*aspectRatio*zPos, maxRadius*aspectRatio*zPos, -maxRadius*zPos, maxRadius*zPos, -maxRadius, maxRadius);
00710 }
00711 
00715 
00716 int GLView::staticUpdateIndex = 0;
00717 const int GLView::redrawWait = 33;
00718 const float GLView::fieldOfView = 60.0f;
00719 
00722 //  OpenGLParameters GLMoleculeView::parameters = {1.0, 1.0, 1.0, QColor(255, 255, 255), 0.80, 100.0,
00723 //                                                 QColor(0, 0, 0), false, true, true, 5, true, 0, 0.2, 3.0};
00724 GLBaseParameters GLView::baseParameters = {1.0f, 1.0f, 1.0f, 0xffffff, 0.80f, 100.0f,
00725                                            0x000000, false, true, false, true};
00726 

Generated on Fri May 19 14:31:54 2006 for Brabosphere by  doxygen 1.4.6-NO