Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • OM_Update
  • warnings
  • zs/fix_plugin_tcp
  • QtBaseViewer_dependencies
  • ovm-v3
  • mh/dev
  • cleanup/avoid-std-iterator
  • lpsolve-fixup-bundle
  • lukas_seeholzer_selectionHighlighter
  • cxx17
  • cmake-eigen
  • Python_experiments
  • bugfix/build-without-python
  • dev_mh
  • ovm-new-cmake
  • fix-macports-suitesparse-5.3.0
  • featurephysicallyBasedMaterial
  • meshcompiler_fast_update
  • OpenFlipper-4.0
20 results

QtBaseViewer.cc

Blame
  • QtBaseViewer.cc 47.50 KiB
    //=============================================================================
    //
    //                               OpenFlipper
    //        Copyright (C) 2008 by Computer Graphics Group, RWTH Aachen
    //                           www.openflipper.org
    //
    //-----------------------------------------------------------------------------
    //
    //                                License
    //
    //  OpenFlipper is free software: you can redistribute it and/or modify
    //  it under the terms of the GNU Lesser General Public License as published by
    //  the Free Software Foundation, either version 3 of the License, or
    //  (at your option) any later version.
    //
    //  OpenFlipper is distributed in the hope that it will be useful,
    //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    //  GNU Lesser General Public License for more details.
    //
    //  You should have received a copy of the GNU Lesser General Public License
    //  along with OpenFlipper.  If not, see <http://www.gnu.org/licenses/>.
    //
    //-----------------------------------------------------------------------------
    //
    //   $Revision: 4430 $
    //   $Author: moebius $
    //   $Date: 2009-01-23 17:14:32 +0100 (Fr, 23. Jan 2009) $
    //
    //=============================================================================
    
    
    
    
    //=============================================================================
    //
    //  CLASS glViewer - IMPLEMENTATION
    //
    //=============================================================================
    
    
    //== INCLUDES =================================================================
    
    #include "QtBaseViewer.hh"
    #include "QtGLGraphicsScene.hh"
    #include "QtGLGraphicsView.hh"
    #include <ACG/QtWidgets/QtWheel.hh>
    #include <ACG/Scenegraph/DrawModes.hh>
    #include <ACG/GL/gl.hh>
    
    #include <iostream>
    #include <string>
    #include <assert.h>
    
    #include <QMimeData>
    #include <QToolButton>
    #include <QFrame>
    
    #include <QClipboard>
    #include <QApplication>
    #include <QSplitter>
    #include <QLayout>
    #include <QPushButton>
    #include <QLabel>
    #include <QStatusBar>
    #include <QImage>
    #include <QColorDialog>
    #include <QFileDialog>
    #include <QToolTip>
    #include <QTextStream>
    #include <QDateTime>
    #include <QTimer>
    
    #include <QDesktopWidget>
    #include <QMouseEvent>
    #include <QVBoxLayout>
    #include <QKeyEvent>
    #include <QGridLayout>
    #include <QContextMenuEvent>
    #include <QWheelEvent>
    #include <QDropEvent>
    #include <QPixmap>
    #include <QMenu>
    #include <QVariant>
    #include <QButtonGroup>
    #include <QToolBar>
    
    #include <QGraphicsWidget>
    #include <QGraphicsGridLayout>
    #include <QGraphicsProxyWidget>
    #include <QPainter>
    #include <QPaintEngine>
    
    #ifdef max
    #  undef max
    #endif
    
    #ifdef min
    #  undef min
    #endif
    
    
    //== NAMESPACES ===============================================================
    
    
    
    //== IMPLEMENTATION ==========================================================
    
    static const char          VIEW_MAGIC[] =
      "ACG::QtWidgets::QGLViewerWidget encoded view";
    
    //== IMPLEMENTATION ==========================================================
    
    
    glViewer::glViewer( QtGLGraphicsScene* _scene,
    		    QGLWidget* _glWidget,
    		    QGraphicsWidget* _parent,
    		    const char* /* _name */ ,
    		    QStatusBar *_statusBar) :
      QGraphicsWidget(_parent),
      statusbar_(_statusBar),
      glareaGrabbed_(false),
      projectionUpdateLocked_(false),
      blending_(true),
      glScene_(_scene),
      glWidget_(_glWidget),
      pick_mode_name_(""),
      pick_mode_idx_(-1),
      glstate_(0)
    {
    
      // widget stuff
      createWidgets(_statusBar);
    
      // bind GL context to GL state class
      glstate_ = new ACG::GLState();
      properties_.setglState( glstate_ );
    
      // state
      orthoWidth_       = 2.0;
      isRotating_       = false;
      near_             = 0.1;
      far_              = 100.0;
      fovy_             = 45.0;
    
      focalDist_        = 3.0;
      eyeDist_          = 0.01;
    
      sceneGraphRoot_   = 0;
      curDrawMode_      = ACG::SceneGraph::DrawModes::NONE;
      availDrawModes_   = ACG::SceneGraph::DrawModes::NONE;
    
      normalsMode_      = DONT_TOUCH_NORMALS;
      projectionMode_   = PERSPECTIVE_PROJECTION;
    
    
      light_matrix_.identity();
    
      trackMouse_ = false;
    
      // stereo
      stereo_ = false;
    
    
      pickMenu_ = 0;
      drawMenu_ = 0;
    
    
      // Note: we start locked (initialization of updateLocked_)
      // will be unlocked in initializeGL()
    
      QSizePolicy sp = sizePolicy();
      sp.setHorizontalPolicy( QSizePolicy::Expanding );
      sp.setVerticalPolicy( QSizePolicy::Expanding );
      sp.setHorizontalStretch( 1 );
      sp.setVerticalStretch( 1 );
      setSizePolicy( sp );
    
      redrawTime_.start ();
    
      // timer for animation
      timer_ = new QTimer( this );
      connect( timer_, SIGNAL(timeout()), this, SLOT( slotAnimation()) );
    
      allowRotation_ = true;
    
    
      connect( &properties_,SIGNAL(updated()), this, SLOT( slotPropertiesUpdated() ) );
      connect( &properties_,SIGNAL(actionModeChanged(Viewer::ActionMode)), this, SLOT( updateActionMode(Viewer::ActionMode) ) );
    
      properties_.setExamineMode();
    
      setAcceptDrops(true);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    glViewer::~glViewer()
    {
      delete glstate_;
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    QSize
    glViewer::sizeHint() const
    {
      return QSize( 600, 600 );
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::setStatusBar(QStatusBar* _sb)
    {
      statusbar_ = _sb;
    }
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::makeCurrent() {
      glWidget_->makeCurrent();
    }
    
    void glViewer::swapBuffers() {
      glWidget_->swapBuffers();
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::sceneGraph(ACG::SceneGraph::BaseNode* _root)
    {
      sceneGraphRoot_ = _root;
    
      if (sceneGraphRoot_)
      {
        // get draw modes
        ACG::SceneGraph::CollectDrawModesAction action;
        ACG::SceneGraph::traverse(sceneGraphRoot_, action);
        availDrawModes_ = action.drawModes();
        updatePopupMenu();
    
        // get scene size
        ACG::SceneGraph::BoundingBoxAction act;
        ACG::SceneGraph::traverse(sceneGraphRoot_, act);
    
        ACG::Vec3d bbmin = (ACG::Vec3d) act.bbMin();
        ACG::Vec3d bbmax = (ACG::Vec3d) act.bbMax();
    
        if ( ( bbmin[0] > bbmax[0] ) ||
             ( bbmin[1] > bbmax[1] ) ||
             ( bbmin[2] > bbmax[2] )   )
          setScenePos( ACG::Vec3d( 0.0,0.0,0.0 ) , 1.0 );
        else
          setScenePos( ( bbmin + bbmax )        * 0.5,
                       ( bbmax - bbmin ).norm() * 0.5 );
      }
    
      updateGL();
    
      emit(signalSceneGraphChanged(sceneGraphRoot_));
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::trackMouse(bool _track)
    {
      trackMouse_ = _track;
    }
    
    
    //-----------------------------------------------------------------------------
    
    void glViewer::perspectiveProjection()
    {
      projectionMode(PERSPECTIVE_PROJECTION);
      updateGL();
    }
    
    
    void glViewer::orthographicProjection()
    {
      projectionMode(ORTHOGRAPHIC_PROJECTION);
      updateGL();
    }
    
    
    void glViewer::toggleProjectionMode()
    {
      if (projectionMode_ == ORTHOGRAPHIC_PROJECTION)
        projectionMode(PERSPECTIVE_PROJECTION);
      else
        projectionMode(ORTHOGRAPHIC_PROJECTION);
    
      updateGL();
    }
    
    
    void glViewer::projectionMode(ProjectionMode _p)
    {
      if ((projectionMode_ = _p) == ORTHOGRAPHIC_PROJECTION)
        emit projectionModeChanged( true );
      else
        emit projectionModeChanged( false );
    
      updateProjectionMatrix();
    }
    
    
    void glViewer::updateProjectionMatrix()
    {
      if( projectionUpdateLocked_ )
        return;
    
      makeCurrent();
    
      glstate_->reset_projection();
    
      switch (projectionMode_)
      {
        case ORTHOGRAPHIC_PROJECTION:
        {
          double aspect = (double) glWidth() / (double) glHeight();
          glstate_->ortho( -orthoWidth_, orthoWidth_,
    		       -orthoWidth_/aspect, orthoWidth_/aspect,
    		       near_, far_ );
          break;
        }
    
        case PERSPECTIVE_PROJECTION:
        {
          glstate_->perspective(fovy_,
    			    (GLdouble) glWidth() / (GLdouble) glHeight(),
    			    near_, far_);
          break;
        }
      }
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::setScenePos(const ACG::Vec3d& _center, double _radius)
    {
      scene_center_ = trackball_center_ = _center;
      scene_radius_ = trackball_radius_ = _radius;
    
      orthoWidth_ = 2.0   * scene_radius_;
      near_       = 0.001 * scene_radius_;
      far_        = 10.0  * scene_radius_;
    
      updateProjectionMatrix();
      updateGL();
    }
    
    
    //----------------------------------------------------------------------------
    
    
    void glViewer::viewingDirection( const ACG::Vec3d& _dir, const ACG::Vec3d& _up )
    {
      // calc eye point for this direction
      ACG::Vec3d eye = scene_center_ - _dir*(3.0*scene_radius_);
    
      glstate_->reset_modelview();
      glstate_->lookAt((ACG::Vec3d)eye, (ACG::Vec3d)scene_center_, (ACG::Vec3d)_up);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::updateActionMode(Viewer::ActionMode)
    {
    
      trackMouse(false);
    
    
      switch ( properties_.actionMode() )
      {
        case Viewer::ExamineMode:
        {
          setCursor(Qt::PointingHandCursor);
          break;
        }
    
    
        case Viewer::LightMode:
        {
          setCursor(Qt::PointingHandCursor);
          break;
        }
    
    
        case Viewer::PickingMode:
        {
          setCursor(Qt::ArrowCursor);
          if (pick_mode_idx_ != -1) {
    	trackMouse(pick_modes_[pick_mode_idx_].tracking);
            setCursor(pick_modes_[pick_mode_idx_].cursor);
          }
    
          break;
        }
    
    
        case Viewer::QuestionMode:
        {
          setCursor(Qt::WhatsThisCursor);
          break;
        }
      }
    
      //emit pickmodeChanged with either the name of the current pickmode or an empty string
      if( properties_.pickingMode() )
        emit(signalPickModeChanged(pick_mode_name_));
      else
        emit(signalPickModeChanged(""));
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::normalsMode(NormalsMode _mode)
    {
      makeCurrent();
    
      switch(normalsMode_ = _mode)
      {
        case DONT_TOUCH_NORMALS:
          glDisable(GL_NORMALIZE);
          break;
    
        case NORMALIZE_NORMALS:
          glEnable(GL_NORMALIZE);
          break;
      }
    
      updateGL();
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void
    glViewer::copyToImage( QImage& _image,
    			   unsigned int /* _l */ , unsigned int /* _t */ ,
    			   unsigned int /* _w */ , unsigned int /* _h */ ,
    			   GLenum /* _buffer */ )
    {
      makeCurrent();
      _image = glWidget_->grabFrameBuffer(true);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::updateGL()
    {
      if (!properties_.updateLocked() && isVisible() )
      {
        update();
      }
    }
    
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::drawScene()
    {
      QTime  timer;
      timer.start();
    
    
      // *****************************************************************
      // Adjust clipping planes
      // *****************************************************************
      // Far plane
      ACG::Vec3d c = glstate_->modelview().transform_point(scene_center_);
    
      // Set far plane
      far_    = std::max(0.0002f * scene_radius_,  -(c[2] - scene_radius_));
    
      // Set near plane
      near_   = std::max(0.0001f * scene_radius_,  -(c[2] + scene_radius_));
    
      // measure distance from scene center ( as projection onto the z-Axis )
    //   if ( -c[2] < scene_radius_ ) {
    //     std::cerr << "Camera in scene radius" << std::endl;
    //
    //   }
    //
    //   std::cerr << "-c[2]   : " << -c[2] << std::endl;
    //   std::cerr << "radius  : " << scene_radius_ << std::endl;
    //   std::cerr << "z-range : " << far_ - near_ << std::endl;
    //   std::cerr << "Near    : " << near_ << std::endl;
    //   std::cerr << "Far     : " << far_ << std::endl;
    //   near_   = std::max(far_ / 256.0f,  -(c[2] + scene_radius_));
    
      updateProjectionMatrix();
    
      // store time since last repaint in gl state and restart timer
      glstate_->set_msSinceLastRedraw (redrawTime_.restart ());
    
      // draw mono or stereo
      makeCurrent();
      if (stereo_) drawScene_stereo();
      else         drawScene_mono();
    
      glFinish();
      frame_time_ = timer.elapsed();
    
    
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::drawScene_mono()
    {
      if (sceneGraphRoot_)
      {
        if (! properties_.renderPicking() ) {
          ACG::SceneGraph::DrawAction action(curDrawMode_, false);
          ACG::SceneGraph::traverse(sceneGraphRoot_, action, *glstate_, curDrawMode_);
    
          if( blending_ )
          {
            ACG::SceneGraph::DrawAction action(curDrawMode_, true);
            ACG::SceneGraph::traverse(sceneGraphRoot_, action, *glstate_, curDrawMode_);
          }
        } else {
    
          // prepare GL state
          makeCurrent();
    
          glDisable(GL_LIGHTING);
          glClear(GL_DEPTH_BUFFER_BIT);
          glInitNames();
          glPushName((GLuint) 0);
    
          // do the picking
          ACG::SceneGraph::PickAction action(properties_.renderPickingMode());
          ACG::SceneGraph::traverse(sceneGraphRoot_, action, *glstate_);
    
          glEnable(GL_LIGHTING);
        }
      }
    
      draw_lights();
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void
    glViewer::drawScene_stereo()
    {
      double l, r, t, b, w, h, a, radians, wd2, ndfl;
    
      w = glWidth();
      h = glHeight();
      a = w / h;
    
      radians = fovy_ * 0.5 / 180.0 * M_PI;
      wd2     = near_ * tan(radians);
      ndfl    = near_ / focalDist_ * scene_radius_;
    
      l = -a*wd2;
      r =  a*wd2;
      t =  wd2;
      b = -wd2;
    
      double offset  = 0.5 * eyeDist_;
      double offset2 = offset * ndfl;
    
    
    
      // left eye
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      glFrustum(l+offset2, r+offset2, b, t, near_, far_);
      glTranslatef(-offset, 0.0, 0.0);
      glMatrixMode(GL_MODELVIEW);
      glDrawBuffer(GL_BACK_LEFT);
      glstate_->clearBuffers ();
      glClear(GL_DEPTH_BUFFER_BIT);
      drawScene_mono();
    
    
      // right eye
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      glFrustum(l-offset2, r-offset2, b, t, near_, far_);
      glTranslatef(offset, 0.0, 0.0);
      glMatrixMode(GL_MODELVIEW);
      glDrawBuffer(GL_BACK_RIGHT);
      glstate_->clearBuffers ();
      glClear(GL_DEPTH_BUFFER_BIT);
      drawScene_mono();
      glDrawBuffer(GL_BACK);
    }
    
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::setHome()
    {
      home_modelview_          = glstate_->modelview();
      home_inverse_modelview_  = glstate_->inverse_modelview();
      homeOrthoWidth_          = orthoWidth_;
      home_center_             = trackball_center_;
      home_radius_             = trackball_radius_;
    }
    
    
    void glViewer::home()
    {
      makeCurrent();
      glstate_->set_modelview(home_modelview_, home_inverse_modelview_);
      orthoWidth_ = homeOrthoWidth_;
      trackball_center_ = home_center_;
      trackball_radius_ = home_radius_;
      updateProjectionMatrix();
      updateGL();
    
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::viewAll()
    {
      makeCurrent();
    
      // move center (in camera coords) to origin and translate in -z dir
      translate(-(glstate_->modelview().transform_point(scene_center_))
    	    - ACG::Vec3d(0.0, 0.0, 3.0*scene_radius_ ));
    
      orthoWidth_ = 1.1*scene_radius_;
      double aspect = (double) glWidth() / (double) glHeight();
      if (aspect > 1.0) orthoWidth_ *= aspect;
      updateProjectionMatrix();
      updateGL();
    
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::flyTo(const QPoint& _pos, bool _move_back)
    {
      makeCurrent();
    
      unsigned int nodeIdx, targetIdx;
      ACG::Vec3d hitPoint;
    
      if (pick( ACG::SceneGraph::PICK_ANYTHING, _pos, nodeIdx, targetIdx, &hitPoint))
      {
        if (projectionMode_ == PERSPECTIVE_PROJECTION)
        {
          ACG::Vec3d eye(glstate_->eye());
          ACG::Vec3d t = hitPoint - eye;
          ACG::Vec3d e = eye + t * (_move_back ? -0.5f : 0.5f);
          flyTo(e, hitPoint, 300);
        }
        else
        {
          // Zoom in or out?
          orthoWidth_ *= _move_back ? 2.0 : 0.5;
    
          // Set the double click point as the new trackball center
          // Rotations will use this point as the center.
          trackball_center_ = hitPoint;
    
          /// @todo : Translate view such that hitpoint is in center of viewport
    
          // Update the projection matrix
          updateProjectionMatrix();
    
          // Redraw scene
          updateGL();
        }
    
    
      }
    }
    
    
    void glViewer::flyTo(const ACG::Vec3d&  _position,
    			 const ACG::Vec3d&  _center,
    			 double        _time)
    {
      makeCurrent();
    
      // compute rotation
      ACG::Vec3d c = glstate_->modelview().transform_point(_center);
      ACG::Vec3d p = glstate_->modelview().transform_point(_position);
      ACG::Vec3d view =(p-c).normalize();
      ACG::Vec3d z(0,0,1);
      ACG::Vec3d axis = (z % -view).normalize();
      double angle = acos(std::max(-1.0,
    			      std::min(1.0,
    				       (z | view)))) / M_PI * 180.0;
    
      if (angle > 175)
        axis  = ACG::Vec3d(0,1,0);
    
    
      // compute translation
      ACG::Vec3d target = glstate_->modelview().transform_point(_center);
      ACG::Vec3d trans ( -target[0],
    		-target[1],
    		-target[2] - (_position-_center).norm() );
    
    
    
      // how many frames in _time ms ?
      unsigned int  frames = (unsigned int)(_time / frame_time_);
      if (frames > 1000) frames=1000;
    
    
    
    
      // animate it
      if (frames > 10)
      {
        ACG::Vec3d t = trans / (double)frames;
        double a = angle / (double)frames;
    
        for (unsigned int i=0; i<frames; ++i)
        {
          translate(t);
          if (fabs(a) > FLT_MIN)
    	     rotate(axis, a, _center);
    
          update();
          qApp->processEvents();
        }
      }
    
    
      // no animation
      else
      {
        translate(trans);
        if (fabs(angle) > FLT_MIN)
          rotate(axis, angle, _center);
    
        updateGL();
      }
    
    
      trackball_center_ = _center;
      trackball_radius_ = std::max(scene_radius_,
    			       (_center-_position).norm()*0.9f);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::setView(const ACG::GLMatrixd& _modelview,
    			                  const ACG::GLMatrixd& _inverse_modelview)
    {
      makeCurrent();
      glstate_->set_modelview(_modelview, _inverse_modelview);
      updateGL();
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::initializeGL()
    {
      // we use GLEW to manage extensions
      // initialize it first
      glewInit();
    
    
      // lock update
      properties_.lockUpdate();
    
      // init GL state
      glstate_->initialize();
    
      // OpenGL state
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_LIGHTING);
      glDisable(GL_DITHER);
      glShadeModel( GL_FLAT );
    
    
      projectionMode(   projectionMode_   );
      normalsMode(      normalsMode_      );
    
      // Update all settings which would require a redraw
      applyProperties();
    
      // light sources
      light_matrix_.identity();
      update_lights();
    
    
      // scene pos and size
      scene_center_ = trackball_center_ = ACG::Vec3d( 0.0, 0.0, 0.0 );
      scene_radius_ = trackball_radius_ = 1.0;
      orthoWidth_   = 2.0;
    
    
      // modelview
      glstate_->translate(0.0, 0.0, -3.0);
      setHome();
    
    
      // pixel transfer
      glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
      glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
      glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      glPixelStorei(GL_PACK_ROW_LENGTH, 0);
      glPixelStorei(GL_PACK_SKIP_ROWS, 0);
      glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
      glPixelStorei(GL_PACK_ALIGNMENT, 1);
    
    
      // unlock update (we started locked)
      properties_.unLockUpdate();
    }
    
    
    //-----------------------------------------------------------------------------
    
    /**
     *
     */
    void  glViewer::draw_lights() {
    //   makeCurrent();
    // //
    //   glMatrixMode(GL_MODELVIEW);
    //   glPushMatrix();
    //   glLoadIdentity();
    //   glMultMatrixd(light_matrix_.data());
    //
    //   std::cerr << "light_matrix_\n" << light_matrix_ << std::endl;
    // //
    //   glPointSize(3);
    // //
    //   glColor3f(1.0,1.0,1.0);
    // //   glDisable(GL_LIGHTING);
    // //   glDisable(GL_BLEND);
    //   glBegin(GL_LINES);
    //     glVertex3f(0.0,  0.0, -1000.0);
    //     glVertex3f(0.0,  0.0, 10000.0);
    //     glVertex3f(0.0, -1000.0,0.0);
    //     glVertex3f(0.0, 1000.0,0.0);
    //     glVertex3f(1000.0,0.0,0.0);
    //     glVertex3f(-1000.0,0.0,0.0);
    // //     glVertex3d(0.0,  0.0, 1.1);
    // //     glVertex3d(-1.0,  1.0, 0.8);
    // //     glVertex3d( 1.0,  1.0, 0.8);
    //
    //   glEnd();
    // //   glEnable(GL_LIGHTING);
    // //   glEnable(GL_BLEND);
    // //
    //   glPopMatrix();
    
    }
    
    void glViewer::update_lights()
    {
      makeCurrent();
    
      glMatrixMode(GL_MODELVIEW);
      glPushMatrix();
      glLoadIdentity();
      glMultMatrixd(light_matrix_.data());
    
      GLfloat pos[4], col[4];
    
      col[0] = col[1] = col[2] = 0.7;
      pos[3] = col[3] = 0.0;
    
    #define SET_LIGHT(i,x,y,z) { 			\
        pos[0]=x; pos[1]=y; pos[2]=z;		\
        glLightfv(GL_LIGHT##i, GL_POSITION, pos);	\
        glLightfv(GL_LIGHT##i, GL_DIFFUSE,  col);	\
        glLightfv(GL_LIGHT##i, GL_SPECULAR, col);	\
        glEnable(GL_LIGHT##i);			\
      }
    
      SET_LIGHT(0,  0.0,  0.0, 1.0);
      SET_LIGHT(1, -1.0,  1.0, 0.7);
      SET_LIGHT(2,  1.0,  1.0, 0.7);
    
      col[0] = col[1] = col[2] = 0.3; col[3] = 1.0;
      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
    
      glPopMatrix();
    }
    
    
    void glViewer::rotate_lights(ACG::Vec3d& _axis, double _angle)
    {
      light_matrix_.rotate(_angle, _axis[0], _axis[1], _axis[2], ACG::MULT_FROM_LEFT);
      update_lights();
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::paintGL()
    {
      static bool initialized = false;
      if (!initialized)
      {
        // we use GLEW to manage extensions
        // initialize it first
        glewInit();
    
        // lock update
        properties_.lockUpdate();
    
        // init GL state
        glstate_->initialize();
    
        // initialize lights
        light_matrix_.identity();
    
        // scene pos and size
        scene_center_ = trackball_center_ = ACG::Vec3d( 0.0, 0.0, 0.0 );
        scene_radius_ = trackball_radius_ = 1.0;
        orthoWidth_   = 2.0;
    
        // modelview
        glstate_->translate(0.0, 0.0, -3.0);
        setHome();
    
        // pixel transfer
        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
        glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
        glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glPixelStorei(GL_PACK_ROW_LENGTH, 0);
        glPixelStorei(GL_PACK_SKIP_ROWS, 0);
        glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
        glPixelStorei(GL_PACK_ALIGNMENT, 1);
    
        // unlock update (we started locked)
        properties_.unLockUpdate();
    
        initialized = true;
      }
    
      if (!properties_.updateLocked())
      {
        properties_.lockUpdate();
    
        glPushAttrib (GL_ALL_ATTRIB_BITS);
    
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_LIGHTING);
        glDisable(GL_DITHER);
        glShadeModel( GL_FLAT );
    
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
    
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
    
    
        normalsMode(      normalsMode_      );
    
        applyProperties();
    
        // light sources
        update_lights();
    
        glstate_->setState ();
    
        glColor4f(1.0,0.0,0.0,1.0);
    
        // clear (stereo mode clears buffers on its own)
        if (!stereo_)
          glstate_->clearBuffers ();
    
        properties_.unLockUpdate();
    
        // draw scene
        drawScene();
    
        glPopMatrix();
    
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
    
        glPopAttrib ();
      }
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::resizeEvent(QGraphicsSceneResizeEvent *)
    {
      updateProjectionMatrix();
      glstate_->viewport(scenePos().x(),
    		     scene()->height () - scenePos().y() - size().height (),
    		     size().width (), size().height (),
    		     scene()->width (), scene()->height ());
      update();
    }
    
    void glViewer::moveEvent (QGraphicsSceneMoveEvent *)
    {
      glstate_->viewport(scenePos().x(),
    		     scene()->height () - scenePos().y() - size().height (),
    		     size().width (), size().height (),
    		     scene()->width (), scene()->height ());
      update();
    }
    
    //-----------------------------------------------------------------------------
    
    void glViewer::paint(QPainter * _painter, const QStyleOptionGraphicsItem *, QWidget *)
    {
      if (_painter->paintEngine()->type() != QPaintEngine::OpenGL) {
        std::cerr << "glViewer: paint needs a QGLWidget to be set as viewport on the graphics view\n";
        return;
      }
      paintGL ();
    }
    
    void glViewer::encodeView(QString& _view)
    {
      const ACG::GLMatrixd m = glstate_->modelview();
      const ACG::GLMatrixd p = glstate_->projection();
    
      _view.sprintf("%s\n"
                    "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
                    "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
                    "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
                    "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
                    "%d %d %d %lf\n",
                    VIEW_MAGIC,
                    m(0,0), m(0,1), m(0,2), m(0,3),
                    m(1,0), m(1,1), m(1,2), m(1,3),
                    m(2,0), m(2,1), m(2,2), m(2,3),
                    m(3,0), m(3,1), m(3,2), m(3,3),
                    p(0,0), p(0,1), p(0,2), p(0,3),
                    p(1,0), p(1,1), p(1,2), p(1,3),
                    p(2,0), p(2,1), p(2,2), p(2,3),
                    p(3,0), p(3,1), p(3,2), p(3,3),
                    glWidth(), glHeight(),
                    projectionMode_,
                    orthoWidth_ );
    }
    
    
    //----------------------------------------------------------------------------
    
    
    bool glViewer::decodeView(const QString& _view)
    {
      if (_view.left(sizeof(VIEW_MAGIC)-1) != QString(VIEW_MAGIC))
        return false;
    
    
    
      ACG::GLMatrixd m, p;
      int            w, h, pMode;
    
      sscanf( (_view.toAscii().data())+sizeof(VIEW_MAGIC)-1,
    	  "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
    	  "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
    	  "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
    	  "%lf %lf %lf %lf\n%lf %lf %lf %lf\n"
    	  "%d %d %d %lf\n",
    	  &m(0,0), &m(0,1), &m(0,2), &m(0,3),
    	  &m(1,0), &m(1,1), &m(1,2), &m(1,3),
    	  &m(2,0), &m(2,1), &m(2,2), &m(2,3),
    	  &m(3,0), &m(3,1), &m(3,2), &m(3,3),
    	  &p(0,0), &p(0,1), &p(0,2), &p(0,3),
    	  &p(1,0), &p(1,1), &p(1,2), &p(1,3),
    	  &p(2,0), &p(2,1), &p(2,2), &p(2,3),
    	  &p(3,0), &p(3,1), &p(3,2), &p(3,3),
    	  &w, &h,
    	  &pMode,
    	  &orthoWidth_ );
    
      makeCurrent();
    
      if (projectionMode_ != (ProjectionMode)pMode)
        projectionMode((ProjectionMode)pMode);
    
      glstate_->set_modelview(m);
    
    
      std::cerr << "Todo : Add Checkbox if size should also be pasted" << std::endl;
    
    //   if (w>0 && h>0 &&
    //       action_["PasteDropSize"]->isChecked() )
    //   {
    //     glstate_->set_projection(p);
    //     glView_->setFixedSize(w,h);
    //     updateGeometry();
    //   }
    
    
      updateGL();
    
    
      return true;
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::actionCopyView()
    {
      QString view; encodeView(view);
      QApplication::clipboard()->setText(view);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::actionPasteView()
    {
      QString view; view=QApplication::clipboard()->text();
      decodeView(view);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::actionDrawMenu( QAction * _action )
    {
      unsigned int mode( _action->data().toUInt() );
    
      // combine draw modes
      if (qApp->keyboardModifiers() & Qt::ShiftModifier)
      {
        if (drawMode() & mode)
          drawMode(drawMode() & ~mode);
        else
          drawMode(drawMode() | mode);
      }
    
      // simply switch draw mode
      else
      {
        // clear all other checked items
        std::vector< QAction * >::iterator aIter, aEnd;
    
        aEnd = drawMenuActions_.end();
        for( aIter = drawMenuActions_.begin();
    	 aIter != aEnd;
    	 ++aIter )
        {
          if( (*aIter)->data().toUInt() != mode )
    	  (*aIter)->setChecked( false );
        }
    
        drawMode(mode);
      }
    
      hidePopupMenus();
      updateGL();
    }
    
    
    //-----------------------------------------------------------------------------
    
    void
    glViewer::createWidgets(QStatusBar* _sb)
    {
      setStatusBar(_sb);
      drawMenu_=0;
      pickMenu_=0;
    
      // Construct GL context & widget
    
      wheelZ_=new ACG::QtWidgets::QtWheel( 0,"wheel-z",ACG::QtWidgets::QtWheel::Vertical);
      wheelZ_->setMinimumSize(wheelZ_->sizeHint());
      wheelZ_->setMaximumSize(wheelZ_->sizeHint());
      connect(wheelZ_,SIGNAL(angleChangedBy(double)),
    	  this,SLOT(slotWheelZ(double)));
      wheelZ_->setToolTip( "Translate along <b>z-axis</b>.");
      wheelZ_->setWhatsThis( "Translate along <b>z-axis</b>.");
    
      wheelY_=new ACG::QtWidgets::QtWheel( 0,"wheel-y",ACG::QtWidgets::QtWheel::Horizontal);
      wheelY_->setMinimumSize(wheelY_->sizeHint());
      wheelY_->setMaximumSize(wheelY_->sizeHint());
      connect(wheelY_,SIGNAL(angleChangedBy(double)),
              this,SLOT(slotWheelY(double)));
      wheelY_->setToolTip("Rotate around <b>y-axis</b>.");
      wheelY_->setWhatsThis( "Rotate around <b>y-axis</b>.");
    
      wheelX_=new ACG::QtWidgets::QtWheel( 0,"wheel-x",ACG::QtWidgets::QtWheel::Vertical);
      wheelX_->setMinimumSize(wheelX_->sizeHint());
      wheelX_->setMaximumSize(wheelX_->sizeHint());
      connect(wheelX_,SIGNAL(angleChangedBy(double)),
              this,SLOT(slotWheelX(double)));
      wheelX_->setToolTip("Rotate around <b>x-axis</b>.");
      wheelX_->setWhatsThis( "Rotate around <b>x-axis</b>.");
    
    
      QGraphicsWidget *wheelX = glScene_->addWidget (wheelX_);
      QGraphicsWidget *wheelY = glScene_->addWidget (wheelY_);
      QGraphicsWidget *wheelZ = glScene_->addWidget (wheelZ_);
    
      wheelX_->setWindowOpacity (0.5);
      wheelY_->setWindowOpacity (0.5);
      wheelZ_->setWindowOpacity (0.5);
    
      wheelX->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
      wheelY->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
      wheelZ->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
    
      glBaseLayout_ = new QGraphicsGridLayout;
      glBaseLayout_->addItem(wheelX, 1, 0);
      glBaseLayout_->addItem(wheelY, 2, 1);
      glBaseLayout_->addItem(wheelZ, 1, 3);
    
      glBaseLayout_->setColumnStretchFactor(0,0);
      glBaseLayout_->setColumnStretchFactor(1,0);
      glBaseLayout_->setColumnStretchFactor(2,1);
      glBaseLayout_->setColumnStretchFactor(3,0);
    
      glBaseLayout_->setRowStretchFactor(0,1);
      glBaseLayout_->setRowStretchFactor(1,0);
      glBaseLayout_->setRowStretchFactor(2,0);
    
      setLayout(glBaseLayout_);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::updatePopupMenu()
    {
      //
      // Draw mode menu
      //
    
      if ( ! drawMenu_ )
      {
        drawMenu_ = new QMenu( scene()->views().first() );
        connect( drawMenu_, SIGNAL( aboutToHide() ),
    	     this, SLOT( hidePopupMenus() ) );
    
      }
    
      QActionGroup * drawGroup = new QActionGroup( this );
      drawGroup->setExclusive( false );
      connect( drawGroup, SIGNAL( triggered( QAction * ) ),
    	   this, SLOT( actionDrawMenu( QAction * ) ) );
    
    
    
      drawMenuActions_.clear();
    
      std::vector< unsigned int > draw_mode_id;
    
      draw_mode_id = ACG::SceneGraph::DrawModes::getDrawModeIDs( availDrawModes_ );
    
      for ( unsigned int i = 0; i < draw_mode_id.size(); ++i )
      {
        unsigned int id    = draw_mode_id[i];
        std::string  descr = ACG::SceneGraph::DrawModes::description( id );
    
        QAction * action = new QAction( descr.c_str(), drawGroup );
        action->setData( QVariant( id ) );
        action->setCheckable( true );
        action->setChecked( ACG::SceneGraph::DrawModes::containsId( curDrawMode_, id ) );
        drawMenuActions_.push_back( action );
      }
    
    
      drawMenu_->clear();
      drawMenu_->addActions( drawGroup->actions() );
    
    
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::hidePopupMenus()
    {
      if ( drawMenu_ )
      {
        drawMenu_->blockSignals(true);
        drawMenu_->hide();
        drawMenu_->blockSignals(false);
      }
    
      if ( pickMenu_ )
      {
        pickMenu_->blockSignals(true);
        pickMenu_->hide();
        pickMenu_->blockSignals(false);
      }
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::translate(const ACG::Vec3d& _trans)
    {
      makeCurrent();
      glstate_->translate(_trans[0], _trans[1], _trans[2], ACG::MULT_FROM_LEFT);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::initModelviewMatrix()
    {
      makeCurrent();
      glstate_->reset_modelview();
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::rotate(const ACG::Vec3d&  _axis,
                              double        _angle,
                              const ACG::Vec3d&  _center)
    {
      makeCurrent();
      ACG::Vec3d t = glstate_->modelview().transform_point(_center);
      glstate_->translate(-t[0], -t[1], -t[2], ACG::MULT_FROM_LEFT);
      glstate_->rotate(_angle, _axis[0], _axis[1], _axis[2], ACG::MULT_FROM_LEFT);
      glstate_->translate( t[0],  t[1],  t[2], ACG::MULT_FROM_LEFT);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    unsigned int glViewer::glWidth() const {
      return size().width();
    }
    unsigned int glViewer::glHeight() const {
      return size().height();
    }
    QSize glViewer::glSize() const {
      return QSize(size().width(),size().height());
    }
    QPoint glViewer::glMapFromGlobal( const QPoint& _pos ) const {
      QPoint p (scene()->views().front()->mapFromGlobal(_pos));
      QPointF f (mapFromScene(QPointF(p.x(), p.y ())));
      return QPoint (f.x(), f.y());
    }
    
    QPoint glViewer::glMapToGlobal( const QPoint& _pos ) const {
      QPointF f (mapToScene(QPointF(_pos.x(), _pos.y ())));
      QPoint p (f.x(), f.y());
      return scene()->views().front()->mapToGlobal(p);
    }
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::slotWheelX(double _dAngle)
    {
      rotate(ACG::Vec3d(1,0,0),ACG::QtWidgets::QtWheel::deg(ACG::QtWidgets::QtWheel::clip(_dAngle)));
      updateGL();
    
    }
    
    void glViewer::slotWheelY(double _dAngle)
    {
      rotate(ACG::Vec3d(0,1,0),ACG::QtWidgets::QtWheel::deg(ACG::QtWidgets::QtWheel::clip(_dAngle)));
      updateGL();
    
    }
    
    void glViewer::slotWheelZ(double _dist)
    {
      double dz=_dist*0.5/M_PI*scene_radius_*2.0;
      translate(ACG::Vec3d(0,0,dz));
      updateGL();
    
    }
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::grabGLArea()
    {
      glareaGrabbed_ = true;
    
      setCursor(Qt::BlankCursor);
      grabMouse();
      grabKeyboard();
    }
    
    void glViewer::releaseGLArea()
    {
      glareaGrabbed_ = false;
    
      ungrabMouse();
      ungrabKeyboard();
      setCursor(Qt::ArrowCursor);
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::contextMenuEvent(QGraphicsSceneContextMenuEvent* _e)
    {
      QPoint p (_e->pos().x(), _e->pos().y());
      emit signalCustomContextMenuRequested (p);
    }
    
    
    //-----------------------------------------------------------------------------
    
    void glViewer::glMousePressEvent(QMouseEvent* _event)
    {
      // right button pressed => popup menu (ignore here)
      if (_event->button() != Qt::RightButton )
      {
        switch (properties_.actionMode())
        {
          case Viewer::ExamineMode:
            if ((_event->modifiers() & Qt::ControlModifier)) // drag&drop
              emit startDragEvent( _event );
            else
              viewMouseEvent(_event); // examine
            break;
    
          case Viewer::LightMode:
    	lightMouseEvent(_event);
    	break;
    
          case Viewer::PickingMode: // give event to application
            emit(signalMouseEvent(_event, pick_mode_name_));
            emit(signalMouseEvent(_event));
            break;
    
          case Viewer::QuestionMode: // give event to application
            emit(signalMouseEventIdentify(_event));
            break;
        }
      }
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::glMouseDoubleClickEvent(QMouseEvent* _event)
    {
      switch (properties_.actionMode())
      {
        case Viewer::ExamineMode:
          viewMouseEvent(_event);
          break;
    
        case Viewer::LightMode:
          lightMouseEvent(_event);
          break;
    
        case Viewer::PickingMode: // give event to application
          emit(signalMouseEvent(_event, pick_mode_name_));
          emit(signalMouseEvent(_event));
          break;
    
        case Viewer::QuestionMode: // give event to application
          emit(signalMouseEventIdentify(_event));
          break;
      }
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::glMouseMoveEvent(QMouseEvent* _event)
    {
      switch ( properties_.actionMode() )
      {
        case Viewer::ExamineMode:
          viewMouseEvent(_event);
          break;
    
        case Viewer::LightMode:
          lightMouseEvent(_event);
          break;
    
        case Viewer::PickingMode:
          // give event to application
          // deliver mouse moves with no button down, if tracking is enabled,
          if ((_event->buttons() & (Qt::LeftButton | Qt::MidButton | Qt::RightButton))
              || trackMouse_)
          {
            emit(signalMouseEvent(_event, pick_mode_name_));
            emit(signalMouseEvent(_event));
          }
          break;
    
        case Viewer::QuestionMode: // give event to application
          emit(signalMouseEventIdentify(_event));
          break;
    
        default: // avoid warning
          break;
      }
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::glMouseReleaseEvent(QMouseEvent* _event)
    {
    //   if (_event->button() == Qt::RightButton )
    //     hidePopupMenus();
    
      if (_event->button() != Qt::RightButton ||
          properties_.pickingMode() )
      {
        switch ( properties_.actionMode() )
        {
          case Viewer::ExamineMode:
            viewMouseEvent(_event);
            break;
    
          case Viewer::LightMode:
            lightMouseEvent(_event);
            break;
    
          case Viewer::PickingMode: // give event to application
            emit(signalMouseEvent(_event, pick_mode_name_));
            emit(signalMouseEvent(_event));
            break;
    
          case Viewer::QuestionMode: // give event to application
            emit(signalMouseEventIdentify(_event));
            break;
    
          default: // avoid warning
            break;
        }
      }
    
      isRotating_ = false;
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::glMouseWheelEvent(QWheelEvent* _event)
    {
      switch ( properties_.actionMode() )
      {
        case Viewer::ExamineMode:
          viewWheelEvent(_event);
          break;
    
        case Viewer::PickingMode: // give event to application
          emit(signalWheelEvent(_event, pick_mode_name_));
          break;
    
        default: // avoid warning
          break;
      }
    
      isRotating_ = false;
    }
    
    //-----------------------------------------------------------------------------
    
    void glViewer::dragEnterEvent(QGraphicsSceneDragDropEvent* _e)
    {
      std::cerr << "dragEnter" << std::endl;
    
      QPoint p (_e->pos().x(), _e->pos().y());
      QDragEnterEvent de(p, _e->possibleActions(), _e->mimeData(), _e->buttons(),
    		     _e->modifiers ());
      emit dragEnterEvent(&de);
      _e->accept();
    }
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::dropEvent(QGraphicsSceneDragDropEvent* _e)
    {
      std::cerr << "drop" << std::endl;
    
      QPoint p (_e->pos().x(), _e->pos().y());
      QDropEvent de(p, _e->possibleActions(), _e->mimeData(), _e->buttons(),
    		_e->modifiers ());
      emit dropEvent(&de);
      _e->accept();
    }
    
    //-----------------------------------------------------------------------------
    
    void glViewer::updatePickMenu()
    {
      delete pickMenu_;
    
      pickMenu_ = new QMenu( 0 );
      connect( pickMenu_, SIGNAL( aboutToHide() ),
    	   this, SLOT( hidePopupMenus() ) );
    
      QActionGroup * ag = new QActionGroup( pickMenu_ );
      ag->setExclusive( true );
    
      for (unsigned int i=0; i<pick_modes_.size(); ++i) {
        if ( !pick_modes_[i].visible )
          continue;
    
        if (pick_modes_[i].name == "Separator")
        {
          if ((i > 0) && (i<pick_modes_.size()-1)) // not first, not last
    	   pickMenu_->addSeparator();
        }
        else
        {
          QAction * ac = new QAction( pick_modes_[i].name.c_str(), ag );
          ac->setData( QVariant( i ) );
          ac->setCheckable( true );
    
          if ((int)i == pick_mode_idx_)
            ac->setChecked( true );
    
          pickMenu_->addAction( ac );
        }
      }
    
      connect( ag, SIGNAL( triggered( QAction * ) ),
    	   this, SLOT( actionPickMenu( QAction * ) ));
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::actionPickMenu( QAction * _action )
    {
      int _id = _action->data().toInt();
      if (_id < (int) pick_modes_.size() )
      {
        pickMode( _id );
      }
    
      properties_.setPickingMode();
    
      hidePopupMenus();
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void
    glViewer::viewMouseEvent(QMouseEvent* _event)
    {
      switch (_event->type())
      {
        case QEvent::MouseButtonPress:
        {
          // shift key -> set rotation center
          if (_event->modifiers() & Qt::ShiftModifier)
          {
            ACG::Vec3d c;
            if (fast_pick(_event->pos(), c))
            {
              trackball_center_ = c;
              trackball_radius_ = std::max(scene_radius_, (c-glstate_->eye()).norm()*0.9f);
            }
          }
    
          lastPoint_hitSphere_ = mapToSphere( lastPoint2D_=_event->pos(),
                     lastPoint3D_ );
          isRotating_ = true;
          timer_->stop();
    
    
          break;
        }
    
    
        case QEvent::MouseButtonDblClick:
        {
          if (allowRotation_)
            flyTo(_event->pos(), _event->button()==Qt::MidButton);
          break;
        }
    
    
        case QEvent::MouseMove:
        {
          double factor  = 1.0;
    
          if (_event->modifiers() == Qt::ShiftModifier)
            factor = properties_.wheelZoomFactorShift();
    
          // mouse button should be pressed
          if (_event->buttons() & (Qt::LeftButton | Qt::MidButton))
          {
            QPoint newPoint2D = _event->pos();
    
            if ( (newPoint2D.x()<0) || (newPoint2D.x() > (int)glWidth()) ||
                 (newPoint2D.y()<0) || (newPoint2D.y() > (int)glHeight()) )
              return;
    
            double value_x, value_y;
            ACG::Vec3d  newPoint3D;
            bool   newPoint_hitSphere = mapToSphere( newPoint2D, newPoint3D );
    
            makeCurrent();
    
            // move in z direction
            if ( (_event->buttons() & Qt::LeftButton) &&
                  (_event->buttons() & Qt::MidButton))
            {
              switch (projectionMode())
              {
                case PERSPECTIVE_PROJECTION:
                {
                  value_y = scene_radius_ * ((newPoint2D.y() - lastPoint2D_.y())) * 3.0 / (double) glHeight();
                  translate( ACG::Vec3d(0.0, 0.0, value_y * factor ) );
                  updateGL();
                  break;
                }
    
                case ORTHOGRAPHIC_PROJECTION:
                {
                  value_y = ((newPoint2D.y() - lastPoint2D_.y())) * orthoWidth_ / (double) glHeight();
                  orthoWidth_ -= value_y  * factor;
                  updateProjectionMatrix();
                  updateGL();
                  break;
                }
              }
            }
    
            // move in x,y direction
            else if (_event->buttons() & Qt::MidButton)
            {
              value_x = scene_radius_ * ((newPoint2D.x() - lastPoint2D_.x())) * 2.0 / (double) glWidth();
              value_y = scene_radius_ * ((newPoint2D.y() - lastPoint2D_.y())) * 2.0 / (double) glHeight();
              translate( ACG::Vec3d(value_x  * factor , -value_y  * factor , 0.0) );
            }
    
            // rotate
            else if (allowRotation_ && (_event->buttons() & Qt::LeftButton) )
            {
              ACG::Vec3d axis(1.0,0.0,0.0);
              double angle(0.0);
    
              if ( lastPoint_hitSphere_ ) {
    
                if ( ( newPoint_hitSphere = mapToSphere( newPoint2D,
                                    newPoint3D ) ) ) {
                  axis = lastPoint3D_ % newPoint3D;
                  double cos_angle = ( lastPoint3D_ | newPoint3D );
                  if ( fabs(cos_angle) < 1.0 ) {
                    angle = acos( cos_angle ) * 180.0 / M_PI  * factor ;
                    angle *= 2.0; // inventor rotation
                  }
                }
    
                rotate(axis, angle);
    
              }
    
              lastRotationAxis_  = axis;
              lastRotationAngle_ = angle;
            }
    
            lastPoint2D_ = newPoint2D;
            lastPoint3D_ = newPoint3D;
            lastPoint_hitSphere_ = newPoint_hitSphere;
    
            updateGL();
            lastMoveTime_.restart();
          }
          break;
        }
    
    
    
        case QEvent::MouseButtonRelease:
        {
          lastPoint_hitSphere_ = false;
    
          // continue rotation ?
          if ( isRotating_ &&
          (_event->button() == Qt::LeftButton) &&
          (!(_event->buttons() & Qt::MidButton)) &&
          (lastMoveTime_.elapsed() < 10) && properties_.animation() )
            timer_->start(0);
          break;
        }
    
        default: // avoid warning
          break;
      }
    
    
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void
    glViewer::lightMouseEvent(QMouseEvent* _event)
    {
      switch (_event->type())
      {
        case QEvent::MouseButtonPress:
        {
          lastPoint_hitSphere_ = mapToSphere( lastPoint2D_=_event->pos(),
                     lastPoint3D_ );
          isRotating_ = true;
          timer_->stop();
          break;
        }
    
    
        case QEvent::MouseMove:
        {
          // rotate lights
          if (_event->buttons() & Qt::LeftButton)
          {
       QPoint newPoint2D = _event->pos();
    
       if ( (newPoint2D.x()<0) || (newPoint2D.x() > (int)glWidth()) ||
            (newPoint2D.y()<0) || (newPoint2D.y() > (int)glHeight()) )
         return;
    
    
       ACG::Vec3d  newPoint3D;
       bool   newPoint_hitSphere = mapToSphere( newPoint2D, newPoint3D );
    
       makeCurrent();
    
       ACG::Vec3d axis(1.0,0.0,0.0);
       double angle(0.0);
    
       if ( lastPoint_hitSphere_ )
       {
         if ( ( newPoint_hitSphere = mapToSphere( newPoint2D, newPoint3D ) ) )
         {
           axis = lastPoint3D_ % newPoint3D;
           double cos_angle = ( lastPoint3D_ | newPoint3D );
           if ( fabs(cos_angle) < 1.0 ) {
             angle = acos( cos_angle ) * 180.0 / M_PI;
             angle *= 2.0;
           }
         }
         rotate_lights(axis, angle);
       }
    
       lastPoint2D_ = newPoint2D;
       lastPoint3D_ = newPoint3D;
       lastPoint_hitSphere_ = newPoint_hitSphere;
    
       updateGL();
          }
          break;
        }
    
    
        default: // avoid warning
          break;
      }
    }
    
    //-----------------------------------------------------------------------------
    
    void glViewer::viewWheelEvent( QWheelEvent* _event)
    {
      double factor = properties_.wheelZoomFactor();
    
      if (_event->modifiers() == Qt::ShiftModifier)
        factor = properties_.wheelZoomFactorShift();
    
      switch (projectionMode())
      {
        case PERSPECTIVE_PROJECTION:
        {
          double d = -(double)_event->delta() / 120.0 * 0.2 * factor * trackball_radius_/3;
          translate( ACG::Vec3d(0.0, 0.0, d) );
          updateGL();
          break;
        }
    
        case ORTHOGRAPHIC_PROJECTION:
        {
          double d = (double)_event->delta() / 120.0 * 0.2 * factor * orthoWidth_;
          orthoWidth_ += d;
          updateProjectionMatrix();
          updateGL();
          break;
        }
      }
    
    
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    bool glViewer::mapToSphere(const QPoint& _v2D, ACG::Vec3d& _v3D) const
    {
      if ( (_v2D.x() >= 0) && (_v2D.x() < (int)glWidth()) &&
           (_v2D.y() >= 0) && (_v2D.y() < (int)glHeight()) )
      {
        double x   = (double)(_v2D.x() - ((double)glWidth() / 2.0))  / (double)glWidth();
        double y   = (double)(((double)glHeight() / 2.0) - _v2D.y()) / (double)glHeight();
        double sinx         = sin(M_PI * x * 0.5);
        double siny         = sin(M_PI * y * 0.5);
        double sinx2siny2   = sinx * sinx + siny * siny;
    
        _v3D[0] = sinx;
        _v3D[1] = siny;
        _v3D[2] = sinx2siny2 < 1.0 ? sqrt(1.0 - sinx2siny2) : 0.0;
    
        return true;
      }
      else return false;
    }
    
    
    //-----------------------------------------------------------------------------
    
    
    void glViewer::slotAnimation()
    {
      static int msecs=0, count=0;
      QTime t;
      t.start();
      makeCurrent();
      rotate( lastRotationAxis_, lastRotationAngle_ );
      updateGL();
    
      if (!properties_.updateLocked()) {
        msecs += t.elapsed();
        if (count==10) {
          assert(statusbar_!=0);
          char s[100];
          sprintf( s, "%.3f fps", (10000.0 / (float)msecs) );
          statusbar_->showMessage(s,2000);
          count = msecs = 0;
        }
        else
          ++count;
      }
    }
    
    void glViewer::applyProperties() {
      glstate_->set_clear_color( properties_.backgroundColor() );
    
      if (properties_.isCCWFront() )
        glFrontFace( GL_CCW );
      else
        glFrontFace( GL_CW );
    
      if ( properties_.backFaceCulling() )
        glEnable( GL_CULL_FACE );
      else
        glDisable( GL_CULL_FACE );
    
    }
    
    void glViewer::slotPropertiesUpdated() {
      makeCurrent();
      applyProperties();
      updateGL();
    }
    
    
    void glViewer::snapshot()
    {
       makeCurrent();
    
       qApp->processEvents();
       makeCurrent();
       paintGL();
       glFinish();
    
       QImage snapshot;
       copyToImage(snapshot, scenePos().x(), scenePos().y(), glWidth(), glHeight(), GL_BACK);
    
       QFileInfo fi(properties_.snapshotName());
    
       QString fname = fi.path() + QDir::separator() +fi.baseName() + "." + QString::number(properties_.snapshotCounter()) + ".";
    
       QString format="png";
    
       if (fi.completeSuffix() == "ppm")
         format="ppmraw";
    
       fname += format;
    
       bool rval=snapshot.save(fname,format.toUpper().toLatin1());
    
    
       assert(statusbar_!=0);
       if (rval)
       {
         statusbar_->showMessage(QString("snapshot: ")+fname,5000);
       }
       else
       {
         statusbar_->showMessage(QString("could not save snapshot to ")+fname);
       }
    
    }
    
    //=============================================================================
    //=============================================================================