diff options
Diffstat (limited to 'eigen/demos')
-rw-r--r-- | eigen/demos/CMakeLists.txt | 13 | ||||
-rw-r--r-- | eigen/demos/mandelbrot/CMakeLists.txt | 21 | ||||
-rw-r--r-- | eigen/demos/mandelbrot/README | 10 | ||||
-rw-r--r-- | eigen/demos/mandelbrot/mandelbrot.cpp | 213 | ||||
-rw-r--r-- | eigen/demos/mandelbrot/mandelbrot.h | 71 | ||||
-rw-r--r-- | eigen/demos/mix_eigen_and_c/README | 9 | ||||
-rw-r--r-- | eigen/demos/mix_eigen_and_c/binary_library.cpp | 185 | ||||
-rw-r--r-- | eigen/demos/mix_eigen_and_c/binary_library.h | 71 | ||||
-rw-r--r-- | eigen/demos/mix_eigen_and_c/example.c | 65 | ||||
-rw-r--r-- | eigen/demos/opengl/CMakeLists.txt | 28 | ||||
-rw-r--r-- | eigen/demos/opengl/README | 13 | ||||
-rw-r--r-- | eigen/demos/opengl/camera.cpp | 264 | ||||
-rw-r--r-- | eigen/demos/opengl/camera.h | 118 | ||||
-rw-r--r-- | eigen/demos/opengl/gpuhelper.cpp | 126 | ||||
-rw-r--r-- | eigen/demos/opengl/gpuhelper.h | 207 | ||||
-rw-r--r-- | eigen/demos/opengl/icosphere.cpp | 120 | ||||
-rw-r--r-- | eigen/demos/opengl/icosphere.h | 30 | ||||
-rw-r--r-- | eigen/demos/opengl/quaternion_demo.cpp | 656 | ||||
-rw-r--r-- | eigen/demos/opengl/quaternion_demo.h | 114 | ||||
-rw-r--r-- | eigen/demos/opengl/trackball.cpp | 59 | ||||
-rw-r--r-- | eigen/demos/opengl/trackball.h | 42 |
21 files changed, 2435 insertions, 0 deletions
diff --git a/eigen/demos/CMakeLists.txt b/eigen/demos/CMakeLists.txt new file mode 100644 index 0000000..b0d2edd --- /dev/null +++ b/eigen/demos/CMakeLists.txt @@ -0,0 +1,13 @@ +project(EigenDemos) + +add_custom_target(demos) + +if(NOT EIGEN_TEST_NOQT) + find_package(Qt4) + if(QT4_FOUND) + add_subdirectory(mandelbrot) + add_subdirectory(opengl) + else(QT4_FOUND) + message(STATUS "Qt4 not found, so disabling the mandelbrot and opengl demos") + endif(QT4_FOUND) +endif() diff --git a/eigen/demos/mandelbrot/CMakeLists.txt b/eigen/demos/mandelbrot/CMakeLists.txt new file mode 100644 index 0000000..5c500e0 --- /dev/null +++ b/eigen/demos/mandelbrot/CMakeLists.txt @@ -0,0 +1,21 @@ +find_package(Qt4 REQUIRED) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +if (CMAKE_COMPILER_IS_GNUCXX) + set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") + add_definitions ( "-DNDEBUG" ) +endif (CMAKE_COMPILER_IS_GNUCXX) + +include_directories( ${QT_INCLUDE_DIR} ) + +set(mandelbrot_SRCS + mandelbrot.cpp +) + +qt4_automoc(${mandelbrot_SRCS}) + +add_executable(mandelbrot ${mandelbrot_SRCS}) +add_dependencies(demos mandelbrot) + +target_link_libraries(mandelbrot ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}) diff --git a/eigen/demos/mandelbrot/README b/eigen/demos/mandelbrot/README new file mode 100644 index 0000000..a451d65 --- /dev/null +++ b/eigen/demos/mandelbrot/README @@ -0,0 +1,10 @@ +*** Mandelbrot demo *** + +Controls: +* Left mouse button to center view at a point. +* Drag vertically with left mouse button to zoom in and out. + +Be sure to enable SSE2 or AltiVec to improve performance. + +The number of iterations, and the choice between single and double precision, are +determined at runtime depending on the zoom level. diff --git a/eigen/demos/mandelbrot/mandelbrot.cpp b/eigen/demos/mandelbrot/mandelbrot.cpp new file mode 100644 index 0000000..5d575d5 --- /dev/null +++ b/eigen/demos/mandelbrot/mandelbrot.cpp @@ -0,0 +1,213 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "mandelbrot.h" +#include <iostream> +#include<QtGui/QPainter> +#include<QtGui/QImage> +#include<QtGui/QMouseEvent> +#include<QtCore/QTime> + +void MandelbrotWidget::resizeEvent(QResizeEvent *) +{ + if(size < width() * height()) + { + std::cout << "reallocate buffer" << std::endl; + size = width() * height(); + if(buffer) delete[]buffer; + buffer = new unsigned char[4*size]; + } +} + +template<typename T> struct iters_before_test { enum { ret = 8 }; }; +template<> struct iters_before_test<double> { enum { ret = 16 }; }; + +template<typename Real> void MandelbrotThread::render(int img_width, int img_height) +{ + enum { packetSize = Eigen::internal::packet_traits<Real>::size }; // number of reals in a Packet + typedef Eigen::Array<Real, packetSize, 1> Packet; // wrap a Packet as a vector + + enum { iters_before_test = iters_before_test<Real>::ret }; + max_iter = (max_iter / iters_before_test) * iters_before_test; + const int alignedWidth = (img_width/packetSize)*packetSize; + unsigned char *const buffer = widget->buffer; + const double xradius = widget->xradius; + const double yradius = xradius * img_height / img_width; + const int threadcount = widget->threadcount; + typedef Eigen::Array<Real, 2, 1> Vector2; + Vector2 start(widget->center.x() - widget->xradius, widget->center.y() - yradius); + Vector2 step(2*widget->xradius/img_width, 2*yradius/img_height); + total_iter = 0; + + for(int y = id; y < img_height; y += threadcount) + { + int pix = y * img_width; + + // for each pixel, we're going to do the iteration z := z^2 + c where z and c are complex numbers, + // starting with z = c = complex coord of the pixel. pzi and pzr denote the real and imaginary parts of z. + // pci and pcr denote the real and imaginary parts of c. + + Packet pzi_start, pci_start; + for(int i = 0; i < packetSize; i++) pzi_start[i] = pci_start[i] = start.y() + y * step.y(); + + for(int x = 0; x < alignedWidth; x += packetSize, pix += packetSize) + { + Packet pcr, pci = pci_start, pzr, pzi = pzi_start, pzr_buf; + for(int i = 0; i < packetSize; i++) pzr[i] = pcr[i] = start.x() + (x+i) * step.x(); + + // do the iterations. Every iters_before_test iterations we check for divergence, + // in which case we can stop iterating. + int j = 0; + typedef Eigen::Matrix<int, packetSize, 1> Packeti; + Packeti pix_iter = Packeti::Zero(), // number of iteration per pixel in the packet + pix_dont_diverge; // whether or not each pixel has already diverged + do + { + for(int i = 0; i < iters_before_test/4; i++) // peel the inner loop by 4 + { +# define ITERATE \ + pzr_buf = pzr; \ + pzr = pzr.square(); \ + pzr -= pzi.square(); \ + pzr += pcr; \ + pzi = (2*pzr_buf)*pzi; \ + pzi += pci; + ITERATE ITERATE ITERATE ITERATE + } + pix_dont_diverge = ((pzr.square() + pzi.square()) + .eval() // temporary fix as what follows is not yet vectorized by Eigen + <= Packet::Constant(4)) + // the 4 here is not a magic value, it's a math fact that if + // the square modulus is >4 then divergence is inevitable. + .template cast<int>(); + pix_iter += iters_before_test * pix_dont_diverge; + j++; + total_iter += iters_before_test * packetSize; + } + while(j < max_iter/iters_before_test && pix_dont_diverge.any()); // any() is not yet vectorized by Eigen + + // compute pixel colors + for(int i = 0; i < packetSize; i++) + { + buffer[4*(pix+i)] = 255*pix_iter[i]/max_iter; + buffer[4*(pix+i)+1] = 0; + buffer[4*(pix+i)+2] = 0; + } + } + + // if the width is not a multiple of packetSize, fill the remainder in black + for(int x = alignedWidth; x < img_width; x++, pix++) + buffer[4*pix] = buffer[4*pix+1] = buffer[4*pix+2] = 0; + } + return; +} + +void MandelbrotThread::run() +{ + setTerminationEnabled(true); + double resolution = widget->xradius*2/widget->width(); + max_iter = 128; + if(resolution < 1e-4f) max_iter += 128 * ( - 4 - std::log10(resolution)); + int img_width = widget->width()/widget->draft; + int img_height = widget->height()/widget->draft; + single_precision = resolution > 1e-7f; + + if(single_precision) + render<float>(img_width, img_height); + else + render<double>(img_width, img_height); +} + +void MandelbrotWidget::paintEvent(QPaintEvent *) +{ + static float max_speed = 0; + long long total_iter = 0; + + QTime time; + time.start(); + for(int th = 0; th < threadcount; th++) + threads[th]->start(QThread::LowPriority); + for(int th = 0; th < threadcount; th++) + { + threads[th]->wait(); + total_iter += threads[th]->total_iter; + } + int elapsed = time.elapsed(); + + if(draft == 1) + { + float speed = elapsed ? float(total_iter)*1000/elapsed : 0; + max_speed = std::max(max_speed, speed); + std::cout << threadcount << " threads, " + << elapsed << " ms, " + << speed << " iters/s (max " << max_speed << ")" << std::endl; + int packetSize = threads[0]->single_precision + ? int(Eigen::internal::packet_traits<float>::size) + : int(Eigen::internal::packet_traits<double>::size); + setWindowTitle(QString("resolution ")+QString::number(xradius*2/width(), 'e', 2) + +QString(", %1 iterations per pixel, ").arg(threads[0]->max_iter) + +(threads[0]->single_precision ? QString("single ") : QString("double ")) + +QString("precision, ") + +(packetSize==1 ? QString("no vectorization") + : QString("vectorized (%1 per packet)").arg(packetSize))); + } + + QImage image(buffer, width()/draft, height()/draft, QImage::Format_RGB32); + QPainter painter(this); + painter.drawImage(QPoint(0, 0), image.scaled(width(), height())); + + if(draft>1) + { + draft /= 2; + setWindowTitle(QString("recomputing at 1/%1 resolution...").arg(draft)); + update(); + } +} + +void MandelbrotWidget::mousePressEvent(QMouseEvent *event) +{ + if( event->buttons() & Qt::LeftButton ) + { + lastpos = event->pos(); + double yradius = xradius * height() / width(); + center = Eigen::Vector2d(center.x() + (event->pos().x() - width()/2) * xradius * 2 / width(), + center.y() + (event->pos().y() - height()/2) * yradius * 2 / height()); + draft = 16; + for(int th = 0; th < threadcount; th++) + threads[th]->terminate(); + update(); + } +} + +void MandelbrotWidget::mouseMoveEvent(QMouseEvent *event) +{ + QPoint delta = event->pos() - lastpos; + lastpos = event->pos(); + if( event->buttons() & Qt::LeftButton ) + { + double t = 1 + 5 * double(delta.y()) / height(); + if(t < 0.5) t = 0.5; + if(t > 2) t = 2; + xradius *= t; + draft = 16; + for(int th = 0; th < threadcount; th++) + threads[th]->terminate(); + update(); + } +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MandelbrotWidget w; + w.show(); + return app.exec(); +} + +#include "mandelbrot.moc" diff --git a/eigen/demos/mandelbrot/mandelbrot.h b/eigen/demos/mandelbrot/mandelbrot.h new file mode 100644 index 0000000..a687fd0 --- /dev/null +++ b/eigen/demos/mandelbrot/mandelbrot.h @@ -0,0 +1,71 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MANDELBROT_H +#define MANDELBROT_H + +#include <Eigen/Core> +#include <QtGui/QApplication> +#include <QtGui/QWidget> +#include <QtCore/QThread> + +class MandelbrotWidget; + +class MandelbrotThread : public QThread +{ + friend class MandelbrotWidget; + MandelbrotWidget *widget; + long long total_iter; + int id, max_iter; + bool single_precision; + + public: + MandelbrotThread(MandelbrotWidget *w, int i) : widget(w), id(i) {} + void run(); + template<typename Real> void render(int img_width, int img_height); +}; + +class MandelbrotWidget : public QWidget +{ + Q_OBJECT + + friend class MandelbrotThread; + Eigen::Vector2d center; + double xradius; + int size; + unsigned char *buffer; + QPoint lastpos; + int draft; + MandelbrotThread **threads; + int threadcount; + + protected: + void resizeEvent(QResizeEvent *); + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + public: + MandelbrotWidget() : QWidget(), center(0,0), xradius(2), + size(0), buffer(0), draft(16) + { + setAutoFillBackground(false); + threadcount = QThread::idealThreadCount(); + threads = new MandelbrotThread*[threadcount]; + for(int th = 0; th < threadcount; th++) threads[th] = new MandelbrotThread(this, th); + } + ~MandelbrotWidget() + { + if(buffer) delete[]buffer; + for(int th = 0; th < threadcount; th++) delete threads[th]; + delete[] threads; + } +}; + +#endif // MANDELBROT_H diff --git a/eigen/demos/mix_eigen_and_c/README b/eigen/demos/mix_eigen_and_c/README new file mode 100644 index 0000000..21dba86 --- /dev/null +++ b/eigen/demos/mix_eigen_and_c/README @@ -0,0 +1,9 @@ +This is an example of how one can wrap some of Eigen into a C library. + +To try this with GCC, do: + + g++ -c binary_library.cpp -O2 -msse2 -I ../.. + gcc example.c binary_library.o -o example -lstdc++ + ./example + +TODO: add CMakeLists, add more explanations here
\ No newline at end of file diff --git a/eigen/demos/mix_eigen_and_c/binary_library.cpp b/eigen/demos/mix_eigen_and_c/binary_library.cpp new file mode 100644 index 0000000..15a2d03 --- /dev/null +++ b/eigen/demos/mix_eigen_and_c/binary_library.cpp @@ -0,0 +1,185 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// This C++ file compiles to binary code that can be linked to by your C program, +// thanks to the extern "C" syntax used in the declarations in binary_library.h. + +#include "binary_library.h" + +#include <Eigen/Core> + +using namespace Eigen; + +/************************* pointer conversion methods **********************************************/ + +////// class MatrixXd ////// + +inline MatrixXd& c_to_eigen(C_MatrixXd* ptr) +{ + return *reinterpret_cast<MatrixXd*>(ptr); +} + +inline const MatrixXd& c_to_eigen(const C_MatrixXd* ptr) +{ + return *reinterpret_cast<const MatrixXd*>(ptr); +} + +inline C_MatrixXd* eigen_to_c(MatrixXd& ref) +{ + return reinterpret_cast<C_MatrixXd*>(&ref); +} + +inline const C_MatrixXd* eigen_to_c(const MatrixXd& ref) +{ + return reinterpret_cast<const C_MatrixXd*>(&ref); +} + +////// class Map<MatrixXd> ////// + +inline Map<MatrixXd>& c_to_eigen(C_Map_MatrixXd* ptr) +{ + return *reinterpret_cast<Map<MatrixXd>*>(ptr); +} + +inline const Map<MatrixXd>& c_to_eigen(const C_Map_MatrixXd* ptr) +{ + return *reinterpret_cast<const Map<MatrixXd>*>(ptr); +} + +inline C_Map_MatrixXd* eigen_to_c(Map<MatrixXd>& ref) +{ + return reinterpret_cast<C_Map_MatrixXd*>(&ref); +} + +inline const C_Map_MatrixXd* eigen_to_c(const Map<MatrixXd>& ref) +{ + return reinterpret_cast<const C_Map_MatrixXd*>(&ref); +} + + +/************************* implementation of classes **********************************************/ + + +////// class MatrixXd ////// + + +C_MatrixXd* MatrixXd_new(int rows, int cols) +{ + return eigen_to_c(*new MatrixXd(rows,cols)); +} + +void MatrixXd_delete(C_MatrixXd *m) +{ + delete &c_to_eigen(m); +} + +double* MatrixXd_data(C_MatrixXd *m) +{ + return c_to_eigen(m).data(); +} + +void MatrixXd_set_zero(C_MatrixXd *m) +{ + c_to_eigen(m).setZero(); +} + +void MatrixXd_resize(C_MatrixXd *m, int rows, int cols) +{ + c_to_eigen(m).resize(rows,cols); +} + +void MatrixXd_copy(C_MatrixXd *dst, const C_MatrixXd *src) +{ + c_to_eigen(dst) = c_to_eigen(src); +} + +void MatrixXd_copy_map(C_MatrixXd *dst, const C_Map_MatrixXd *src) +{ + c_to_eigen(dst) = c_to_eigen(src); +} + +void MatrixXd_set_coeff(C_MatrixXd *m, int i, int j, double coeff) +{ + c_to_eigen(m)(i,j) = coeff; +} + +double MatrixXd_get_coeff(const C_MatrixXd *m, int i, int j) +{ + return c_to_eigen(m)(i,j); +} + +void MatrixXd_print(const C_MatrixXd *m) +{ + std::cout << c_to_eigen(m) << std::endl; +} + +void MatrixXd_multiply(const C_MatrixXd *m1, const C_MatrixXd *m2, C_MatrixXd *result) +{ + c_to_eigen(result) = c_to_eigen(m1) * c_to_eigen(m2); +} + +void MatrixXd_add(const C_MatrixXd *m1, const C_MatrixXd *m2, C_MatrixXd *result) +{ + c_to_eigen(result) = c_to_eigen(m1) + c_to_eigen(m2); +} + + + +////// class Map_MatrixXd ////// + + +C_Map_MatrixXd* Map_MatrixXd_new(double *array, int rows, int cols) +{ + return eigen_to_c(*new Map<MatrixXd>(array,rows,cols)); +} + +void Map_MatrixXd_delete(C_Map_MatrixXd *m) +{ + delete &c_to_eigen(m); +} + +void Map_MatrixXd_set_zero(C_Map_MatrixXd *m) +{ + c_to_eigen(m).setZero(); +} + +void Map_MatrixXd_copy(C_Map_MatrixXd *dst, const C_Map_MatrixXd *src) +{ + c_to_eigen(dst) = c_to_eigen(src); +} + +void Map_MatrixXd_copy_matrix(C_Map_MatrixXd *dst, const C_MatrixXd *src) +{ + c_to_eigen(dst) = c_to_eigen(src); +} + +void Map_MatrixXd_set_coeff(C_Map_MatrixXd *m, int i, int j, double coeff) +{ + c_to_eigen(m)(i,j) = coeff; +} + +double Map_MatrixXd_get_coeff(const C_Map_MatrixXd *m, int i, int j) +{ + return c_to_eigen(m)(i,j); +} + +void Map_MatrixXd_print(const C_Map_MatrixXd *m) +{ + std::cout << c_to_eigen(m) << std::endl; +} + +void Map_MatrixXd_multiply(const C_Map_MatrixXd *m1, const C_Map_MatrixXd *m2, C_Map_MatrixXd *result) +{ + c_to_eigen(result) = c_to_eigen(m1) * c_to_eigen(m2); +} + +void Map_MatrixXd_add(const C_Map_MatrixXd *m1, const C_Map_MatrixXd *m2, C_Map_MatrixXd *result) +{ + c_to_eigen(result) = c_to_eigen(m1) + c_to_eigen(m2); +} diff --git a/eigen/demos/mix_eigen_and_c/binary_library.h b/eigen/demos/mix_eigen_and_c/binary_library.h new file mode 100644 index 0000000..0b983ad --- /dev/null +++ b/eigen/demos/mix_eigen_and_c/binary_library.h @@ -0,0 +1,71 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// This is a pure C header, no C++ here. +// The functions declared here will be implemented in C++ but +// we don't have to know, because thanks to the extern "C" syntax, +// they will be compiled to C object code. + +#ifdef __cplusplus +extern "C" +{ +#endif + + // just dummy empty structs to give different pointer types, + // instead of using void* which would be type unsafe + struct C_MatrixXd {}; + struct C_Map_MatrixXd {}; + + // the C_MatrixXd class, wraps some of the functionality + // of Eigen::MatrixXd. + struct C_MatrixXd* MatrixXd_new(int rows, int cols); + void MatrixXd_delete (struct C_MatrixXd *m); + double* MatrixXd_data (struct C_MatrixXd *m); + void MatrixXd_set_zero (struct C_MatrixXd *m); + void MatrixXd_resize (struct C_MatrixXd *m, int rows, int cols); + void MatrixXd_copy (struct C_MatrixXd *dst, + const struct C_MatrixXd *src); + void MatrixXd_copy_map (struct C_MatrixXd *dst, + const struct C_Map_MatrixXd *src); + void MatrixXd_set_coeff (struct C_MatrixXd *m, + int i, int j, double coeff); + double MatrixXd_get_coeff (const struct C_MatrixXd *m, + int i, int j); + void MatrixXd_print (const struct C_MatrixXd *m); + void MatrixXd_add (const struct C_MatrixXd *m1, + const struct C_MatrixXd *m2, + struct C_MatrixXd *result); + void MatrixXd_multiply (const struct C_MatrixXd *m1, + const struct C_MatrixXd *m2, + struct C_MatrixXd *result); + + // the C_Map_MatrixXd class, wraps some of the functionality + // of Eigen::Map<MatrixXd> + struct C_Map_MatrixXd* Map_MatrixXd_new(double *array, int rows, int cols); + void Map_MatrixXd_delete (struct C_Map_MatrixXd *m); + void Map_MatrixXd_set_zero (struct C_Map_MatrixXd *m); + void Map_MatrixXd_copy (struct C_Map_MatrixXd *dst, + const struct C_Map_MatrixXd *src); + void Map_MatrixXd_copy_matrix(struct C_Map_MatrixXd *dst, + const struct C_MatrixXd *src); + void Map_MatrixXd_set_coeff (struct C_Map_MatrixXd *m, + int i, int j, double coeff); + double Map_MatrixXd_get_coeff (const struct C_Map_MatrixXd *m, + int i, int j); + void Map_MatrixXd_print (const struct C_Map_MatrixXd *m); + void Map_MatrixXd_add (const struct C_Map_MatrixXd *m1, + const struct C_Map_MatrixXd *m2, + struct C_Map_MatrixXd *result); + void Map_MatrixXd_multiply (const struct C_Map_MatrixXd *m1, + const struct C_Map_MatrixXd *m2, + struct C_Map_MatrixXd *result); + +#ifdef __cplusplus +} // end extern "C" +#endif
\ No newline at end of file diff --git a/eigen/demos/mix_eigen_and_c/example.c b/eigen/demos/mix_eigen_and_c/example.c new file mode 100644 index 0000000..508eb54 --- /dev/null +++ b/eigen/demos/mix_eigen_and_c/example.c @@ -0,0 +1,65 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "binary_library.h" +#include "stdio.h" + +void demo_MatrixXd() +{ + struct C_MatrixXd *matrix1, *matrix2, *result; + printf("*** demo_MatrixXd ***\n"); + + matrix1 = MatrixXd_new(3, 3); + MatrixXd_set_zero(matrix1); + MatrixXd_set_coeff(matrix1, 0, 1, 2.5); + MatrixXd_set_coeff(matrix1, 1, 0, 1.4); + printf("Here is matrix1:\n"); + MatrixXd_print(matrix1); + + matrix2 = MatrixXd_new(3, 3); + MatrixXd_multiply(matrix1, matrix1, matrix2); + printf("Here is matrix1*matrix1:\n"); + MatrixXd_print(matrix2); + + MatrixXd_delete(matrix1); + MatrixXd_delete(matrix2); +} + +// this helper function takes a plain C array and prints it in one line +void print_array(double *array, int n) +{ + struct C_Map_MatrixXd *m = Map_MatrixXd_new(array, 1, n); + Map_MatrixXd_print(m); + Map_MatrixXd_delete(m); +} + +void demo_Map_MatrixXd() +{ + struct C_Map_MatrixXd *map; + double array[5]; + int i; + printf("*** demo_Map_MatrixXd ***\n"); + + for(i = 0; i < 5; ++i) array[i] = i; + printf("Initially, the array is:\n"); + print_array(array, 5); + + map = Map_MatrixXd_new(array, 5, 1); + Map_MatrixXd_add(map, map, map); + Map_MatrixXd_delete(map); + + printf("Now the array is:\n"); + print_array(array, 5); +} + +int main() +{ + demo_MatrixXd(); + demo_Map_MatrixXd(); +} diff --git a/eigen/demos/opengl/CMakeLists.txt b/eigen/demos/opengl/CMakeLists.txt new file mode 100644 index 0000000..299aa44 --- /dev/null +++ b/eigen/demos/opengl/CMakeLists.txt @@ -0,0 +1,28 @@ +find_package(Qt4) +find_package(OpenGL) + +if(QT4_FOUND AND OPENGL_FOUND) + + set(QT_USE_QTOPENGL TRUE) + include(${QT_USE_FILE}) + + set(CMAKE_INCLUDE_CURRENT_DIR ON) + + include_directories( ${QT_INCLUDE_DIR} ) + + set(quaternion_demo_SRCS gpuhelper.cpp icosphere.cpp camera.cpp trackball.cpp quaternion_demo.cpp) + + qt4_automoc(${quaternion_demo_SRCS}) + + add_executable(quaternion_demo ${quaternion_demo_SRCS}) + add_dependencies(demos quaternion_demo) + + target_link_libraries(quaternion_demo + ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} + ${QT_QTOPENGL_LIBRARY} ${OPENGL_LIBRARIES} ) + +else() + + message(STATUS "OpenGL demo disabled because Qt4 and/or OpenGL have not been found.") + +endif()
\ No newline at end of file diff --git a/eigen/demos/opengl/README b/eigen/demos/opengl/README new file mode 100644 index 0000000..8fb1649 --- /dev/null +++ b/eigen/demos/opengl/README @@ -0,0 +1,13 @@ + +Navigation: + left button: rotate around the target + middle button: zoom + left button + ctrl quake rotate (rotate around camera position) + middle button + ctrl walk (progress along camera's z direction) + left button: pan (translate in the XY camera's plane) + +R : move the camera to initial position +A : start/stop animation +C : clear the animation +G : add a key frame + diff --git a/eigen/demos/opengl/camera.cpp b/eigen/demos/opengl/camera.cpp new file mode 100644 index 0000000..8a2344c --- /dev/null +++ b/eigen/demos/opengl/camera.cpp @@ -0,0 +1,264 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "camera.h" + +#include "gpuhelper.h" +#include <GL/glu.h> + +#include "Eigen/LU" +using namespace Eigen; + +Camera::Camera() + : mViewIsUptodate(false), mProjIsUptodate(false) +{ + mViewMatrix.setIdentity(); + + mFovY = M_PI/3.; + mNearDist = 1.; + mFarDist = 50000.; + + mVpX = 0; + mVpY = 0; + + setPosition(Vector3f::Constant(100.)); + setTarget(Vector3f::Zero()); +} + +Camera& Camera::operator=(const Camera& other) +{ + mViewIsUptodate = false; + mProjIsUptodate = false; + + mVpX = other.mVpX; + mVpY = other.mVpY; + mVpWidth = other.mVpWidth; + mVpHeight = other.mVpHeight; + + mTarget = other.mTarget; + mFovY = other.mFovY; + mNearDist = other.mNearDist; + mFarDist = other.mFarDist; + + mViewMatrix = other.mViewMatrix; + mProjectionMatrix = other.mProjectionMatrix; + + return *this; +} + +Camera::Camera(const Camera& other) +{ + *this = other; +} + +Camera::~Camera() +{ +} + + +void Camera::setViewport(uint offsetx, uint offsety, uint width, uint height) +{ + mVpX = offsetx; + mVpY = offsety; + mVpWidth = width; + mVpHeight = height; + + mProjIsUptodate = false; +} + +void Camera::setViewport(uint width, uint height) +{ + mVpWidth = width; + mVpHeight = height; + + mProjIsUptodate = false; +} + +void Camera::setFovY(float value) +{ + mFovY = value; + mProjIsUptodate = false; +} + +Vector3f Camera::direction(void) const +{ + return - (orientation() * Vector3f::UnitZ()); +} +Vector3f Camera::up(void) const +{ + return orientation() * Vector3f::UnitY(); +} +Vector3f Camera::right(void) const +{ + return orientation() * Vector3f::UnitX(); +} + +void Camera::setDirection(const Vector3f& newDirection) +{ + // TODO implement it computing the rotation between newDirection and current dir ? + Vector3f up = this->up(); + + Matrix3f camAxes; + + camAxes.col(2) = (-newDirection).normalized(); + camAxes.col(0) = up.cross( camAxes.col(2) ).normalized(); + camAxes.col(1) = camAxes.col(2).cross( camAxes.col(0) ).normalized(); + setOrientation(Quaternionf(camAxes)); + + mViewIsUptodate = false; +} + +void Camera::setTarget(const Vector3f& target) +{ + mTarget = target; + if (!mTarget.isApprox(position())) + { + Vector3f newDirection = mTarget - position(); + setDirection(newDirection.normalized()); + } +} + +void Camera::setPosition(const Vector3f& p) +{ + mFrame.position = p; + mViewIsUptodate = false; +} + +void Camera::setOrientation(const Quaternionf& q) +{ + mFrame.orientation = q; + mViewIsUptodate = false; +} + +void Camera::setFrame(const Frame& f) +{ + mFrame = f; + mViewIsUptodate = false; +} + +void Camera::rotateAroundTarget(const Quaternionf& q) +{ + Matrix4f mrot, mt, mtm; + + // update the transform matrix + updateViewMatrix(); + Vector3f t = mViewMatrix * mTarget; + + mViewMatrix = Translation3f(t) + * q + * Translation3f(-t) + * mViewMatrix; + + Quaternionf qa(mViewMatrix.linear()); + qa = qa.conjugate(); + setOrientation(qa); + setPosition(- (qa * mViewMatrix.translation()) ); + + 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(); + if(dist > d) + { + setPosition(position() + direction() * d); + mViewIsUptodate = false; + } +} + +void Camera::localTranslate(const Vector3f& t) +{ + Vector3f trans = orientation() * t; + setPosition( position() + trans ); + setTarget( mTarget + trans ); + + mViewIsUptodate = false; +} + +void Camera::updateViewMatrix(void) const +{ + if(!mViewIsUptodate) + { + Quaternionf q = orientation().conjugate(); + mViewMatrix.linear() = q.toRotationMatrix(); + mViewMatrix.translation() = - (mViewMatrix.linear() * position()); + + mViewIsUptodate = true; + } +} + +const Affine3f& Camera::viewMatrix(void) const +{ + updateViewMatrix(); + return mViewMatrix; +} + +void Camera::updateProjectionMatrix(void) const +{ + if(!mProjIsUptodate) + { + mProjectionMatrix.setIdentity(); + float aspect = float(mVpWidth)/float(mVpHeight); + float theta = mFovY*0.5; + float range = mFarDist - mNearDist; + float invtan = 1./tan(theta); + + mProjectionMatrix(0,0) = invtan / aspect; + mProjectionMatrix(1,1) = invtan; + mProjectionMatrix(2,2) = -(mNearDist + mFarDist) / range; + mProjectionMatrix(3,2) = -1; + mProjectionMatrix(2,3) = -2 * mNearDist * mFarDist / range; + mProjectionMatrix(3,3) = 0; + + mProjIsUptodate = true; + } +} + +const Matrix4f& Camera::projectionMatrix(void) const +{ + updateProjectionMatrix(); + return mProjectionMatrix; +} + +void Camera::activateGL(void) +{ + glViewport(vpX(), vpY(), vpWidth(), vpHeight()); + gpu.loadMatrix(projectionMatrix(),GL_PROJECTION); + gpu.loadMatrix(viewMatrix().matrix(),GL_MODELVIEW); +} + + +Vector3f Camera::unProject(const Vector2f& uv, float depth) const +{ + Matrix4f inv = mViewMatrix.inverse().matrix(); + return unProject(uv, depth, inv); +} + +Vector3f Camera::unProject(const Vector2f& uv, float depth, const Matrix4f& invModelview) const +{ + updateViewMatrix(); + updateProjectionMatrix(); + + Vector3f a(2.*uv.x()/float(mVpWidth)-1., 2.*uv.y()/float(mVpHeight)-1., 1.); + a.x() *= depth/mProjectionMatrix(0,0); + a.y() *= depth/mProjectionMatrix(1,1); + a.z() = -depth; + // FIXME /\/| + Vector4f b = invModelview * Vector4f(a.x(), a.y(), a.z(), 1.); + return Vector3f(b.x(), b.y(), b.z()); +} diff --git a/eigen/demos/opengl/camera.h b/eigen/demos/opengl/camera.h new file mode 100644 index 0000000..15714d2 --- /dev/null +++ b/eigen/demos/opengl/camera.h @@ -0,0 +1,118 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_CAMERA_H +#define EIGEN_CAMERA_H + +#include <Eigen/Geometry> +#include <QObject> +// #include <frame.h> + +class Frame +{ + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + inline Frame(const Eigen::Vector3f& pos = Eigen::Vector3f::Zero(), + const Eigen::Quaternionf& o = Eigen::Quaternionf()) + : orientation(o), position(pos) + {} + Frame lerp(float alpha, const Frame& other) const + { + return Frame((1.f-alpha)*position + alpha * other.position, + orientation.slerp(alpha,other.orientation)); + } + + Eigen::Quaternionf orientation; + Eigen::Vector3f position; +}; + +class Camera +{ + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + Camera(void); + + Camera(const Camera& other); + + virtual ~Camera(); + + Camera& operator=(const Camera& other); + + void setViewport(uint offsetx, uint offsety, uint width, uint height); + void setViewport(uint width, uint height); + + inline uint vpX(void) const { return mVpX; } + inline uint vpY(void) const { return mVpY; } + inline uint vpWidth(void) const { return mVpWidth; } + inline uint vpHeight(void) const { return mVpHeight; } + + inline float fovY(void) const { return mFovY; } + void setFovY(float value); + + void setPosition(const Eigen::Vector3f& pos); + inline const Eigen::Vector3f& position(void) const { return mFrame.position; } + + void setOrientation(const Eigen::Quaternionf& q); + inline const Eigen::Quaternionf& orientation(void) const { return mFrame.orientation; } + + void setFrame(const Frame& f); + const Frame& frame(void) const { return mFrame; } + + void setDirection(const Eigen::Vector3f& newDirection); + Eigen::Vector3f direction(void) const; + void setUp(const Eigen::Vector3f& vectorUp); + Eigen::Vector3f up(void) const; + Eigen::Vector3f right(void) const; + + void setTarget(const Eigen::Vector3f& target); + inline const Eigen::Vector3f& target(void) { return mTarget; } + + const Eigen::Affine3f& viewMatrix(void) const; + 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); + + /** Setup OpenGL matrices and viewport */ + void activateGL(void); + + Eigen::Vector3f unProject(const Eigen::Vector2f& uv, float depth, const Eigen::Matrix4f& invModelview) const; + Eigen::Vector3f unProject(const Eigen::Vector2f& uv, float depth) const; + + protected: + void updateViewMatrix(void) const; + void updateProjectionMatrix(void) const; + + protected: + + uint mVpX, mVpY; + uint mVpWidth, mVpHeight; + + Frame mFrame; + + mutable Eigen::Affine3f mViewMatrix; + mutable Eigen::Matrix4f mProjectionMatrix; + + mutable bool mViewIsUptodate; + mutable bool mProjIsUptodate; + + // used by rotateAroundTarget + Eigen::Vector3f mTarget; + + float mFovY; + float mNearDist; + float mFarDist; +}; + +#endif // EIGEN_CAMERA_H diff --git a/eigen/demos/opengl/gpuhelper.cpp b/eigen/demos/opengl/gpuhelper.cpp new file mode 100644 index 0000000..fd236b1 --- /dev/null +++ b/eigen/demos/opengl/gpuhelper.cpp @@ -0,0 +1,126 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "gpuhelper.h" +#include "icosphere.h" +#include <GL/glu.h> +// PLEASE don't look at this old code... ;) + +#include <fstream> +#include <algorithm> + +GpuHelper gpu; + +GpuHelper::GpuHelper() +{ + mVpWidth = mVpHeight = 0; + mCurrentMatrixTarget = 0; + mInitialized = false; +} + +GpuHelper::~GpuHelper() +{ +} + +void GpuHelper::pushProjectionMode2D(ProjectionMode2D pm) +{ + // switch to 2D projection + pushMatrix(Matrix4f::Identity(),GL_PROJECTION); + + if(pm==PM_Normalized) + { + //glOrtho(-1., 1., -1., 1., 0., 1.); + } + else if(pm==PM_Viewport) + { + GLint vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glOrtho(0., vp[2], 0., vp[3], -1., 1.); + } + + pushMatrix(Matrix4f::Identity(),GL_MODELVIEW); +} + +void GpuHelper::popProjectionMode2D(void) +{ + popMatrix(GL_PROJECTION); + popMatrix(GL_MODELVIEW); +} + +void GpuHelper::drawVector(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect /* = 50.*/) +{ + static GLUquadricObj *cylindre = gluNewQuadric(); + glColor4fv(color.data()); + float length = vec.norm(); + pushMatrix(GL_MODELVIEW); + glTranslatef(position.x(), position.y(), position.z()); + Vector3f ax = Matrix3f::Identity().col(2).cross(vec); + ax.normalize(); + Vector3f tmp = vec; + tmp.normalize(); + float angle = 180.f/M_PI * acos(tmp.z()); + if (angle>1e-3) + glRotatef(angle, ax.x(), ax.y(), ax.z()); + gluCylinder(cylindre, length/aspect, length/aspect, 0.8*length, 10, 10); + glTranslatef(0.0,0.0,0.8*length); + gluCylinder(cylindre, 2.0*length/aspect, 0.0, 0.2*length, 10, 10); + + popMatrix(GL_MODELVIEW); +} + +void GpuHelper::drawVectorBox(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect) +{ + static GLUquadricObj *cylindre = gluNewQuadric(); + glColor4fv(color.data()); + float length = vec.norm(); + pushMatrix(GL_MODELVIEW); + glTranslatef(position.x(), position.y(), position.z()); + Vector3f ax = Matrix3f::Identity().col(2).cross(vec); + ax.normalize(); + Vector3f tmp = vec; + tmp.normalize(); + float angle = 180.f/M_PI * acos(tmp.z()); + if (angle>1e-3) + glRotatef(angle, ax.x(), ax.y(), ax.z()); + gluCylinder(cylindre, length/aspect, length/aspect, 0.8*length, 10, 10); + glTranslatef(0.0,0.0,0.8*length); + glScalef(4.0*length/aspect,4.0*length/aspect,4.0*length/aspect); + drawUnitCube(); + popMatrix(GL_MODELVIEW); +} + +void GpuHelper::drawUnitCube(void) +{ + static float vertices[][3] = { + {-0.5,-0.5,-0.5}, + { 0.5,-0.5,-0.5}, + {-0.5, 0.5,-0.5}, + { 0.5, 0.5,-0.5}, + {-0.5,-0.5, 0.5}, + { 0.5,-0.5, 0.5}, + {-0.5, 0.5, 0.5}, + { 0.5, 0.5, 0.5}}; + + glBegin(GL_QUADS); + glNormal3f(0,0,-1); glVertex3fv(vertices[0]); glVertex3fv(vertices[2]); glVertex3fv(vertices[3]); glVertex3fv(vertices[1]); + glNormal3f(0,0, 1); glVertex3fv(vertices[4]); glVertex3fv(vertices[5]); glVertex3fv(vertices[7]); glVertex3fv(vertices[6]); + glNormal3f(0,-1,0); glVertex3fv(vertices[0]); glVertex3fv(vertices[1]); glVertex3fv(vertices[5]); glVertex3fv(vertices[4]); + glNormal3f(0, 1,0); glVertex3fv(vertices[2]); glVertex3fv(vertices[6]); glVertex3fv(vertices[7]); glVertex3fv(vertices[3]); + glNormal3f(-1,0,0); glVertex3fv(vertices[0]); glVertex3fv(vertices[4]); glVertex3fv(vertices[6]); glVertex3fv(vertices[2]); + glNormal3f( 1,0,0); glVertex3fv(vertices[1]); glVertex3fv(vertices[3]); glVertex3fv(vertices[7]); glVertex3fv(vertices[5]); + glEnd(); +} + +void GpuHelper::drawUnitSphere(int level) +{ + static IcoSphere sphere; + sphere.draw(level); +} + + diff --git a/eigen/demos/opengl/gpuhelper.h b/eigen/demos/opengl/gpuhelper.h new file mode 100644 index 0000000..9ff98e9 --- /dev/null +++ b/eigen/demos/opengl/gpuhelper.h @@ -0,0 +1,207 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_GPUHELPER_H +#define EIGEN_GPUHELPER_H + +#include <Eigen/Geometry> +#include <GL/gl.h> +#include <vector> + +using namespace Eigen; + +typedef Vector4f Color; + +class GpuHelper +{ + public: + + GpuHelper(); + + ~GpuHelper(); + + enum ProjectionMode2D { PM_Normalized = 1, PM_Viewport = 2 }; + void pushProjectionMode2D(ProjectionMode2D pm); + void popProjectionMode2D(); + + /** Multiply the OpenGL matrix \a matrixTarget by the matrix \a mat. + Essentially, this helper function automatically calls glMatrixMode(matrixTarget) if required + and does a proper call to the right glMultMatrix*() function according to the scalar type + and storage order. + \warning glMatrixMode() must never be called directly. If your're unsure, use forceMatrixMode(). + \sa Matrix, loadMatrix(), forceMatrixMode() + */ + template<typename Scalar, int _Flags> + void multMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget); + + /** Load the matrix \a mat to the OpenGL matrix \a matrixTarget. + Essentially, this helper function automatically calls glMatrixMode(matrixTarget) if required + and does a proper call to the right glLoadMatrix*() or glLoadIdentity() function according to the scalar type + and storage order. + \warning glMatrixMode() must never be called directly. If your're unsure, use forceMatrixMode(). + \sa Matrix, multMatrix(), forceMatrixMode() + */ + template<typename Scalar, int _Flags> + void loadMatrix(const Eigen::Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget); + + template<typename Scalar, typename Derived> + void loadMatrix( + const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&, + GLenum matrixTarget); + + /** Make the matrix \a matrixTarget the current OpenGL matrix target. + Call this function before loadMatrix() or multMatrix() if you cannot guarantee that glMatrixMode() + has never been called after the last loadMatrix() or multMatrix() calls. + \todo provides a debug mode checking the sanity of the cached matrix mode. + */ + inline void forceMatrixTarget(GLenum matrixTarget) {glMatrixMode(mCurrentMatrixTarget=matrixTarget);} + + inline void setMatrixTarget(GLenum matrixTarget); + + /** Push the OpenGL matrix \a matrixTarget and load \a mat. + */ + template<typename Scalar, int _Flags> + inline void pushMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget); + + template<typename Scalar, typename Derived> + void pushMatrix( + const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&, + GLenum matrixTarget); + + /** Push and clone the OpenGL matrix \a matrixTarget + */ + inline void pushMatrix(GLenum matrixTarget); + + /** Pop the OpenGL matrix \a matrixTarget + */ + inline void popMatrix(GLenum matrixTarget); + + 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 drawUnitSphere(int level=0); + + /// draw the \a nofElement first elements + inline void draw(GLenum mode, uint nofElement); + + /// draw a range of elements + inline void draw(GLenum mode, uint start, uint end); + + /// draw an indexed subset + inline void draw(GLenum mode, const std::vector<uint>* pIndexes); + +protected: + + void update(void); + + GLuint mColorBufferId; + int mVpWidth, mVpHeight; + GLenum mCurrentMatrixTarget; + bool mInitialized; +}; + +/** Singleton shortcut +*/ +extern GpuHelper gpu; + + +/** \internal +*/ +template<bool RowMajor, int _Flags> struct GlMatrixHelper; + +template<int _Flags> struct GlMatrixHelper<false,_Flags> +{ + static void loadMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glLoadMatrixf(mat.data()); } + static void loadMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glLoadMatrixd(mat.data()); } + static void multMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glMultMatrixf(mat.data()); } + static void multMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glMultMatrixd(mat.data()); } +}; + +template<int _Flags> struct GlMatrixHelper<true,_Flags> +{ + static void loadMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glLoadMatrixf(mat.transpose().eval().data()); } + static void loadMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glLoadMatrixd(mat.transpose().eval().data()); } + static void multMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glMultMatrixf(mat.transpose().eval().data()); } + static void multMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glMultMatrixd(mat.transpose().eval().data()); } +}; + +inline void GpuHelper::setMatrixTarget(GLenum matrixTarget) +{ + if (matrixTarget != mCurrentMatrixTarget) + glMatrixMode(mCurrentMatrixTarget=matrixTarget); +} + +template<typename Scalar, int _Flags> +void GpuHelper::multMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget) +{ + setMatrixTarget(matrixTarget); + GlMatrixHelper<_Flags&Eigen::RowMajorBit, _Flags>::multMatrix(mat); +} + +template<typename Scalar, typename Derived> +void GpuHelper::loadMatrix( + const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&, + GLenum matrixTarget) +{ + setMatrixTarget(matrixTarget); + glLoadIdentity(); +} + +template<typename Scalar, int _Flags> +void GpuHelper::loadMatrix(const Eigen::Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget) +{ + setMatrixTarget(matrixTarget); + GlMatrixHelper<(_Flags&Eigen::RowMajorBit)!=0, _Flags>::loadMatrix(mat); +} + +inline void GpuHelper::pushMatrix(GLenum matrixTarget) +{ + setMatrixTarget(matrixTarget); + glPushMatrix(); +} + +template<typename Scalar, int _Flags> +inline void GpuHelper::pushMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget) +{ + pushMatrix(matrixTarget); + GlMatrixHelper<_Flags&Eigen::RowMajorBit,_Flags>::loadMatrix(mat); +} + +template<typename Scalar, typename Derived> +void GpuHelper::pushMatrix( + const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&, + GLenum matrixTarget) +{ + pushMatrix(matrixTarget); + glLoadIdentity(); +} + +inline void GpuHelper::popMatrix(GLenum matrixTarget) +{ + setMatrixTarget(matrixTarget); + glPopMatrix(); +} + +inline void GpuHelper::draw(GLenum mode, uint nofElement) +{ + glDrawArrays(mode, 0, nofElement); +} + + +inline void GpuHelper::draw(GLenum mode, const std::vector<uint>* pIndexes) +{ + glDrawElements(mode, pIndexes->size(), GL_UNSIGNED_INT, &(pIndexes->front())); +} + +inline void GpuHelper::draw(GLenum mode, uint start, uint end) +{ + glDrawArrays(mode, start, end-start); +} + +#endif // EIGEN_GPUHELPER_H diff --git a/eigen/demos/opengl/icosphere.cpp b/eigen/demos/opengl/icosphere.cpp new file mode 100644 index 0000000..39444cb --- /dev/null +++ b/eigen/demos/opengl/icosphere.cpp @@ -0,0 +1,120 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "icosphere.h" + +#include <GL/gl.h> +#include <map> + +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) +{ + typedef unsigned long long Key; + std::map<Key,int> edgeMap; + 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 ids0[3], // indices of outer vertices + ids1[3]; // indices of edge vertices + for (int k=0; k<3; ++k) + { + int k1 = (k+1)%3; + int e0 = indices[i+k]; + int e1 = indices[i+k1]; + ids0[k] = e0; + if (e1>e0) + std::swap(e0,e1); + Key edgeKey = Key(e0) | (Key(e1)<<32); + std::map<Key,int>::iterator it = edgeMap.find(edgeKey); + if (it==edgeMap.end()) + { + ids1[k] = mVertices.size(); + edgeMap[edgeKey] = ids1[k]; + mVertices.push_back( (mVertices[e0]+mVertices[e1]).normalized() ); + } + else + ids1[k] = it->second; + } + refinedIndices.push_back(ids0[0]); refinedIndices.push_back(ids1[0]); refinedIndices.push_back(ids1[2]); + refinedIndices.push_back(ids0[1]); refinedIndices.push_back(ids1[1]); refinedIndices.push_back(ids1[0]); + refinedIndices.push_back(ids0[2]); refinedIndices.push_back(ids1[2]); refinedIndices.push_back(ids1[1]); + refinedIndices.push_back(ids1[0]); refinedIndices.push_back(ids1[1]); refinedIndices.push_back(ids1[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/eigen/demos/opengl/icosphere.h b/eigen/demos/opengl/icosphere.h new file mode 100644 index 0000000..b0210ed --- /dev/null +++ b/eigen/demos/opengl/icosphere.h @@ -0,0 +1,30 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#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/eigen/demos/opengl/quaternion_demo.cpp b/eigen/demos/opengl/quaternion_demo.cpp new file mode 100644 index 0000000..0416561 --- /dev/null +++ b/eigen/demos/opengl/quaternion_demo.cpp @@ -0,0 +1,656 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#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; + + // leval 0 + mCenters.push_back(Vector3f::Zero()); + parents.push_back(-1); + mRadii.push_back(radius); + + // generate level 1 using icosphere vertices + 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 + }; + + // generate other levels + 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; +}; + + +// generic linear interpolation method +template<typename T> T lerp(float t, const T& a, const T& b) +{ + return a*(1-t) + b*t; +} + +// quaternion slerp +template<> Quaternionf lerp(float t, const Quaternionf& a, const Quaternionf& b) +{ return a.slerp(t,b); } + +// linear interpolation of a frame using the type OrientationType +// to perform the interpolation of the orientations +template<typename OrientationType> +inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b) +{ + return 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) + { + // 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.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()); } +}; + +// Euler angles slerp +template<> EulerAngles<float> lerp(float t, const EulerAngles<float>& a, const EulerAngles<float>& b) +{ + EulerAngles<float> res; + res.coeffs() = lerp(t, a.coeffs(), b.coeffs()); + return res; +} + + +RenderingWidget::RenderingWidget() +{ + mAnimate = false; + mCurrentTrackingMode = TM_NO_TRACK; + mNavMode = NavTurnAround; + mLerpMode = LerpQuaternion; + mRotationMode = RotationStable; + mTrackball.setCamera(&mCamera); + + // required to capture key press events + setFocusPolicy(Qt::ClickFocus); +} + +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 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)); + + // draw the fractal object + float sqrt3 = internal::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()) + { + // end + currentFrame = lo->second; + stopAnimation(); + } + else if(hi==m_timeline.begin()) + { + // start + 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; + // add a frame + case Qt::Key_G: + grabFrame(); + break; + // clear the time line + case Qt::Key_C: + m_timeline.clear(); + break; + // move the camera to initial pos + case Qt::Key_R: + resetCamera(); + break; + // start/stop the animation + case Qt::Key_A: + if (mAnimate) + { + stopAnimation(); + } + else + { + m_alpha = 0; + connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate())); + m_timer.start(1000/30); + mAnimate = true; + } + break; + default: + break; + } + + updateGL(); +} + +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) +{ + // tracking + 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()); + + // speedup the transformations + if(e->modifiers() & Qt::ShiftModifier) + { + dx *= 10.; + dy *= 10.; + } + + switch(mCurrentTrackingMode) + { + case TM_ROTATE_AROUND: + case TM_LOCAL_ROTATE: + if (mRotationMode==RotationStable) + { + // use the stable trackball implementation mapping + // the 2D coordinates to 3D points on a sphere. + mTrackball.track(Vector2i(e->pos().x(), e->pos().y())); + } + else + { + // standard approach mapping the x and y displacements as rotations + // around the camera's X and Y axes. + Quaternionf q = AngleAxisf( dx*M_PI, Vector3f::UnitY()) + * AngleAxisf(-dy*M_PI, Vector3f::UnitX()); + if (mCurrentTrackingMode==TM_LOCAL_ROTATE) + mCamera.localRotate(q); + else + mCamera.rotateAroundTarget(q); + } + break; + case TM_ZOOM : + mCamera.zoom(dy*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); + + // Clear buffers + 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()); + + // 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 duration = aux0.orientation.angularDistance(aux1.orientation) * 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 the 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(); + + { + QPushButton* but = new QPushButton("reset"); + but->setToolTip("move the camera to initial position (with animation)"); + layout->addWidget(but); + connect(but, SIGNAL(clicked()), this, SLOT(resetCamera())); + } + { + // navigation mode + QGroupBox* box = new QGroupBox("navigation mode"); + QVBoxLayout* boxLayout = new QVBoxLayout; + QButtonGroup* group = new QButtonGroup(panel); + QRadioButton* but; + but = new QRadioButton("turn around"); + but->setToolTip("look around an object"); + group->addButton(but, NavTurnAround); + 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 + 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); + } + { + // interpolation mode + QGroupBox* box = new QGroupBox("spherical interpolation"); + QVBoxLayout* boxLayout = new QVBoxLayout; + QButtonGroup* group = new QButtonGroup(panel); + QRadioButton* but; + but = new QRadioButton("quaternion slerp"); + group->addButton(but, LerpQuaternion); + 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" + diff --git a/eigen/demos/opengl/quaternion_demo.h b/eigen/demos/opengl/quaternion_demo.h new file mode 100644 index 0000000..dbff46c --- /dev/null +++ b/eigen/demos/opengl/quaternion_demo.h @@ -0,0 +1,114 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_QUATERNION_DEMO_H +#define EIGEN_QUATERNION_DEMO_H + +#include "gpuhelper.h" +#include "camera.h" +#include "trackball.h" +#include <map> +#include <QTimer> +#include <QtGui/QApplication> +#include <QtOpenGL/QGLWidget> +#include <QtGui/QMainWindow> + +class RenderingWidget : public QGLWidget +{ + Q_OBJECT + + typedef std::map<float,Frame> TimeLine; + TimeLine m_timeline; + Frame lerpFrame(float t); + + Frame mInitFrame; + bool mAnimate; + float m_alpha; + + enum TrackMode { + TM_NO_TRACK=0, TM_ROTATE_AROUND, TM_ZOOM, + TM_LOCAL_ROTATE, TM_FLY_Z, TM_FLY_PAN + }; + + enum NavMode { + NavTurnAround, + NavFly + }; + + enum LerpMode { + LerpQuaternion, + LerpEulerAngles + }; + + enum RotationMode { + RotationStable, + RotationStandard + }; + + Camera mCamera; + TrackMode mCurrentTrackingMode; + NavMode mNavMode; + LerpMode mLerpMode; + RotationMode mRotationMode; + Vector2i mMouseCoords; + Trackball mTrackball; + + QTimer m_timer; + + 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 grabFrame(void); + virtual void stopAnimation(); + + virtual void setNavMode(int); + virtual void setLerpMode(int); + virtual void setRotationMode(int); + virtual void resetCamera(); + + protected: + + virtual void initializeGL(); + virtual void resizeGL(int width, int height); + virtual void paintGL(); + + //-------------------------------------------------------------------------------- + virtual void mousePressEvent(QMouseEvent * e); + virtual void mouseReleaseEvent(QMouseEvent * e); + virtual void mouseMoveEvent(QMouseEvent * e); + virtual void keyPressEvent(QKeyEvent * e); + //-------------------------------------------------------------------------------- + + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + RenderingWidget(); + ~RenderingWidget() { } + + QWidget* createNavigationControlWidget(); +}; + +class QuaternionDemo : public QMainWindow +{ + Q_OBJECT + public: + QuaternionDemo(); + protected: + RenderingWidget* mRenderingWidget; +}; + +#endif // EIGEN_QUATERNION_DEMO_H diff --git a/eigen/demos/opengl/trackball.cpp b/eigen/demos/opengl/trackball.cpp new file mode 100644 index 0000000..77ac790 --- /dev/null +++ b/eigen/demos/opengl/trackball.cpp @@ -0,0 +1,59 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "trackball.h" +#include "camera.h" + +using namespace Eigen; + +void Trackball::track(const Vector2i& point2D) +{ + if (mpCamera==0) + return; + Vector3f newPoint3D; + bool newPointOk = mapToSphere(point2D, newPoint3D); + + if (mLastPointOk && newPointOk) + { + Vector3f axis = mLastPoint3D.cross(newPoint3D).normalized(); + float cos_angle = mLastPoint3D.dot(newPoint3D); + if ( internal::abs(cos_angle) < 1.0 ) + { + float angle = 2. * acos(cos_angle); + if (mMode==Around) + mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(angle, axis))); + else + mpCamera->localRotate(Quaternionf(AngleAxisf(-angle, axis))); + } + } + + mLastPoint3D = newPoint3D; + mLastPointOk = newPointOk; +} + +bool Trackball::mapToSphere(const Vector2i& p2, Vector3f& v3) +{ + if ((p2.x() >= 0) && (p2.x() <= int(mpCamera->vpWidth())) && + (p2.y() >= 0) && (p2.y() <= int(mpCamera->vpHeight())) ) + { + double x = (double)(p2.x() - 0.5*mpCamera->vpWidth()) / (double)mpCamera->vpWidth(); + double y = (double)(0.5*mpCamera->vpHeight() - p2.y()) / (double)mpCamera->vpHeight(); + double sinx = sin(M_PI * x * 0.5); + double siny = sin(M_PI * y * 0.5); + double sinx2siny2 = sinx * sinx + siny * siny; + + v3.x() = sinx; + v3.y() = siny; + v3.z() = sinx2siny2 < 1.0 ? sqrt(1.0 - sinx2siny2) : 0.0; + + return true; + } + else + return false; +} diff --git a/eigen/demos/opengl/trackball.h b/eigen/demos/opengl/trackball.h new file mode 100644 index 0000000..1ea842f --- /dev/null +++ b/eigen/demos/opengl/trackball.h @@ -0,0 +1,42 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_TRACKBALL_H +#define EIGEN_TRACKBALL_H + +#include <Eigen/Geometry> + +class Camera; + +class Trackball +{ + public: + + enum Mode {Around, Local}; + + Trackball() : mpCamera(0) {} + + void start(Mode m = Around) { mMode = m; mLastPointOk = false; } + + void setCamera(Camera* pCam) { mpCamera = pCam; } + + void track(const Eigen::Vector2i& newPoint2D); + + protected: + + bool mapToSphere( const Eigen::Vector2i& p2, Eigen::Vector3f& v3); + + Camera* mpCamera; + Eigen::Vector3f mLastPoint3D; + Mode mMode; + bool mLastPointOk; + +}; + +#endif // EIGEN_TRACKBALL_H |