aboutsummaryrefslogtreecommitdiffhomepage
path: root/demos
diff options
context:
space:
mode:
authorGravatar Gael Guennebaud <g.gael@free.fr>2008-09-09 18:50:45 +0000
committerGravatar Gael Guennebaud <g.gael@free.fr>2008-09-09 18:50:45 +0000
commit146c9e449443453f3932b60cd1bab47f688403e7 (patch)
treef7f18d29a7a7755515db377b5de9658899de66bb /demos
parentd3a70b7facea2919b5ee0451d3c639dedb00ea30 (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.txt2
-rw-r--r--demos/opengl/camera.cpp11
-rw-r--r--demos/opengl/camera.h1
-rw-r--r--demos/opengl/gpuhelper.cpp78
-rw-r--r--demos/opengl/gpuhelper.h2
-rw-r--r--demos/opengl/icosphere.cpp119
-rw-r--r--demos/opengl/icosphere.h45
-rw-r--r--demos/opengl/quaternion_demo.cpp345
-rw-r--r--demos/opengl/quaternion_demo.h49
-rw-r--r--demos/opengl/trackball.cpp12
-rw-r--r--demos/opengl/trackball.h6
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;
};