/*===========================================================================*\
* *
* OpenMesh *
* Copyright (C) 2001-2012 by Computer Graphics Group, RWTH Aachen *
* www.openmesh.org *
* *
*---------------------------------------------------------------------------*
* This file is part of OpenMesh. *
* *
* OpenMesh 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 with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenMesh 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 for more details. *
* *
* You should have received a copy of the GNU LesserGeneral Public *
* License along with OpenMesh. If not, *
* see . *
* *
\*===========================================================================*/
/*===========================================================================*\
* *
* $Revision$ *
* $Date$ *
* *
\*===========================================================================*/
//== INCLUDES =================================================================
#ifdef _MSC_VER
# pragma warning(disable: 4267 4311 4305)
#endif
#include
#include
#include
// --------------------
#ifdef ARCH_DARWIN
# include
#else
# include
#endif
// --------------------
#include
#include
#include
#include
#include
#include
#include
// --------------------
#include
#include
#if !defined(M_PI)
# define M_PI 3.1415926535897932
#endif
const double TRACKBALL_RADIUS = 0.6;
using namespace Qt;
using namespace OpenMesh;
//== IMPLEMENTATION ==========================================================
std::string QGLViewerWidget::nomode_ = "";
//----------------------------------------------------------------------------
QGLViewerWidget::QGLViewerWidget( QWidget* _parent )
: QGLWidget( _parent )
{
init();
}
//----------------------------------------------------------------------------
QGLViewerWidget::
QGLViewerWidget( QGLFormat& _fmt, QWidget* _parent )
: QGLWidget( _fmt, _parent )
{
init();
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::init(void)
{
// qt stuff
setAttribute(Qt::WA_NoSystemBackground, true);
setFocusPolicy(Qt::StrongFocus);
setAcceptDrops( true );
setCursor(PointingHandCursor);
// popup menu
popup_menu_ = new QMenu(this);
draw_modes_group_ = new QActionGroup(this);
connect( draw_modes_group_, SIGNAL(triggered(QAction*)),
this, SLOT(slotDrawMode(QAction*)));
// init draw modes
n_draw_modes_ = 0;
//draw_mode_ = 3;
QAction *a;
a = add_draw_mode("Wireframe");
a->setShortcut(QKeySequence(Key_W));
add_draw_mode("Solid Flat");
a = add_draw_mode("Solid Smooth");
a->setShortcut(QKeySequence(Key_S));
a->setChecked(true);
slotDrawMode(a);
}
//----------------------------------------------------------------------------
QGLViewerWidget::~QGLViewerWidget()
{
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::setDefaultMaterial(void)
{
GLfloat mat_a[] = {0.1, 0.1, 0.1, 1.0};
GLfloat mat_d[] = {0.7, 0.7, 0.5, 1.0};
GLfloat mat_s[] = {1.0, 1.0, 1.0, 1.0};
GLfloat shine[] = {120.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_a);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_d);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_s);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::setDefaultLight(void)
{
GLfloat pos1[] = { 0.1, 0.1, -0.02, 0.0};
GLfloat pos2[] = {-0.1, 0.1, -0.02, 0.0};
GLfloat pos3[] = { 0.0, 0.0, 0.1, 0.0};
GLfloat col1[] = { 0.7, 0.7, 0.8, 1.0};
GLfloat col2[] = { 0.8, 0.7, 0.7, 1.0};
GLfloat col3[] = { 1.0, 1.0, 1.0, 1.0};
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0,GL_POSITION, pos1);
glLightfv(GL_LIGHT0,GL_DIFFUSE, col1);
glLightfv(GL_LIGHT0,GL_SPECULAR, col1);
glEnable(GL_LIGHT1);
glLightfv(GL_LIGHT1,GL_POSITION, pos2);
glLightfv(GL_LIGHT1,GL_DIFFUSE, col2);
glLightfv(GL_LIGHT1,GL_SPECULAR, col2);
glEnable(GL_LIGHT2);
glLightfv(GL_LIGHT2,GL_POSITION, pos3);
glLightfv(GL_LIGHT2,GL_DIFFUSE, col3);
glLightfv(GL_LIGHT2,GL_SPECULAR, col3);
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::initializeGL()
{
// OpenGL state
glClearColor(0.0, 0.0, 0.0, 0.0);
glDisable( GL_DITHER );
glEnable( GL_DEPTH_TEST );
// Material
setDefaultMaterial();
// Lighting
glLoadIdentity();
setDefaultLight();
// Fog
GLfloat fogColor[4] = { 0.3, 0.3, 0.4, 1.0 };
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogfv(GL_FOG_COLOR, fogColor);
glFogf(GL_FOG_DENSITY, 0.35);
glHint(GL_FOG_HINT, GL_DONT_CARE);
glFogf(GL_FOG_START, 5.0f);
glFogf(GL_FOG_END, 25.0f);
// scene pos and size
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix_);
set_scene_pos(Vec3f(0.0, 0.0, 0.0), 1.0);
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::resizeGL( int _w, int _h )
{
update_projection_matrix();
glViewport(0, 0, _w, _h);
updateGL();
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode( GL_PROJECTION );
glLoadMatrixd( projection_matrix_ );
glMatrixMode( GL_MODELVIEW );
glLoadMatrixd( modelview_matrix_ );
if (draw_mode_)
{
assert(draw_mode_ <= n_draw_modes_);
draw_scene(draw_mode_names_[draw_mode_-1]);
}
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::draw_scene(const std::string& _draw_mode)
{
if (_draw_mode == "Wireframe")
{
glDisable(GL_LIGHTING);
glutWireTeapot(0.5);
}
else if (_draw_mode == "Solid Flat")
{
glEnable(GL_LIGHTING);
glShadeModel(GL_FLAT);
glutSolidTeapot(0.5);
}
else if (_draw_mode == "Solid Smooth")
{
glEnable(GL_LIGHTING);
glShadeModel(GL_SMOOTH);
glutSolidTeapot(0.5);
}
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::mousePressEvent( QMouseEvent* _event )
{
// popup menu
if (_event->button() == RightButton && _event->buttons()== RightButton )
{
popup_menu_->exec(QCursor::pos());
}
else
{
last_point_ok_ = map_to_sphere( last_point_2D_=_event->pos(),
last_point_3D_ );
}
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::mouseMoveEvent( QMouseEvent* _event )
{
QPoint newPoint2D = _event->pos();
// Left button: rotate around center_
// Middle button: translate object
// Left & middle button: zoom in/out
Vec3f newPoint3D;
bool newPoint_hitSphere = map_to_sphere( newPoint2D, newPoint3D );
float dx = newPoint2D.x() - last_point_2D_.x();
float dy = newPoint2D.y() - last_point_2D_.y();
float w = width();
float h = height();
// enable GL context
makeCurrent();
// move in z direction
if ( (_event->buttons() == (LeftButton+MidButton)) ||
(_event->buttons() == LeftButton && _event->modifiers() == ControlModifier))
{
float value_y = radius_ * dy * 3.0 / h;
translate(Vec3f(0.0, 0.0, value_y));
}
// move in x,y direction
else if ( (_event->buttons() == MidButton) ||
(_event->buttons() == LeftButton && _event->modifiers() == AltModifier) )
{
float z = - (modelview_matrix_[ 2]*center_[0] +
modelview_matrix_[ 6]*center_[1] +
modelview_matrix_[10]*center_[2] +
modelview_matrix_[14]) /
(modelview_matrix_[ 3]*center_[0] +
modelview_matrix_[ 7]*center_[1] +
modelview_matrix_[11]*center_[2] +
modelview_matrix_[15]);
float aspect = w / h;
float near_plane = 0.01 * radius_;
float top = tan(fovy()/2.0f*M_PI/180.0f) * near_plane;
float right = aspect*top;
translate(Vec3f( 2.0*dx/w*right/near_plane*z,
-2.0*dy/h*top/near_plane*z,
0.0f));
}
// rotate
else if (_event->buttons() == LeftButton) {
if (last_point_ok_) {
if ((newPoint_hitSphere = map_to_sphere(newPoint2D, newPoint3D))) {
Vec3f axis = last_point_3D_ % newPoint3D;
if (axis.sqrnorm() < 1e-7) {
axis = Vec3f(1, 0, 0);
} else {
axis.normalize();
}
// find the amount of rotation
Vec3f d = last_point_3D_ - newPoint3D;
float t = 0.5 * d.norm() / TRACKBALL_RADIUS;
if (t < -1.0)
t = -1.0;
else if (t > 1.0)
t = 1.0;
float phi = 2.0 * asin(t);
float angle = phi * 180.0 / M_PI;
rotate(axis, angle);
}
}
}
// remember this point
last_point_2D_ = newPoint2D;
last_point_3D_ = newPoint3D;
last_point_ok_ = newPoint_hitSphere;
// trigger redraw
updateGL();
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::mouseReleaseEvent( QMouseEvent* /* _event */ )
{
last_point_ok_ = false;
}
//-----------------------------------------------------------------------------
void QGLViewerWidget::wheelEvent(QWheelEvent* _event)
{
// Use the mouse wheel to zoom in/out
float d = -(float)_event->delta() / 120.0 * 0.2 * radius_;
translate(Vec3f(0.0, 0.0, d));
updateGL();
_event->accept();
}
//----------------------------------------------------------------------------
void QGLViewerWidget::keyPressEvent( QKeyEvent* _event)
{
switch( _event->key() )
{
case Key_Print:
slotSnapshot();
break;
case Key_H:
std::cout << "Keys:\n";
std::cout << " Print\tMake snapshot\n";
std::cout << " C\tenable/disable back face culling\n";
std::cout << " F\tenable/disable fog\n";
std::cout << " I\tDisplay information\n";
std::cout << " N\tenable/disable display of vertex normals\n";
std::cout << " Shift N\tenable/disable display of face normals\n";
std::cout << " Shift P\tperformance check\n";
break;
case Key_C:
if ( glIsEnabled( GL_CULL_FACE ) )
{
glDisable( GL_CULL_FACE );
std::cout << "Back face culling: disabled\n";
}
else
{
glEnable( GL_CULL_FACE );
std::cout << "Back face culling: enabled\n";
}
updateGL();
break;
case Key_F:
if ( glIsEnabled( GL_FOG ) )
{
glDisable( GL_FOG );
std::cout << "Fog: disabled\n";
}
else
{
glEnable( GL_FOG );
std::cout << "Fog: enabled\n";
}
updateGL();
break;
case Key_I:
std::cout << "Scene radius: " << radius_ << std::endl;
std::cout << "Scene center: " << center_ << std::endl;
break;
case Key_P:
if (_event->modifiers() & ShiftModifier)
{
double fps = performance();
std::cout << "fps: "
<< std::setiosflags (std::ios_base::fixed)
<< fps << std::endl;
}
break;
case Key_Q:
case Key_Escape:
qApp->quit();
}
_event->ignore();
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::translate( const OpenMesh::Vec3f& _trans )
{
// Translate the object by _trans
// Update modelview_matrix_
makeCurrent();
glLoadIdentity();
glTranslated( _trans[0], _trans[1], _trans[2] );
glMultMatrixd( modelview_matrix_ );
glGetDoublev( GL_MODELVIEW_MATRIX, modelview_matrix_);
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::rotate( const OpenMesh::Vec3f& _axis, float _angle )
{
// Rotate around center center_, axis _axis, by angle _angle
// Update modelview_matrix_
Vec3f t( modelview_matrix_[0]*center_[0] +
modelview_matrix_[4]*center_[1] +
modelview_matrix_[8]*center_[2] +
modelview_matrix_[12],
modelview_matrix_[1]*center_[0] +
modelview_matrix_[5]*center_[1] +
modelview_matrix_[9]*center_[2] +
modelview_matrix_[13],
modelview_matrix_[2]*center_[0] +
modelview_matrix_[6]*center_[1] +
modelview_matrix_[10]*center_[2] +
modelview_matrix_[14] );
makeCurrent();
glLoadIdentity();
glTranslatef(t[0], t[1], t[2]);
glRotated( _angle, _axis[0], _axis[1], _axis[2]);
glTranslatef(-t[0], -t[1], -t[2]);
glMultMatrixd(modelview_matrix_);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix_);
}
//----------------------------------------------------------------------------
bool
QGLViewerWidget::map_to_sphere( const QPoint& _v2D, OpenMesh::Vec3f& _v3D )
{
// This is actually doing the Sphere/Hyperbolic sheet hybrid thing,
// based on Ken Shoemake's ArcBall in Graphics Gems IV, 1993.
double x = (2.0*_v2D.x() - width())/width();
double y = -(2.0*_v2D.y() - height())/height();
double xval = x;
double yval = y;
double x2y2 = xval*xval + yval*yval;
const double rsqr = TRACKBALL_RADIUS*TRACKBALL_RADIUS;
_v3D[0] = xval;
_v3D[1] = yval;
if (x2y2 < 0.5*rsqr) {
_v3D[2] = sqrt(rsqr - x2y2);
} else {
_v3D[2] = 0.5*rsqr/sqrt(x2y2);
}
return true;
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::update_projection_matrix()
{
makeCurrent();
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective(45.0, (GLfloat) width() / (GLfloat) height(),
0.01*radius_, 100.0*radius_);
glGetDoublev( GL_PROJECTION_MATRIX, projection_matrix_);
glMatrixMode( GL_MODELVIEW );
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::view_all()
{
translate( Vec3f( -(modelview_matrix_[0]*center_[0] +
modelview_matrix_[4]*center_[1] +
modelview_matrix_[8]*center_[2] +
modelview_matrix_[12]),
-(modelview_matrix_[1]*center_[0] +
modelview_matrix_[5]*center_[1] +
modelview_matrix_[9]*center_[2] +
modelview_matrix_[13]),
-(modelview_matrix_[2]*center_[0] +
modelview_matrix_[6]*center_[1] +
modelview_matrix_[10]*center_[2] +
modelview_matrix_[14] +
3.0*radius_) ) );
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::set_scene_pos( const OpenMesh::Vec3f& _cog, float _radius )
{
center_ = _cog;
radius_ = _radius;
glFogf( GL_FOG_START, 1.5*_radius );
glFogf( GL_FOG_END, 3.0*_radius );
update_projection_matrix();
view_all();
}
//----------------------------------------------------------------------------
QAction*
QGLViewerWidget::add_draw_mode(const std::string& _s)
{
++n_draw_modes_;
draw_mode_names_.push_back(_s);
QActionGroup *grp = draw_modes_group_;
QAction* act = new QAction(tr(_s.c_str()), this);
act->setCheckable(true);
act->setData(n_draw_modes_);
grp->addAction(act);
popup_menu_->addAction(act);
addAction(act, _s.c_str());
return act;
}
void QGLViewerWidget::addAction(QAction* act, const char * name)
{
names_to_actions[name] = act;
Super::addAction(act);
}
void QGLViewerWidget::removeAction(QAction* act)
{
ActionMap::iterator it = names_to_actions.begin(), e = names_to_actions.end();
ActionMap::iterator found = e;
for(; it!=e; ++it) {
if (it->second == act) {
found = it;
break;
}
}
if (found != e) {
names_to_actions.erase(found);
}
popup_menu_->removeAction(act);
draw_modes_group_->removeAction(act);
Super::removeAction(act);
}
void QGLViewerWidget::removeAction(const char* name)
{
QString namestr = QString(name);
ActionMap::iterator e = names_to_actions.end();
ActionMap::iterator found = names_to_actions.find(namestr);
if (found != e) {
removeAction(found->second);
}
}
QAction* QGLViewerWidget::findAction(const char* name)
{
QString namestr = QString(name);
ActionMap::iterator e = names_to_actions.end();
ActionMap::iterator found = names_to_actions.find(namestr);
if (found != e) {
return found->second;
}
return 0;
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::del_draw_mode(const std::string& _s)
{
QString cmp = _s.c_str();
QList actions_ = popup_menu_->actions();
QList::iterator it=actions_.begin(), e=actions_.end();
for(; it!=e; ++it) {
if ((*it)->text() == cmp) { break; }
}
#if _DEBUG
assert( it != e );
#else
if ( it == e )
return;
#endif
popup_menu_->removeAction(*it);
//QActionGroup *grp = draw_modes_group_;
}
//----------------------------------------------------------------------------
void
QGLViewerWidget::slotDrawMode(QAction* _mode)
{
// save draw mode
draw_mode_ = _mode->data().toInt();
updateGL();
// check selected draw mode
//popup_menu_->setItemChecked(draw_mode_, true);
}
//----------------------------------------------------------------------------
double
QGLViewerWidget::performance()
{
setCursor( Qt::WaitCursor );
double fps(0.0);
makeCurrent();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
OpenMesh::Utils::Timer timer;
unsigned int frames = 60;
const float angle = 360.0/(float)frames;
unsigned int i;
Vec3f axis;
glFinish();
timer.start();
for (i=0, axis=Vec3f(1,0,0); iprocessEvents();
timer.cont();
for (i=0, axis=Vec3f(0,1,0); iprocessEvents();
timer.cont();
for (i=0, axis=Vec3f(0,0,1); i fbuffer(3*w*h);
qApp->processEvents();
makeCurrent();
updateGL();
glFinish();
glReadBuffer( buffer );
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
paintGL();
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, &fbuffer[0] );
unsigned int x,y,offset;
for (y=0; y