mirror of
https://gitlab.com/libeigen/eigen.git
synced 2025-08-13 20:26:03 +08:00
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.
This commit is contained in:
parent
146c9e4494
commit
5e9ee8863e
@ -205,18 +205,6 @@ void Camera::localTranslate(const Vector3f& t)
|
|||||||
mViewIsUptodate = false;
|
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
|
void Camera::updateViewMatrix(void) const
|
||||||
{
|
{
|
||||||
if(!mViewIsUptodate)
|
if(!mViewIsUptodate)
|
||||||
|
@ -95,7 +95,6 @@ class Camera
|
|||||||
void zoom(float d);
|
void zoom(float d);
|
||||||
|
|
||||||
void localTranslate(const Eigen::Vector3f& t);
|
void localTranslate(const Eigen::Vector3f& t);
|
||||||
void localRotate(float dTheta, float dPhi);
|
|
||||||
|
|
||||||
/** Setup OpenGL matrices and viewport */
|
/** Setup OpenGL matrices and viewport */
|
||||||
void activateGL(void);
|
void activateGL(void);
|
||||||
|
@ -37,32 +37,92 @@
|
|||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QGroupBox>
|
||||||
|
|
||||||
using namespace Eigen;
|
using namespace Eigen;
|
||||||
|
|
||||||
|
|
||||||
|
// generic linear interpolation method
|
||||||
template<typename T> T lerp(float t, const T& a, const T& b)
|
template<typename T> T lerp(float t, const T& a, const T& b)
|
||||||
{
|
{
|
||||||
return a*(1-t) + b*t;
|
return a*(1-t) + b*t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// quaternion slerp
|
||||||
template<> Quaternionf lerp(float t, const Quaternionf& a, const Quaternionf& b)
|
template<> Quaternionf lerp(float t, const Quaternionf& a, const Quaternionf& b)
|
||||||
{ return a.slerp(t,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
|
||||||
return AngleAxisf(lerp(t,a.angle(),b.angle()),
|
|
||||||
lerp(t,a.axis(),b.axis()).normalized());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename OrientationType>
|
template<typename OrientationType>
|
||||||
inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b)
|
inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b)
|
||||||
{
|
{
|
||||||
return Frame(::lerp(alpha,a.position,b.position),
|
return Frame(lerp(alpha,a.position,b.position),
|
||||||
Quaternionf(::lerp(alpha,OrientationType(a.orientation),OrientationType(b.orientation))));
|
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.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()
|
RenderingWidget::RenderingWidget()
|
||||||
{
|
{
|
||||||
mAnimate = false;
|
mAnimate = false;
|
||||||
@ -158,7 +218,15 @@ void RenderingWidget::animate()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
float s = (m_alpha - lo->first)/(hi->first - lo->first);
|
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();
|
currentFrame.orientation.coeffs().normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,40 +241,40 @@ void RenderingWidget::keyPressEvent(QKeyEvent * e)
|
|||||||
{
|
{
|
||||||
switch(e->key())
|
switch(e->key())
|
||||||
{
|
{
|
||||||
case Qt::Key_Up:
|
case Qt::Key_Up:
|
||||||
mCamera.zoom(2);
|
mCamera.zoom(2);
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Down:
|
case Qt::Key_Down:
|
||||||
mCamera.zoom(-2);
|
mCamera.zoom(-2);
|
||||||
break;
|
break;
|
||||||
// add a frame
|
// add a frame
|
||||||
case Qt::Key_G:
|
case Qt::Key_G:
|
||||||
grabFrame();
|
grabFrame();
|
||||||
break;
|
break;
|
||||||
// clear the time line
|
// clear the time line
|
||||||
case Qt::Key_C:
|
case Qt::Key_C:
|
||||||
m_timeline.clear();
|
m_timeline.clear();
|
||||||
break;
|
break;
|
||||||
// move the camera to initial pos
|
// move the camera to initial pos
|
||||||
case Qt::Key_R:
|
case Qt::Key_R:
|
||||||
resetCamera();
|
resetCamera();
|
||||||
break;
|
break;
|
||||||
// start/stop the animation
|
// start/stop the animation
|
||||||
case Qt::Key_A:
|
case Qt::Key_A:
|
||||||
if (mAnimate)
|
if (mAnimate)
|
||||||
{
|
{
|
||||||
stopAnimation();
|
stopAnimation();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_alpha = 0;
|
m_alpha = 0;
|
||||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
|
connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
|
||||||
m_timer.start(1000/30);
|
m_timer.start(1000/30);
|
||||||
mAnimate = true;
|
mAnimate = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGL();
|
updateGL();
|
||||||
@ -266,6 +334,7 @@ void RenderingWidget::mouseMoveEvent(QMouseEvent* e)
|
|||||||
float dx = float(e->x() - mMouseCoords.x()) / float(mCamera.vpWidth());
|
float dx = float(e->x() - mMouseCoords.x()) / float(mCamera.vpWidth());
|
||||||
float dy = - float(e->y() - mMouseCoords.y()) / float(mCamera.vpHeight());
|
float dy = - float(e->y() - mMouseCoords.y()) / float(mCamera.vpHeight());
|
||||||
|
|
||||||
|
// speedup the transformations
|
||||||
if(e->modifiers() & Qt::ShiftModifier)
|
if(e->modifiers() & Qt::ShiftModifier)
|
||||||
{
|
{
|
||||||
dx *= 10.;
|
dx *= 10.;
|
||||||
@ -276,16 +345,32 @@ void RenderingWidget::mouseMoveEvent(QMouseEvent* e)
|
|||||||
{
|
{
|
||||||
case TM_ROTATE_AROUND:
|
case TM_ROTATE_AROUND:
|
||||||
case TM_LOCAL_ROTATE:
|
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;
|
break;
|
||||||
case TM_ZOOM :
|
case TM_ZOOM :
|
||||||
mCamera.zoom(dy*50);
|
mCamera.zoom(dy*100);
|
||||||
break;
|
break;
|
||||||
case TM_FLY_Z :
|
case TM_FLY_Z :
|
||||||
mCamera.localTranslate(Vector3f(0, 0, -dy*100));
|
mCamera.localTranslate(Vector3f(0, 0, -dy*200));
|
||||||
break;
|
break;
|
||||||
case TM_FLY_PAN :
|
case TM_FLY_PAN :
|
||||||
mCamera.localTranslate(Vector3f(dx*100, dy*100, 0));
|
mCamera.localTranslate(Vector3f(dx*200, dy*200, 0));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -486,51 +571,68 @@ QWidget* RenderingWidget::createNavigationControlWidget()
|
|||||||
QWidget* panel = new QWidget();
|
QWidget* panel = new QWidget();
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
QVBoxLayout* layout = new QVBoxLayout();
|
||||||
|
|
||||||
{
|
|
||||||
// navigation mode
|
|
||||||
QButtonGroup* group = new QButtonGroup(panel);
|
|
||||||
QRadioButton* but;
|
|
||||||
but = new QRadioButton("turn around");
|
|
||||||
group->addButton(but, NavTurnAround);
|
|
||||||
layout->addWidget(but);
|
|
||||||
but = new QRadioButton("fly");
|
|
||||||
group->addButton(but, NavFly);
|
|
||||||
layout->addWidget(but);
|
|
||||||
group->button(mNavMode)->setChecked(true);
|
|
||||||
connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setNavMode(int)));
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
QPushButton* but = new QPushButton("reset");
|
QPushButton* but = new QPushButton("reset");
|
||||||
|
but->setToolTip("move the camera to initial position (with animation)");
|
||||||
layout->addWidget(but);
|
layout->addWidget(but);
|
||||||
connect(but, SIGNAL(clicked()), this, SLOT(resetCamera()));
|
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
|
// track ball, rotation mode
|
||||||
|
QGroupBox* box = new QGroupBox("rotation mode");
|
||||||
|
QVBoxLayout* boxLayout = new QVBoxLayout;
|
||||||
QButtonGroup* group = new QButtonGroup(panel);
|
QButtonGroup* group = new QButtonGroup(panel);
|
||||||
QRadioButton* but;
|
QRadioButton* but;
|
||||||
but = new QRadioButton("stable trackball");
|
but = new QRadioButton("stable trackball");
|
||||||
group->addButton(but, RotationStable);
|
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");
|
but = new QRadioButton("standard rotation");
|
||||||
group->addButton(but, RotationStandard);
|
group->addButton(but, RotationStandard);
|
||||||
layout->addWidget(but);
|
boxLayout->addWidget(but);
|
||||||
but->setEnabled(false);
|
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);
|
group->button(mRotationMode)->setChecked(true);
|
||||||
connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setRotationMode(int)));
|
connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setRotationMode(int)));
|
||||||
|
box->setLayout(boxLayout);
|
||||||
|
layout->addWidget(box);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// interpolation mode
|
// interpolation mode
|
||||||
|
QGroupBox* box = new QGroupBox("spherical interpolation");
|
||||||
|
QVBoxLayout* boxLayout = new QVBoxLayout;
|
||||||
QButtonGroup* group = new QButtonGroup(panel);
|
QButtonGroup* group = new QButtonGroup(panel);
|
||||||
QRadioButton* but;
|
QRadioButton* but;
|
||||||
but = new QRadioButton("quaternion slerp");
|
but = new QRadioButton("quaternion slerp");
|
||||||
group->addButton(but, LerpQuaternion);
|
group->addButton(but, LerpQuaternion);
|
||||||
layout->addWidget(but);
|
boxLayout->addWidget(but);
|
||||||
|
but->setToolTip("use quaternion spherical interpolation\nto interpolate orientations");
|
||||||
but = new QRadioButton("euler angles");
|
but = new QRadioButton("euler angles");
|
||||||
group->addButton(but, LerpEulerAngles);
|
group->addButton(but, LerpEulerAngles);
|
||||||
layout->addWidget(but);
|
boxLayout->addWidget(but);
|
||||||
but->setEnabled(false);
|
but->setToolTip("use Euler angles to interpolate orientations");
|
||||||
group->button(mNavMode)->setChecked(true);
|
group->button(mNavMode)->setChecked(true);
|
||||||
connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setLerpMode(int)));
|
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));
|
layout->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
|
||||||
panel->setLayout(layout);
|
panel->setLayout(layout);
|
||||||
|
@ -40,9 +40,9 @@ void Trackball::track(const Vector2i& point2D)
|
|||||||
float cos_angle = mLastPoint3D.dot(newPoint3D);
|
float cos_angle = mLastPoint3D.dot(newPoint3D);
|
||||||
if ( ei_abs(cos_angle) < 1.0 )
|
if ( ei_abs(cos_angle) < 1.0 )
|
||||||
{
|
{
|
||||||
float angle = acos(cos_angle);
|
float angle = 2. * acos(cos_angle);
|
||||||
if (mMode==Around)
|
if (mMode==Around)
|
||||||
mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(2.*angle, axis))); // *2 to speedup the rotation
|
mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(angle, axis)));
|
||||||
else
|
else
|
||||||
mpCamera->localRotate(Quaternionf(AngleAxisf(-angle, axis)));
|
mpCamera->localRotate(Quaternionf(AngleAxisf(-angle, axis)));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user