diff options
author | Gael Guennebaud <g.gael@free.fr> | 2008-09-09 23:17:14 +0000 |
---|---|---|
committer | Gael Guennebaud <g.gael@free.fr> | 2008-09-09 23:17:14 +0000 |
commit | 5e9ee8863e0ffe38551dcffb195a894129019a71 (patch) | |
tree | e9f60577e4419c9f03a0d2b68a30184efb71ada0 | |
parent | 146c9e449443453f3932b60cd1bab47f688403e7 (diff) |
opengl demo, now working:
- quaternion vs euler angles interpolation (though the Euler angle version looks a bit too bad)
- navigation using either a mapping from 2D screen coordinates to 3D points on a sphere
or the standard approach mapping mouse displacements as rotations around camera's axes.
-rw-r--r-- | demos/opengl/camera.cpp | 12 | ||||
-rw-r--r-- | demos/opengl/camera.h | 1 | ||||
-rw-r--r-- | demos/opengl/quaternion_demo.cpp | 222 | ||||
-rw-r--r-- | demos/opengl/trackball.cpp | 4 |
4 files changed, 164 insertions, 75 deletions
diff --git a/demos/opengl/camera.cpp b/demos/opengl/camera.cpp index f704771b6..85fdc0c82 100644 --- a/demos/opengl/camera.cpp +++ b/demos/opengl/camera.cpp @@ -205,18 +205,6 @@ void Camera::localTranslate(const Vector3f& t) mViewIsUptodate = false; } -void Camera::localRotate(float dTheta, float dPhi) -{ - float dist = (position() - mTarget).norm(); - - setOrientation( AngleAxisf(dTheta, up()) - * AngleAxisf(dPhi, right()) - * orientation()); - mTarget = position() + dist * direction(); - - mViewIsUptodate = false; -} - void Camera::updateViewMatrix(void) const { if(!mViewIsUptodate) diff --git a/demos/opengl/camera.h b/demos/opengl/camera.h index 811b2c8ef..f6d0d7da3 100644 --- a/demos/opengl/camera.h +++ b/demos/opengl/camera.h @@ -95,7 +95,6 @@ class Camera void zoom(float d); void localTranslate(const Eigen::Vector3f& t); - void localRotate(float dTheta, float dPhi); /** Setup OpenGL matrices and viewport */ void activateGL(void); diff --git a/demos/opengl/quaternion_demo.cpp b/demos/opengl/quaternion_demo.cpp index f4bea262d..dbfc16c6b 100644 --- a/demos/opengl/quaternion_demo.cpp +++ b/demos/opengl/quaternion_demo.cpp @@ -37,32 +37,92 @@ #include <QRadioButton> #include <QDockWidget> #include <QPushButton> +#include <QGroupBox> using namespace Eigen; - +// 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); } -template<> AngleAxisf lerp(float t, const AngleAxisf& a, const AngleAxisf& 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 AngleAxisf(lerp(t,a.angle(),b.angle()), - lerp(t,a.axis(),b.axis()).normalized()); + return Frame(lerp(alpha,a.position,b.position), + Quaternionf(lerp(alpha,OrientationType(a.orientation),OrientationType(b.orientation)))); } -template<typename OrientationType> -inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b) +template<typename _Scalar> class EulerAngles { - return Frame(::lerp(alpha,a.position,b.position), - Quaternionf(::lerp(alpha,OrientationType(a.orientation),OrientationType(b.orientation)))); +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.cwise().cos(); + Vector3 s = m_angles.cwise().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; @@ -158,7 +218,15 @@ void RenderingWidget::animate() else { float s = (m_alpha - lo->first)/(hi->first - lo->first); - currentFrame = ::lerpFrame<Eigen::Quaternionf>(s, lo->second, hi->second); + 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(); } @@ -173,40 +241,40 @@ 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; + 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(); @@ -266,6 +334,7 @@ void RenderingWidget::mouseMoveEvent(QMouseEvent* e) 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.; @@ -276,16 +345,32 @@ void RenderingWidget::mouseMoveEvent(QMouseEvent* e) { case TM_ROTATE_AROUND: case TM_LOCAL_ROTATE: - mTrackball.track(Vector2i(e->pos().x(), e->pos().y())); + 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*50); + mCamera.zoom(dy*100); break; case TM_FLY_Z : - mCamera.localTranslate(Vector3f(0, 0, -dy*100)); + mCamera.localTranslate(Vector3f(0, 0, -dy*200)); break; case TM_FLY_PAN : - mCamera.localTranslate(Vector3f(dx*100, dy*100, 0)); + mCamera.localTranslate(Vector3f(dx*200, dy*200, 0)); break; default: break; @@ -487,50 +572,67 @@ QWidget* RenderingWidget::createNavigationControlWidget() 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); - layout->addWidget(but); + 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); - layout->addWidget(but); + boxLayout->addWidget(but); group->button(mNavMode)->setChecked(true); connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setNavMode(int))); - } - { - QPushButton* but = new QPushButton("reset"); - layout->addWidget(but); - connect(but, SIGNAL(clicked()), this, SLOT(resetCamera())); + 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); - layout->addWidget(but); + 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); - layout->addWidget(but); - but->setEnabled(false); + 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); - layout->addWidget(but); + boxLayout->addWidget(but); + but->setToolTip("use quaternion spherical interpolation\nto interpolate orientations"); but = new QRadioButton("euler angles"); group->addButton(but, LerpEulerAngles); - layout->addWidget(but); - but->setEnabled(false); + 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); diff --git a/demos/opengl/trackball.cpp b/demos/opengl/trackball.cpp index 83e74a649..af2f17d11 100644 --- a/demos/opengl/trackball.cpp +++ b/demos/opengl/trackball.cpp @@ -40,9 +40,9 @@ void Trackball::track(const Vector2i& point2D) float cos_angle = mLastPoint3D.dot(newPoint3D); if ( ei_abs(cos_angle) < 1.0 ) { - float angle = acos(cos_angle); + float angle = 2. * acos(cos_angle); if (mMode==Around) - mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(2.*angle, axis))); // *2 to speedup the rotation + mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(angle, axis))); else mpCamera->localRotate(Quaternionf(AngleAxisf(-angle, axis))); } |