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

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