xref: /aosp_15_r20/external/eigen/demos/opengl/quaternion_demo.cpp (revision bf2c37156dfe67e5dfebd6d394bad8b2ab5804d4)
1*bf2c3715SXin Li // This file is part of Eigen, a lightweight C++ template library
2*bf2c3715SXin Li // for linear algebra.
3*bf2c3715SXin Li //
4*bf2c3715SXin Li // Copyright (C) 2008 Gael Guennebaud <[email protected]>
5*bf2c3715SXin Li //
6*bf2c3715SXin Li // This Source Code Form is subject to the terms of the Mozilla
7*bf2c3715SXin Li // Public License v. 2.0. If a copy of the MPL was not distributed
8*bf2c3715SXin Li // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9*bf2c3715SXin Li 
10*bf2c3715SXin Li #include "quaternion_demo.h"
11*bf2c3715SXin Li #include "icosphere.h"
12*bf2c3715SXin Li 
13*bf2c3715SXin Li #include <Eigen/Geometry>
14*bf2c3715SXin Li #include <Eigen/QR>
15*bf2c3715SXin Li #include <Eigen/LU>
16*bf2c3715SXin Li 
17*bf2c3715SXin Li #include <iostream>
18*bf2c3715SXin Li #include <QEvent>
19*bf2c3715SXin Li #include <QMouseEvent>
20*bf2c3715SXin Li #include <QInputDialog>
21*bf2c3715SXin Li #include <QGridLayout>
22*bf2c3715SXin Li #include <QButtonGroup>
23*bf2c3715SXin Li #include <QRadioButton>
24*bf2c3715SXin Li #include <QDockWidget>
25*bf2c3715SXin Li #include <QPushButton>
26*bf2c3715SXin Li #include <QGroupBox>
27*bf2c3715SXin Li 
28*bf2c3715SXin Li using namespace Eigen;
29*bf2c3715SXin Li 
30*bf2c3715SXin Li class FancySpheres
31*bf2c3715SXin Li {
32*bf2c3715SXin Li   public:
33*bf2c3715SXin Li     EIGEN_MAKE_ALIGNED_OPERATOR_NEW
34*bf2c3715SXin Li 
FancySpheres()35*bf2c3715SXin Li     FancySpheres()
36*bf2c3715SXin Li     {
37*bf2c3715SXin Li       const int levels = 4;
38*bf2c3715SXin Li       const float scale = 0.33;
39*bf2c3715SXin Li       float radius = 100;
40*bf2c3715SXin Li       std::vector<int> parents;
41*bf2c3715SXin Li 
42*bf2c3715SXin Li       // leval 0
43*bf2c3715SXin Li       mCenters.push_back(Vector3f::Zero());
44*bf2c3715SXin Li       parents.push_back(-1);
45*bf2c3715SXin Li       mRadii.push_back(radius);
46*bf2c3715SXin Li 
47*bf2c3715SXin Li       // generate level 1 using icosphere vertices
48*bf2c3715SXin Li       radius *= 0.45;
49*bf2c3715SXin Li       {
50*bf2c3715SXin Li         float dist = mRadii[0]*0.9;
51*bf2c3715SXin Li         for (int i=0; i<12; ++i)
52*bf2c3715SXin Li         {
53*bf2c3715SXin Li           mCenters.push_back(mIcoSphere.vertices()[i] * dist);
54*bf2c3715SXin Li           mRadii.push_back(radius);
55*bf2c3715SXin Li           parents.push_back(0);
56*bf2c3715SXin Li         }
57*bf2c3715SXin Li       }
58*bf2c3715SXin Li 
59*bf2c3715SXin Li       static const float angles [10] = {
60*bf2c3715SXin Li         0, 0,
61*bf2c3715SXin Li         M_PI, 0.*M_PI,
62*bf2c3715SXin Li         M_PI, 0.5*M_PI,
63*bf2c3715SXin Li         M_PI, 1.*M_PI,
64*bf2c3715SXin Li         M_PI, 1.5*M_PI
65*bf2c3715SXin Li       };
66*bf2c3715SXin Li 
67*bf2c3715SXin Li       // generate other levels
68*bf2c3715SXin Li       int start = 1;
69*bf2c3715SXin Li       for (int l=1; l<levels; l++)
70*bf2c3715SXin Li       {
71*bf2c3715SXin Li         radius *= scale;
72*bf2c3715SXin Li         int end = mCenters.size();
73*bf2c3715SXin Li         for (int i=start; i<end; ++i)
74*bf2c3715SXin Li         {
75*bf2c3715SXin Li           Vector3f c = mCenters[i];
76*bf2c3715SXin Li           Vector3f ax0 = (c - mCenters[parents[i]]).normalized();
77*bf2c3715SXin Li           Vector3f ax1 = ax0.unitOrthogonal();
78*bf2c3715SXin Li           Quaternionf q;
79*bf2c3715SXin Li           q.setFromTwoVectors(Vector3f::UnitZ(), ax0);
80*bf2c3715SXin Li           Affine3f t = Translation3f(c) * q * Scaling(mRadii[i]+radius);
81*bf2c3715SXin Li           for (int j=0; j<5; ++j)
82*bf2c3715SXin Li           {
83*bf2c3715SXin Li             Vector3f newC = c + ( (AngleAxisf(angles[j*2+1], ax0)
84*bf2c3715SXin Li                                 * AngleAxisf(angles[j*2+0] * (l==1 ? 0.35 : 0.5), ax1)) * ax0)
85*bf2c3715SXin Li                                 * (mRadii[i] + radius*0.8);
86*bf2c3715SXin Li             mCenters.push_back(newC);
87*bf2c3715SXin Li             mRadii.push_back(radius);
88*bf2c3715SXin Li             parents.push_back(i);
89*bf2c3715SXin Li           }
90*bf2c3715SXin Li         }
91*bf2c3715SXin Li         start = end;
92*bf2c3715SXin Li       }
93*bf2c3715SXin Li     }
94*bf2c3715SXin Li 
draw()95*bf2c3715SXin Li     void draw()
96*bf2c3715SXin Li     {
97*bf2c3715SXin Li       int end = mCenters.size();
98*bf2c3715SXin Li       glEnable(GL_NORMALIZE);
99*bf2c3715SXin Li       for (int i=0; i<end; ++i)
100*bf2c3715SXin Li       {
101*bf2c3715SXin Li         Affine3f t = Translation3f(mCenters[i]) * Scaling(mRadii[i]);
102*bf2c3715SXin Li         gpu.pushMatrix(GL_MODELVIEW);
103*bf2c3715SXin Li         gpu.multMatrix(t.matrix(),GL_MODELVIEW);
104*bf2c3715SXin Li         mIcoSphere.draw(2);
105*bf2c3715SXin Li         gpu.popMatrix(GL_MODELVIEW);
106*bf2c3715SXin Li       }
107*bf2c3715SXin Li       glDisable(GL_NORMALIZE);
108*bf2c3715SXin Li     }
109*bf2c3715SXin Li   protected:
110*bf2c3715SXin Li     std::vector<Vector3f> mCenters;
111*bf2c3715SXin Li     std::vector<float> mRadii;
112*bf2c3715SXin Li     IcoSphere mIcoSphere;
113*bf2c3715SXin Li };
114*bf2c3715SXin Li 
115*bf2c3715SXin Li 
116*bf2c3715SXin Li // generic linear interpolation method
lerp(float t,const T & a,const T & b)117*bf2c3715SXin Li template<typename T> T lerp(float t, const T& a, const T& b)
118*bf2c3715SXin Li {
119*bf2c3715SXin Li   return a*(1-t) + b*t;
120*bf2c3715SXin Li }
121*bf2c3715SXin Li 
122*bf2c3715SXin Li // quaternion slerp
lerp(float t,const Quaternionf & a,const Quaternionf & b)123*bf2c3715SXin Li template<> Quaternionf lerp(float t, const Quaternionf& a, const Quaternionf& b)
124*bf2c3715SXin Li { return a.slerp(t,b); }
125*bf2c3715SXin Li 
126*bf2c3715SXin Li // linear interpolation of a frame using the type OrientationType
127*bf2c3715SXin Li // to perform the interpolation of the orientations
128*bf2c3715SXin Li template<typename OrientationType>
lerpFrame(float alpha,const Frame & a,const Frame & b)129*bf2c3715SXin Li inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b)
130*bf2c3715SXin Li {
131*bf2c3715SXin Li   return Frame(lerp(alpha,a.position,b.position),
132*bf2c3715SXin Li                Quaternionf(lerp(alpha,OrientationType(a.orientation),OrientationType(b.orientation))));
133*bf2c3715SXin Li }
134*bf2c3715SXin Li 
135*bf2c3715SXin Li template<typename _Scalar> class EulerAngles
136*bf2c3715SXin Li {
137*bf2c3715SXin Li public:
138*bf2c3715SXin Li   enum { Dim = 3 };
139*bf2c3715SXin Li   typedef _Scalar Scalar;
140*bf2c3715SXin Li   typedef Matrix<Scalar,3,3> Matrix3;
141*bf2c3715SXin Li   typedef Matrix<Scalar,3,1> Vector3;
142*bf2c3715SXin Li   typedef Quaternion<Scalar> QuaternionType;
143*bf2c3715SXin Li 
144*bf2c3715SXin Li protected:
145*bf2c3715SXin Li 
146*bf2c3715SXin Li   Vector3 m_angles;
147*bf2c3715SXin Li 
148*bf2c3715SXin Li public:
149*bf2c3715SXin Li 
EulerAngles()150*bf2c3715SXin Li   EulerAngles() {}
EulerAngles(Scalar a0,Scalar a1,Scalar a2)151*bf2c3715SXin Li   inline EulerAngles(Scalar a0, Scalar a1, Scalar a2) : m_angles(a0, a1, a2) {}
EulerAngles(const QuaternionType & q)152*bf2c3715SXin Li   inline EulerAngles(const QuaternionType& q) { *this = q; }
153*bf2c3715SXin Li 
coeffs() const154*bf2c3715SXin Li   const Vector3& coeffs() const { return m_angles; }
coeffs()155*bf2c3715SXin Li   Vector3& coeffs() { return m_angles; }
156*bf2c3715SXin Li 
operator =(const QuaternionType & q)157*bf2c3715SXin Li   EulerAngles& operator=(const QuaternionType& q)
158*bf2c3715SXin Li   {
159*bf2c3715SXin Li     Matrix3 m = q.toRotationMatrix();
160*bf2c3715SXin Li     return *this = m;
161*bf2c3715SXin Li   }
162*bf2c3715SXin Li 
operator =(const Matrix3 & m)163*bf2c3715SXin Li   EulerAngles& operator=(const Matrix3& m)
164*bf2c3715SXin Li   {
165*bf2c3715SXin Li     // mat =  cy*cz          -cy*sz           sy
166*bf2c3715SXin Li     //        cz*sx*sy+cx*sz  cx*cz-sx*sy*sz -cy*sx
167*bf2c3715SXin Li     //       -cx*cz*sy+sx*sz  cz*sx+cx*sy*sz  cx*cy
168*bf2c3715SXin Li     m_angles.coeffRef(1) = std::asin(m.coeff(0,2));
169*bf2c3715SXin Li     m_angles.coeffRef(0) = std::atan2(-m.coeff(1,2),m.coeff(2,2));
170*bf2c3715SXin Li     m_angles.coeffRef(2) = std::atan2(-m.coeff(0,1),m.coeff(0,0));
171*bf2c3715SXin Li     return *this;
172*bf2c3715SXin Li   }
173*bf2c3715SXin Li 
toRotationMatrix(void) const174*bf2c3715SXin Li   Matrix3 toRotationMatrix(void) const
175*bf2c3715SXin Li   {
176*bf2c3715SXin Li     Vector3 c = m_angles.array().cos();
177*bf2c3715SXin Li     Vector3 s = m_angles.array().sin();
178*bf2c3715SXin Li     Matrix3 res;
179*bf2c3715SXin Li     res <<  c.y()*c.z(),                    -c.y()*s.z(),                   s.y(),
180*bf2c3715SXin Li             c.z()*s.x()*s.y()+c.x()*s.z(),  c.x()*c.z()-s.x()*s.y()*s.z(),  -c.y()*s.x(),
181*bf2c3715SXin Li             -c.x()*c.z()*s.y()+s.x()*s.z(), c.z()*s.x()+c.x()*s.y()*s.z(),  c.x()*c.y();
182*bf2c3715SXin Li     return res;
183*bf2c3715SXin Li   }
184*bf2c3715SXin Li 
operator QuaternionType()185*bf2c3715SXin Li   operator QuaternionType() { return QuaternionType(toRotationMatrix()); }
186*bf2c3715SXin Li };
187*bf2c3715SXin Li 
188*bf2c3715SXin Li // Euler angles slerp
lerp(float t,const EulerAngles<float> & a,const EulerAngles<float> & b)189*bf2c3715SXin Li template<> EulerAngles<float> lerp(float t, const EulerAngles<float>& a, const EulerAngles<float>& b)
190*bf2c3715SXin Li {
191*bf2c3715SXin Li   EulerAngles<float> res;
192*bf2c3715SXin Li   res.coeffs() = lerp(t, a.coeffs(), b.coeffs());
193*bf2c3715SXin Li   return res;
194*bf2c3715SXin Li }
195*bf2c3715SXin Li 
196*bf2c3715SXin Li 
RenderingWidget()197*bf2c3715SXin Li RenderingWidget::RenderingWidget()
198*bf2c3715SXin Li {
199*bf2c3715SXin Li   mAnimate = false;
200*bf2c3715SXin Li   mCurrentTrackingMode = TM_NO_TRACK;
201*bf2c3715SXin Li   mNavMode = NavTurnAround;
202*bf2c3715SXin Li   mLerpMode = LerpQuaternion;
203*bf2c3715SXin Li   mRotationMode = RotationStable;
204*bf2c3715SXin Li   mTrackball.setCamera(&mCamera);
205*bf2c3715SXin Li 
206*bf2c3715SXin Li   // required to capture key press events
207*bf2c3715SXin Li   setFocusPolicy(Qt::ClickFocus);
208*bf2c3715SXin Li }
209*bf2c3715SXin Li 
grabFrame(void)210*bf2c3715SXin Li void RenderingWidget::grabFrame(void)
211*bf2c3715SXin Li {
212*bf2c3715SXin Li     // ask user for a time
213*bf2c3715SXin Li     bool ok = false;
214*bf2c3715SXin Li     double t = 0;
215*bf2c3715SXin Li     if (!m_timeline.empty())
216*bf2c3715SXin Li       t = (--m_timeline.end())->first + 1.;
217*bf2c3715SXin Li     t = QInputDialog::getDouble(this, "Eigen's RenderingWidget", "time value: ",
218*bf2c3715SXin Li       t, 0, 1e3, 1, &ok);
219*bf2c3715SXin Li     if (ok)
220*bf2c3715SXin Li     {
221*bf2c3715SXin Li       Frame aux;
222*bf2c3715SXin Li       aux.orientation = mCamera.viewMatrix().linear();
223*bf2c3715SXin Li       aux.position = mCamera.viewMatrix().translation();
224*bf2c3715SXin Li       m_timeline[t] = aux;
225*bf2c3715SXin Li     }
226*bf2c3715SXin Li }
227*bf2c3715SXin Li 
drawScene()228*bf2c3715SXin Li void RenderingWidget::drawScene()
229*bf2c3715SXin Li {
230*bf2c3715SXin Li   static FancySpheres sFancySpheres;
231*bf2c3715SXin Li   float length = 50;
232*bf2c3715SXin Li   gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitX(), Color(1,0,0,1));
233*bf2c3715SXin Li   gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitY(), Color(0,1,0,1));
234*bf2c3715SXin Li   gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitZ(), Color(0,0,1,1));
235*bf2c3715SXin Li 
236*bf2c3715SXin Li   // draw the fractal object
237*bf2c3715SXin Li   float sqrt3 = std::sqrt(3.);
238*bf2c3715SXin Li   glLightfv(GL_LIGHT0, GL_AMBIENT, Vector4f(0.5,0.5,0.5,1).data());
239*bf2c3715SXin Li   glLightfv(GL_LIGHT0, GL_DIFFUSE, Vector4f(0.5,1,0.5,1).data());
240*bf2c3715SXin Li   glLightfv(GL_LIGHT0, GL_SPECULAR, Vector4f(1,1,1,1).data());
241*bf2c3715SXin Li   glLightfv(GL_LIGHT0, GL_POSITION, Vector4f(-sqrt3,-sqrt3,sqrt3,0).data());
242*bf2c3715SXin Li 
243*bf2c3715SXin Li   glLightfv(GL_LIGHT1, GL_AMBIENT, Vector4f(0,0,0,1).data());
244*bf2c3715SXin Li   glLightfv(GL_LIGHT1, GL_DIFFUSE, Vector4f(1,0.5,0.5,1).data());
245*bf2c3715SXin Li   glLightfv(GL_LIGHT1, GL_SPECULAR, Vector4f(1,1,1,1).data());
246*bf2c3715SXin Li   glLightfv(GL_LIGHT1, GL_POSITION, Vector4f(-sqrt3,sqrt3,-sqrt3,0).data());
247*bf2c3715SXin Li 
248*bf2c3715SXin Li   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Vector4f(0.7, 0.7, 0.7, 1).data());
249*bf2c3715SXin Li   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Vector4f(0.8, 0.75, 0.6, 1).data());
250*bf2c3715SXin Li   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Vector4f(1, 1, 1, 1).data());
251*bf2c3715SXin Li   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 64);
252*bf2c3715SXin Li 
253*bf2c3715SXin Li   glEnable(GL_LIGHTING);
254*bf2c3715SXin Li   glEnable(GL_LIGHT0);
255*bf2c3715SXin Li   glEnable(GL_LIGHT1);
256*bf2c3715SXin Li 
257*bf2c3715SXin Li   sFancySpheres.draw();
258*bf2c3715SXin Li   glVertexPointer(3, GL_FLOAT, 0, mVertices[0].data());
259*bf2c3715SXin Li   glNormalPointer(GL_FLOAT, 0, mNormals[0].data());
260*bf2c3715SXin Li   glEnableClientState(GL_VERTEX_ARRAY);
261*bf2c3715SXin Li   glEnableClientState(GL_NORMAL_ARRAY);
262*bf2c3715SXin Li   glDrawArrays(GL_TRIANGLES, 0, mVertices.size());
263*bf2c3715SXin Li   glDisableClientState(GL_VERTEX_ARRAY);
264*bf2c3715SXin Li   glDisableClientState(GL_NORMAL_ARRAY);
265*bf2c3715SXin Li 
266*bf2c3715SXin Li   glDisable(GL_LIGHTING);
267*bf2c3715SXin Li }
268*bf2c3715SXin Li 
animate()269*bf2c3715SXin Li void RenderingWidget::animate()
270*bf2c3715SXin Li {
271*bf2c3715SXin Li   m_alpha += double(m_timer.interval()) * 1e-3;
272*bf2c3715SXin Li 
273*bf2c3715SXin Li   TimeLine::const_iterator hi = m_timeline.upper_bound(m_alpha);
274*bf2c3715SXin Li   TimeLine::const_iterator lo = hi;
275*bf2c3715SXin Li   --lo;
276*bf2c3715SXin Li 
277*bf2c3715SXin Li   Frame currentFrame;
278*bf2c3715SXin Li 
279*bf2c3715SXin Li   if(hi==m_timeline.end())
280*bf2c3715SXin Li   {
281*bf2c3715SXin Li     // end
282*bf2c3715SXin Li     currentFrame = lo->second;
283*bf2c3715SXin Li     stopAnimation();
284*bf2c3715SXin Li   }
285*bf2c3715SXin Li   else if(hi==m_timeline.begin())
286*bf2c3715SXin Li   {
287*bf2c3715SXin Li     // start
288*bf2c3715SXin Li     currentFrame = hi->second;
289*bf2c3715SXin Li   }
290*bf2c3715SXin Li   else
291*bf2c3715SXin Li   {
292*bf2c3715SXin Li     float s = (m_alpha - lo->first)/(hi->first - lo->first);
293*bf2c3715SXin Li     if (mLerpMode==LerpEulerAngles)
294*bf2c3715SXin Li       currentFrame = ::lerpFrame<EulerAngles<float> >(s, lo->second, hi->second);
295*bf2c3715SXin Li     else if (mLerpMode==LerpQuaternion)
296*bf2c3715SXin Li       currentFrame = ::lerpFrame<Eigen::Quaternionf>(s, lo->second, hi->second);
297*bf2c3715SXin Li     else
298*bf2c3715SXin Li     {
299*bf2c3715SXin Li       std::cerr << "Invalid rotation interpolation mode (abort)\n";
300*bf2c3715SXin Li       exit(2);
301*bf2c3715SXin Li     }
302*bf2c3715SXin Li     currentFrame.orientation.coeffs().normalize();
303*bf2c3715SXin Li   }
304*bf2c3715SXin Li 
305*bf2c3715SXin Li   currentFrame.orientation = currentFrame.orientation.inverse();
306*bf2c3715SXin Li   currentFrame.position = - (currentFrame.orientation * currentFrame.position);
307*bf2c3715SXin Li   mCamera.setFrame(currentFrame);
308*bf2c3715SXin Li 
309*bf2c3715SXin Li   updateGL();
310*bf2c3715SXin Li }
311*bf2c3715SXin Li 
keyPressEvent(QKeyEvent * e)312*bf2c3715SXin Li void RenderingWidget::keyPressEvent(QKeyEvent * e)
313*bf2c3715SXin Li {
314*bf2c3715SXin Li     switch(e->key())
315*bf2c3715SXin Li     {
316*bf2c3715SXin Li       case Qt::Key_Up:
317*bf2c3715SXin Li         mCamera.zoom(2);
318*bf2c3715SXin Li         break;
319*bf2c3715SXin Li       case Qt::Key_Down:
320*bf2c3715SXin Li         mCamera.zoom(-2);
321*bf2c3715SXin Li         break;
322*bf2c3715SXin Li       // add a frame
323*bf2c3715SXin Li       case Qt::Key_G:
324*bf2c3715SXin Li         grabFrame();
325*bf2c3715SXin Li         break;
326*bf2c3715SXin Li       // clear the time line
327*bf2c3715SXin Li       case Qt::Key_C:
328*bf2c3715SXin Li         m_timeline.clear();
329*bf2c3715SXin Li         break;
330*bf2c3715SXin Li       // move the camera to initial pos
331*bf2c3715SXin Li       case Qt::Key_R:
332*bf2c3715SXin Li         resetCamera();
333*bf2c3715SXin Li         break;
334*bf2c3715SXin Li       // start/stop the animation
335*bf2c3715SXin Li       case Qt::Key_A:
336*bf2c3715SXin Li         if (mAnimate)
337*bf2c3715SXin Li         {
338*bf2c3715SXin Li           stopAnimation();
339*bf2c3715SXin Li         }
340*bf2c3715SXin Li         else
341*bf2c3715SXin Li         {
342*bf2c3715SXin Li           m_alpha = 0;
343*bf2c3715SXin Li           connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
344*bf2c3715SXin Li           m_timer.start(1000/30);
345*bf2c3715SXin Li           mAnimate = true;
346*bf2c3715SXin Li         }
347*bf2c3715SXin Li         break;
348*bf2c3715SXin Li       default:
349*bf2c3715SXin Li         break;
350*bf2c3715SXin Li     }
351*bf2c3715SXin Li 
352*bf2c3715SXin Li     updateGL();
353*bf2c3715SXin Li }
354*bf2c3715SXin Li 
stopAnimation()355*bf2c3715SXin Li void RenderingWidget::stopAnimation()
356*bf2c3715SXin Li {
357*bf2c3715SXin Li   disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
358*bf2c3715SXin Li   m_timer.stop();
359*bf2c3715SXin Li   mAnimate = false;
360*bf2c3715SXin Li   m_alpha = 0;
361*bf2c3715SXin Li }
362*bf2c3715SXin Li 
mousePressEvent(QMouseEvent * e)363*bf2c3715SXin Li void RenderingWidget::mousePressEvent(QMouseEvent* e)
364*bf2c3715SXin Li {
365*bf2c3715SXin Li   mMouseCoords = Vector2i(e->pos().x(), e->pos().y());
366*bf2c3715SXin Li   bool fly = (mNavMode==NavFly) || (e->modifiers()&Qt::ControlModifier);
367*bf2c3715SXin Li   switch(e->button())
368*bf2c3715SXin Li   {
369*bf2c3715SXin Li     case Qt::LeftButton:
370*bf2c3715SXin Li       if(fly)
371*bf2c3715SXin Li       {
372*bf2c3715SXin Li         mCurrentTrackingMode = TM_LOCAL_ROTATE;
373*bf2c3715SXin Li         mTrackball.start(Trackball::Local);
374*bf2c3715SXin Li       }
375*bf2c3715SXin Li       else
376*bf2c3715SXin Li       {
377*bf2c3715SXin Li         mCurrentTrackingMode = TM_ROTATE_AROUND;
378*bf2c3715SXin Li         mTrackball.start(Trackball::Around);
379*bf2c3715SXin Li       }
380*bf2c3715SXin Li       mTrackball.track(mMouseCoords);
381*bf2c3715SXin Li       break;
382*bf2c3715SXin Li     case Qt::MidButton:
383*bf2c3715SXin Li       if(fly)
384*bf2c3715SXin Li         mCurrentTrackingMode = TM_FLY_Z;
385*bf2c3715SXin Li       else
386*bf2c3715SXin Li         mCurrentTrackingMode = TM_ZOOM;
387*bf2c3715SXin Li       break;
388*bf2c3715SXin Li     case Qt::RightButton:
389*bf2c3715SXin Li         mCurrentTrackingMode = TM_FLY_PAN;
390*bf2c3715SXin Li       break;
391*bf2c3715SXin Li     default:
392*bf2c3715SXin Li       break;
393*bf2c3715SXin Li   }
394*bf2c3715SXin Li }
mouseReleaseEvent(QMouseEvent *)395*bf2c3715SXin Li void RenderingWidget::mouseReleaseEvent(QMouseEvent*)
396*bf2c3715SXin Li {
397*bf2c3715SXin Li     mCurrentTrackingMode = TM_NO_TRACK;
398*bf2c3715SXin Li     updateGL();
399*bf2c3715SXin Li }
400*bf2c3715SXin Li 
mouseMoveEvent(QMouseEvent * e)401*bf2c3715SXin Li void RenderingWidget::mouseMoveEvent(QMouseEvent* e)
402*bf2c3715SXin Li {
403*bf2c3715SXin Li     // tracking
404*bf2c3715SXin Li     if(mCurrentTrackingMode != TM_NO_TRACK)
405*bf2c3715SXin Li     {
406*bf2c3715SXin Li         float dx =   float(e->x() - mMouseCoords.x()) / float(mCamera.vpWidth());
407*bf2c3715SXin Li         float dy = - float(e->y() - mMouseCoords.y()) / float(mCamera.vpHeight());
408*bf2c3715SXin Li 
409*bf2c3715SXin Li         // speedup the transformations
410*bf2c3715SXin Li         if(e->modifiers() & Qt::ShiftModifier)
411*bf2c3715SXin Li         {
412*bf2c3715SXin Li           dx *= 10.;
413*bf2c3715SXin Li           dy *= 10.;
414*bf2c3715SXin Li         }
415*bf2c3715SXin Li 
416*bf2c3715SXin Li         switch(mCurrentTrackingMode)
417*bf2c3715SXin Li         {
418*bf2c3715SXin Li           case TM_ROTATE_AROUND:
419*bf2c3715SXin Li           case TM_LOCAL_ROTATE:
420*bf2c3715SXin Li             if (mRotationMode==RotationStable)
421*bf2c3715SXin Li             {
422*bf2c3715SXin Li               // use the stable trackball implementation mapping
423*bf2c3715SXin Li               // the 2D coordinates to 3D points on a sphere.
424*bf2c3715SXin Li               mTrackball.track(Vector2i(e->pos().x(), e->pos().y()));
425*bf2c3715SXin Li             }
426*bf2c3715SXin Li             else
427*bf2c3715SXin Li             {
428*bf2c3715SXin Li               // standard approach mapping the x and y displacements as rotations
429*bf2c3715SXin Li               // around the camera's X and Y axes.
430*bf2c3715SXin Li               Quaternionf q = AngleAxisf( dx*M_PI, Vector3f::UnitY())
431*bf2c3715SXin Li                             * AngleAxisf(-dy*M_PI, Vector3f::UnitX());
432*bf2c3715SXin Li               if (mCurrentTrackingMode==TM_LOCAL_ROTATE)
433*bf2c3715SXin Li                 mCamera.localRotate(q);
434*bf2c3715SXin Li               else
435*bf2c3715SXin Li                 mCamera.rotateAroundTarget(q);
436*bf2c3715SXin Li             }
437*bf2c3715SXin Li             break;
438*bf2c3715SXin Li           case TM_ZOOM :
439*bf2c3715SXin Li             mCamera.zoom(dy*100);
440*bf2c3715SXin Li             break;
441*bf2c3715SXin Li           case TM_FLY_Z :
442*bf2c3715SXin Li             mCamera.localTranslate(Vector3f(0, 0, -dy*200));
443*bf2c3715SXin Li             break;
444*bf2c3715SXin Li           case TM_FLY_PAN :
445*bf2c3715SXin Li             mCamera.localTranslate(Vector3f(dx*200, dy*200, 0));
446*bf2c3715SXin Li             break;
447*bf2c3715SXin Li           default:
448*bf2c3715SXin Li             break;
449*bf2c3715SXin Li         }
450*bf2c3715SXin Li 
451*bf2c3715SXin Li         updateGL();
452*bf2c3715SXin Li     }
453*bf2c3715SXin Li 
454*bf2c3715SXin Li     mMouseCoords = Vector2i(e->pos().x(), e->pos().y());
455*bf2c3715SXin Li }
456*bf2c3715SXin Li 
paintGL()457*bf2c3715SXin Li void RenderingWidget::paintGL()
458*bf2c3715SXin Li {
459*bf2c3715SXin Li   glEnable(GL_DEPTH_TEST);
460*bf2c3715SXin Li   glDisable(GL_CULL_FACE);
461*bf2c3715SXin Li   glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
462*bf2c3715SXin Li   glDisable(GL_COLOR_MATERIAL);
463*bf2c3715SXin Li   glDisable(GL_BLEND);
464*bf2c3715SXin Li   glDisable(GL_ALPHA_TEST);
465*bf2c3715SXin Li   glDisable(GL_TEXTURE_1D);
466*bf2c3715SXin Li   glDisable(GL_TEXTURE_2D);
467*bf2c3715SXin Li   glDisable(GL_TEXTURE_3D);
468*bf2c3715SXin Li 
469*bf2c3715SXin Li   // Clear buffers
470*bf2c3715SXin Li   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
471*bf2c3715SXin Li 
472*bf2c3715SXin Li   mCamera.activateGL();
473*bf2c3715SXin Li 
474*bf2c3715SXin Li   drawScene();
475*bf2c3715SXin Li }
476*bf2c3715SXin Li 
initializeGL()477*bf2c3715SXin Li void RenderingWidget::initializeGL()
478*bf2c3715SXin Li {
479*bf2c3715SXin Li   glClearColor(1., 1., 1., 0.);
480*bf2c3715SXin Li   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
481*bf2c3715SXin Li   glDepthMask(GL_TRUE);
482*bf2c3715SXin Li   glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
483*bf2c3715SXin Li 
484*bf2c3715SXin Li   mCamera.setPosition(Vector3f(-200, -200, -200));
485*bf2c3715SXin Li   mCamera.setTarget(Vector3f(0, 0, 0));
486*bf2c3715SXin Li   mInitFrame.orientation = mCamera.orientation().inverse();
487*bf2c3715SXin Li   mInitFrame.position = mCamera.viewMatrix().translation();
488*bf2c3715SXin Li }
489*bf2c3715SXin Li 
resizeGL(int width,int height)490*bf2c3715SXin Li void RenderingWidget::resizeGL(int width, int height)
491*bf2c3715SXin Li {
492*bf2c3715SXin Li     mCamera.setViewport(width,height);
493*bf2c3715SXin Li }
494*bf2c3715SXin Li 
setNavMode(int m)495*bf2c3715SXin Li void RenderingWidget::setNavMode(int m)
496*bf2c3715SXin Li {
497*bf2c3715SXin Li   mNavMode = NavMode(m);
498*bf2c3715SXin Li }
499*bf2c3715SXin Li 
setLerpMode(int m)500*bf2c3715SXin Li void RenderingWidget::setLerpMode(int m)
501*bf2c3715SXin Li {
502*bf2c3715SXin Li   mLerpMode = LerpMode(m);
503*bf2c3715SXin Li }
504*bf2c3715SXin Li 
setRotationMode(int m)505*bf2c3715SXin Li void RenderingWidget::setRotationMode(int m)
506*bf2c3715SXin Li {
507*bf2c3715SXin Li   mRotationMode = RotationMode(m);
508*bf2c3715SXin Li }
509*bf2c3715SXin Li 
resetCamera()510*bf2c3715SXin Li void RenderingWidget::resetCamera()
511*bf2c3715SXin Li {
512*bf2c3715SXin Li   if (mAnimate)
513*bf2c3715SXin Li     stopAnimation();
514*bf2c3715SXin Li   m_timeline.clear();
515*bf2c3715SXin Li   Frame aux0 = mCamera.frame();
516*bf2c3715SXin Li   aux0.orientation = aux0.orientation.inverse();
517*bf2c3715SXin Li   aux0.position = mCamera.viewMatrix().translation();
518*bf2c3715SXin Li   m_timeline[0] = aux0;
519*bf2c3715SXin Li 
520*bf2c3715SXin Li   Vector3f currentTarget = mCamera.target();
521*bf2c3715SXin Li   mCamera.setTarget(Vector3f::Zero());
522*bf2c3715SXin Li 
523*bf2c3715SXin Li   // compute the rotation duration to move the camera to the target
524*bf2c3715SXin Li   Frame aux1 = mCamera.frame();
525*bf2c3715SXin Li   aux1.orientation = aux1.orientation.inverse();
526*bf2c3715SXin Li   aux1.position = mCamera.viewMatrix().translation();
527*bf2c3715SXin Li   float duration = aux0.orientation.angularDistance(aux1.orientation) * 0.9;
528*bf2c3715SXin Li   if (duration<0.1) duration = 0.1;
529*bf2c3715SXin Li 
530*bf2c3715SXin Li   // put the camera at that time step:
531*bf2c3715SXin Li   aux1 = aux0.lerp(duration/2,mInitFrame);
532*bf2c3715SXin Li   // and make it look at the target again
533*bf2c3715SXin Li   aux1.orientation = aux1.orientation.inverse();
534*bf2c3715SXin Li   aux1.position = - (aux1.orientation * aux1.position);
535*bf2c3715SXin Li   mCamera.setFrame(aux1);
536*bf2c3715SXin Li   mCamera.setTarget(Vector3f::Zero());
537*bf2c3715SXin Li 
538*bf2c3715SXin Li   // add this camera keyframe
539*bf2c3715SXin Li   aux1.orientation = aux1.orientation.inverse();
540*bf2c3715SXin Li   aux1.position = mCamera.viewMatrix().translation();
541*bf2c3715SXin Li   m_timeline[duration] = aux1;
542*bf2c3715SXin Li 
543*bf2c3715SXin Li   m_timeline[2] = mInitFrame;
544*bf2c3715SXin Li   m_alpha = 0;
545*bf2c3715SXin Li   animate();
546*bf2c3715SXin Li   connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
547*bf2c3715SXin Li   m_timer.start(1000/30);
548*bf2c3715SXin Li   mAnimate = true;
549*bf2c3715SXin Li }
550*bf2c3715SXin Li 
createNavigationControlWidget()551*bf2c3715SXin Li QWidget* RenderingWidget::createNavigationControlWidget()
552*bf2c3715SXin Li {
553*bf2c3715SXin Li   QWidget* panel = new QWidget();
554*bf2c3715SXin Li   QVBoxLayout* layout = new QVBoxLayout();
555*bf2c3715SXin Li 
556*bf2c3715SXin Li   {
557*bf2c3715SXin Li     QPushButton* but = new QPushButton("reset");
558*bf2c3715SXin Li     but->setToolTip("move the camera to initial position (with animation)");
559*bf2c3715SXin Li     layout->addWidget(but);
560*bf2c3715SXin Li     connect(but, SIGNAL(clicked()), this, SLOT(resetCamera()));
561*bf2c3715SXin Li   }
562*bf2c3715SXin Li   {
563*bf2c3715SXin Li     // navigation mode
564*bf2c3715SXin Li     QGroupBox* box = new QGroupBox("navigation mode");
565*bf2c3715SXin Li     QVBoxLayout* boxLayout = new QVBoxLayout;
566*bf2c3715SXin Li     QButtonGroup* group = new QButtonGroup(panel);
567*bf2c3715SXin Li     QRadioButton* but;
568*bf2c3715SXin Li     but = new QRadioButton("turn around");
569*bf2c3715SXin Li     but->setToolTip("look around an object");
570*bf2c3715SXin Li     group->addButton(but, NavTurnAround);
571*bf2c3715SXin Li     boxLayout->addWidget(but);
572*bf2c3715SXin Li     but = new QRadioButton("fly");
573*bf2c3715SXin Li     but->setToolTip("free navigation like a spaceship\n(this mode can also be enabled pressing the \"shift\" key)");
574*bf2c3715SXin Li     group->addButton(but, NavFly);
575*bf2c3715SXin Li     boxLayout->addWidget(but);
576*bf2c3715SXin Li     group->button(mNavMode)->setChecked(true);
577*bf2c3715SXin Li     connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setNavMode(int)));
578*bf2c3715SXin Li     box->setLayout(boxLayout);
579*bf2c3715SXin Li     layout->addWidget(box);
580*bf2c3715SXin Li   }
581*bf2c3715SXin Li   {
582*bf2c3715SXin Li     // track ball, rotation mode
583*bf2c3715SXin Li     QGroupBox* box = new QGroupBox("rotation mode");
584*bf2c3715SXin Li     QVBoxLayout* boxLayout = new QVBoxLayout;
585*bf2c3715SXin Li     QButtonGroup* group = new QButtonGroup(panel);
586*bf2c3715SXin Li     QRadioButton* but;
587*bf2c3715SXin Li     but = new QRadioButton("stable trackball");
588*bf2c3715SXin Li     group->addButton(but, RotationStable);
589*bf2c3715SXin Li     boxLayout->addWidget(but);
590*bf2c3715SXin Li     but->setToolTip("use the stable trackball implementation mapping\nthe 2D coordinates to 3D points on a sphere");
591*bf2c3715SXin Li     but = new QRadioButton("standard rotation");
592*bf2c3715SXin Li     group->addButton(but, RotationStandard);
593*bf2c3715SXin Li     boxLayout->addWidget(but);
594*bf2c3715SXin Li     but->setToolTip("standard approach mapping the x and y displacements\nas rotations around the camera's X and Y axes");
595*bf2c3715SXin Li     group->button(mRotationMode)->setChecked(true);
596*bf2c3715SXin Li     connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setRotationMode(int)));
597*bf2c3715SXin Li     box->setLayout(boxLayout);
598*bf2c3715SXin Li     layout->addWidget(box);
599*bf2c3715SXin Li   }
600*bf2c3715SXin Li   {
601*bf2c3715SXin Li     // interpolation mode
602*bf2c3715SXin Li     QGroupBox* box = new QGroupBox("spherical interpolation");
603*bf2c3715SXin Li     QVBoxLayout* boxLayout = new QVBoxLayout;
604*bf2c3715SXin Li     QButtonGroup* group = new QButtonGroup(panel);
605*bf2c3715SXin Li     QRadioButton* but;
606*bf2c3715SXin Li     but = new QRadioButton("quaternion slerp");
607*bf2c3715SXin Li     group->addButton(but, LerpQuaternion);
608*bf2c3715SXin Li     boxLayout->addWidget(but);
609*bf2c3715SXin Li     but->setToolTip("use quaternion spherical interpolation\nto interpolate orientations");
610*bf2c3715SXin Li     but = new QRadioButton("euler angles");
611*bf2c3715SXin Li     group->addButton(but, LerpEulerAngles);
612*bf2c3715SXin Li     boxLayout->addWidget(but);
613*bf2c3715SXin Li     but->setToolTip("use Euler angles to interpolate orientations");
614*bf2c3715SXin Li     group->button(mNavMode)->setChecked(true);
615*bf2c3715SXin Li     connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setLerpMode(int)));
616*bf2c3715SXin Li     box->setLayout(boxLayout);
617*bf2c3715SXin Li     layout->addWidget(box);
618*bf2c3715SXin Li   }
619*bf2c3715SXin Li   layout->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
620*bf2c3715SXin Li   panel->setLayout(layout);
621*bf2c3715SXin Li   return panel;
622*bf2c3715SXin Li }
623*bf2c3715SXin Li 
QuaternionDemo()624*bf2c3715SXin Li QuaternionDemo::QuaternionDemo()
625*bf2c3715SXin Li {
626*bf2c3715SXin Li   mRenderingWidget = new RenderingWidget();
627*bf2c3715SXin Li   setCentralWidget(mRenderingWidget);
628*bf2c3715SXin Li 
629*bf2c3715SXin Li   QDockWidget* panel = new QDockWidget("navigation", this);
630*bf2c3715SXin Li   panel->setAllowedAreas((QFlags<Qt::DockWidgetArea>)(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea));
631*bf2c3715SXin Li   addDockWidget(Qt::RightDockWidgetArea, panel);
632*bf2c3715SXin Li   panel->setWidget(mRenderingWidget->createNavigationControlWidget());
633*bf2c3715SXin Li }
634*bf2c3715SXin Li 
main(int argc,char * argv[])635*bf2c3715SXin Li int main(int argc, char *argv[])
636*bf2c3715SXin Li {
637*bf2c3715SXin Li   std::cout << "Navigation:\n";
638*bf2c3715SXin Li   std::cout << "  left button:           rotate around the target\n";
639*bf2c3715SXin Li   std::cout << "  middle button:         zoom\n";
640*bf2c3715SXin Li   std::cout << "  left button + ctrl     quake rotate (rotate around camera position)\n";
641*bf2c3715SXin Li   std::cout << "  middle button + ctrl   walk (progress along camera's z direction)\n";
642*bf2c3715SXin Li   std::cout << "  left button:           pan (translate in the XY camera's plane)\n\n";
643*bf2c3715SXin Li   std::cout << "R : move the camera to initial position\n";
644*bf2c3715SXin Li   std::cout << "A : start/stop animation\n";
645*bf2c3715SXin Li   std::cout << "C : clear the animation\n";
646*bf2c3715SXin Li   std::cout << "G : add a key frame\n";
647*bf2c3715SXin Li 
648*bf2c3715SXin Li   QApplication app(argc, argv);
649*bf2c3715SXin Li   QuaternionDemo demo;
650*bf2c3715SXin Li   demo.resize(600,500);
651*bf2c3715SXin Li   demo.show();
652*bf2c3715SXin Li   return app.exec();
653*bf2c3715SXin Li }
654*bf2c3715SXin Li 
655*bf2c3715SXin Li #include "quaternion_demo.moc"
656*bf2c3715SXin Li 
657