You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							656 lines
						
					
					
						
							19 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							656 lines
						
					
					
						
							19 KiB
						
					
					
				
								// This file is part of Eigen, a lightweight C++ template library
							 | 
						|
								// for linear algebra.
							 | 
						|
								//
							 | 
						|
								// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
							 | 
						|
								//
							 | 
						|
								// This Source Code Form is subject to the terms of the Mozilla
							 | 
						|
								// Public License v. 2.0. If a copy of the MPL was not distributed
							 | 
						|
								// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
							 | 
						|
								
							 | 
						|
								#include "quaternion_demo.h"
							 | 
						|
								#include "icosphere.h"
							 | 
						|
								
							 | 
						|
								#include <Eigen/Geometry>
							 | 
						|
								#include <Eigen/QR>
							 | 
						|
								#include <Eigen/LU>
							 | 
						|
								
							 | 
						|
								#include <iostream>
							 | 
						|
								#include <QEvent>
							 | 
						|
								#include <QMouseEvent>
							 | 
						|
								#include <QInputDialog>
							 | 
						|
								#include <QGridLayout>
							 | 
						|
								#include <QButtonGroup>
							 | 
						|
								#include <QRadioButton>
							 | 
						|
								#include <QDockWidget>
							 | 
						|
								#include <QPushButton>
							 | 
						|
								#include <QGroupBox>
							 | 
						|
								
							 | 
						|
								using namespace Eigen;
							 | 
						|
								
							 | 
						|
								class FancySpheres
							 | 
						|
								{
							 | 
						|
								  public:
							 | 
						|
								    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
							 | 
						|
								    
							 | 
						|
								    FancySpheres()
							 | 
						|
								    {
							 | 
						|
								      const int levels = 4;
							 | 
						|
								      const float scale = 0.33;
							 | 
						|
								      float radius = 100;
							 | 
						|
								      std::vector<int> parents;
							 | 
						|
								
							 | 
						|
								      // leval 0
							 | 
						|
								      mCenters.push_back(Vector3f::Zero());
							 | 
						|
								      parents.push_back(-1);
							 | 
						|
								      mRadii.push_back(radius);
							 | 
						|
								
							 | 
						|
								      // generate level 1 using icosphere vertices
							 | 
						|
								      radius *= 0.45;
							 | 
						|
								      {
							 | 
						|
								        float dist = mRadii[0]*0.9;
							 | 
						|
								        for (int i=0; i<12; ++i)
							 | 
						|
								        {
							 | 
						|
								          mCenters.push_back(mIcoSphere.vertices()[i] * dist);
							 | 
						|
								          mRadii.push_back(radius);
							 | 
						|
								          parents.push_back(0);
							 | 
						|
								        }
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      static const float angles [10] = {
							 | 
						|
								        0, 0,
							 | 
						|
								        M_PI, 0.*M_PI,
							 | 
						|
								        M_PI, 0.5*M_PI,
							 | 
						|
								        M_PI, 1.*M_PI,
							 | 
						|
								        M_PI, 1.5*M_PI
							 | 
						|
								      };
							 | 
						|
								
							 | 
						|
								      // generate other levels
							 | 
						|
								      int start = 1;
							 | 
						|
								      for (int l=1; l<levels; l++)
							 | 
						|
								      {
							 | 
						|
								        radius *= scale;
							 | 
						|
								        int end = mCenters.size();
							 | 
						|
								        for (int i=start; i<end; ++i)
							 | 
						|
								        {
							 | 
						|
								          Vector3f c = mCenters[i];
							 | 
						|
								          Vector3f ax0 = (c - mCenters[parents[i]]).normalized();
							 | 
						|
								          Vector3f ax1 = ax0.unitOrthogonal();
							 | 
						|
								          Quaternionf q;
							 | 
						|
								          q.setFromTwoVectors(Vector3f::UnitZ(), ax0);
							 | 
						|
								          Affine3f t = Translation3f(c) * q * Scaling(mRadii[i]+radius);
							 | 
						|
								          for (int j=0; j<5; ++j)
							 | 
						|
								          {
							 | 
						|
								            Vector3f newC = c + ( (AngleAxisf(angles[j*2+1], ax0)
							 | 
						|
								                                * AngleAxisf(angles[j*2+0] * (l==1 ? 0.35 : 0.5), ax1)) * ax0)
							 | 
						|
								                                * (mRadii[i] + radius*0.8);
							 | 
						|
								            mCenters.push_back(newC);
							 | 
						|
								            mRadii.push_back(radius);
							 | 
						|
								            parents.push_back(i);
							 | 
						|
								          }
							 | 
						|
								        }
							 | 
						|
								        start = end;
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void draw()
							 | 
						|
								    {
							 | 
						|
								      int end = mCenters.size();
							 | 
						|
								      glEnable(GL_NORMALIZE);
							 | 
						|
								      for (int i=0; i<end; ++i)
							 | 
						|
								      {
							 | 
						|
								        Affine3f t = Translation3f(mCenters[i]) * Scaling(mRadii[i]);
							 | 
						|
								        gpu.pushMatrix(GL_MODELVIEW);
							 | 
						|
								        gpu.multMatrix(t.matrix(),GL_MODELVIEW);
							 | 
						|
								        mIcoSphere.draw(2);
							 | 
						|
								        gpu.popMatrix(GL_MODELVIEW);
							 | 
						|
								      }
							 | 
						|
								      glDisable(GL_NORMALIZE);
							 | 
						|
								    }
							 | 
						|
								  protected:
							 | 
						|
								    std::vector<Vector3f> mCenters;
							 | 
						|
								    std::vector<float> mRadii;
							 | 
						|
								    IcoSphere mIcoSphere;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								// generic linear interpolation method
							 | 
						|
								template<typename T> T lerp(float t, const T& a, const T& b)
							 | 
						|
								{
							 | 
						|
								  return a*(1-t) + b*t;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// quaternion slerp
							 | 
						|
								template<> Quaternionf lerp(float t, const Quaternionf& a, const Quaternionf& b)
							 | 
						|
								{ return a.slerp(t,b); }
							 | 
						|
								
							 | 
						|
								// linear interpolation of a frame using the type OrientationType
							 | 
						|
								// to perform the interpolation of the orientations
							 | 
						|
								template<typename OrientationType>
							 | 
						|
								inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b)
							 | 
						|
								{
							 | 
						|
								  return Frame(lerp(alpha,a.position,b.position),
							 | 
						|
								               Quaternionf(lerp(alpha,OrientationType(a.orientation),OrientationType(b.orientation))));
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								template<typename _Scalar> class EulerAngles
							 | 
						|
								{
							 | 
						|
								public:
							 | 
						|
								  enum { Dim = 3 };
							 | 
						|
								  typedef _Scalar Scalar;
							 | 
						|
								  typedef Matrix<Scalar,3,3> Matrix3;
							 | 
						|
								  typedef Matrix<Scalar,3,1> Vector3;
							 | 
						|
								  typedef Quaternion<Scalar> QuaternionType;
							 | 
						|
								
							 | 
						|
								protected:
							 | 
						|
								
							 | 
						|
								  Vector3 m_angles;
							 | 
						|
								
							 | 
						|
								public:
							 | 
						|
								
							 | 
						|
								  EulerAngles() {}
							 | 
						|
								  inline EulerAngles(Scalar a0, Scalar a1, Scalar a2) : m_angles(a0, a1, a2) {}
							 | 
						|
								  inline EulerAngles(const QuaternionType& q) { *this = q; }
							 | 
						|
								
							 | 
						|
								  const Vector3& coeffs() const { return m_angles; }
							 | 
						|
								  Vector3& coeffs() { return m_angles; }
							 | 
						|
								
							 | 
						|
								  EulerAngles& operator=(const QuaternionType& q)
							 | 
						|
								  {
							 | 
						|
								    Matrix3 m = q.toRotationMatrix();
							 | 
						|
								    return *this = m;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  EulerAngles& operator=(const Matrix3& m)
							 | 
						|
								  {
							 | 
						|
								    // mat =  cy*cz          -cy*sz           sy
							 | 
						|
								    //        cz*sx*sy+cx*sz  cx*cz-sx*sy*sz -cy*sx
							 | 
						|
								    //       -cx*cz*sy+sx*sz  cz*sx+cx*sy*sz  cx*cy
							 | 
						|
								    m_angles.coeffRef(1) = std::asin(m.coeff(0,2));
							 | 
						|
								    m_angles.coeffRef(0) = std::atan2(-m.coeff(1,2),m.coeff(2,2));
							 | 
						|
								    m_angles.coeffRef(2) = std::atan2(-m.coeff(0,1),m.coeff(0,0));
							 | 
						|
								    return *this;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  Matrix3 toRotationMatrix(void) const
							 | 
						|
								  {
							 | 
						|
								    Vector3 c = m_angles.array().cos();
							 | 
						|
								    Vector3 s = m_angles.array().sin();
							 | 
						|
								    Matrix3 res;
							 | 
						|
								    res <<  c.y()*c.z(),                    -c.y()*s.z(),                   s.y(),
							 | 
						|
								            c.z()*s.x()*s.y()+c.x()*s.z(),  c.x()*c.z()-s.x()*s.y()*s.z(),  -c.y()*s.x(),
							 | 
						|
								            -c.x()*c.z()*s.y()+s.x()*s.z(), c.z()*s.x()+c.x()*s.y()*s.z(),  c.x()*c.y();
							 | 
						|
								    return res;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  operator QuaternionType() { return QuaternionType(toRotationMatrix()); }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								// Euler angles slerp
							 | 
						|
								template<> EulerAngles<float> lerp(float t, const EulerAngles<float>& a, const EulerAngles<float>& b)
							 | 
						|
								{
							 | 
						|
								  EulerAngles<float> res;
							 | 
						|
								  res.coeffs() = lerp(t, a.coeffs(), b.coeffs());
							 | 
						|
								  return res;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								RenderingWidget::RenderingWidget()
							 | 
						|
								{
							 | 
						|
								  mAnimate = false;
							 | 
						|
								  mCurrentTrackingMode = TM_NO_TRACK;
							 | 
						|
								  mNavMode = NavTurnAround;
							 | 
						|
								  mLerpMode = LerpQuaternion;
							 | 
						|
								  mRotationMode = RotationStable;
							 | 
						|
								  mTrackball.setCamera(&mCamera);
							 | 
						|
								
							 | 
						|
								  // required to capture key press events
							 | 
						|
								  setFocusPolicy(Qt::ClickFocus);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::grabFrame(void)
							 | 
						|
								{
							 | 
						|
								    // ask user for a time
							 | 
						|
								    bool ok = false;
							 | 
						|
								    double t = 0;
							 | 
						|
								    if (!m_timeline.empty())
							 | 
						|
								      t = (--m_timeline.end())->first + 1.;
							 | 
						|
								    t = QInputDialog::getDouble(this, "Eigen's RenderingWidget", "time value: ",
							 | 
						|
								      t, 0, 1e3, 1, &ok);
							 | 
						|
								    if (ok)
							 | 
						|
								    {
							 | 
						|
								      Frame aux;
							 | 
						|
								      aux.orientation = mCamera.viewMatrix().linear();
							 | 
						|
								      aux.position = mCamera.viewMatrix().translation();
							 | 
						|
								      m_timeline[t] = aux;
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::drawScene()
							 | 
						|
								{
							 | 
						|
								  static FancySpheres sFancySpheres;
							 | 
						|
								  float length = 50;
							 | 
						|
								  gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitX(), Color(1,0,0,1));
							 | 
						|
								  gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitY(), Color(0,1,0,1));
							 | 
						|
								  gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitZ(), Color(0,0,1,1));
							 | 
						|
								
							 | 
						|
								  // draw the fractal object
							 | 
						|
								  float sqrt3 = internal::sqrt(3.);
							 | 
						|
								  glLightfv(GL_LIGHT0, GL_AMBIENT, Vector4f(0.5,0.5,0.5,1).data());
							 | 
						|
								  glLightfv(GL_LIGHT0, GL_DIFFUSE, Vector4f(0.5,1,0.5,1).data());
							 | 
						|
								  glLightfv(GL_LIGHT0, GL_SPECULAR, Vector4f(1,1,1,1).data());
							 | 
						|
								  glLightfv(GL_LIGHT0, GL_POSITION, Vector4f(-sqrt3,-sqrt3,sqrt3,0).data());
							 | 
						|
								
							 | 
						|
								  glLightfv(GL_LIGHT1, GL_AMBIENT, Vector4f(0,0,0,1).data());
							 | 
						|
								  glLightfv(GL_LIGHT1, GL_DIFFUSE, Vector4f(1,0.5,0.5,1).data());
							 | 
						|
								  glLightfv(GL_LIGHT1, GL_SPECULAR, Vector4f(1,1,1,1).data());
							 | 
						|
								  glLightfv(GL_LIGHT1, GL_POSITION, Vector4f(-sqrt3,sqrt3,-sqrt3,0).data());
							 | 
						|
								
							 | 
						|
								  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Vector4f(0.7, 0.7, 0.7, 1).data());
							 | 
						|
								  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Vector4f(0.8, 0.75, 0.6, 1).data());
							 | 
						|
								  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Vector4f(1, 1, 1, 1).data());
							 | 
						|
								  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 64);
							 | 
						|
								
							 | 
						|
								  glEnable(GL_LIGHTING);
							 | 
						|
								  glEnable(GL_LIGHT0);
							 | 
						|
								  glEnable(GL_LIGHT1);
							 | 
						|
								
							 | 
						|
								  sFancySpheres.draw();
							 | 
						|
								  glVertexPointer(3, GL_FLOAT, 0, mVertices[0].data());
							 | 
						|
								  glNormalPointer(GL_FLOAT, 0, mNormals[0].data());
							 | 
						|
								  glEnableClientState(GL_VERTEX_ARRAY);
							 | 
						|
								  glEnableClientState(GL_NORMAL_ARRAY);
							 | 
						|
								  glDrawArrays(GL_TRIANGLES, 0, mVertices.size());
							 | 
						|
								  glDisableClientState(GL_VERTEX_ARRAY);
							 | 
						|
								  glDisableClientState(GL_NORMAL_ARRAY);
							 | 
						|
								
							 | 
						|
								  glDisable(GL_LIGHTING);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::animate()
							 | 
						|
								{
							 | 
						|
								  m_alpha += double(m_timer.interval()) * 1e-3;
							 | 
						|
								
							 | 
						|
								  TimeLine::const_iterator hi = m_timeline.upper_bound(m_alpha);
							 | 
						|
								  TimeLine::const_iterator lo = hi;
							 | 
						|
								  --lo;
							 | 
						|
								
							 | 
						|
								  Frame currentFrame;
							 | 
						|
								
							 | 
						|
								  if(hi==m_timeline.end())
							 | 
						|
								  {
							 | 
						|
								    // end
							 | 
						|
								    currentFrame = lo->second;
							 | 
						|
								    stopAnimation();
							 | 
						|
								  }
							 | 
						|
								  else if(hi==m_timeline.begin())
							 | 
						|
								  {
							 | 
						|
								    // start
							 | 
						|
								    currentFrame = hi->second;
							 | 
						|
								  }
							 | 
						|
								  else
							 | 
						|
								  {
							 | 
						|
								    float s = (m_alpha - lo->first)/(hi->first - lo->first);
							 | 
						|
								    if (mLerpMode==LerpEulerAngles)
							 | 
						|
								      currentFrame = ::lerpFrame<EulerAngles<float> >(s, lo->second, hi->second);
							 | 
						|
								    else if (mLerpMode==LerpQuaternion)
							 | 
						|
								      currentFrame = ::lerpFrame<Eigen::Quaternionf>(s, lo->second, hi->second);
							 | 
						|
								    else
							 | 
						|
								    {
							 | 
						|
								      std::cerr << "Invalid rotation interpolation mode (abort)\n";
							 | 
						|
								      exit(2);
							 | 
						|
								    }
							 | 
						|
								    currentFrame.orientation.coeffs().normalize();
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  currentFrame.orientation = currentFrame.orientation.inverse();
							 | 
						|
								  currentFrame.position = - (currentFrame.orientation * currentFrame.position);
							 | 
						|
								  mCamera.setFrame(currentFrame);
							 | 
						|
								
							 | 
						|
								  updateGL();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::keyPressEvent(QKeyEvent * e)
							 | 
						|
								{
							 | 
						|
								    switch(e->key())
							 | 
						|
								    {
							 | 
						|
								      case Qt::Key_Up:
							 | 
						|
								        mCamera.zoom(2);
							 | 
						|
								        break;
							 | 
						|
								      case Qt::Key_Down:
							 | 
						|
								        mCamera.zoom(-2);
							 | 
						|
								        break;
							 | 
						|
								      // add a frame
							 | 
						|
								      case Qt::Key_G:
							 | 
						|
								        grabFrame();
							 | 
						|
								        break;
							 | 
						|
								      // clear the time line
							 | 
						|
								      case Qt::Key_C:
							 | 
						|
								        m_timeline.clear();
							 | 
						|
								        break;
							 | 
						|
								      // move the camera to initial pos
							 | 
						|
								      case Qt::Key_R:
							 | 
						|
								        resetCamera();
							 | 
						|
								        break;
							 | 
						|
								      // start/stop the animation
							 | 
						|
								      case Qt::Key_A:
							 | 
						|
								        if (mAnimate)
							 | 
						|
								        {
							 | 
						|
								          stopAnimation();
							 | 
						|
								        }
							 | 
						|
								        else
							 | 
						|
								        {
							 | 
						|
								          m_alpha = 0;
							 | 
						|
								          connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
							 | 
						|
								          m_timer.start(1000/30);
							 | 
						|
								          mAnimate = true;
							 | 
						|
								        }
							 | 
						|
								        break;
							 | 
						|
								      default:
							 | 
						|
								        break;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    updateGL();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::stopAnimation()
							 | 
						|
								{
							 | 
						|
								  disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
							 | 
						|
								  m_timer.stop();
							 | 
						|
								  mAnimate = false;
							 | 
						|
								  m_alpha = 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::mousePressEvent(QMouseEvent* e)
							 | 
						|
								{
							 | 
						|
								  mMouseCoords = Vector2i(e->pos().x(), e->pos().y());
							 | 
						|
								  bool fly = (mNavMode==NavFly) || (e->modifiers()&Qt::ControlModifier);
							 | 
						|
								  switch(e->button())
							 | 
						|
								  {
							 | 
						|
								    case Qt::LeftButton:
							 | 
						|
								      if(fly)
							 | 
						|
								      {
							 | 
						|
								        mCurrentTrackingMode = TM_LOCAL_ROTATE;
							 | 
						|
								        mTrackball.start(Trackball::Local);
							 | 
						|
								      }
							 | 
						|
								      else
							 | 
						|
								      {
							 | 
						|
								        mCurrentTrackingMode = TM_ROTATE_AROUND;
							 | 
						|
								        mTrackball.start(Trackball::Around);
							 | 
						|
								      }
							 | 
						|
								      mTrackball.track(mMouseCoords);
							 | 
						|
								      break;
							 | 
						|
								    case Qt::MidButton:
							 | 
						|
								      if(fly)
							 | 
						|
								        mCurrentTrackingMode = TM_FLY_Z;
							 | 
						|
								      else
							 | 
						|
								        mCurrentTrackingMode = TM_ZOOM;
							 | 
						|
								      break;
							 | 
						|
								    case Qt::RightButton:
							 | 
						|
								        mCurrentTrackingMode = TM_FLY_PAN;
							 | 
						|
								      break;
							 | 
						|
								    default:
							 | 
						|
								      break;
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								void RenderingWidget::mouseReleaseEvent(QMouseEvent*)
							 | 
						|
								{
							 | 
						|
								    mCurrentTrackingMode = TM_NO_TRACK;
							 | 
						|
								    updateGL();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::mouseMoveEvent(QMouseEvent* e)
							 | 
						|
								{
							 | 
						|
								    // tracking
							 | 
						|
								    if(mCurrentTrackingMode != TM_NO_TRACK)
							 | 
						|
								    {
							 | 
						|
								        float dx =   float(e->x() - mMouseCoords.x()) / float(mCamera.vpWidth());
							 | 
						|
								        float dy = - float(e->y() - mMouseCoords.y()) / float(mCamera.vpHeight());
							 | 
						|
								
							 | 
						|
								        // speedup the transformations
							 | 
						|
								        if(e->modifiers() & Qt::ShiftModifier)
							 | 
						|
								        {
							 | 
						|
								          dx *= 10.;
							 | 
						|
								          dy *= 10.;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        switch(mCurrentTrackingMode)
							 | 
						|
								        {
							 | 
						|
								          case TM_ROTATE_AROUND:
							 | 
						|
								          case TM_LOCAL_ROTATE:
							 | 
						|
								            if (mRotationMode==RotationStable)
							 | 
						|
								            {
							 | 
						|
								              // use the stable trackball implementation mapping
							 | 
						|
								              // the 2D coordinates to 3D points on a sphere.
							 | 
						|
								              mTrackball.track(Vector2i(e->pos().x(), e->pos().y()));
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								              // standard approach mapping the x and y displacements as rotations
							 | 
						|
								              // around the camera's X and Y axes.
							 | 
						|
								              Quaternionf q = AngleAxisf( dx*M_PI, Vector3f::UnitY())
							 | 
						|
								                            * AngleAxisf(-dy*M_PI, Vector3f::UnitX());
							 | 
						|
								              if (mCurrentTrackingMode==TM_LOCAL_ROTATE)
							 | 
						|
								                mCamera.localRotate(q);
							 | 
						|
								              else
							 | 
						|
								                mCamera.rotateAroundTarget(q);
							 | 
						|
								            }
							 | 
						|
								            break;
							 | 
						|
								          case TM_ZOOM :
							 | 
						|
								            mCamera.zoom(dy*100);
							 | 
						|
								            break;
							 | 
						|
								          case TM_FLY_Z :
							 | 
						|
								            mCamera.localTranslate(Vector3f(0, 0, -dy*200));
							 | 
						|
								            break;
							 | 
						|
								          case TM_FLY_PAN :
							 | 
						|
								            mCamera.localTranslate(Vector3f(dx*200, dy*200, 0));
							 | 
						|
								            break;
							 | 
						|
								          default:
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        updateGL();
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    mMouseCoords = Vector2i(e->pos().x(), e->pos().y());
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::paintGL()
							 | 
						|
								{
							 | 
						|
								  glEnable(GL_DEPTH_TEST);
							 | 
						|
								  glDisable(GL_CULL_FACE);
							 | 
						|
								  glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
							 | 
						|
								  glDisable(GL_COLOR_MATERIAL);
							 | 
						|
								  glDisable(GL_BLEND);
							 | 
						|
								  glDisable(GL_ALPHA_TEST);
							 | 
						|
								  glDisable(GL_TEXTURE_1D);
							 | 
						|
								  glDisable(GL_TEXTURE_2D);
							 | 
						|
								  glDisable(GL_TEXTURE_3D);
							 | 
						|
								
							 | 
						|
								  // Clear buffers
							 | 
						|
								  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
							 | 
						|
								
							 | 
						|
								  mCamera.activateGL();
							 | 
						|
								
							 | 
						|
								  drawScene();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::initializeGL()
							 | 
						|
								{
							 | 
						|
								  glClearColor(1., 1., 1., 0.);
							 | 
						|
								  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
							 | 
						|
								  glDepthMask(GL_TRUE);
							 | 
						|
								  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
							 | 
						|
								
							 | 
						|
								  mCamera.setPosition(Vector3f(-200, -200, -200));
							 | 
						|
								  mCamera.setTarget(Vector3f(0, 0, 0));
							 | 
						|
								  mInitFrame.orientation = mCamera.orientation().inverse();
							 | 
						|
								  mInitFrame.position = mCamera.viewMatrix().translation();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::resizeGL(int width, int height)
							 | 
						|
								{
							 | 
						|
								    mCamera.setViewport(width,height);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::setNavMode(int m)
							 | 
						|
								{
							 | 
						|
								  mNavMode = NavMode(m);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::setLerpMode(int m)
							 | 
						|
								{
							 | 
						|
								  mLerpMode = LerpMode(m);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::setRotationMode(int m)
							 | 
						|
								{
							 | 
						|
								  mRotationMode = RotationMode(m);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void RenderingWidget::resetCamera()
							 | 
						|
								{
							 | 
						|
								  if (mAnimate)
							 | 
						|
								    stopAnimation();
							 | 
						|
								  m_timeline.clear();
							 | 
						|
								  Frame aux0 = mCamera.frame();
							 | 
						|
								  aux0.orientation = aux0.orientation.inverse();
							 | 
						|
								  aux0.position = mCamera.viewMatrix().translation();
							 | 
						|
								  m_timeline[0] = aux0;
							 | 
						|
								
							 | 
						|
								  Vector3f currentTarget = mCamera.target();
							 | 
						|
								  mCamera.setTarget(Vector3f::Zero());
							 | 
						|
								
							 | 
						|
								  // compute the rotation duration to move the camera to the target
							 | 
						|
								  Frame aux1 = mCamera.frame();
							 | 
						|
								  aux1.orientation = aux1.orientation.inverse();
							 | 
						|
								  aux1.position = mCamera.viewMatrix().translation();
							 | 
						|
								  float duration = aux0.orientation.angularDistance(aux1.orientation) * 0.9;
							 | 
						|
								  if (duration<0.1) duration = 0.1;
							 | 
						|
								
							 | 
						|
								  // put the camera at that time step:
							 | 
						|
								  aux1 = aux0.lerp(duration/2,mInitFrame);
							 | 
						|
								  // and make it look at the target again
							 | 
						|
								  aux1.orientation = aux1.orientation.inverse();
							 | 
						|
								  aux1.position = - (aux1.orientation * aux1.position);
							 | 
						|
								  mCamera.setFrame(aux1);
							 | 
						|
								  mCamera.setTarget(Vector3f::Zero());
							 | 
						|
								
							 | 
						|
								  // add this camera keyframe
							 | 
						|
								  aux1.orientation = aux1.orientation.inverse();
							 | 
						|
								  aux1.position = mCamera.viewMatrix().translation();
							 | 
						|
								  m_timeline[duration] = aux1;
							 | 
						|
								
							 | 
						|
								  m_timeline[2] = mInitFrame;
							 | 
						|
								  m_alpha = 0;
							 | 
						|
								  animate();
							 | 
						|
								  connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
							 | 
						|
								  m_timer.start(1000/30);
							 | 
						|
								  mAnimate = true;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								QWidget* RenderingWidget::createNavigationControlWidget()
							 | 
						|
								{
							 | 
						|
								  QWidget* panel = new QWidget();
							 | 
						|
								  QVBoxLayout* layout = new QVBoxLayout();
							 | 
						|
								
							 | 
						|
								  {
							 | 
						|
								    QPushButton* but = new QPushButton("reset");
							 | 
						|
								    but->setToolTip("move the camera to initial position (with animation)");
							 | 
						|
								    layout->addWidget(but);
							 | 
						|
								    connect(but, SIGNAL(clicked()), this, SLOT(resetCamera()));
							 | 
						|
								  }
							 | 
						|
								  {
							 | 
						|
								    // navigation mode
							 | 
						|
								    QGroupBox* box = new QGroupBox("navigation mode");
							 | 
						|
								    QVBoxLayout* boxLayout = new QVBoxLayout;
							 | 
						|
								    QButtonGroup* group = new QButtonGroup(panel);
							 | 
						|
								    QRadioButton* but;
							 | 
						|
								    but = new QRadioButton("turn around");
							 | 
						|
								    but->setToolTip("look around an object");
							 | 
						|
								    group->addButton(but, NavTurnAround);
							 | 
						|
								    boxLayout->addWidget(but);
							 | 
						|
								    but = new QRadioButton("fly");
							 | 
						|
								    but->setToolTip("free navigation like a spaceship\n(this mode can also be enabled pressing the \"shift\" key)");
							 | 
						|
								    group->addButton(but, NavFly);
							 | 
						|
								    boxLayout->addWidget(but);
							 | 
						|
								    group->button(mNavMode)->setChecked(true);
							 | 
						|
								    connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setNavMode(int)));
							 | 
						|
								    box->setLayout(boxLayout);
							 | 
						|
								    layout->addWidget(box);
							 | 
						|
								  }
							 | 
						|
								  {
							 | 
						|
								    // track ball, rotation mode
							 | 
						|
								    QGroupBox* box = new QGroupBox("rotation mode");
							 | 
						|
								    QVBoxLayout* boxLayout = new QVBoxLayout;
							 | 
						|
								    QButtonGroup* group = new QButtonGroup(panel);
							 | 
						|
								    QRadioButton* but;
							 | 
						|
								    but = new QRadioButton("stable trackball");
							 | 
						|
								    group->addButton(but, RotationStable);
							 | 
						|
								    boxLayout->addWidget(but);
							 | 
						|
								    but->setToolTip("use the stable trackball implementation mapping\nthe 2D coordinates to 3D points on a sphere");
							 | 
						|
								    but = new QRadioButton("standard rotation");
							 | 
						|
								    group->addButton(but, RotationStandard);
							 | 
						|
								    boxLayout->addWidget(but);
							 | 
						|
								    but->setToolTip("standard approach mapping the x and y displacements\nas rotations around the camera's X and Y axes");
							 | 
						|
								    group->button(mRotationMode)->setChecked(true);
							 | 
						|
								    connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setRotationMode(int)));
							 | 
						|
								    box->setLayout(boxLayout);
							 | 
						|
								    layout->addWidget(box);
							 | 
						|
								  }
							 | 
						|
								  {
							 | 
						|
								    // interpolation mode
							 | 
						|
								    QGroupBox* box = new QGroupBox("spherical interpolation");
							 | 
						|
								    QVBoxLayout* boxLayout = new QVBoxLayout;
							 | 
						|
								    QButtonGroup* group = new QButtonGroup(panel);
							 | 
						|
								    QRadioButton* but;
							 | 
						|
								    but = new QRadioButton("quaternion slerp");
							 | 
						|
								    group->addButton(but, LerpQuaternion);
							 | 
						|
								    boxLayout->addWidget(but);
							 | 
						|
								    but->setToolTip("use quaternion spherical interpolation\nto interpolate orientations");
							 | 
						|
								    but = new QRadioButton("euler angles");
							 | 
						|
								    group->addButton(but, LerpEulerAngles);
							 | 
						|
								    boxLayout->addWidget(but);
							 | 
						|
								    but->setToolTip("use Euler angles to interpolate orientations");
							 | 
						|
								    group->button(mNavMode)->setChecked(true);
							 | 
						|
								    connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setLerpMode(int)));
							 | 
						|
								    box->setLayout(boxLayout);
							 | 
						|
								    layout->addWidget(box);
							 | 
						|
								  }
							 | 
						|
								  layout->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
							 | 
						|
								  panel->setLayout(layout);
							 | 
						|
								  return panel;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								QuaternionDemo::QuaternionDemo()
							 | 
						|
								{
							 | 
						|
								  mRenderingWidget = new RenderingWidget();
							 | 
						|
								  setCentralWidget(mRenderingWidget);
							 | 
						|
								
							 | 
						|
								  QDockWidget* panel = new QDockWidget("navigation", this);
							 | 
						|
								  panel->setAllowedAreas((QFlags<Qt::DockWidgetArea>)(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea));
							 | 
						|
								  addDockWidget(Qt::RightDockWidgetArea, panel);
							 | 
						|
								  panel->setWidget(mRenderingWidget->createNavigationControlWidget());
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								int main(int argc, char *argv[])
							 | 
						|
								{
							 | 
						|
								  std::cout << "Navigation:\n";
							 | 
						|
								  std::cout << "  left button:           rotate around the target\n";
							 | 
						|
								  std::cout << "  middle button:         zoom\n";
							 | 
						|
								  std::cout << "  left button + ctrl     quake rotate (rotate around camera position)\n";
							 | 
						|
								  std::cout << "  middle button + ctrl   walk (progress along camera's z direction)\n";
							 | 
						|
								  std::cout << "  left button:           pan (translate in the XY camera's plane)\n\n";
							 | 
						|
								  std::cout << "R : move the camera to initial position\n";
							 | 
						|
								  std::cout << "A : start/stop animation\n";
							 | 
						|
								  std::cout << "C : clear the animation\n";
							 | 
						|
								  std::cout << "G : add a key frame\n";
							 | 
						|
								
							 | 
						|
								  QApplication app(argc, argv);
							 | 
						|
								  QuaternionDemo demo;
							 | 
						|
								  demo.resize(600,500);
							 | 
						|
								  demo.show();
							 | 
						|
								  return app.exec();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								#include "quaternion_demo.moc"
							 | 
						|
								
							 |