glviewer.cpp
Go to the documentation of this file.
00001 /* This file is part of RGBDSLAM.
00002  * 
00003  * RGBDSLAM is free software: you can redistribute it and/or modify
00004  * it under the terms of the GNU General Public License as published by
00005  * the Free Software Foundation, either version 3 of the License, or
00006  * (at your option) any later version.
00007  * 
00008  * RGBDSLAM is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  * 
00013  * You should have received a copy of the GNU General Public License
00014  * along with RGBDSLAM.  If not, see <http://www.gnu.org/licenses/>.
00015  */
00016 
00017 //(For ARM Hardware)
00018 #ifndef DUMMYGLVIEWER 
00019 
00020 #include "ros/ros.h"
00021 #include <QtGui>
00022 #include <QtOpenGL>
00023 #include <QThread>
00024 #include <GL/glut.h>
00025 #include "boost/foreach.hpp"
00026 #include <cmath>
00027 #include <QApplication>
00028 #include <QAction>
00029 #include <QMenu>
00030 #ifdef GL2PS
00031 #include <gl2ps.h>
00032 #endif
00033 #include "glviewer.h"
00034 #include "misc2.h"
00035 #include "scoped_timer.h"
00036 
00037 #ifndef GL_MULTISAMPLE
00038 #define GL_MULTISAMPLE  0x809D
00039 #endif
00040 
00041 const double PI= 3.14159265358979323846;
00042 
00043 template <typename PointType>
00044 inline bool validXYZ(const PointType& p, float max_depth){
00045       if(max_depth < p.z) return false;
00046       return std::isfinite(p.z) && std::isfinite(p.y) && std::isfinite(p.x);
00047 };
00048 
00049 template <typename PointType>
00050 inline void setGLColor(const PointType& p){
00051     static int previous_segment = -1;
00052     unsigned char b,g,r;
00053 #ifdef RGB_IS_4TH_DIM
00054     b = *(  (unsigned char*)(&p.data[3]));
00055     g = *(1+(unsigned char*)(&p.data[3]));
00056     r = *(2+(unsigned char*)(&p.data[3]));
00057 #elif defined(HEMACLOUDS)
00058     if(p.segment == 0 || ParameterServer::instance()->get<int>("segment_to_optimize") < 0) {//No segment: original color
00059       b = *(  (unsigned char*)(&p.rgb));
00060       g = *(1+(unsigned char*)(&p.rgb));
00061       r = *(2+(unsigned char*)(&p.rgb));
00062     } else if(ParameterServer::instance()->get<int>("segment_to_optimize") != 0) {
00063       if(p.segment == ParameterServer::instance()->get<int>("segment_to_optimize")) {
00064         r = 0;   g = 255; b = 0;//right segment: green
00065       } else {
00066         r = 255; g = 0;   b = 0; //wrong segment: red
00067       }
00068     } else {  //segment_to_optimize == 0
00069       switch(p.segment & 15){
00070         case 0 : r = 0;   g = 0;   b = 0;   ROS_INFO_COND(p.segment != previous_segment, "%d is black      ", p.segment); break;//black          
00071         case 1 : r = 128; g = 128; b = 0;   ROS_INFO_COND(p.segment != previous_segment, "%d is yellow     ", p.segment); break;//yellow         
00072         case 2 : r = 128; g = 0;   b = 0;   ROS_INFO_COND(p.segment != previous_segment, "%d is red        ", p.segment); break;//red         
00073         case 3 : r = 128; g = 0;   b = 128; ROS_INFO_COND(p.segment != previous_segment, "%d is magenta    ", p.segment); break;//magenta         
00074         case 4 : r = 0;   g = 128; b = 128; ROS_INFO_COND(p.segment != previous_segment, "%d is cyan       ", p.segment); break;//cyan         
00075         case 5 : r = 0;   g = 128; b = 0;   ROS_INFO_COND(p.segment != previous_segment, "%d is green      ", p.segment); break;//green         
00076         case 6 : r = 0;   g = 0;   b = 128; ROS_INFO_COND(p.segment != previous_segment, "%d is blue       ", p.segment); break;//blue         
00077         case 7 : r = 255; g = 255; b = 255; ROS_INFO_COND(p.segment != previous_segment, "%d is white      ", p.segment); break;//white         
00078         case 8 : r = 128; g = 128; b = 128; ROS_INFO_COND(p.segment != previous_segment, "%d is gray       ", p.segment); break;//gray         
00079         case 9 : r = 255; g = 255; b = 128; ROS_INFO_COND(p.segment != previous_segment, "%d is yellowish  ", p.segment); break;//yellowish         
00080         case 10: r = 255; g = 128; b = 128; ROS_INFO_COND(p.segment != previous_segment, "%d is redish     ", p.segment); break;//redish         
00081         case 11: r = 255; g = 128; b = 255; ROS_INFO_COND(p.segment != previous_segment, "%d is magentaish ", p.segment); break;//magentaish         
00082         case 12: r = 128; g = 255; b = 255; ROS_INFO_COND(p.segment != previous_segment, "%d is cyanish    ", p.segment); break;//cyanish         
00083         case 13: r = 128; g = 255; b = 128; ROS_INFO_COND(p.segment != previous_segment, "%d is greenish   ", p.segment); break;//greenish         
00084         case 14: r = 128; g = 128; b = 255; ROS_INFO_COND(p.segment != previous_segment, "%d is blueish    ", p.segment); break;//blueish         
00085         case 15: r = 255; g = 255; b = 255; ROS_INFO_COND(p.segment != previous_segment, "%d is white      ", p.segment); break;//white         
00086         default: ROS_ERROR("Impossible value %d", p.segment % 8);
00087       }
00088       previous_segment = p.segment;
00089     }
00090 #else
00091     b = *(  (unsigned char*)(&p.rgb));
00092     g = *(1+(unsigned char*)(&p.rgb));
00093     r = *(2+(unsigned char*)(&p.rgb));
00094 #endif
00095     glColor3ub(r,g,b); //glColor3f(1.0,1.0,1.0);
00096 };
00097 
00098 GLViewer::GLViewer(QWidget *parent)
00099     : QGLWidget(QGLFormat(QGL::NoSampleBuffers), parent),
00100     //: QGLWidget(QGLFormat(QGL::SampleBuffers|QGL::StereoBuffers), parent),
00101       polygon_mode(GL_FILL),
00102       cloud_list_indices(),
00103       cloud_matrices(new QList<QMatrix4x4>()),
00104       show_poses_(ParameterServer::instance()->get<bool>("show_axis")),
00105       show_ids_(false),
00106       show_grid_(false), 
00107       show_tfs_(false), 
00108       show_edges_(ParameterServer::instance()->get<bool>("show_axis")),
00109       show_clouds_(true),
00110       show_octomap_(true),
00111       show_features_(false),
00112       follow_mode_(true),
00113       stereo_(false),
00114       black_background_(true),
00115       width_(0),
00116       height_(0),
00117       stereo_shift_(0.1),
00118       fov_(100.0/180.0*PI),
00119       rotation_stepping_(1.0),
00120       myparent(parent),
00121       button_pressed_(false),
00122       non_interactive_update_(false),
00123       fast_rendering_step_(1),
00124       external_renderable(NULL) 
00125 {
00126     this->initialPosition();
00127     this->format().setSwapInterval(0);
00128     bg_col_[0] = bg_col_[1] = bg_col_[2] = bg_col_[3] = 0.01;//almost black background (almost, so that the see-through rendering bug on my pc doesn't occur
00129     ROS_DEBUG_COND(!this->format().stereo(), "Stereo not supported");
00130     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); //can make good use of more space
00131     viewpoint_tf_.setToIdentity();
00132     this->renderText(0,0,0, "RGBDSLAMv2", QFont("Monospace", 14));
00133 }
00134 
00135 GLViewer::~GLViewer() { }
00136 
00137 void GLViewer::initialPosition() {
00138     xRot = 180*16.0;//180° turn around x
00139     yRot = 0;
00140     zRot = 0;
00141     xTra = 0;
00142     yTra = 0;
00143     zTra = -50;//go back 50 (pixels?)
00144 }
00145 
00146 QSize GLViewer::minimumSizeHint() const {
00147     return QSize(400, 400);
00148 }
00149 
00150 QSize GLViewer::sizeHint() const {
00151     return QSize(640, 480);
00152 }
00153 
00154 static void qNormalizeAngle(int &angle) {
00155     while (angle < 0)
00156         angle += 360 * 16;
00157     while (angle > 360 * 16)
00158         angle -= 360 * 16;
00159 }
00160 
00161 void GLViewer::setXRotation(int angle) { 
00162     qNormalizeAngle(angle);
00163     if (angle != xRot) {
00164         xRot = angle;
00165         //clearAndUpdate();
00166     }
00167 }
00168 
00169 
00170 void GLViewer::setYRotation(int angle) {
00171     qNormalizeAngle(angle);
00172     if (angle != yRot) {
00173         yRot = angle;
00174         //clearAndUpdate();
00175     }
00176 }
00177 
00178 void GLViewer::setZRotation(int angle) {
00179     qNormalizeAngle(angle);
00180     if (angle != zRot) {
00181         zRot = angle;
00182         //clearAndUpdate();
00183     }
00184 }
00185 
00186 void GLViewer::setRotationGrid(double rot_step_in_degree) {
00187   rotation_stepping_ = rot_step_in_degree;
00188 }
00189 
00190 void GLViewer::setStereoShift(double shift) {
00191   stereo_shift_ = shift;
00192   clearAndUpdate();
00193 }
00194 
00195 void GLViewer::initializeGL() {
00196     glClearColor(bg_col_[0],bg_col_[1],bg_col_[2],bg_col_[3]); 
00197     glEnable (GL_BLEND); 
00198     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00199     glEnable(GL_DEPTH_TEST);
00200     glEnable(GL_CULL_FACE);
00201     glEnable(GL_LINE_SMOOTH);
00202     //glEnable(GL_POINT_SMOOTH);
00203     //glShadeModel(GL_SMOOTH);
00204     glDisable(GL_LIGHTING);
00205     //glEnable(GL_LIGHT0);
00206     //glEnable(GL_MULTISAMPLE);
00207     //gluPerspective(fov_, 1.00, 0.01, 1e9); //1.38 = tan(57/2°)/tan(43/2°)
00209     //static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
00210     //glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
00211 }
00212 //assumes gl mode for triangles
00213 inline
00214 void GLViewer::drawTriangle(const point_type& p1, const point_type& p2, const point_type& p3){
00215     setGLColor(p1);
00216     glVertex3f(p1.x, p1.y, p1.z);
00217 
00218     setGLColor(p2);
00219     glVertex3f(p2.x, p2.y, p2.z);
00220 
00221     setGLColor(p3);
00222     glVertex3f(p3.x, p3.y, p3.z);
00223 }
00224 
00225 void GLViewer::drawGrid(){
00226     //glEnable (GL_LINE_STIPPLE);
00227     //glLineStipple (1, 0x0F0F);
00228     //glEnable(GL_BLEND); 
00229     glBegin(GL_LINES);
00230     glLineWidth(1);
00231     ParameterServer* ps = ParameterServer::instance();
00232     float scale = ps->get<double>("gl_cell_size");
00233     //glColor4f(1-bg_col_[0],1-bg_col_[1],1-bg_col_[2],0.4); //invers of background, transp
00234     glColor3f(0.5,0.5,0.5); //gray
00235     int size = ps->get<int>("gl_grid_size_xy")/2;
00236     for(int i = -size; i <= size ; i++){
00237       //glColor4f(0.2,0.5,0.2,1.0); //green, the color of the y axis
00238       glVertex3f(i*scale,  size*scale, 0);
00239       glVertex3f(i*scale, -size*scale, 0);
00240       //glColor4f(0.5,0.2,0.2,1.0); //red, the color of the x axis
00241       glVertex3f( size*scale, i*scale, 0);
00242       glVertex3f(-size*scale, i*scale, 0);
00243     }
00244     size = ps->get<int>("gl_grid_size_xz")/2;
00245     for(int i = -size; i <= size ; i++){
00246       //glColor4f(0.2,0.2,0.5,1.0); //blue, the color of the z axis
00247       glVertex3f(i*scale, 0,  size*scale);
00248       glVertex3f(i*scale, 0, -size*scale);
00249       //glColor4f(0.5,0.2,0.2,1.0); //red, the color of the x axis
00250       glVertex3f( size*scale, 0, i*scale);
00251       glVertex3f(-size*scale, 0, i*scale);
00252     }
00253     size = ps->get<int>("gl_grid_size_yz")/2;
00254     for(int i = -size; i <= size ; i++){
00255       //glColor4f(0.2,0.2,0.5,1.0); //blue, the color of the z axis
00256       glVertex3f(0, i*scale,  size*scale);
00257       glVertex3f(0, i*scale, -size*scale);
00258       //glColor4f(0.2,0.5,0.2,1.0); //green, the color of the y axis
00259       glVertex3f(0,  size*scale, i*scale);
00260       glVertex3f(0, -size*scale, i*scale);
00261     }
00262     glEnd();
00263     //glDisable (GL_LINE_STIPPLE);
00264 }
00265 
00266 void GLViewer::drawAxes(float scale, float thickness){
00267     glEnable(GL_BLEND); 
00268     glBegin(GL_LINES);
00269     glLineWidth(thickness);
00270     glColor4f (0.9, 0, 0, 1.0);
00271     glVertex3f(0, 0, 0);
00272     glColor4f (0.9, 0, 0, 0.0);
00273     glVertex3f(scale, 0, 0);
00274     glColor4f (0, 0.9, 0, 1.0);
00275     glVertex3f(0, 0, 0);
00276     glColor4f (0, 0.9, 0, 0.0);
00277     glVertex3f(0, scale, 0);
00278     glColor4f (0, 0, 0.9, 1.0);
00279     glVertex3f(0, 0, 0);
00280     glColor4f (0, 0, 0.9, 0.0);
00281     glVertex3f(0, 0, scale);
00282     glEnd();
00283 }
00284 void GLViewer::makeCurrent(){
00285   ScopedTimer s(__FUNCTION__);
00286   if(context() != context()->currentContext()){
00287     ScopedTimer s("QGLWidget::makeCurrent");
00288     QGLWidget::makeCurrent();
00289   }
00290 }
00291 void GLViewer::paintGL() {
00292     if(!this->isVisible()) return;
00293     ScopedTimer s(__FUNCTION__);
00294     //ROS_INFO("This is paint-thread %d", (unsigned int)QThread::currentThreadId());
00295     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00296     glPointSize(ParameterServer::instance()->get<double>("gl_point_size"));
00297     if(stereo_){
00298         this->format().setStereo(true);
00299         float ratio = (float)(width_) / (float) height_;
00300         glViewport(0, 0, width_/2, height_);
00301         glMatrixMode(GL_PROJECTION);
00302         glLoadIdentity();
00303         gluPerspective(fov_, ratio, 0.1, 1e4); 
00304         glMatrixMode(GL_MODELVIEW);
00305         drawClouds(stereo_shift_);
00306 
00307         glViewport(width_/2, 0, width_/2, height_);
00308         glMatrixMode(GL_PROJECTION);
00309         glLoadIdentity();
00310         gluPerspective(fov_, ratio, 0.1, 1e4); 
00311         glMatrixMode(GL_MODELVIEW);
00312     }
00313     if(cloud_matrices->size() == 0){
00314       glColor4f(1,0,0,1);    
00315       this->renderText(-0.36,0.1,1, QString("R"),   QFont("Serif", 28, QFont::Bold, false));
00316       glColor4f(0,1,0,1);    
00317       this->renderText(-0.27,0.1,1, QString("G"),   QFont("Serif", 28, QFont::Bold, false));
00318       glColor4f(0,0,1,1);    
00319       this->renderText(-0.18,0.1,1, QString("B"),   QFont("Serif", 28, QFont::Bold, false));
00320       glColor4f(.5,.5,.5,1); 
00321       this->renderText(-0.09,0.1,1, QString("D"),   QFont("Serif", 28, QFont::Bold, false));
00322       glColor4f(1-bg_col_[0],1-bg_col_[1],1-bg_col_[2],1.0); //inverse of bg color
00323       this->renderText(0.01,0.1,1, QString("S"), QFont("Sans",  28, -1,false));
00324       this->renderText(0.08,0.1,1, QString("L"), QFont("Sans",  28, -1,false));
00325       this->renderText(0.15,0.1,1, QString("A"), QFont("Sans",  28, -1,false));
00326       this->renderText(0.23,0.1,1, QString("M"), QFont("Sans",  28, -1,false));
00327       glColor4f(1-bg_col_[0],1-bg_col_[1],1-bg_col_[2],0.2); //inverse of bg color
00328       this->renderText(0.33,0.1,1, QString("v2"),   QFont("Sans",  28, -1, true));
00329     }
00330     drawClouds(0.0);
00331     drawRenderable();
00332 }
00333 
00334 void GLViewer::drawRenderable() {
00335   if(show_octomap_ && external_renderable != NULL){
00336     external_renderable->render();
00337   }
00338 }
00339 void GLViewer::drawOneCloud(int i) {
00340         glPushMatrix();
00341         glMultMatrixd(static_cast<GLdouble*>( (*cloud_matrices)[i].data() ));//works as long as qreal and GLdouble are typedefs to double (might depend on hardware)
00342         if(show_clouds_) glCallList(cloud_list_indices[i]);
00343         if(show_features_ && feature_list_indices.size()>i){
00344           glCallList(feature_list_indices[i]);
00345         }
00346         glPopMatrix();
00347 }
00348 void GLViewer::drawClouds(float xshift) {
00349     ScopedTimer s(__FUNCTION__);
00350     ParameterServer* ps = ParameterServer::instance();
00351     if(follow_mode_){
00352         int id = cloud_matrices->size()-1;
00353         if(id >= 0)setViewPoint((*cloud_matrices)[id]);
00354     }
00355     glDisable (GL_BLEND);  //Don't blend the clouds, they have no alpha values
00356     glLoadIdentity();
00357     //Camera transformation
00358     glTranslatef(xTra+xshift, yTra, zTra);
00359     if(button_pressed_){ //Show axis of left-right movement
00360       drawNavigationAxis(0, 0.5, "mouse up/down");
00361     }
00362     int x_steps = (xRot / 16.0)/rotation_stepping_;
00363     glRotatef(x_steps*rotation_stepping_, 1.0, 0.0, 0.0);
00364 
00365     if(button_pressed_){ //Show axis of left-right movement
00366       drawNavigationAxis(1, 0.5, "mouse left/right");
00367     }
00368     int y_steps = (yRot / 16.0)/rotation_stepping_;
00369     glRotatef(y_steps*rotation_stepping_, 0.0, 1.0, 0.0);
00370 
00371     int z_steps = (zRot / 16.0)/rotation_stepping_;
00372     glRotatef(z_steps*rotation_stepping_, 0.0, 0.0, 1.0);
00373     if(button_pressed_){ 
00374       drawNavigationAxis(2, 0.5, "ctrl + left/right");
00375     }
00376 
00377     glMultMatrixd(static_cast<GLdouble*>( viewpoint_tf_.data() ));//works as long as qreal and GLdouble are typedefs to double (might depend on hardware)
00378     if(show_grid_) {
00379       drawGrid(); //Draw a 10x10 grid with 1m x 1m cells
00380     }
00381     if(show_poses_) drawAxes(0.5);//Show origin as big axis
00382 
00383     ROS_DEBUG("Drawing %i PointClouds", cloud_list_indices.size());
00384     int step = 1;
00385     if(button_pressed_ || non_interactive_update_){
00386       step = std::max(step,(int)fast_rendering_step_); //if dynamic adapted value is larger: use
00387       step = std::max(step,ps->get<int>("fast_rendering_step")); //if fixed positive is larger: Use.
00388     }
00389     int last_cloud = std::min(cloud_list_indices.size(), cloud_matrices->size());
00390     int first_cloud = 0;
00391 
00392     //For only viewing a single cloud
00393     int specific_cloud = ps->get<int>("show_cloud_with_id");
00394     if(specific_cloud >= 0){ 
00395       drawOneCloud(specific_cloud);
00396     }
00397     else //Show all
00398     {
00399       int i = 0;
00400       for(; i < last_cloud - 10; i+=step){
00401           ROS_DEBUG("Drawing %d. PointCloud", i);
00402           drawOneCloud(i);
00403       }
00404       for(int j = std::max(last_cloud - 10, 0); j < last_cloud; j++){
00405           ROS_DEBUG("Drawing %d. PointCloud.", j);
00406           drawOneCloud(j);
00407       }
00408 
00409     }
00410 
00411     glDisable(GL_DEPTH_TEST);
00412     if(show_edges_) drawEdges();
00413 
00414     for(int i = 0; i<cloud_list_indices.size() && i<cloud_matrices->size(); i++){
00415         glPushMatrix();
00416         glMultMatrixd(static_cast<GLdouble*>( (*cloud_matrices)[i].data() ));//works as long as qreal and GLdouble are typedefs to double (might depend on hardware)
00417         if(show_poses_) drawAxes((i + 1 == cloud_list_indices.size()) ? 0.5:0.075); //Draw last pose Big
00418         if(show_ids_) {
00419           glColor4f(1-bg_col_[0],1-bg_col_[1],1-bg_col_[2],1.0); //inverse of bg color
00420           this->renderText(0.,0.,0.,QString::number(i), QFont("Monospace", 8));
00421         }
00422         glPopMatrix();
00423     }
00424     glEnable(GL_DEPTH_TEST);
00425 }
00426 
00427 void GLViewer::resizeGL(int width, int height)
00428 {
00429     width_ = width;
00430     height_ = height;
00431     //int side = qMin(width, height);
00432     glViewport(0, 0, width, height);
00433     //glViewport((width - side) / 2, (height - side) / 2, side, side);
00434 
00435     glMatrixMode(GL_PROJECTION);
00436     glLoadIdentity();
00437 //#ifdef QT_OPENGL_ES_1
00438 //    glOrthof(-0.5, +0.5, -0.5, +0.5, 1.0, 15.0);
00439 //#else
00440 //    glOrtho(-0.5, +0.5, -0.5, +0.5, 1.0, 15.0);
00441 //#endif
00442     //gluPerspective(fov_, 1.38, 0.01, 1e9); //1.38 = tan(57/2°)/tan(43/2°) as kinect has viewing angles 57 and 43
00443     float ratio = (float)width / (float) height;
00444     gluPerspective(fov_, ratio, 0.1, 1e4); 
00445     glMatrixMode(GL_MODELVIEW);
00446 }
00447 
00448 void GLViewer::mouseDoubleClickEvent(QMouseEvent *event) {
00449     //Initial position
00450     this->initialPosition();
00451     if(cloud_matrices->size()>0){
00452       int id = 0;
00453       switch (QApplication::keyboardModifiers()){
00454         case Qt::NoModifier:  
00455             id = cloud_matrices->size()-1; //latest pose
00456             follow_mode_ = true;
00457             setViewPoint((*cloud_matrices)[id]); //first pose
00458             break;
00459         case Qt::ControlModifier:  
00460             follow_mode_ = false;
00461             setViewPoint((*cloud_matrices)[id]); //first pose
00462             break;
00463         case Qt::ShiftModifier:  
00464             viewpoint_tf_.setToIdentity(); //initial Pose (usually same as first pose)
00465       }
00466     }
00467     if(setClickedPosition(event->x(), event->y())){
00468       //Pose selection only works if follow_mode_ = false
00469       follow_mode_ = false;
00470     }
00471     clearAndUpdate();
00472 }
00473 void GLViewer::toggleStereo(bool flag){
00474   stereo_ = flag;
00475   resizeGL(width_, height_);
00476   clearAndUpdate();
00477 }
00478 void GLViewer::toggleBackgroundColor(bool flag){
00479   black_background_ = flag;
00480   if(flag){
00481     bg_col_[0] = bg_col_[1] = bg_col_[2] = bg_col_[3] = 0.01;//almost black background (almost, so that the see-through rendering bug on my pc doesn't occur
00482   }
00483   else{
00484     bg_col_[0] = bg_col_[1] = bg_col_[2] = 1.0;//white background
00485   }
00486   glClearColor(bg_col_[0],bg_col_[1],bg_col_[2],bg_col_[3]); 
00487   clearAndUpdate();
00488 }
00489 void GLViewer::toggleShowFeatures(bool flag){
00490   show_features_ = flag;
00491   clearAndUpdate();
00492 }
00493 void GLViewer::toggleShowOctoMap(bool flag){
00494   show_octomap_ = flag;
00495   clearAndUpdate();
00496 }
00497 void GLViewer::toggleShowClouds(bool flag){
00498   show_clouds_ = flag;
00499   clearAndUpdate();
00500 }
00501 void GLViewer::toggleShowTFs(bool flag){
00502   show_tfs_ = flag;
00503   clearAndUpdate();
00504 }
00505 void GLViewer::toggleShowGrid(bool flag){
00506   show_grid_ = flag;
00507   clearAndUpdate();
00508 }
00509 void GLViewer::toggleShowIDs(bool flag){
00510   show_ids_ = flag;
00511   clearAndUpdate();
00512 }
00513 void GLViewer::toggleShowEdges(bool flag){
00514   show_edges_ = flag;
00515   clearAndUpdate();
00516 }
00517 void GLViewer::toggleShowPoses(bool flag){
00518   show_poses_ = flag;
00519   clearAndUpdate();
00520 }
00521 void GLViewer::toggleFollowMode(bool flag){
00522   follow_mode_ = flag;
00523 }
00524 
00526 void GLViewer::mouseReleaseEvent(QMouseEvent *event) {
00527   button_pressed_ = false;
00528   clearAndUpdate();
00529 
00530   if(event->button() == Qt::RightButton)
00531   {
00532       QMenu menu;
00533       QMenu* viewMenu = &menu; //ease copy and paste from qt_gui.cpp
00534       GLViewer* glviewer = this;//ease copy and paste from qt_gui.cpp
00535 
00536       QAction *toggleCloudDisplay = new QAction(tr("Show &Clouds"), this);
00537       toggleCloudDisplay->setCheckable(true);
00538       toggleCloudDisplay->setChecked(show_clouds_);
00539       toggleCloudDisplay->setStatusTip(tr("Toggle whether point clouds should be rendered"));
00540       connect(toggleCloudDisplay, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowClouds(bool)));
00541       viewMenu->addAction(toggleCloudDisplay);
00542 
00543       QAction *toggleOctoMapDisplay = new QAction(tr("Show &Octomap"), this);
00544       toggleOctoMapDisplay->setShortcut(QString("O"));
00545       toggleOctoMapDisplay->setCheckable(true);
00546       toggleOctoMapDisplay->setChecked(show_octomap_);
00547       toggleOctoMapDisplay->setStatusTip(tr("Toggle whether octomap should be displayed"));
00548       connect(toggleOctoMapDisplay, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowOctoMap(bool)));
00549       viewMenu->addAction(toggleOctoMapDisplay);
00550 
00551       QAction *toggleShowPosesAct = new QAction(tr("Show &Poses of Graph"), this);
00552       toggleShowPosesAct->setShortcut(QString("P"));
00553       toggleShowPosesAct->setCheckable(true);
00554       toggleShowPosesAct->setChecked(show_poses_);
00555       toggleShowPosesAct->setStatusTip(tr("Display poses as axes"));
00556       connect(toggleShowPosesAct, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowPoses(bool)));
00557       viewMenu->addAction(toggleShowPosesAct);
00558 
00559       QAction *toggleShowIDsAct = new QAction(tr("Show Pose IDs"), this);
00560       toggleShowIDsAct->setShortcut(QString("I"));
00561       toggleShowIDsAct->setCheckable(true);
00562       toggleShowIDsAct->setChecked(show_ids_);
00563       toggleShowIDsAct->setStatusTip(tr("Display pose ids at axes"));
00564       connect(toggleShowIDsAct, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowIDs(bool)));
00565       viewMenu->addAction(toggleShowIDsAct);
00566 
00567       QAction *toggleShowEdgesAct = new QAction(tr("Show &Edges of Graph"), this);
00568       toggleShowEdgesAct->setShortcut(QString("E"));
00569       toggleShowEdgesAct->setCheckable(true);
00570       toggleShowEdgesAct->setChecked(show_edges_);
00571       toggleShowEdgesAct->setStatusTip(tr("Display edges of pose graph as lines"));
00572       connect(toggleShowEdgesAct, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowEdges(bool)));
00573       viewMenu->addAction(toggleShowEdgesAct);
00574 
00575       QAction *toggleShowTFs = new QAction(tr("Show Pose TFs"), this);
00576       toggleShowTFs->setCheckable(true);
00577       toggleShowTFs->setChecked(show_tfs_);
00578       toggleShowTFs->setStatusTip(tr("Display pose transformations at axes"));
00579       connect(toggleShowTFs, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowTFs(bool)));
00580       viewMenu->addAction(toggleShowTFs);
00581 
00582       QAction *toggleShowFeatures = new QAction(tr("Show &Feature Locations"), this);
00583       toggleShowFeatures->setCheckable(true);
00584       toggleShowFeatures->setChecked(show_features_);
00585       toggleShowFeatures->setStatusTip(tr("Toggle whether feature locations should be rendered"));
00586       connect(toggleShowFeatures, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowFeatures(bool)));
00587       viewMenu->addAction(toggleShowFeatures);
00588 
00589       QAction *toggleTriangulationAct = new QAction(tr("&Toggle Triangulation"), this);
00590       toggleTriangulationAct->setShortcut(QString("T"));
00591       toggleTriangulationAct->setStatusTip(tr("Switch between surface, wireframe and point cloud"));
00592       connect(toggleTriangulationAct, SIGNAL(triggered(bool)), glviewer, SLOT(toggleTriangulation()));
00593       viewMenu->addAction(toggleTriangulationAct);
00594 
00595       QAction *toggleFollowAct = new QAction(tr("Follow &Camera"), this);
00596       toggleFollowAct->setShortcut(QString("Shift+F"));
00597       toggleFollowAct->setCheckable(true);
00598       toggleFollowAct->setChecked(follow_mode_);
00599       toggleFollowAct->setStatusTip(tr("Always use viewpoint of last frame (except zoom)"));
00600       connect(toggleFollowAct, SIGNAL(toggled(bool)), glviewer, SLOT(toggleFollowMode(bool)));
00601       viewMenu->addAction(toggleFollowAct);
00602 
00603       QAction *toggleShowGrid = new QAction(tr("Show Grid"), this);
00604       toggleShowGrid->setCheckable(true);
00605       toggleShowGrid->setChecked(show_grid_);
00606       toggleShowGrid->setStatusTip(tr("Display XY plane grid"));
00607       connect(toggleShowGrid, SIGNAL(toggled(bool)), glviewer, SLOT(toggleShowGrid(bool)));
00608       viewMenu->addAction(toggleShowGrid);
00609 
00610       QAction *toggleBGColor = new QAction(tr("Toggle Background"), this);
00611       toggleBGColor->setShortcut(QString("B"));
00612       toggleBGColor->setCheckable(true);
00613       toggleBGColor->setChecked(black_background_);
00614       toggleBGColor->setStatusTip(tr("Toggle whether background should be white or black"));
00615       connect(toggleBGColor, SIGNAL(toggled(bool)), glviewer, SLOT(toggleBackgroundColor(bool)));
00616       viewMenu->addAction(toggleBGColor);
00617 
00618       viewMenu->exec(mapToGlobal(event->pos()));
00619   }
00620   QGLWidget::mouseReleaseEvent(event);  //Dont forget to pass on the event to parent
00621 }
00622 
00623 void GLViewer::mousePressEvent(QMouseEvent *event) {
00624   button_pressed_ = true;
00625   lastPos = event->pos();
00626 }
00627 
00628 void GLViewer::wheelEvent(QWheelEvent *event) {
00629       double size;
00630       switch (QApplication::keyboardModifiers()){
00631         case Qt::ControlModifier:  
00632             size = ParameterServer::instance()->get<double>("gl_point_size");
00633             /* event->delta():
00634              * Returns the distance that the wheel is rotated, in eighths of a
00635              * degree. A positive value indicates that the wheel was rotated
00636              * forwards away from the user; a negative value indicates that the
00637              * wheel was rotated backwards toward the user.  Most mouse types
00638              * work in steps of 15 degrees, in which case the delta value is a
00639              * multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
00640              */
00641             size = std::max(1.0, size + event->delta()/120.0);
00642             ParameterServer::instance()->set<double>("gl_point_size", size);
00643             break;
00644         case Qt::ShiftModifier:  
00645         case Qt::NoModifier:  
00646         default:
00647           zTra += (-zTra/50.0)*((float)event->delta())/25.0; 
00648       }
00649     clearAndUpdate();
00650 }
00651 void GLViewer::mouseMoveEvent(QMouseEvent *event) {//TODO: consolidate setRotation methods
00652     int dx = event->x() - lastPos.x();
00653     int dy = event->y() - lastPos.y();
00654 
00655     if (event->buttons() & Qt::LeftButton) {
00656       switch (QApplication::keyboardModifiers()){
00657         case Qt::NoModifier:  
00658             setXRotation(xRot - 8 * dy);
00659             setYRotation(yRot + 8 * dx);
00660             break;
00661         case Qt::ControlModifier:  
00662             setXRotation(xRot - 8 * dy);
00663             setZRotation(zRot + 8 * dx);
00664             break;
00665         case Qt::ShiftModifier:  
00666             //Translate, weighted by zoom factor, to have smaller motions when viewing small areas
00667             xTra += (-zTra/50.0)*dx/200.0;
00668             yTra -= (-zTra/50.0)*dy/200.0;
00669       }
00670       clearAndUpdate();
00671     } else if (event->buttons() & Qt::MidButton) {
00672             //Translate, weighted by zoom factor, to have smaller motions when viewing small areas
00673             xTra += (-zTra/50.0)*dx/200.0;
00674             yTra -= (-zTra/50.0)*dy/200.0;
00675             clearAndUpdate();
00676     }
00677 
00678     lastPos = event->pos();
00679 }
00680 
00681 void GLViewer::updateTransforms(QList<QMatrix4x4>* transforms){
00682     ROS_WARN_COND(transforms->size() < cloud_matrices->size(), "Got less transforms than before!");
00683     // This doesn't deep copy, but should work, as qlist maintains a reference count 
00684     //FIXME: This should be replaced by a mutex'ed delete. Requires also to mutex all the read accesses
00685     QList<QMatrix4x4>* cloud_matrices_tmp = cloud_matrices;
00686     cloud_matrices = transforms; 
00687     ROS_DEBUG("New Cloud matrices size: %d", cloud_matrices->size());
00688     //clearAndUpdate();
00689     //FIXME: This should be replaced by a mutex'ed delete. Requires also to mutex all the read accesses
00690     delete cloud_matrices_tmp;
00691 }
00692 
00693 void GLViewer::addPointCloud(pointcloud_type * pc, QMatrix4x4 transform){
00694     ROS_DEBUG("pc pointer in addPointCloud: %p (this is %p in thread %d)", pc, this, (unsigned int)QThread::currentThreadId());
00695     ParameterServer* ps = ParameterServer::instance();
00696     std::string display_type = ps->get<std::string>("cloud_display_type");
00697     if(!pc->isOrganized() || ps->get<double>("squared_meshing_threshold") < 0){
00698       pointCloud2GLPoints(pc);
00699     } else {
00700       if(display_type == "TRIANGLES"){
00701         pointCloud2GLTriangleList(pc);
00702       } else if(display_type == "POINTS"){
00703         pointCloud2GLPoints(pc);
00704       } else if(display_type == "ELLIPSOIDS"){
00705         pointCloud2GLEllipsoids(pc);
00706       } else if(display_type == "NONE"){
00707         cloud_list_indices.push_back(0);
00708       } else { //TRIANGLE_STRIP is default, because it is generated fastest. It is also displayed the smoothes (timings seem worse than for POINTS, yet "perceived" fps are much better)
00709         pointCloud2GLStrip(pc);
00710       }
00711     }
00712     cloud_matrices->push_back(transform); //keep for later
00713     Q_EMIT cloudRendered(pc);
00714     non_interactive_update_ = true;
00715     ScopedTimer s("Rendering", false);
00716     clearAndUpdate();
00717     if(s.elapsed() > 0.05) { //Try to maintain high speed rendering if button is pressed
00718       fast_rendering_step_++;
00719       ROS_INFO("Increased renderer skipto every %d.", fast_rendering_step_);
00720     } else if(s.elapsed() < 0.01 && fast_rendering_step_ > 1) { //Try to maintain high rendering quality, if fast enough 
00721       fast_rendering_step_--;
00722       ROS_INFO("Decreased renderer skip to every %d.", fast_rendering_step_);
00723     } else 
00724       ROS_INFO("No change to renderer skip (%d).", fast_rendering_step_);
00725     non_interactive_update_ = false;
00726     //QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers);
00727     /*
00728     std::string file_prefix = ParameterServer::instance()->get<std::string>("screencast_path_prefix");
00729     if(!file_prefix.empty()){
00730       QString filename;
00731       filename.sprintf("%s%.5d.png", file_prefix.c_str(), cloud_matrices->size());
00732       QPixmap::grabWidget(this->myparent).save(filename);
00733     }
00734     */
00735 }
00736 
00737 void GLViewer::addFeatures(const std::vector<Eigen::Vector4f, Eigen::aligned_allocator<Eigen::Vector4f> >* feature_locations_3d)
00738 {
00739     ScopedTimer s(__FUNCTION__);
00740     ROS_DEBUG("Making GL list from feature points");
00741     GLuint feature_list_index = glGenLists(1);
00742     if(!feature_list_index) {
00743         ROS_ERROR("No display list could be created");
00744         return;
00745     }
00746     glNewList(feature_list_index, GL_COMPILE);
00747     feature_list_indices.push_back(feature_list_index);
00748     glLineWidth(3*ParameterServer::instance()->get<double>("gl_point_size"));
00749     glBegin(GL_LINES);
00750     float r = (float)rand()/(float)RAND_MAX;
00751     float g = (float)rand()/(float)RAND_MAX;
00752     float b = (float)rand()/(float)RAND_MAX;
00753     BOOST_FOREACH(const Eigen::Vector4f& ft, *feature_locations_3d)
00754     {
00755       if(std::isfinite(ft[2])){
00756         glColor4f(r,g,b, 1.0); // color, non transp
00757         //drawEllipsoid(0.001*ft[2], 0.001*ft[2], depth_std_dev(ft[2]), ft);
00758         glVertex3f(ft[0], ft[1], ft[2]);
00759         glColor4f(r,g,b, 0.0); // color, fully transp
00760         glVertex3f(ft[0], ft[1], ft[2]-depth_std_dev(ft[2]));
00761       }
00762     }
00763     glEnd();
00764     glLineWidth(1.0);
00765     glEndList();
00766 }
00767 
00768 inline float squaredEuclideanDistance(point_type p1, point_type p2){
00769   float dx = p1.x - p2.x;
00770   float dy = p1.y - p2.y;
00771   float dz = p1.z - p2.z;
00772   return dx*dx + dy*dy + dz*dz;
00773 }
00774 
00775 
00776 void GLViewer::pointCloud2GLStrip(pointcloud_type * pc){
00777     ScopedTimer s(__FUNCTION__);
00778     ROS_DEBUG("Making GL list from point-cloud pointer %p in thread %d", pc, (unsigned int)QThread::currentThreadId());
00779     GLuint cloud_list_index = glGenLists(1);
00780     if(!cloud_list_index) {
00781         ROS_ERROR("No display list could be created");
00782         return;
00783     }
00784     const float mesh_thresh = ParameterServer::instance()->get<double>("squared_meshing_threshold");
00785     const float max_depth = ParameterServer::instance()->get<double>("maximum_depth");
00786     glNewList(cloud_list_index, GL_COMPILE);
00787     //ROS_INFO_COND(!pc->is_dense, "Expected dense cloud for opengl drawing");
00788     point_type origin;
00789     origin.x = 0;
00790     origin.y = 0;
00791     origin.z = 0;
00792 
00793     float depth;
00794     bool strip_on = false, flip = false; //if flip is true, first the lower then the upper is inserted
00795     const int w=pc->width, h=pc->height;
00796     unsigned char b,g,r;
00797     const int step = ParameterServer::instance()->get<int>("visualization_skip_step");
00798     for( int y = 0; y < h-step; y+=step){ //go through every point and make two triangles 
00799         for( int x = 0; x < w-step; x+=step){//for it and its neighbours right and/or down
00800             using namespace pcl;
00801             if(!strip_on){ //Generate vertices for new triangle
00802                 const point_type* ll = &pc->points[(x)+(y+step)*w]; //one down (lower left corner)
00803                 if(!validXYZ(*ll, max_depth)) continue; // both new triangles in this step would use this point
00804                 const point_type* ur = &pc->points[(x+step)+y*w]; //one right (upper right corner)
00805                 if(!validXYZ(*ur, max_depth)) continue; // both new triangles in this step would use this point
00806           
00807                 const point_type* ul = &pc->points[x+y*w]; //current point (upper right)
00808                 if(validXYZ(*ul, max_depth)){ //ul, ur, ll all valid
00809                   depth = squaredEuclideanDistance(*ul,origin);
00810                   if (squaredEuclideanDistance(*ul,*ll)/depth <= mesh_thresh  and 
00811                       squaredEuclideanDistance(*ul,*ll)/depth <= mesh_thresh  and
00812                       squaredEuclideanDistance(*ur,*ll)/depth <= mesh_thresh){
00813                     glBegin(GL_TRIANGLE_STRIP);
00814                     strip_on = true;
00815                     flip = false; //correct order, upper first
00816                     //Prepare the first two vertices of a triangle strip
00817                     //drawTriangle(*ul, *ll, *ur);
00818                     setGLColor(*ul);
00819 
00820                     //glColor3ub(255,0,0);
00821                     glVertex3f(ul->x, ul->y, ul->z);
00822                   }
00823                 } 
00824                 if(!strip_on) { //can't use the point on the upper left, should I still init a triangle?
00825                   const point_type* lr = &pc->points[(x+step)+(y+step)*w]; //one right-down (lower right)
00826                   if(!validXYZ(*lr, max_depth)) {
00827                     //if this is not valid, there is no way to make a new triangle in the next step
00828                     //and one could have been drawn starting in this step, only if ul had been valid
00829                     x++;
00830                     continue;
00831                   } else { //at least one can be started at the lower left
00832                     depth = squaredEuclideanDistance(*ur,origin);
00833                     if (squaredEuclideanDistance(*ur,*ll)/depth <= mesh_thresh  and 
00834                         squaredEuclideanDistance(*lr,*ll)/depth <= mesh_thresh  and
00835                         squaredEuclideanDistance(*ur,*lr)/depth <= mesh_thresh){
00836                       glBegin(GL_TRIANGLE_STRIP);
00837                       strip_on = true;
00838                       flip = true; //but the lower has to be inserted first, for correct order
00839                     }
00840                   }
00841                 }
00842                 if(strip_on) { //Be this the second or the first vertex, insert it
00843                   setGLColor(*ll);
00844 
00845                   //glColor3ub(0,255,0);
00846                   glVertex3f(ll->x, ll->y, ll->z);
00847                 }
00848                 continue; //not relevant but demonstrate that nothing else is done in this iteration
00849             } // end strip was off
00850             else 
00851             {//neighbours to the left and left down are already set
00852               const point_type* ul;
00853               if(flip){ ul = &pc->points[(x)+(y+step)*w]; } //one down (lower left corner) 
00854               else { ul = &pc->points[x+y*w]; } //current point (upper right)
00855               if(validXYZ(*ul, max_depth)){ //Neighbours to the left are prepared
00856                 depth = squaredEuclideanDistance(*ul,origin);
00857                 if (squaredEuclideanDistance(*ul,*(ul-step))/depth > mesh_thresh){
00858                   glEnd();
00859                   strip_on = false;
00860                   continue;
00861                 }
00862                 //Complete the triangle with both leftern neighbors
00863                 //drawTriangle(*ul, *ll, *ur);
00864                 setGLColor(*ul);
00865 
00866                 //glColor3ub(255,0,0);
00867                 glVertex3f(ul->x, ul->y, ul->z);
00868               } else {
00869                 glEnd();
00870                 strip_on = false;
00871                 continue; //TODO: Could restart with next point instead
00872               }
00873               //The following point connects one to the left with the other on this horizontal level
00874               const point_type* ll;
00875               if(flip){ ll = &pc->points[x+y*w]; } //current point (upper right)
00876               else { ll = &pc->points[(x)+(y+step)*w]; } //one down (lower left corner) 
00877               if(validXYZ(*ll, max_depth)){ 
00878                 depth = squaredEuclideanDistance(*ll,origin);
00879                 if (squaredEuclideanDistance(*ul,*ll)/depth > mesh_thresh or
00880                     squaredEuclideanDistance(*ul,*(ul-step))/depth > mesh_thresh or
00881                     squaredEuclideanDistance(*ll,*(ll-step))/depth > mesh_thresh){
00882                   glEnd();
00883                   strip_on = false;
00884                   continue;
00885                 }
00886                 setGLColor(*ul);
00887 
00888                 glVertex3f(ll->x, ll->y, ll->z);
00889               } else {
00890                 glEnd();
00891                 strip_on = false;
00892                 continue;
00893               }
00894             }//completed triangles if strip is running
00895         }
00896         if(strip_on) glEnd();
00897         strip_on = false;
00898     }
00899     ROS_DEBUG("Compiled pointcloud into list %i",  cloud_list_index);
00900     glEndList();
00901     cloud_list_indices.push_back(cloud_list_index);
00902     //pointcloud_type pc_empty;
00903     //pc_empty.points.swap(pc->points);
00904     //pc->width = 0;
00905     //pc->height = 0;
00906 }
00907 
00908 void GLViewer::deleteLastNode(){
00909   if(cloud_list_indices.size() <= 1){
00910     this->reset();
00911     return;
00912   }
00913         GLuint nodeId = cloud_list_indices.back();
00914         cloud_list_indices.pop_back();
00915         glDeleteLists(nodeId,1);
00916         GLuint ftId = feature_list_indices.back();
00917         feature_list_indices.pop_back();
00918         glDeleteLists(ftId,1);
00919 }
00920 
00922 void GLViewer::pointCloud2GLEllipsoids(pointcloud_type * pc){
00923     ScopedTimer s(__FUNCTION__);
00924     ROS_DEBUG("Making GL list from point-cloud pointer %p in thread %d", pc, (unsigned int)QThread::currentThreadId());
00925     GLuint cloud_list_index = glGenLists(1);
00926     if(!cloud_list_index) {
00927         ROS_ERROR("No display list could be created");
00928         return;
00929     }
00930     cloud_list_indices.push_back(cloud_list_index);
00931     glNewList(cloud_list_index, GL_COMPILE);
00932     //ROS_INFO_COND(!pc->is_dense, "Expected dense cloud for opengl drawing");
00933     point_type origin;
00934     origin.x = 0;
00935     origin.y = 0;
00936     origin.z = 0;
00937 
00938     const float max_depth = ParameterServer::instance()->get<double>("maximum_depth");
00939     float depth;
00940     unsigned int w=pc->width, h=pc->height;
00941     for(unsigned int x = 0; x < w; x++){
00942         for(unsigned int y = 0; y < h; y++){
00943             //using namespace pcl;
00944             const point_type& p = pc->points[x+y*w]; //current point
00945             if(!(validXYZ(p, max_depth))) continue;
00946             setGLColor(p);
00947             drawEllipsoid(0.001*p.z, 0.001*p.z, depth_std_dev(p.z), p.getVector4fMap());
00948         }
00949     }
00950     glEnd();
00951     ROS_DEBUG("Compiled pointcloud into list %i",  cloud_list_index);
00952     glEndList();
00953 }
00954 
00955 void GLViewer::pointCloud2GLPoints(pointcloud_type * pc){
00956     ScopedTimer s(__FUNCTION__);
00957     ROS_DEBUG("Making GL list from point-cloud pointer %p in thread %d", pc, (unsigned int)QThread::currentThreadId());
00958     GLuint cloud_list_index = glGenLists(1);
00959     if(!cloud_list_index) {
00960         ROS_ERROR("No display list could be created");
00961         return;
00962     }
00963     cloud_list_indices.push_back(cloud_list_index);
00964     glNewList(cloud_list_index, GL_COMPILE);
00965     glBegin(GL_POINTS);
00966     //ROS_INFO_COND(!pc->is_dense, "Expected dense cloud for opengl drawing");
00967     point_type origin;
00968     origin.x = 0;
00969     origin.y = 0;
00970     origin.z = 0;
00971 
00972     const float max_depth = ParameterServer::instance()->get<double>("maximum_depth");
00973     float depth;
00974     unsigned int w=pc->width, h=pc->height;
00975     for(unsigned int x = 0; x < w; x++){
00976         for(unsigned int y = 0; y < h; y++){
00977             using namespace pcl;
00978             const point_type* p = &pc->points[x+y*w]; //current point
00979             if(!(validXYZ(*p, max_depth))) continue;
00980             setGLColor(*p);
00981             glVertex3f(p->x, p->y, p->z);
00982         }
00983     }
00984     glEnd();
00985     ROS_DEBUG("Compiled pointcloud into list %i",  cloud_list_index);
00986     glEndList();
00987 }
00988 
00989 void GLViewer::pointCloud2GLTriangleList(pointcloud_type const * pc){
00990     ScopedTimer s(__FUNCTION__);
00991     ROS_DEBUG("Making GL list from point-cloud pointer %p in thread %d", pc, (unsigned int)QThread::currentThreadId());
00992     GLuint cloud_list_index = glGenLists(1);
00993     if(!cloud_list_index) {
00994         ROS_ERROR("No display list could be created");
00995         return;
00996     }
00997     const float mesh_thresh = ParameterServer::instance()->get<double>("squared_meshing_threshold");
00998     cloud_list_indices.push_back(cloud_list_index);
00999     glNewList(cloud_list_index, GL_COMPILE);
01000     glBegin(GL_TRIANGLES);
01001     //ROS_INFO_COND(!pc->is_dense, "Expected dense cloud for opengl drawing");
01002     point_type origin;
01003     origin.x = 0;
01004     origin.y = 0;
01005     origin.z = 0;
01006 
01007     const float max_depth = ParameterServer::instance()->get<double>("maximum_depth");
01008     float depth;
01009     unsigned int w=pc->width, h=pc->height;
01010     for(unsigned int x = 0; x < w-1; x++){
01011         for(unsigned int y = 0; y < h-1; y++){
01012             using namespace pcl;
01013 
01014             const point_type* pi = &pc->points[x+y*w]; //current point
01015 
01016             if(!(validXYZ(*pi, max_depth))) continue;
01017             depth = squaredEuclideanDistance(*pi,origin);
01018 
01019             const point_type* pl = &pc->points[(x+1)+(y+1)*w]; //one right-down
01020             if(!(validXYZ(*pl, max_depth)) or squaredEuclideanDistance(*pi,*pl)/depth > mesh_thresh)  
01021               continue;
01022 
01023             const point_type* pj = &pc->points[(x+1)+y*w]; //one right
01024             if(validXYZ(*pj, max_depth)
01025                and squaredEuclideanDistance(*pi,*pj)/depth <= mesh_thresh  
01026                and squaredEuclideanDistance(*pj,*pl)/depth <= mesh_thresh){
01027               //drawTriangle(*pi, *pj, *pl);
01028               drawTriangle(*pi, *pl, *pj);
01029             }
01030             const point_type* pk = &pc->points[(x)+(y+1)*w]; //one down
01031             
01032             if(validXYZ(*pk, max_depth)
01033                and squaredEuclideanDistance(*pi,*pk)/depth <= mesh_thresh  
01034                and squaredEuclideanDistance(*pk,*pl)/depth <= mesh_thresh){
01035               drawTriangle(*pi, *pk, *pl);
01036             }
01037         }
01038     }
01039     glEnd();
01040     ROS_DEBUG("Compiled pointcloud into list %i",  cloud_list_index);
01041     glEndList();
01042 }
01043 
01044 void GLViewer::reset(){
01045     if(!cloud_list_indices.empty()){
01046       unsigned int max= cloud_list_indices.size() > feature_list_indices.size()? cloud_list_indices.back() : feature_list_indices.back();
01047       glDeleteLists(1,max);
01048     }
01049     cloud_list_indices.clear();
01050     feature_list_indices.clear();
01051     cloud_matrices->clear();
01052     edge_list_.clear();
01053     clearAndUpdate();
01054 }
01055 QImage GLViewer::renderList(QMatrix4x4 transform, int list_id){
01056     return QImage();
01057 }
01058 
01059 void GLViewer::setEdges(const QList<QPair<int, int> >* edge_list){
01060   //if(edge_list_) delete edge_list_;
01061   edge_list_ = *edge_list;
01062   delete edge_list;
01063 }
01064 
01065 void GLViewer::drawEdges(){
01066   if(edge_list_.empty()) return;
01067   //glEnable (GL_LINE_STIPPLE);
01068   //glLineStipple (1, 0x1111);
01069   glBegin(GL_LINES);
01070   glLineWidth(12);
01071   for(int i = 0; i < edge_list_.size(); i++){
01072     int id1 = edge_list_[i].first;
01073     int id2 = edge_list_[i].second;
01074     float x,y,z;
01075     if(cloud_matrices->size() > id1 && cloud_matrices->size() > id2){//only happens in weird circumstances
01076       if(abs(id1 - id2) == 1){//consecutive
01077         glColor4f(bg_col_[0],1-bg_col_[1],1-bg_col_[2],1.0); //cyan on black, red on white
01078       } else if(abs(id1 - id2) > 20){//consider a loop closure
01079         glColor4f(1-bg_col_[0],1-bg_col_[1],bg_col_[2],0.4); //orange on black, blue on black, transp
01080       } else { //near predecessor
01081         glColor4f(1-bg_col_[0],1-bg_col_[1],1-bg_col_[2],0.4); //inverse of bg color, but transp
01082       }
01083       x = (*cloud_matrices)[id1](0,3);
01084       y = (*cloud_matrices)[id1](1,3);
01085       z = (*cloud_matrices)[id1](2,3);
01086       glVertex3f(x,y,z);
01087       x = (*cloud_matrices)[id2](0,3);
01088       y = (*cloud_matrices)[id2](1,3);
01089       z = (*cloud_matrices)[id2](2,3);
01090       glVertex3f(x,y,z);
01091     }
01092     //This happens if the edges are updated faster than the poses/clouds
01093     else ROS_WARN("Not enough cloud matrices (%d) for vertex ids (%d and %d)", cloud_matrices->size(), id1, id2);
01094   }
01095   glEnd();
01096   //glDisable (GL_LINE_STIPPLE);
01097 
01098   if(show_tfs_){
01099     glDisable(GL_DEPTH_TEST);
01100     glColor4f(1-bg_col_[0],1-bg_col_[1],1-bg_col_[2],1.0); //inverse of bg color, non transp
01101     for(int i = 0; i < edge_list_.size(); i++){
01102       int id1 = edge_list_[i].first;
01103       int id2 = edge_list_[i].second;
01104       if(cloud_matrices->size() > id1 && cloud_matrices->size() > id2){//only happens in weird circumstances
01105         Eigen::Vector3f x1((*cloud_matrices)[id1](0,3), (*cloud_matrices)[id1](1,3), (*cloud_matrices)[id1](2,3));
01106         Eigen::Vector3f x2((*cloud_matrices)[id2](0,3), (*cloud_matrices)[id2](1,3), (*cloud_matrices)[id2](2,3));
01107         Eigen::Vector3f dx = x2 - x1;
01108         Eigen::Vector3f xm = x1 + 0.5*dx;
01109 
01110         QString edge_info;
01111         edge_info.sprintf("%d-%d:\n(%.2f, %.2f, %.2f)", id1, id2, dx(0), dx(1), dx(2));
01112         this->renderText(xm(0), xm(1), xm(2), edge_info, QFont("Monospace", 6));
01113       }
01114       //This happens if the edges are updated faster than the poses/clouds
01115       else ROS_WARN("Not enough cloud matrices (%d) for vertex ids (%d and %d)", cloud_matrices->size(), id1, id2);
01116     }
01117     glEnable(GL_DEPTH_TEST);
01118   }
01119 }
01120 
01121 
01122 void GLViewer::setViewPoint(QMatrix4x4 new_vp){
01124     viewpoint_tf_ = new_vp.inverted();
01125 }
01126 
01127 bool GLViewer::setClickedPosition(int x, int y) {
01128     GLint viewport[4];
01129     GLdouble modelview[16];
01130     GLdouble projection[16];
01131     GLfloat winX, winY, winZ;
01132     GLdouble posX, posY, posZ;
01133 
01134     glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
01135     glGetDoublev( GL_PROJECTION_MATRIX, projection );
01136     glGetIntegerv( GL_VIEWPORT, viewport );
01137 
01138     winX = (float)x;
01139     winY = (float)viewport[3] - (float)y;
01140     glReadPixels( x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
01141     if(winZ != 1){ //default value, where nothing was rendered
01142       gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
01143       ROS_INFO_STREAM((float)winZ << ", [" << posX << "," << posY << "," << posZ << "]");
01144       viewpoint_tf_.setToIdentity();
01145       viewpoint_tf_(0,3) = -posX;
01146       viewpoint_tf_(1,3) = -posY;
01147       viewpoint_tf_(2,3) = -posZ;
01148       Q_EMIT clickedPosition(posX, posY, posZ);
01149       return true;
01150     } else {
01151       return false;
01152     }
01153 }
01154 
01155 void GLViewer::toggleTriangulation() {
01156     ROS_INFO("Toggling Triangulation");
01157     if(polygon_mode == GL_FILL){ // Turn on Pointcloud mode
01158         polygon_mode = GL_POINT;
01159     //Wireframe mode is Slooow
01160     //} else if(polygon_mode == GL_POINT){ // Turn on Wireframe mode
01161         //polygon_mode = GL_LINE;
01162     } else { // Turn on Surface mode
01163         polygon_mode = GL_FILL;
01164     }
01165     glPolygonMode(GL_FRONT_AND_BACK, polygon_mode);
01166     clearAndUpdate();
01167 }
01168 
01169 void GLViewer::drawToPS(QString filename){
01170 #ifdef GL2PS
01171 
01172   FILE *fp = fopen(qPrintable(filename), "wb");
01173   if(!fp) ROS_ERROR("Could not open file %s", qPrintable(filename));
01174   GLint buffsize = 0, state = GL2PS_OVERFLOW;
01175   GLint viewport[4];
01176   char *oldlocale = setlocale(LC_NUMERIC, "C");
01177 
01178 
01179   glGetIntegerv(GL_VIEWPORT, viewport);
01180 
01181   while( state == GL2PS_OVERFLOW ){
01182     buffsize += 1024*1024;
01183     gl2psBeginPage ( "GL View", "RGBD-SLAM", viewport,
01184                      GL2PS_PDF, GL2PS_BSP_SORT, GL2PS_SILENT |
01185                      GL2PS_SIMPLE_LINE_OFFSET | GL2PS_NO_BLENDING |
01186                      GL2PS_OCCLUSION_CULL | GL2PS_BEST_ROOT,
01187                      GL_RGBA, 0, NULL, 0, 0, 0, buffsize,
01188                      fp, "LatexFile" );
01189     drawClouds(0.0);
01190     state = gl2psEndPage();
01191   }
01192   setlocale(LC_NUMERIC, oldlocale);
01193   fclose(fp);
01194 #else
01195   QMessageBox::warning(this, tr("This functionality is not supported."), tr("This feature needs to be compiled in. To do so, install libgl2ps-dev and set the USE_GL2PS flag at the top of CMakeLists.txt. The feature has been removed to ease the installation process, sorry for the inconvenience."));
01196 #endif
01197 }
01198 
01199 
01200 inline void GLViewer::clearAndUpdate(){
01201   ScopedTimer s(__FUNCTION__);
01202   makeCurrent();
01203   paintGL();
01204   if(this->format().doubleBuffer())
01205   {
01206     ScopedTimer s("SwapBuffers");
01207     swapBuffers();
01208   }
01209 }
01210 
01212 void drawEllipsoid(float fA, float fB, float fC, const Eigen::Vector4f& p)
01213 {
01214   unsigned int uiStacks = 4, uiSlices = 4;
01215         float tStep = (PI) / (float)uiSlices;
01216         float sStep = (PI) / (float)uiStacks;
01217         //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
01218         for(float t = -PI/2; t <= (PI/2)+.0001; t += tStep)
01219         {
01220                 glBegin(GL_TRIANGLE_STRIP);
01221                 for(float s = -PI; s <= PI+.0001; s += sStep)
01222                 {
01223                         glVertex3f(p[0] + fA * cos(t) * cos(s), 
01224                  p[1] + fB * cos(t) * sin(s), 
01225                  p[2] + fC * sin(t));
01226                         glVertex3f(p[0] + fA * cos(t+tStep) * cos(s), 
01227                  p[1] + fB * cos(t+tStep) * sin(s), 
01228                  p[2] + fC * sin(t+tStep));
01229                 }
01230                 glEnd();
01231         }
01232 }
01233 
01234 void GLViewer::drawNavigationAxis(int axis_idx, float scale, QString text){
01235   float coords[3] = { 0.0, 0.0, 0.0 };
01236   float colors[3] = { 1.0, 1.0, 1.0 };
01237   colors[axis_idx] = 0.0;
01238   glEnable(GL_BLEND); 
01239   glBegin(GL_LINES);
01240   glColor3fv(colors);
01241   coords[axis_idx] = scale * 10;
01242   glVertex3fv(coords);
01243   coords[axis_idx] = -scale * 10;
01244   glVertex3fv(coords);
01245   glEnd();
01246   coords[axis_idx] = -scale;
01247   this->renderText(coords[0],coords[1]+0.01,coords[2],text, QFont("Monospace", 8));
01248 }
01249 void GLViewer::setRenderable(Renderable* r){
01250   ROS_INFO("Setting Renderable");
01251   external_renderable = (Renderable*)r;
01252 }
01253 
01254 #else //ifndef DUMMYGLVIEWER (ARM Hardware)
01255 #include "glviewer.h"
01256 //Empty methods
01257 GLViewer::GLViewer(QWidget *parent) : QGLWidget(parent) { }
01258 GLViewer::~GLViewer() { }
01259 void GLViewer::initialPosition() { }
01260 QSize GLViewer::minimumSizeHint() const { return QSize(0, 0); }
01261 QSize GLViewer::sizeHint() const { return QSize(0, 0); }
01262 void GLViewer::setXRotation(int angle) { } 
01263 void GLViewer::setYRotation(int angle) { } 
01264 void GLViewer::setRotationGrid(double rot_step_in_degree) { } 
01265 void GLViewer::setStereoShift(double shift) { } 
01266 void GLViewer::initializeGL() { }
01267 void GLViewer::drawTriangle(const point_type& p1, const point_type& p2, const point_type& p3){ }
01268 void GLViewer::drawGrid(){ }
01269 void GLViewer::drawAxes(float scale, float thickness){ }
01270 void GLViewer::makeCurrent(){ }
01271 void GLViewer::paintGL() { }
01272 void GLViewer::drawRenderable() { }
01273 void GLViewer::drawOneCloud(int i) { }
01274 void GLViewer::drawClouds(float xshift) { }
01275 void GLViewer::resizeGL(int width, int height) { }
01276 void GLViewer::mouseDoubleClickEvent(QMouseEvent *event) { }
01277 void GLViewer::toggleStereo(bool flag){ }
01278 void GLViewer::toggleBackgroundColor(bool flag){ }
01279 void GLViewer::toggleShowFeatures(bool flag){ }
01280 void GLViewer::toggleShowOctoMap(bool flag){ }
01281 void GLViewer::toggleShowClouds(bool flag){ }
01282 void GLViewer::toggleShowTFs(bool flag){ }
01283 void GLViewer::toggleShowGrid(bool flag){ }
01284 void GLViewer::toggleShowIDs(bool flag){ }
01285 void GLViewer::toggleShowEdges(bool flag){ }
01286 void GLViewer::toggleShowPoses(bool flag){ }
01287 void GLViewer::toggleFollowMode(bool flag){ }
01288 void GLViewer::mouseReleaseEvent(QMouseEvent *event) { }
01289 void GLViewer::mousePressEvent(QMouseEvent *event) { } 
01290 void GLViewer::wheelEvent(QWheelEvent *event) { }
01291 void GLViewer::mouseMoveEvent(QMouseEvent *event) { } 
01292 void GLViewer::updateTransforms(QList<QMatrix4x4>* transforms){ }
01293 void GLViewer::addPointCloud(pointcloud_type * pc, QMatrix4x4 transform){ }
01294 void GLViewer::addFeatures(const std::vector<Eigen::Vector4f, Eigen::aligned_allocator<Eigen::Vector4f> >* feature_locations_3d) { }
01295 void GLViewer::pointCloud2GLStrip(pointcloud_type * pc){ }
01296 void GLViewer::deleteLastNode(){ }
01297 void GLViewer::pointCloud2GLEllipsoids(pointcloud_type * pc){ }
01298 void GLViewer::pointCloud2GLPoints(pointcloud_type * pc){ }
01299 void GLViewer::pointCloud2GLTriangleList(pointcloud_type const * pc){ }
01300 void GLViewer::reset(){ }
01301 QImage GLViewer::renderList(QMatrix4x4 transform, int list_id){ return QImage(); }
01302 void GLViewer::setEdges(const QList<QPair<int, int> >* edge_list){ } 
01303 void GLViewer::drawEdges(){ } 
01304 void GLViewer::setViewPoint(QMatrix4x4 new_vp){ } 
01305 bool GLViewer::setClickedPosition(int x, int y) { } 
01306 void GLViewer::toggleTriangulation() { } 
01307 void GLViewer::drawToPS(QString filename){ }
01308 void GLViewer::clearAndUpdate(){ }
01309 void GLViewer::drawNavigationAxis(int axis_idx, float scale, QString text){ }
01310 void GLViewer::setRenderable(Renderable* r){ }
01311 
01312 #endif


rgbdslam_v2
Author(s): Felix Endres, Juergen Hess, Nikolas Engelhard
autogenerated on Thu Jun 6 2019 21:49:45