|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "quaternion_demo.h" |
|
|
#include "icosphere.h" |
|
|
|
|
|
#include <Eigen/Geometry> |
|
|
#include <Eigen/QR> |
|
|
#include <Eigen/LU> |
|
|
|
|
|
#include <iostream> |
|
|
#include <QEvent> |
|
|
#include <QMouseEvent> |
|
|
#include <QInputDialog> |
|
|
#include <QGridLayout> |
|
|
#include <QButtonGroup> |
|
|
#include <QRadioButton> |
|
|
#include <QDockWidget> |
|
|
#include <QPushButton> |
|
|
#include <QGroupBox> |
|
|
|
|
|
using namespace Eigen; |
|
|
|
|
|
class FancySpheres |
|
|
{ |
|
|
public: |
|
|
EIGEN_MAKE_ALIGNED_OPERATOR_NEW |
|
|
|
|
|
FancySpheres() |
|
|
{ |
|
|
const int levels = 4; |
|
|
const float scale = 0.33; |
|
|
float radius = 100; |
|
|
std::vector<int> parents; |
|
|
|
|
|
|
|
|
mCenters.push_back(Vector3f::Zero()); |
|
|
parents.push_back(-1); |
|
|
mRadii.push_back(radius); |
|
|
|
|
|
|
|
|
radius *= 0.45; |
|
|
{ |
|
|
float dist = mRadii[0]*0.9; |
|
|
for (int i=0; i<12; ++i) |
|
|
{ |
|
|
mCenters.push_back(mIcoSphere.vertices()[i] * dist); |
|
|
mRadii.push_back(radius); |
|
|
parents.push_back(0); |
|
|
} |
|
|
} |
|
|
|
|
|
static const float angles [10] = { |
|
|
0, 0, |
|
|
M_PI, 0.*M_PI, |
|
|
M_PI, 0.5*M_PI, |
|
|
M_PI, 1.*M_PI, |
|
|
M_PI, 1.5*M_PI |
|
|
}; |
|
|
|
|
|
|
|
|
int start = 1; |
|
|
for (int l=1; l<levels; l++) |
|
|
{ |
|
|
radius *= scale; |
|
|
int end = mCenters.size(); |
|
|
for (int i=start; i<end; ++i) |
|
|
{ |
|
|
Vector3f c = mCenters[i]; |
|
|
Vector3f ax0 = (c - mCenters[parents[i]]).normalized(); |
|
|
Vector3f ax1 = ax0.unitOrthogonal(); |
|
|
Quaternionf q; |
|
|
q.setFromTwoVectors(Vector3f::UnitZ(), ax0); |
|
|
Affine3f t = Translation3f(c) * q * Scaling(mRadii[i]+radius); |
|
|
for (int j=0; j<5; ++j) |
|
|
{ |
|
|
Vector3f newC = c + ( (AngleAxisf(angles[j*2+1], ax0) |
|
|
* AngleAxisf(angles[j*2+0] * (l==1 ? 0.35 : 0.5), ax1)) * ax0) |
|
|
* (mRadii[i] + radius*0.8); |
|
|
mCenters.push_back(newC); |
|
|
mRadii.push_back(radius); |
|
|
parents.push_back(i); |
|
|
} |
|
|
} |
|
|
start = end; |
|
|
} |
|
|
} |
|
|
|
|
|
void draw() |
|
|
{ |
|
|
int end = mCenters.size(); |
|
|
glEnable(GL_NORMALIZE); |
|
|
for (int i=0; i<end; ++i) |
|
|
{ |
|
|
Affine3f t = Translation3f(mCenters[i]) * Scaling(mRadii[i]); |
|
|
gpu.pushMatrix(GL_MODELVIEW); |
|
|
gpu.multMatrix(t.matrix(),GL_MODELVIEW); |
|
|
mIcoSphere.draw(2); |
|
|
gpu.popMatrix(GL_MODELVIEW); |
|
|
} |
|
|
glDisable(GL_NORMALIZE); |
|
|
} |
|
|
protected: |
|
|
std::vector<Vector3f> mCenters; |
|
|
std::vector<float> mRadii; |
|
|
IcoSphere mIcoSphere; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
template<typename T> T lerp(float t, const T& a, const T& b) |
|
|
{ |
|
|
return a*(1-t) + b*t; |
|
|
} |
|
|
|
|
|
|
|
|
template<> Quaternionf lerp(float t, const Quaternionf& a, const Quaternionf& b) |
|
|
{ return a.slerp(t,b); } |
|
|
|
|
|
|
|
|
|
|
|
template<typename OrientationType> |
|
|
inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b) |
|
|
{ |
|
|
return Frame(lerp(alpha,a.position,b.position), |
|
|
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) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
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.array().cos(); |
|
|
Vector3 s = m_angles.array().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()); } |
|
|
}; |
|
|
|
|
|
|
|
|
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; |
|
|
mCurrentTrackingMode = TM_NO_TRACK; |
|
|
mNavMode = NavTurnAround; |
|
|
mLerpMode = LerpQuaternion; |
|
|
mRotationMode = RotationStable; |
|
|
mTrackball.setCamera(&mCamera); |
|
|
|
|
|
|
|
|
setFocusPolicy(Qt::ClickFocus); |
|
|
} |
|
|
|
|
|
void RenderingWidget::grabFrame(void) |
|
|
{ |
|
|
|
|
|
bool ok = false; |
|
|
double t = 0; |
|
|
if (!m_timeline.empty()) |
|
|
t = (--m_timeline.end())->first + 1.; |
|
|
t = QInputDialog::getDouble(this, "Eigen's RenderingWidget", "time value: ", |
|
|
t, 0, 1e3, 1, &ok); |
|
|
if (ok) |
|
|
{ |
|
|
Frame aux; |
|
|
aux.orientation = mCamera.viewMatrix().linear(); |
|
|
aux.position = mCamera.viewMatrix().translation(); |
|
|
m_timeline[t] = aux; |
|
|
} |
|
|
} |
|
|
|
|
|
void RenderingWidget::drawScene() |
|
|
{ |
|
|
static FancySpheres sFancySpheres; |
|
|
float length = 50; |
|
|
gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitX(), Color(1,0,0,1)); |
|
|
gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitY(), Color(0,1,0,1)); |
|
|
gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitZ(), Color(0,0,1,1)); |
|
|
|
|
|
|
|
|
float sqrt3 = std::sqrt(3.); |
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, Vector4f(0.5,0.5,0.5,1).data()); |
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, Vector4f(0.5,1,0.5,1).data()); |
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, Vector4f(1,1,1,1).data()); |
|
|
glLightfv(GL_LIGHT0, GL_POSITION, Vector4f(-sqrt3,-sqrt3,sqrt3,0).data()); |
|
|
|
|
|
glLightfv(GL_LIGHT1, GL_AMBIENT, Vector4f(0,0,0,1).data()); |
|
|
glLightfv(GL_LIGHT1, GL_DIFFUSE, Vector4f(1,0.5,0.5,1).data()); |
|
|
glLightfv(GL_LIGHT1, GL_SPECULAR, Vector4f(1,1,1,1).data()); |
|
|
glLightfv(GL_LIGHT1, GL_POSITION, Vector4f(-sqrt3,sqrt3,-sqrt3,0).data()); |
|
|
|
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Vector4f(0.7, 0.7, 0.7, 1).data()); |
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Vector4f(0.8, 0.75, 0.6, 1).data()); |
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Vector4f(1, 1, 1, 1).data()); |
|
|
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 64); |
|
|
|
|
|
glEnable(GL_LIGHTING); |
|
|
glEnable(GL_LIGHT0); |
|
|
glEnable(GL_LIGHT1); |
|
|
|
|
|
sFancySpheres.draw(); |
|
|
glVertexPointer(3, GL_FLOAT, 0, mVertices[0].data()); |
|
|
glNormalPointer(GL_FLOAT, 0, mNormals[0].data()); |
|
|
glEnableClientState(GL_VERTEX_ARRAY); |
|
|
glEnableClientState(GL_NORMAL_ARRAY); |
|
|
glDrawArrays(GL_TRIANGLES, 0, mVertices.size()); |
|
|
glDisableClientState(GL_VERTEX_ARRAY); |
|
|
glDisableClientState(GL_NORMAL_ARRAY); |
|
|
|
|
|
glDisable(GL_LIGHTING); |
|
|
} |
|
|
|
|
|
void RenderingWidget::animate() |
|
|
{ |
|
|
m_alpha += double(m_timer.interval()) * 1e-3; |
|
|
|
|
|
TimeLine::const_iterator hi = m_timeline.upper_bound(m_alpha); |
|
|
TimeLine::const_iterator lo = hi; |
|
|
--lo; |
|
|
|
|
|
Frame currentFrame; |
|
|
|
|
|
if(hi==m_timeline.end()) |
|
|
{ |
|
|
|
|
|
currentFrame = lo->second; |
|
|
stopAnimation(); |
|
|
} |
|
|
else if(hi==m_timeline.begin()) |
|
|
{ |
|
|
|
|
|
currentFrame = hi->second; |
|
|
} |
|
|
else |
|
|
{ |
|
|
float s = (m_alpha - lo->first)/(hi->first - lo->first); |
|
|
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 = currentFrame.orientation.inverse(); |
|
|
currentFrame.position = - (currentFrame.orientation * currentFrame.position); |
|
|
mCamera.setFrame(currentFrame); |
|
|
|
|
|
updateGL(); |
|
|
} |
|
|
|
|
|
void RenderingWidget::keyPressEvent(QKeyEvent * e) |
|
|
{ |
|
|
switch(e->key()) |
|
|
{ |
|
|
case Qt::Key_Up: |
|
|
mCamera.zoom(2); |
|
|
break; |
|
|
case Qt::Key_Down: |
|
|
mCamera.zoom(-2); |
|
|
break; |
|
|
|
|
|
case Qt::Key_G: |
|
|
grabFrame(); |
|
|
break; |
|
|
|
|
|
case Qt::Key_C: |
|
|
m_timeline.clear(); |
|
|
break; |
|
|
|
|
|
case Qt::Key_R: |
|
|
resetCamera(); |
|
|
break; |
|
|
|
|
|
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(); |
|
|
} |
|
|
|
|
|
void RenderingWidget::stopAnimation() |
|
|
{ |
|
|
disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(animate())); |
|
|
m_timer.stop(); |
|
|
mAnimate = false; |
|
|
m_alpha = 0; |
|
|
} |
|
|
|
|
|
void RenderingWidget::mousePressEvent(QMouseEvent* e) |
|
|
{ |
|
|
mMouseCoords = Vector2i(e->pos().x(), e->pos().y()); |
|
|
bool fly = (mNavMode==NavFly) || (e->modifiers()&Qt::ControlModifier); |
|
|
switch(e->button()) |
|
|
{ |
|
|
case Qt::LeftButton: |
|
|
if(fly) |
|
|
{ |
|
|
mCurrentTrackingMode = TM_LOCAL_ROTATE; |
|
|
mTrackball.start(Trackball::Local); |
|
|
} |
|
|
else |
|
|
{ |
|
|
mCurrentTrackingMode = TM_ROTATE_AROUND; |
|
|
mTrackball.start(Trackball::Around); |
|
|
} |
|
|
mTrackball.track(mMouseCoords); |
|
|
break; |
|
|
case Qt::MidButton: |
|
|
if(fly) |
|
|
mCurrentTrackingMode = TM_FLY_Z; |
|
|
else |
|
|
mCurrentTrackingMode = TM_ZOOM; |
|
|
break; |
|
|
case Qt::RightButton: |
|
|
mCurrentTrackingMode = TM_FLY_PAN; |
|
|
break; |
|
|
default: |
|
|
break; |
|
|
} |
|
|
} |
|
|
void RenderingWidget::mouseReleaseEvent(QMouseEvent*) |
|
|
{ |
|
|
mCurrentTrackingMode = TM_NO_TRACK; |
|
|
updateGL(); |
|
|
} |
|
|
|
|
|
void RenderingWidget::mouseMoveEvent(QMouseEvent* e) |
|
|
{ |
|
|
|
|
|
if(mCurrentTrackingMode != TM_NO_TRACK) |
|
|
{ |
|
|
float dx = float(e->x() - mMouseCoords.x()) / float(mCamera.vpWidth()); |
|
|
float dy = - float(e->y() - mMouseCoords.y()) / float(mCamera.vpHeight()); |
|
|
|
|
|
|
|
|
if(e->modifiers() & Qt::ShiftModifier) |
|
|
{ |
|
|
dx *= 10.; |
|
|
dy *= 10.; |
|
|
} |
|
|
|
|
|
switch(mCurrentTrackingMode) |
|
|
{ |
|
|
case TM_ROTATE_AROUND: |
|
|
case TM_LOCAL_ROTATE: |
|
|
if (mRotationMode==RotationStable) |
|
|
{ |
|
|
|
|
|
|
|
|
mTrackball.track(Vector2i(e->pos().x(), e->pos().y())); |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
|
|
|
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*100); |
|
|
break; |
|
|
case TM_FLY_Z : |
|
|
mCamera.localTranslate(Vector3f(0, 0, -dy*200)); |
|
|
break; |
|
|
case TM_FLY_PAN : |
|
|
mCamera.localTranslate(Vector3f(dx*200, dy*200, 0)); |
|
|
break; |
|
|
default: |
|
|
break; |
|
|
} |
|
|
|
|
|
updateGL(); |
|
|
} |
|
|
|
|
|
mMouseCoords = Vector2i(e->pos().x(), e->pos().y()); |
|
|
} |
|
|
|
|
|
void RenderingWidget::paintGL() |
|
|
{ |
|
|
glEnable(GL_DEPTH_TEST); |
|
|
glDisable(GL_CULL_FACE); |
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); |
|
|
glDisable(GL_COLOR_MATERIAL); |
|
|
glDisable(GL_BLEND); |
|
|
glDisable(GL_ALPHA_TEST); |
|
|
glDisable(GL_TEXTURE_1D); |
|
|
glDisable(GL_TEXTURE_2D); |
|
|
glDisable(GL_TEXTURE_3D); |
|
|
|
|
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
|
|
|
|
mCamera.activateGL(); |
|
|
|
|
|
drawScene(); |
|
|
} |
|
|
|
|
|
void RenderingWidget::initializeGL() |
|
|
{ |
|
|
glClearColor(1., 1., 1., 0.); |
|
|
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); |
|
|
glDepthMask(GL_TRUE); |
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
|
|
|
|
|
mCamera.setPosition(Vector3f(-200, -200, -200)); |
|
|
mCamera.setTarget(Vector3f(0, 0, 0)); |
|
|
mInitFrame.orientation = mCamera.orientation().inverse(); |
|
|
mInitFrame.position = mCamera.viewMatrix().translation(); |
|
|
} |
|
|
|
|
|
void RenderingWidget::resizeGL(int width, int height) |
|
|
{ |
|
|
mCamera.setViewport(width,height); |
|
|
} |
|
|
|
|
|
void RenderingWidget::setNavMode(int m) |
|
|
{ |
|
|
mNavMode = NavMode(m); |
|
|
} |
|
|
|
|
|
void RenderingWidget::setLerpMode(int m) |
|
|
{ |
|
|
mLerpMode = LerpMode(m); |
|
|
} |
|
|
|
|
|
void RenderingWidget::setRotationMode(int m) |
|
|
{ |
|
|
mRotationMode = RotationMode(m); |
|
|
} |
|
|
|
|
|
void RenderingWidget::resetCamera() |
|
|
{ |
|
|
if (mAnimate) |
|
|
stopAnimation(); |
|
|
m_timeline.clear(); |
|
|
Frame aux0 = mCamera.frame(); |
|
|
aux0.orientation = aux0.orientation.inverse(); |
|
|
aux0.position = mCamera.viewMatrix().translation(); |
|
|
m_timeline[0] = aux0; |
|
|
|
|
|
Vector3f currentTarget = mCamera.target(); |
|
|
mCamera.setTarget(Vector3f::Zero()); |
|
|
|
|
|
|
|
|
Frame aux1 = mCamera.frame(); |
|
|
aux1.orientation = aux1.orientation.inverse(); |
|
|
aux1.position = mCamera.viewMatrix().translation(); |
|
|
float duration = aux0.orientation.angularDistance(aux1.orientation) * 0.9; |
|
|
if (duration<0.1) duration = 0.1; |
|
|
|
|
|
|
|
|
aux1 = aux0.lerp(duration/2,mInitFrame); |
|
|
|
|
|
aux1.orientation = aux1.orientation.inverse(); |
|
|
aux1.position = - (aux1.orientation * aux1.position); |
|
|
mCamera.setFrame(aux1); |
|
|
mCamera.setTarget(Vector3f::Zero()); |
|
|
|
|
|
|
|
|
aux1.orientation = aux1.orientation.inverse(); |
|
|
aux1.position = mCamera.viewMatrix().translation(); |
|
|
m_timeline[duration] = aux1; |
|
|
|
|
|
m_timeline[2] = mInitFrame; |
|
|
m_alpha = 0; |
|
|
animate(); |
|
|
connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate())); |
|
|
m_timer.start(1000/30); |
|
|
mAnimate = true; |
|
|
} |
|
|
|
|
|
QWidget* RenderingWidget::createNavigationControlWidget() |
|
|
{ |
|
|
QWidget* panel = new QWidget(); |
|
|
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())); |
|
|
} |
|
|
{ |
|
|
|
|
|
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); |
|
|
} |
|
|
{ |
|
|
|
|
|
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); |
|
|
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); |
|
|
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); |
|
|
} |
|
|
{ |
|
|
|
|
|
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); |
|
|
boxLayout->addWidget(but); |
|
|
but->setToolTip("use quaternion spherical interpolation\nto interpolate orientations"); |
|
|
but = new QRadioButton("euler angles"); |
|
|
group->addButton(but, LerpEulerAngles); |
|
|
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); |
|
|
return panel; |
|
|
} |
|
|
|
|
|
QuaternionDemo::QuaternionDemo() |
|
|
{ |
|
|
mRenderingWidget = new RenderingWidget(); |
|
|
setCentralWidget(mRenderingWidget); |
|
|
|
|
|
QDockWidget* panel = new QDockWidget("navigation", this); |
|
|
panel->setAllowedAreas((QFlags<Qt::DockWidgetArea>)(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea)); |
|
|
addDockWidget(Qt::RightDockWidgetArea, panel); |
|
|
panel->setWidget(mRenderingWidget->createNavigationControlWidget()); |
|
|
} |
|
|
|
|
|
int main(int argc, char *argv[]) |
|
|
{ |
|
|
std::cout << "Navigation:\n"; |
|
|
std::cout << " left button: rotate around the target\n"; |
|
|
std::cout << " middle button: zoom\n"; |
|
|
std::cout << " left button + ctrl quake rotate (rotate around camera position)\n"; |
|
|
std::cout << " middle button + ctrl walk (progress along camera's z direction)\n"; |
|
|
std::cout << " left button: pan (translate in the XY camera's plane)\n\n"; |
|
|
std::cout << "R : move the camera to initial position\n"; |
|
|
std::cout << "A : start/stop animation\n"; |
|
|
std::cout << "C : clear the animation\n"; |
|
|
std::cout << "G : add a key frame\n"; |
|
|
|
|
|
QApplication app(argc, argv); |
|
|
QuaternionDemo demo; |
|
|
demo.resize(600,500); |
|
|
demo.show(); |
|
|
return app.exec(); |
|
|
} |
|
|
|
|
|
#include "quaternion_demo.moc" |
|
|
|
|
|
|