diff options
author | Gael Guennebaud <g.gael@free.fr> | 2008-09-09 18:50:45 +0000 |
---|---|---|
committer | Gael Guennebaud <g.gael@free.fr> | 2008-09-09 18:50:45 +0000 |
commit | 146c9e449443453f3932b60cd1bab47f688403e7 (patch) | |
tree | f7f18d29a7a7755515db377b5de9658899de66bb /demos | |
parent | d3a70b7facea2919b5ee0451d3c639dedb00ea30 (diff) |
various stuff in opengl demos such as a better model,
stable trackball for the fly navigation mode, and started
to put some GUI elements...
Diffstat (limited to 'demos')
-rw-r--r-- | demos/opengl/CMakeLists.txt | 2 | ||||
-rw-r--r-- | demos/opengl/camera.cpp | 11 | ||||
-rw-r--r-- | demos/opengl/camera.h | 1 | ||||
-rw-r--r-- | demos/opengl/gpuhelper.cpp | 78 | ||||
-rw-r--r-- | demos/opengl/gpuhelper.h | 2 | ||||
-rw-r--r-- | demos/opengl/icosphere.cpp | 119 | ||||
-rw-r--r-- | demos/opengl/icosphere.h | 45 | ||||
-rw-r--r-- | demos/opengl/quaternion_demo.cpp | 345 | ||||
-rw-r--r-- | demos/opengl/quaternion_demo.h | 49 | ||||
-rw-r--r-- | demos/opengl/trackball.cpp | 12 | ||||
-rw-r--r-- | demos/opengl/trackball.h | 6 |
11 files changed, 529 insertions, 141 deletions
diff --git a/demos/opengl/CMakeLists.txt b/demos/opengl/CMakeLists.txt index 06b49459c..968ed6cb4 100644 --- a/demos/opengl/CMakeLists.txt +++ b/demos/opengl/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories( ${QT_INCLUDE_DIR} ) -set(quaternion_demo_SRCS gpuhelper.cpp camera.cpp trackball.cpp quaternion_demo.cpp) +set(quaternion_demo_SRCS gpuhelper.cpp icosphere.cpp camera.cpp trackball.cpp quaternion_demo.cpp) qt4_automoc(${quaternion_demo_SRCS}) diff --git a/demos/opengl/camera.cpp b/demos/opengl/camera.cpp index 26118e6ba..f704771b6 100644 --- a/demos/opengl/camera.cpp +++ b/demos/opengl/camera.cpp @@ -42,8 +42,7 @@ Camera::Camera() mVpX = 0; mVpY = 0; - setPosition(Vector3f::Constant(50.)); - + setPosition(Vector3f::Constant(100.)); setTarget(Vector3f::Zero()); } @@ -179,6 +178,14 @@ void Camera::rotateAroundTarget(const Quaternionf& q) mViewIsUptodate = true; } +void Camera::localRotate(const Quaternionf& q) +{ + float dist = (position() - mTarget).norm(); + setOrientation(orientation() * q); + mTarget = position() + dist * direction(); + mViewIsUptodate = false; +} + void Camera::zoom(float d) { float dist = (position() - mTarget).norm(); diff --git a/demos/opengl/camera.h b/demos/opengl/camera.h index 46c709a93..811b2c8ef 100644 --- a/demos/opengl/camera.h +++ b/demos/opengl/camera.h @@ -91,6 +91,7 @@ class Camera const Eigen::Matrix4f& projectionMatrix(void) const; void rotateAroundTarget(const Eigen::Quaternionf& q); + void localRotate(const Eigen::Quaternionf& q); void zoom(float d); void localTranslate(const Eigen::Vector3f& t); diff --git a/demos/opengl/gpuhelper.cpp b/demos/opengl/gpuhelper.cpp index 71348737c..921c95f04 100644 --- a/demos/opengl/gpuhelper.cpp +++ b/demos/opengl/gpuhelper.cpp @@ -23,6 +23,7 @@ // Eigen. If not, see <http://www.gnu.org/licenses/>. #include "gpuhelper.h" +#include "icosphere.h" #include <GL/glu.h> // PLEASE don't look at this old code... ;) @@ -31,26 +32,6 @@ GpuHelper gpu; -//-------------------------------------------------------------------------------- -// icosahedron -//-------------------------------------------------------------------------------- -#define X .525731112119133606 -#define Z .850650808352039932 - -static GLfloat vdata[12][3] = { - {-X, 0.0, Z}, {X, 0.0, Z}, {-X, 0.0, -Z}, {X, 0.0, -Z}, - {0.0, Z, X}, {0.0, Z, -X}, {0.0, -Z, X}, {0.0, -Z, -X}, - {Z, X, 0.0}, {-Z, X, 0.0}, {Z, -X, 0.0}, {-Z, -X, 0.0} -}; - -static GLint tindices[20][3] = { - {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1}, - {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3}, - {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6}, - {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11} }; -//-------------------------------------------------------------------------------- - - GpuHelper::GpuHelper() { mVpWidth = mVpHeight = 0; @@ -151,61 +132,10 @@ void GpuHelper::drawUnitCube(void) glEnd(); } -void _normalize(float* v) -{ - float s = 1.f/ei_sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); - for (uint k=0; k<3; ++k) - v[k] *= s; -} - -void _subdivide(float *v1, float *v2, float *v3, long depth) +void GpuHelper::drawUnitSphere(int level) { - GLfloat v12[3], v23[3], v31[3]; - GLint i; - - if (depth == 0) { - //drawtriangle(v1, v2, v3); - glNormal3fv(v1); - glVertex3fv(v1); - - glNormal3fv(v3); - glVertex3fv(v3); - - glNormal3fv(v2); - glVertex3fv(v2); - - return; - } - for (i = 0; i < 3; i++) { - v12[i] = v1[i]+v2[i]; - v23[i] = v2[i]+v3[i]; - v31[i] = v3[i]+v1[i]; - } - _normalize(v12); - _normalize(v23); - _normalize(v31); - _subdivide(v1, v12, v31, depth-1); - _subdivide(v2, v23, v12, depth-1); - _subdivide(v3, v31, v23, depth-1); - _subdivide(v12, v23, v31, depth-1); -} - -void GpuHelper::drawUnitLightSphere(int level) -{ - static int dlId = 0; - if (!dlId) - { - dlId = glGenLists(1); - glNewList(dlId, GL_COMPILE); - glBegin(GL_TRIANGLES); - for (int i = 0; i < 20; i++) - { - _subdivide(&vdata[tindices[i][0]][0], &vdata[tindices[i][1]][0], &vdata[tindices[i][2]][0], 1); - } - glEnd(); - glEndList(); - } - glCallList(dlId); + static IcoSphere sphere; + sphere.draw(level); } diff --git a/demos/opengl/gpuhelper.h b/demos/opengl/gpuhelper.h index fd07d53fe..4450eb581 100644 --- a/demos/opengl/gpuhelper.h +++ b/demos/opengl/gpuhelper.h @@ -100,7 +100,7 @@ class GpuHelper void drawVector(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect = 50.); void drawVectorBox(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect = 50.); void drawUnitCube(void); - void drawUnitLightSphere(int level=0); + void drawUnitSphere(int level=0); /// draw the \a nofElement first elements inline void draw(GLenum mode, uint nofElement); diff --git a/demos/opengl/icosphere.cpp b/demos/opengl/icosphere.cpp new file mode 100644 index 000000000..5f7823124 --- /dev/null +++ b/demos/opengl/icosphere.cpp @@ -0,0 +1,119 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// Eigen is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// Alternatively, you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License and a copy of the GNU General Public License along with +// Eigen. If not, see <http://www.gnu.org/licenses/>. + +#include "icosphere.h" + +#include <GL/gl.h> + +using namespace Eigen; + +//-------------------------------------------------------------------------------- +// icosahedron data +//-------------------------------------------------------------------------------- +#define X .525731112119133606 +#define Z .850650808352039932 + +static GLfloat vdata[12][3] = { + {-X, 0.0, Z}, {X, 0.0, Z}, {-X, 0.0, -Z}, {X, 0.0, -Z}, + {0.0, Z, X}, {0.0, Z, -X}, {0.0, -Z, X}, {0.0, -Z, -X}, + {Z, X, 0.0}, {-Z, X, 0.0}, {Z, -X, 0.0}, {-Z, -X, 0.0} +}; + +static GLint tindices[20][3] = { + {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1}, + {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3}, + {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6}, + {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11} }; +//-------------------------------------------------------------------------------- + +IcoSphere::IcoSphere(unsigned int levels) +{ + // init with an icosahedron + for (int i = 0; i < 12; i++) + mVertices.push_back(Map<Vector3f>(vdata[i])); + mIndices.push_back(new std::vector<int>); + std::vector<int>& indices = *mIndices.back(); + for (int i = 0; i < 20; i++) + { + for (int k = 0; k < 3; k++) + indices.push_back(tindices[i][k]); + } + mListIds.push_back(0); + + while(mIndices.size()<levels) + _subdivide(); +} + +const std::vector<int>& IcoSphere::indices(int level) const +{ + while (level>=int(mIndices.size())) + const_cast<IcoSphere*>(this)->_subdivide(); + return *mIndices[level]; +} + +void IcoSphere::_subdivide(void) +{ + const std::vector<int>& indices = *mIndices.back(); + mIndices.push_back(new std::vector<int>); + std::vector<int>& refinedIndices = *mIndices.back(); + int end = indices.size(); + for (int i=0; i<end; i+=3) + { + int i0, i1, i2; + Vector3f v0 = mVertices[i0=indices[i+0]]; + Vector3f v1 = mVertices[i1=indices[i+1]]; + Vector3f v2 = mVertices[i2=indices[i+2]]; + int start = mVertices.size(); + mVertices.push_back( (v0+v1).normalized() ); + mVertices.push_back( (v1+v2).normalized() ); + mVertices.push_back( (v2+v0).normalized() ); + refinedIndices.push_back(i0); refinedIndices.push_back(start+0); refinedIndices.push_back(start+2); + refinedIndices.push_back(i1); refinedIndices.push_back(start+1); refinedIndices.push_back(start+0); + refinedIndices.push_back(i2); refinedIndices.push_back(start+2); refinedIndices.push_back(start+1); + refinedIndices.push_back(start+0); refinedIndices.push_back(start+1); refinedIndices.push_back(start+2); + } + mListIds.push_back(0); +} + +void IcoSphere::draw(int level) +{ + while (level>=int(mIndices.size())) + const_cast<IcoSphere*>(this)->_subdivide(); + if (mListIds[level]==0) + { + mListIds[level] = glGenLists(1); + glNewList(mListIds[level], GL_COMPILE); + glVertexPointer(3, GL_FLOAT, 0, mVertices[0].data()); + glNormalPointer(GL_FLOAT, 0, mVertices[0].data()); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glDrawElements(GL_TRIANGLES, mIndices[level]->size(), GL_UNSIGNED_INT, &(mIndices[level]->at(0))); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glEndList(); + } + glCallList(mListIds[level]); +} + + diff --git a/demos/opengl/icosphere.h b/demos/opengl/icosphere.h new file mode 100644 index 000000000..e5fa39217 --- /dev/null +++ b/demos/opengl/icosphere.h @@ -0,0 +1,45 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// Eigen is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// Alternatively, you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License and a copy of the GNU General Public License along with +// Eigen. If not, see <http://www.gnu.org/licenses/>. + +#ifndef EIGEN_ICOSPHERE_H +#define EIGEN_ICOSPHERE_H + +#include <Eigen/Core> +#include <vector> + +class IcoSphere +{ + public: + IcoSphere(unsigned int levels=1); + const std::vector<Eigen::Vector3f>& vertices() const { return mVertices; } + const std::vector<int>& indices(int level) const; + void draw(int level); + protected: + void _subdivide(); + std::vector<Eigen::Vector3f> mVertices; + std::vector<std::vector<int>*> mIndices; + std::vector<int> mListIds; +}; + +#endif // EIGEN_ICOSPHERE_H diff --git a/demos/opengl/quaternion_demo.cpp b/demos/opengl/quaternion_demo.cpp index aef6083e2..f4bea262d 100644 --- a/demos/opengl/quaternion_demo.cpp +++ b/demos/opengl/quaternion_demo.cpp @@ -23,6 +23,7 @@ // Eigen. If not, see <http://www.gnu.org/licenses/>. #include "quaternion_demo.h" +#include "icosphere.h" #include <Eigen/Array> #include <Eigen/QR> @@ -31,6 +32,11 @@ #include <QEvent> #include <QMouseEvent> #include <QInputDialog> +#include <QGridLayout> +#include <QButtonGroup> +#include <QRadioButton> +#include <QDockWidget> +#include <QPushButton> using namespace Eigen; @@ -57,21 +63,27 @@ inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b) Quaternionf(::lerp(alpha,OrientationType(a.orientation),OrientationType(b.orientation)))); } -QuaternionDemo::QuaternionDemo() +RenderingWidget::RenderingWidget() { mAnimate = false; - mTrackMode = TM_NO_TRACK; + mCurrentTrackingMode = TM_NO_TRACK; + mNavMode = NavTurnAround; + mLerpMode = LerpQuaternion; + mRotationMode = RotationStable; mTrackball.setCamera(&mCamera); + + // required to capture key press events + setFocusPolicy(Qt::ClickFocus); } -void QuaternionDemo::grabFrame(void) +void RenderingWidget::grabFrame(void) { // ask user for a time bool ok = false; double t = 0; if (!m_timeline.empty()) t = (--m_timeline.end())->first + 1.; - t = QInputDialog::getDouble(this, "Eigen's QuaternionDemo", "time value: ", + t = QInputDialog::getDouble(this, "Eigen's RenderingWidget", "time value: ", t, 0, 1e3, 1, &ok); if (ok) { @@ -82,20 +94,47 @@ void QuaternionDemo::grabFrame(void) } } -void QuaternionDemo::drawScene() +void RenderingWidget::drawScene() { 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)); -} - -void QuaternionDemo::drawPath() -{ + // draw the fractal object + float sqrt3 = ei_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); + + glColor3f(0.4, 0.7, 0.4); + 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 QuaternionDemo::animate() +void RenderingWidget::animate() { m_alpha += double(m_timer.interval()) * 1e-3; @@ -130,7 +169,7 @@ void QuaternionDemo::animate() updateGL(); } -void QuaternionDemo::keyPressEvent(QKeyEvent * e) +void RenderingWidget::keyPressEvent(QKeyEvent * e) { switch(e->key()) { @@ -150,18 +189,8 @@ void QuaternionDemo::keyPressEvent(QKeyEvent * e) break; // move the camera to initial pos case Qt::Key_R: - { - if (mAnimate) - stopAnimation(); - m_timeline.clear(); - float duration = 3/*AngleAxisf(mCamera.orientation().inverse() - * mInitFrame.orientation).angle()*/; - Frame aux = mCamera.frame(); - aux.orientation = aux.orientation.inverse(); - aux.position = mCamera.viewMatrix().translation(); - m_timeline[0] = aux; - m_timeline[duration] = mInitFrame; - } + resetCamera(); + break; // start/stop the animation case Qt::Key_A: if (mAnimate) @@ -183,7 +212,7 @@ void QuaternionDemo::keyPressEvent(QKeyEvent * e) updateGL(); } -void QuaternionDemo::stopAnimation() +void RenderingWidget::stopAnimation() { disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(animate())); m_timer.stop(); @@ -191,46 +220,48 @@ void QuaternionDemo::stopAnimation() m_alpha = 0; } -void QuaternionDemo::mousePressEvent(QMouseEvent* e) +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(e->modifiers()&Qt::ControlModifier) + if(fly) { - mTrackMode = TM_QUAKE_ROTATE; + mCurrentTrackingMode = TM_LOCAL_ROTATE; + mTrackball.start(Trackball::Local); } else { - mTrackMode = TM_ROTATE_AROUND; - mTrackball.reset(); - mTrackball.track(mMouseCoords); + mCurrentTrackingMode = TM_ROTATE_AROUND; + mTrackball.start(Trackball::Around); } + mTrackball.track(mMouseCoords); break; case Qt::MidButton: - if(e->modifiers()&Qt::ControlModifier) - mTrackMode = TM_QUAKE_WALK; + if(fly) + mCurrentTrackingMode = TM_FLY_Z; else - mTrackMode = TM_ZOOM; + mCurrentTrackingMode = TM_ZOOM; break; case Qt::RightButton: - mTrackMode = TM_QUAKE_PAN; + mCurrentTrackingMode = TM_FLY_PAN; break; default: break; } } -void QuaternionDemo::mouseReleaseEvent(QMouseEvent*) +void RenderingWidget::mouseReleaseEvent(QMouseEvent*) { - mTrackMode = TM_NO_TRACK; + mCurrentTrackingMode = TM_NO_TRACK; updateGL(); } -void QuaternionDemo::mouseMoveEvent(QMouseEvent* e) +void RenderingWidget::mouseMoveEvent(QMouseEvent* e) { // tracking - if(mTrackMode != TM_NO_TRACK) + 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()); @@ -241,23 +272,21 @@ void QuaternionDemo::mouseMoveEvent(QMouseEvent* e) dy *= 10.; } - switch(mTrackMode) + switch(mCurrentTrackingMode) { - case TM_ROTATE_AROUND : + case TM_ROTATE_AROUND: + case TM_LOCAL_ROTATE: mTrackball.track(Vector2i(e->pos().x(), e->pos().y())); break; case TM_ZOOM : mCamera.zoom(dy*50); break; - case TM_QUAKE_WALK : - mCamera.localTranslate(Vector3f(0, 0, dy*100)); + case TM_FLY_Z : + mCamera.localTranslate(Vector3f(0, 0, -dy*100)); break; - case TM_QUAKE_PAN : + case TM_FLY_PAN : mCamera.localTranslate(Vector3f(dx*100, dy*100, 0)); break; - case TM_QUAKE_ROTATE : - mCamera.localRotate(-dx*M_PI, dy*M_PI); - break; default: break; } @@ -268,7 +297,7 @@ void QuaternionDemo::mouseMoveEvent(QMouseEvent* e) mMouseCoords = Vector2i(e->pos().x(), e->pos().y()); } -void QuaternionDemo::paintGL() +void RenderingWidget::paintGL() { glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); @@ -288,26 +317,242 @@ void QuaternionDemo::paintGL() drawScene(); } -void QuaternionDemo::initializeGL() +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); - mInitFrame.orientation = mCamera.viewMatrix().linear(); + mCamera.setPosition(Vector3f(-200, -200, -200)); + mCamera.setTarget(Vector3f(0, 0, 0)); + mInitFrame.orientation = mCamera.orientation().inverse(); mInitFrame.position = mCamera.viewMatrix().translation(); + + // create a kind of fractal sphere + { + IcoSphere pattern; + + int levels = 3; + float scale = 0.45; + float radius = 100; + std::vector<Vector3f> centers; + std::vector<int> parents; + std::vector<float> radii; + centers.push_back(Vector3f::Zero()); + parents.push_back(-1); + radii.push_back(radius); + radius *= scale; + + // generate level 1 using icosphere vertices + { + float dist = radii[0]*0.9; + for (int i=0; i<12; ++i) + { + centers.push_back(pattern.vertices()[i] * dist); + radii.push_back(radius); + parents.push_back(0); + } + } + + scale = 0.33; + 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}; + + // generate other levels + int start = 1; + float maxAngle = M_PI/2; + for (int l=1; l<levels; l++) + { + radius *= scale; + int end = centers.size(); + for (int i=start; i<end; ++i) + { + Vector3f c = centers[i]; + Vector3f ax0, ax1; + if (parents[i]==-1) + ax0 = Vector3f::UnitZ(); + else + ax0 = (c - centers[parents[i]]).normalized(); + ax1 = ax0.unitOrthogonal(); + Quaternionf q; + q.setFromTwoVectors(Vector3f::UnitZ(), ax0); + Transform3f t = Translation3f(c) * q * Scaling3f(radii[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)*(radii[i] + radius*0.8); + centers.push_back(newC); + radii.push_back(radius); + parents.push_back(i); + } + } + start = end; + maxAngle = M_PI/2; + } + parents.clear(); + // instanciate the geometry + { + const std::vector<int>& sphereIndices = pattern.indices(2); + std::cout << "instanciate geometry... (" << sphereIndices.size() * centers.size() << " vertices)\n"; + mVertices.reserve(sphereIndices.size() * centers.size()); + mNormals.reserve(sphereIndices.size() * centers.size()); + int end = centers.size(); + for (int i=0; i<end; ++i) + { + Transform3f t = Translation3f(centers[i]) * Scaling3f(radii[i]); + // copy vertices + for (unsigned int j=0; j<sphereIndices.size(); ++j) + { + Vector3f v = pattern.vertices()[sphereIndices[j]]; + mVertices.push_back(t * v); + mNormals.push_back(v); + } + } + } + } } -void QuaternionDemo::resizeGL(int width, int height) +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()); + + // compute the rotation duration to move the camera to the target + Frame aux1 = mCamera.frame(); + aux1.orientation = aux1.orientation.inverse(); + aux1.position = mCamera.viewMatrix().translation(); + float rangle = AngleAxisf(aux0.orientation.inverse() * aux1.orientation).angle(); + if (rangle>M_PI) + rangle = 2.*M_PI - rangle; + float duration = rangle * 0.9; + if (duration<0.1) duration = 0.1; + + // put the camera at that time step: + aux1 = aux0.lerp(duration/2,mInitFrame); + // and make it look at teh target again + aux1.orientation = aux1.orientation.inverse(); + aux1.position = - (aux1.orientation * aux1.position); + mCamera.setFrame(aux1); + mCamera.setTarget(Vector3f::Zero()); + + // add this camera keyframe + 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(); + + { + // 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"); + layout->addWidget(but); + connect(but, SIGNAL(clicked()), this, SLOT(resetCamera())); + } + { + // track ball, rotation mode + QButtonGroup* group = new QButtonGroup(panel); + QRadioButton* but; + but = new QRadioButton("stable trackball"); + group->addButton(but, RotationStable); + layout->addWidget(but); + but = new QRadioButton("standard rotation"); + group->addButton(but, RotationStandard); + layout->addWidget(but); + but->setEnabled(false); + group->button(mRotationMode)->setChecked(true); + connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setRotationMode(int))); + } + { + // interpolation mode + QButtonGroup* group = new QButtonGroup(panel); + QRadioButton* but; + but = new QRadioButton("quaternion slerp"); + group->addButton(but, LerpQuaternion); + layout->addWidget(but); + but = new QRadioButton("euler angles"); + group->addButton(but, LerpEulerAngles); + layout->addWidget(but); + but->setEnabled(false); + group->button(mNavMode)->setChecked(true); + connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setLerpMode(int))); + } + 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[]) { QApplication app(argc, argv); QuaternionDemo demo; + demo.resize(600,500); demo.show(); return app.exec(); } diff --git a/demos/opengl/quaternion_demo.h b/demos/opengl/quaternion_demo.h index 11b003097..f0b883e19 100644 --- a/demos/opengl/quaternion_demo.h +++ b/demos/opengl/quaternion_demo.h @@ -32,8 +32,9 @@ #include <QTimer> #include <QtGui/QApplication> #include <QtOpenGL/QGLWidget> +#include <QtGui/QMainWindow> -class QuaternionDemo : public QGLWidget +class RenderingWidget : public QGLWidget { Q_OBJECT @@ -45,14 +46,31 @@ class QuaternionDemo : public QGLWidget bool mAnimate; float m_alpha; - enum TrackMode { TM_NO_TRACK=0, TM_ROTATE_AROUND, TM_ZOOM, - TM_QUAKE_ROTATE, TM_QUAKE_WALK, TM_QUAKE_PAN + TM_LOCAL_ROTATE, TM_FLY_Z, TM_FLY_PAN + }; + + enum NavMode { + NavTurnAround, + NavFly + }; + + enum LerpMode { + LerpQuaternion, + LerpEulerAngles + }; + + enum RotationMode { + RotationStable, + RotationStandard }; Camera mCamera; - TrackMode mTrackMode; + TrackMode mCurrentTrackingMode; + NavMode mNavMode; + LerpMode mLerpMode; + RotationMode mRotationMode; Vector2i mMouseCoords; Trackball mTrackball; @@ -60,15 +78,23 @@ class QuaternionDemo : public QGLWidget void setupCamera(); + std::vector<Vector3f> mVertices; + std::vector<Vector3f> mNormals; + std::vector<int> mIndices; + protected slots: virtual void animate(void); virtual void drawScene(void); - virtual void drawPath(void); virtual void grabFrame(void); virtual void stopAnimation(); + virtual void setNavMode(int); + virtual void setLerpMode(int); + virtual void setRotationMode(int); + virtual void resetCamera(); + protected: virtual void initializeGL(); @@ -83,8 +109,19 @@ class QuaternionDemo : public QGLWidget //-------------------------------------------------------------------------------- public: + RenderingWidget(); + ~RenderingWidget() { } + + QWidget* createNavigationControlWidget(); +}; + +class QuaternionDemo : public QMainWindow +{ + Q_OBJECT + public: QuaternionDemo(); - ~QuaternionDemo() { } + protected: + RenderingWidget* mRenderingWidget; }; #endif // EIGEN_QUATERNION_DEMO_H diff --git a/demos/opengl/trackball.cpp b/demos/opengl/trackball.cpp index f66243d0d..83e74a649 100644 --- a/demos/opengl/trackball.cpp +++ b/demos/opengl/trackball.cpp @@ -27,12 +27,12 @@ using namespace Eigen; -void Trackball::track(const Vector2i& newPoint2D) +void Trackball::track(const Vector2i& point2D) { if (mpCamera==0) return; Vector3f newPoint3D; - bool newPointOk = mapToSphere(newPoint2D, newPoint3D); + bool newPointOk = mapToSphere(point2D, newPoint3D); if (mLastPointOk && newPointOk) { @@ -40,12 +40,14 @@ void Trackball::track(const Vector2i& newPoint2D) float cos_angle = mLastPoint3D.dot(newPoint3D); if ( ei_abs(cos_angle) < 1.0 ) { - float angle = 2.0 * acos(cos_angle); - mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(angle, axis))); + float angle = acos(cos_angle); + if (mMode==Around) + mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(2.*angle, axis))); // *2 to speedup the rotation + else + mpCamera->localRotate(Quaternionf(AngleAxisf(-angle, axis))); } } - mLastPoint2D = newPoint2D; mLastPoint3D = newPoint3D; mLastPointOk = newPointOk; } diff --git a/demos/opengl/trackball.h b/demos/opengl/trackball.h index 29413becd..e9a899586 100644 --- a/demos/opengl/trackball.h +++ b/demos/opengl/trackball.h @@ -33,9 +33,11 @@ class Trackball { public: + enum Mode {Around, Local}; + Trackball() : mpCamera(0) {} - void reset() { mLastPointOk = false; } + void start(Mode m = Around) { mMode = m; mLastPointOk = false; } void setCamera(Camera* pCam) { mpCamera = pCam; } @@ -46,8 +48,8 @@ class Trackball bool mapToSphere( const Eigen::Vector2i& p2, Eigen::Vector3f& v3); Camera* mpCamera; - Eigen::Vector2i mLastPoint2D; Eigen::Vector3f mLastPoint3D; + Mode mMode; bool mLastPointOk; }; |