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