worldsim/src/renderworld.cpp

00001 /********************************************************************************
00002  *  WorldSim -- library for robot simulations                                   *
00003  *  Copyright (C) 2008-2011 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #include "renderworld.h"
00021 #include "phyjoint.h"
00022 
00023 #include <QImage>
00024 #include <QColor>
00025 #include <QKeyEvent>
00026 #include <QMenu>
00027 #include <QAction>
00028 #include <QList>
00029 #include <cmath>
00030 #include <QDir>
00031 #include <QLinkedList>
00032 #include <QMutexLocker>
00033 #include <QEvent>
00034 
00035 // These instructions are needed because QT 4.8 no longer depends on glu, so we
00036 // have to include it here explicitly
00037 #ifdef FARSA_MAC
00038 # include <GLUT/glut.h>
00039 #else
00040 # include <GL/glu.h>
00041 #endif
00042 
00043 using namespace qglviewer;
00044 
00045 #define GLMultMatrix glMultMatrixf
00046 // for double use #define GLMultMatrix glMultMatrixd
00047 
00048 #include "qglviewer/camera.h"
00049 #if QT_VERSION >= 0x040000
00050 # include <QWheelEvent>
00051 #endif
00052 
00053 namespace farsa {
00054  
00055 QMap<QString, QImage>* RenderWObjectContainer::textmap = NULL;
00056 unsigned int RenderWObjectContainer::textmapRefCounter = 0;
00057 
00058 RenderWObjectContainer::RenderWObjectContainer( QString wResName ) :
00059     worldResourceName(wResName),
00060     worldv(NULL),
00061     mutex(QMutex::Recursive)
00062 {
00063     if ( textmap == NULL ) {
00064         textmap = new QMap<QString, QImage>();
00065         (*textmap)["tile1"].load( ":/tiles/16tile10.jpg" );
00066         (*textmap)["tile2"].load( ":/tiles/16tile07.jpg" );
00067         (*textmap)["white"].load( ":/white.jpg" );
00068         (*textmap)["tile3"].load( ":/tiles/16tile11.jpg" );
00069         (*textmap)["tile4"].load( ":/tiles/16tile-B.jpg" );
00070         (*textmap)["tile5"].load( ":/tiles/16tile12.jpg" );
00071         (*textmap)["tile6"].load( ":/tiles/16tile04.jpg" );
00072         (*textmap)["tile7"].load( ":/tiles/tile01.jpg" );
00073         (*textmap)["tile8"].load( ":/tiles/16tile02.jpg" );
00074         (*textmap)["tile9"].load( ":/tiles/16tile05.jpg" );
00075         (*textmap)["tile10"].load( ":/tiles/16tile08.jpg" );
00076         (*textmap)["icub"].load( ":/tiles/16tile11.jpg" ); //.load( ":/metal/iron05.jpg" );
00077         (*textmap)["icubFace"].load( ":/covers/face.jpg" );
00078         (*textmap)["blueye"].load( ":/covers/eyep2_b.jpg" );
00079         (*textmap)["metal"].load( ":/metal/iron05.jpg" );
00080         (*textmap)["marXbot_12leds"].load( ":/covers/marxbot_12leds.jpg" );
00081         //--- The order of the texture is:
00082         // 0 => TOP
00083         // 1 => BACK
00084         // 2 => FRONT
00085         // 3 => BOTTOM
00086         // 4 => RIGHT
00087         // 5 => LEFT
00088     /*  skyb[0].load( ":/skybox/sb_top.jpg" );
00089         skyb[1].load( ":/skybox/sb_back.jpg" );
00090         skyb[2].load( ":/skybox/sb_front.jpg" );
00091         skyb[3].load( ":/skybox/sb_bottom.jpg" );
00092         skyb[4].load( ":/skybox/sb_right.jpg" );
00093         skyb[5].load( ":/skybox/sb_left.jpg" );*/
00094         (*textmap)["skyb0"].load( ":/skybox/sb2_top.jpg" );
00095         (*textmap)["skyb1"].load( ":/skybox/sb2_back.jpg" );
00096         (*textmap)["skyb2"].load( ":/skybox/sb2_front.jpg" );
00097         (*textmap)["skyb3"].load( ":/ground/cobbles01.jpg" );
00098         (*textmap)["skyb4"].load( ":/skybox/sb2_right.jpg" );
00099         (*textmap)["skyb5"].load( ":/skybox/sb2_left.jpg" );
00100     }
00101     textmapRefCounter++;
00102 
00103     usableResources(QStringList() << worldResourceName);
00104 }
00105 
00106 RenderWObjectContainer::~RenderWObjectContainer() {
00107     foreach( RenderWObject* ro, graphs ) {
00108         delete ro;
00109     }
00110     textmapRefCounter--;
00111     if (textmapRefCounter == 0) {
00112         delete textmap;
00113         textmap = NULL;
00114     }
00115 }
00116 
00117 bool RenderWObjectContainer::addTextureImage( QString filename, QString texturename ) {
00118     QMutexLocker locker(&mutex);
00119 
00120     return ((*textmap)[texturename].load( filename ));
00121 }
00122 
00123 void RenderWObjectContainer::setWorld( World* newworld ) {
00124     // Here we simply declare a resource, the resourceChanged handler will take care of setting things up
00125     declareResource( worldResourceName, newworld );
00126 }
00127 
00128 RenderWObject* RenderWObjectContainer::operator[]( const WObject* obj ) {
00129     QMutexLocker locker(&mutex);
00130 
00131     //--- not efficient !! OPTIMIZE ME
00132     for( int i=0; i<graphs.size(); i++ ) {
00133         if ( graphs[i]->object() == obj ) {
00134             return graphs[i];
00135         }
00136     }
00137     return NULL;
00138 }
00139 
00140 RenderWObject* RenderWObjectContainer::createRenderWObjectFor( const WObject* obj, RenderWObjectContainer* container ) {
00141     initFactory();
00142     const QMetaObject* metaObj = obj->metaObject();
00143     while( metaObj ) {
00144         QString classname = metaObj->className();
00145         if (fac->contains(classname)) {
00146             return (*fac)[classname]->create( (WObject*)obj, container );
00147         }
00148         metaObj = metaObj->superClass();
00149     }
00150 
00151     // Cannot find a specific renderer, using the generic one (which doesn't render anything...
00152     // at least we save a crash)
00153     return (*fac)["farsa::WObject"]->create( (WObject*)obj, container );
00154 }
00155 
00156 void RenderWObjectContainer::addObject( WObject* wobj ) {
00157     // Always take the resource mutex first and then our mutex to avoid deadlocks
00158     ResourcesLocker resourceLocker(this); // Here because perhaps the RenderWObject need to access the world
00159     QMutexLocker locker(&mutex);
00160 
00161     graphs.append( createRenderWObjectFor( wobj, this ) );
00162 }
00163 
00164 void RenderWObjectContainer::removeObject( WObject* wobj ) {
00165     // Always take the resource mutex first and then our mutex to avoid deadlocks
00166     ResourcesLocker resourceLocker(this); // Here because perhaps the RenderWObject need to access the world
00167     QMutexLocker locker(&mutex);
00168 
00169     for( int i=0; i<graphs.size(); i++ ) {
00170         if ( graphs[i]->object() == wobj ) {
00171             RenderWObject* ro = graphs[i];
00172             graphs.remove( i );
00173             ro->objectAlreadyDestroyed();
00174             delete ro;
00175             break;
00176         }
00177     }
00178 }
00179 
00180 void RenderWObjectContainer::resourceChanged(QString name, ResourceChangeType changeType)
00181 {
00182     QMutexLocker locker(&mutex);
00183 
00184     // We ignore all resources except world
00185     if (name != worldResourceName) {
00186         return;
00187     }
00188 
00189     // Removing all objects, they refer to the old world
00190     foreach( RenderWObject* ro, graphs ) {
00191         delete ro;
00192     }
00193     graphs.clear();
00194 
00195     // If world was deleted, we simply set the pointer to NULL, otherwise we set the pointer
00196     // to the new world
00197     if (changeType == Deleted) {
00198         worldv = NULL;
00199     } else {
00200         worldv = getResource<World>();
00201     }
00202 
00203     // Creating renderes for all objects
00204     if (worldv != NULL) {
00205         foreach( WObject* obj, worldv->objects() ) {
00206             graphs.append( createRenderWObjectFor( obj, this ) );
00207         }
00208     }
00209 }
00210 
00211 void RenderWObjectContainer::applyTexture( QGLContext* gw, QString texts ) {
00212     QMutexLocker locker(&mutex);
00213 
00214     if ( textmap->contains( texts ) ) {
00215         if ( !textGLId.contains( texts ) ) {
00216             textGLId[texts] = gw->bindTexture( (*textmap)[texts], GL_TEXTURE_2D, GL_RGB );
00217         }
00218         glBindTexture( GL_TEXTURE_2D, textGLId[texts] );
00219         glEnable( GL_TEXTURE_2D );
00220     }/* else {
00221         glDisable( GL_TEXTURE_2D );
00222     }*/
00223 }
00224 
00225 void RenderWObjectContainer::setupColorTexture( QGLContext* gw, RenderWObject* robj ) {
00226     // Always take the resource mutex first and then our mutex to avoid deadlocks
00227     ResourcesLocker resourceLocker(this);
00228     QMutexLocker locker(&mutex);
00229 
00230     // set the color
00231     glShadeModel( GL_SMOOTH );
00232     glEnable( GL_BLEND );
00233     glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
00234     WObject* obj = robj->object();
00235     while ( obj->useColorTextureOfOwner() && ( obj->owner() != NULL ) ) {
00236         WObject *const owner = dynamic_cast<WObject *>(obj->owner());
00237         if ( owner != NULL ) {
00238             obj = owner;
00239         } else {
00240             break;
00241         }
00242     }
00243     QColor colorv = obj->color();
00244     glColor4f( colorv.redF(), colorv.greenF(), colorv.blueF(), colorv.alphaF() );
00245     applyTexture( gw, obj->texture() );
00246 }
00247 
00248 void RenderWObjectContainer::drawSkyGroundBox( QGLContext* gw ) {
00249     // Always take the resource mutex first and then our mutex to avoid deadlocks
00250     ResourcesLocker resourceLocker(this);
00251     QMutexLocker locker(&mutex);
00252 
00253     if (worldv == NULL) {
00254         return;
00255     }
00256     wVector min, max;
00257     worldv->size( min, max );
00258     wVector m_size = wVector( fabs(max[0]-min[0]), fabs(max[1]-min[1]), fabs(max[1]-min[1]) );
00259 
00260     resourceLocker.unlock(); // We don't need it anymore
00261 
00262     glDisable( GL_LIGHTING );
00263     glShadeModel( GL_FLAT );
00264     glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
00265     //glEnable(GL_TEXTURE_2D);
00266     //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
00267     //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
00268     //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
00269     // the cube will just be drawn as six quads for the sake of simplicity
00270     // for each face, we specify the quad's normal (for lighting), then
00271     // specify the quad's 4 vertices's and associated texture coordinates
00272     // TOP
00273     applyTexture( gw, "skyb0" );
00274     glBegin(GL_QUADS);
00275     glTexCoord2f(0.0, 1.0); glVertex3f( min.x,  max.y, max.z);
00276     glTexCoord2f(0.0, 0.0); glVertex3f( max.x,  max.y, max.z);
00277     glTexCoord2f(1.0, 0.0); glVertex3f( max.x,  min.y, max.z);
00278     glTexCoord2f(1.0, 1.0); glVertex3f( min.x,  min.y, max.z);
00279     glEnd();
00280     // BACK
00281     applyTexture( gw, "skyb1" );
00282     glBegin(GL_QUADS);
00283     glTexCoord2f(1.0, 0.0); glVertex3f( min.x,  max.y,  min.z);
00284     glTexCoord2f(1.0, 1.0); glVertex3f( min.x,  max.y,  max.z);
00285     glTexCoord2f(0.0, 1.0); glVertex3f( min.x,  min.y,  max.z);
00286     glTexCoord2f(0.0, 0.0); glVertex3f( min.x,  min.y,  min.z);
00287     glEnd();
00288     // FRONT
00289     applyTexture( gw, "skyb2" );
00290     glBegin(GL_QUADS);
00291     glTexCoord2f(0.0, 1.0); glVertex3f(max.x,   max.y,  max.z);
00292     glTexCoord2f(0.0, 0.0); glVertex3f(max.x,   max.y,  min.z);
00293     glTexCoord2f(1.0, 0.0); glVertex3f(max.x,   min.y,  min.z);
00294     glTexCoord2f(1.0, 1.0); glVertex3f(max.x,   min.y,  max.z);
00295     glEnd();
00296     // BOTTOM
00297     applyTexture( gw, "skyb3" );
00298     //--- suppose the bottom texture will represent 40x40 cm of ground
00299     //--- and calculate repeating accordlying
00300     float bfs = m_size[1]/0.4;
00301     float bft = m_size[0]/0.4;
00302     glBegin(GL_QUADS);
00303     glTexCoord2f(0.0, bft); glVertex3f( max.x,  max.y,  min.z);
00304     glTexCoord2f(0.0, 0.0); glVertex3f( min.x,  max.y,  min.z);
00305     glTexCoord2f(bfs, 0.0); glVertex3f( min.x,  min.y,  min.z);
00306     glTexCoord2f(bfs, bft); glVertex3f( max.x,  min.y,  min.z);
00307     glEnd();
00308     // RIGHT
00309     applyTexture( gw, "skyb4" );
00310     glBegin(GL_QUADS);
00311     glTexCoord2f(1.0, 1.0); glVertex3f( min.x,  min.y,  max.z);
00312     glTexCoord2f(0.0, 1.0); glVertex3f( max.x,  min.y,  max.z);
00313     glTexCoord2f(0.0, 0.0); glVertex3f( max.x,  min.y,  min.z);
00314     glTexCoord2f(1.0, 0.0); glVertex3f( min.x,  min.y,  min.z);
00315     glEnd();
00316     // LEFT
00317     applyTexture( gw, "skyb5" );
00318     glBegin(GL_QUADS);
00319     glTexCoord2f(0.0, 0.0); glVertex3f( min.x,  max.y,  min.z);
00320     glTexCoord2f(1.0, 0.0); glVertex3f( max.x,  max.y,  min.z);
00321     glTexCoord2f(1.0, 1.0); glVertex3f( max.x,  max.y,  max.z);
00322     glTexCoord2f(0.0, 1.0); glVertex3f( min.x,  max.y,  max.z);
00323     glEnd();
00324 }
00325 
00326 class StandardCamera : public qglviewer::Camera {
00327 public :
00328     StandardCamera() {
00329         orthoSize = 1.0;
00330     };
00331     virtual float zNear() const {
00332         return 0.001f;
00333     };
00334     virtual float zFar() const {
00335         return 1000.0f;
00336     };
00337     void changeOrthoFrustumSize(int delta) {
00338         if (delta > 0) {
00339             orthoSize *= 1.1f;
00340         } else {
00341             orthoSize /= 1.1f;
00342         }
00343     };
00344     virtual void getOrthoWidthHeight(GLdouble &halfWidth, GLdouble &halfHeight) const {
00345         halfHeight = orthoSize;
00346         halfWidth = aspectRatio() * orthoSize;
00347     };
00348 private :
00349     float orthoSize;
00350 };
00351 
00352 class MyCameraConstraint : public qglviewer::Constraint {
00353 public:
00355     MyCameraConstraint( qglviewer::Vec min, qglviewer::Vec max ) {
00356         //--- the pad allow to avoid to reach exactly the bounding-box of the world
00357         qglviewer::Vec pad(0.1,0.1,0.1);
00358         minP = min + pad;
00359         maxP = max - pad;
00360     };
00361     virtual void constrainTranslation( qglviewer::Vec& t, qglviewer::Frame* const fr ) {
00362         //--- Local System
00363         const qglviewer::Frame* ls = fr;
00364         if ( fr->referenceFrame() != NULL ) {
00365             qDebug() << "Using Reference";
00366             ls = fr->referenceFrame();
00367         }
00368         //--- Convert t to world coordinate system
00369         qglviewer::Vec tw = ls->inverseTransformOf( t );
00370         qglviewer::Vec pos = fr->position();
00371         if ( (pos.x + tw.x > maxP.x && t.x > 0) || (pos.x + tw.x < minP.x && t.x < 0) ||
00372              (pos.y + tw.y > maxP.y && t.y > 0) || (pos.y + tw.y < minP.y && t.y < 0) ||
00373              (pos.z + tw.z > maxP.z && t.z > 0) || (pos.z + tw.z < minP.z && t.z < 0) ) {
00374             t.z = 0.0;
00375             t.x = 0.0;
00376             t.y = 0.0;
00377         }
00378     };
00379     //--- boundings
00380     qglviewer::Vec minP;
00381     qglviewer::Vec maxP;
00382 };
00383 
00384 namespace {
00385     // A custom QEvent to force update of RenderWorldWrapperWidget
00386     class ForceRenderWorldUpdateEvent : public QEvent
00387     {
00388     public:
00389         ForceRenderWorldUpdateEvent() :
00390             QEvent(QEvent::User)
00391         {
00392         }
00393 
00394         virtual ~ForceRenderWorldUpdateEvent()
00395         {
00396         }
00397     };
00398 }
00399 
00400 
00401 RenderWorld::RenderWorld( QWidget* parent, QString wResName )
00402     : QGLViewer( parent ), RenderWObjectContainer( wResName ) {
00403     wiref = false;
00404     showskygroundbox = true;
00405     showobjs = true;
00406     showjoints = false;
00407     showaabbs = false;
00408     showcontacts = true;
00409     showforces = false;
00410     currentSelected = -1;
00411 
00412     setStateFileName( QString() );
00413 
00414     setCamera( new StandardCamera() );
00415 
00416     setContextMenuPolicy( Qt::CustomContextMenu );
00417 }
00418 
00419 RenderWorld::~RenderWorld() {
00420     if ( ! stateFileName().isNull() ) {
00421         saveStateToFile();
00422     }
00423 }
00424 
00425 void RenderWorld::slotRemoveObject( WObject* w ) {
00426     // removeObject is already thread-safe, no need to lock mutex here
00427     removeObject( w );
00428 }
00429 
00430 void RenderWorld::slotAddObject( WObject* w ) {
00431     // removeObject is already thread-safe, no need to lock mutex here
00432     addObject( w );
00433 }
00434 
00435 void RenderWorld::onWorldResize() {
00436     // Always take the resource mutex first and then our mutex to avoid deadlocks
00437     ResourcesLocker resourceLocker(this);
00438     QMutexLocker locker(&mutex);
00439 
00440     if (world() == NULL) {
00441         return;
00442     }
00443 
00444     MyCameraConstraint* tmp = dynamic_cast<MyCameraConstraint*>( camera()->frame()->constraint() );
00445     if ( tmp != NULL ) {
00446         wVector min, max;
00447         world()->size( min, max );
00448         setSceneBoundingBox( qglviewer::Vec( min[0], min[1], min[2] ),
00449                              qglviewer::Vec( max[0], max[1], max[2] ) );
00450         tmp->minP = qglviewer::Vec( min[0], min[1], min[2] );
00451         tmp->maxP = qglviewer::Vec( max[0], max[1], max[2] );
00452     }
00453 }
00454 
00455 void RenderWorld::wireframe( bool b ) {
00456     QMutexLocker locker(&mutex);
00457     wiref = b;
00458 }
00459 
00460 void RenderWorld::showSkyGround( bool b ) {
00461     QMutexLocker locker(&mutex);
00462     showskygroundbox = b;
00463 }
00464 
00465 void RenderWorld::showObjects( bool b ) {
00466     QMutexLocker locker(&mutex);
00467     showobjs = b;
00468 }
00469 
00470 void RenderWorld::showJoints( bool b ) {
00471     QMutexLocker locker(&mutex);
00472     showjoints = b;
00473 }
00474 
00475 void RenderWorld::showAABBs( bool b ) {
00476     QMutexLocker locker(&mutex);
00477     showaabbs = b;
00478 }
00479 
00480 void RenderWorld::showContacts( bool b ) {
00481     QMutexLocker locker(&mutex);
00482     showcontacts = b;
00483 }
00484 
00485 void RenderWorld::showForces( bool b ) {
00486     QMutexLocker locker(&mutex);
00487     showforces = b;
00488 }
00489 
00490 void RenderWorld::contextMenu( const QPoint& pos ) {
00492     QList<QAction*> acts;
00493     QAction* act;
00494     if ( showskygroundbox ) {
00495         act = new QAction( "Hide Sky-Ground", this );
00496     } else {
00497         act = new QAction( "Show Sky-Ground", this );
00498     }
00499     act->setCheckable( true );
00500     act->setChecked( showskygroundbox );
00501     connect( act, SIGNAL( toggled(bool) ),
00502              this, SLOT( showSkyGround(bool) ) );
00503     acts.append( act );
00504     if ( wiref ) {
00505         act = new QAction( "Hide Wireframe", this );
00506     } else {
00507         act = new QAction( "Show Wireframe", this );
00508     }
00509     act->setCheckable( true );
00510     act->setChecked( wiref );
00511     connect( act, SIGNAL( toggled(bool) ),
00512              this, SLOT( wireframe(bool) ) );
00513     acts.append( act );
00514 
00515     if ( showobjs ) {
00516         act = new QAction( "Hide Objects", this );
00517     } else {
00518         act = new QAction( "Show Objects", this );
00519     }
00520     act->setCheckable( true );
00521     act->setChecked( showobjs );
00522     connect( act, SIGNAL( toggled(bool) ),
00523              this, SLOT( showObjects(bool) ) );
00524     acts.append( act );
00525 
00526     if ( showjoints ) {
00527         act = new QAction( "Hide Joints", this );
00528     } else {
00529         act = new QAction( "Show Joints", this );
00530     }
00531     act->setCheckable( true );
00532     act->setChecked( showjoints );
00533     connect( act, SIGNAL( toggled(bool) ),
00534              this, SLOT( showJoints(bool) ) );
00535     acts.append( act );
00536 
00537     if ( showaabbs ) {
00538         act = new QAction( "Hide AABBs", this );
00539     } else {
00540         act = new QAction( "Show AABBs", this );
00541     }
00542     act->setCheckable( true );
00543     act->setChecked( showaabbs );
00544     connect( act, SIGNAL( toggled(bool) ),
00545              this, SLOT( showAABBs(bool) ) );
00546     acts.append( act );
00547 
00548     if ( showcontacts ) {
00549         act = new QAction( "Hide Contacts", this );
00550     } else {
00551         act = new QAction( "Show Contacts", this );
00552     }
00553     act->setCheckable( true );
00554     act->setChecked( showcontacts );
00555     connect( act, SIGNAL( toggled(bool) ),
00556              this, SLOT( showContacts(bool) ) );
00557     acts.append( act );
00558 
00559     if ( showforces ) {
00560         act = new QAction( "Hide Forces", this );
00561     } else {
00562         act = new QAction( "Show Forces", this );
00563     }
00564     act->setCheckable( true );
00565     act->setChecked( showforces );
00566     connect( act, SIGNAL( toggled(bool) ),
00567              this, SLOT( showForces(bool) ) );
00568     acts.append( act );
00569 
00570     QMenu::exec( acts, pos );
00571 }
00572 
00573 void RenderWorld::init() {
00574     // Always take the resource mutex first and then our mutex to avoid deadlocks
00575     ResourcesLocker resourceLocker(this);
00576     QMutexLocker locker(&mutex);
00577 
00578     if (world() == NULL) {
00579         return;
00580     }
00581 
00582     if ( ! stateFileName().isNull() ) {
00583         if ( !restoreStateFromFile() ) {
00584         }
00585     }
00586     wVector min, max;
00587     world()->size( min, max );
00588     camera()->frame()->setConstraint( new MyCameraConstraint( qglviewer::Vec( min[0], min[1], min[2] ), qglviewer::Vec( max[0], max[1], max[2] ) ) );
00589     // Light0 is the default ambient light
00590     glEnable(GL_LIGHT0);
00591 }
00592 
00593 void RenderWorld::draw() {
00594     // Always take the resource mutex first and then our mutex to avoid deadlocks
00595     ResourcesLocker resourceLocker(this);
00596     QMutexLocker locker(&mutex);
00597 
00598     if (world() == NULL) {
00599         return;
00600     }
00601 
00602     if ( showskygroundbox ) {
00603         glPushAttrib( GL_ALL_ATTRIB_BITS );
00604         glPushMatrix();
00605         drawSkyGroundBox( (QGLContext*)( QGLContext::currentContext() ) );
00606         glPopMatrix();
00607         glPopAttrib();
00608     }
00609 
00610     //--- the light follow the movement of the camera
00611     float pos[4] = {1.0, 0.5, 1.0, 0.0};
00612     // Directionnal light
00613     camera()->frame()->getPosition(pos[0], pos[1], pos[2]); 
00614     glLightfv( GL_LIGHT0, GL_POSITION, pos );
00615 
00616     //--- Display Contacts - Never in wireframe
00617     if ( showcontacts ) {
00618         glPushAttrib( GL_ALL_ATTRIB_BITS );
00619         //--- wireframe is ignored when drawing Contacts and Joints
00620         glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
00621         glDisable( GL_LIGHTING );
00622         // --- draws Contacts
00623         contactMapIterator itera( world()->contacts() );
00624         while( itera.hasNext() ) {
00625             itera.next();
00626             const contactVec& vec = itera.value();
00627             for( int i=0; i<vec.size(); i++ ) {
00628                 drawSphere( vec[i].worldPos, 0.008f );
00629             }
00630         }
00631         glPopAttrib();
00632     }
00633     //--- Display Joint and Kinematic chains
00634     int alpha = 255;
00635     if ( showjoints ) {
00636         glPushAttrib( GL_ALL_ATTRIB_BITS );
00637         //--- wireframe is ignored when drawing Contacts and Joints
00638         glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
00639         glDisable( GL_LIGHTING );
00640         drawKineChains();
00641         alpha = 130;
00642         glPopAttrib();
00643     }
00644     //--- display objects
00645     if ( showobjs ) {
00646         glPushAttrib( GL_ALL_ATTRIB_BITS );
00647         //--- wireframe only from this point to end
00648         if ( wiref ) {
00649             glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
00650         } else {
00651             glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
00652         }
00653         glEnable( GL_LIGHTING );
00654         PhyObject* pr;
00655         for( int i=0; i<graphics().size(); i++ ) {
00656             RenderWObject* r = graphics()[i];
00657             if ( r->object()->isInvisible() ) continue;
00658             if ( showforces && ( pr=dynamic_cast<PhyObject*>(r->object()) ) ) {
00659                 glPushAttrib( GL_ALL_ATTRIB_BITS );
00660                 glColor4f( 1.0, 0.0, 0.0, 1.0 );
00661                 drawArrow( pr->matrix().w_pos, pr->matrix().w_pos + pr->force(), 0.1f );
00662                 glColor4f( 0.0, 1.0, 0.0, 1.0 );
00663                 drawArrow( pr->matrix().w_pos, pr->matrix().w_pos + pr->torque(), 0.1f );
00664                 alpha = 130;
00665                 glPopAttrib();
00666             }
00667             r->object()->setAlpha( alpha );
00668             glPushAttrib( GL_ALL_ATTRIB_BITS );
00669             r->render( (QGLContext*)( QGLContext::currentContext() ) );
00670             glPopAttrib();
00671             if ( showaabbs && currentSelected == i ) {
00672                 glPushAttrib( GL_ALL_ATTRIB_BITS );
00673                 r->renderAABB( this );
00674                 glPopAttrib();
00675                 wVector dims, minPoint, maxPoint;
00676                 r->calculateOBB( dims, minPoint, maxPoint );
00677                 glPushAttrib( GL_ALL_ATTRIB_BITS );
00678                 drawWireBox( minPoint, maxPoint, r->object()->matrix() );
00679                 glPopAttrib();
00680             }
00681         }
00682     }
00683 
00684     //--- draw some text
00685     glPushAttrib( GL_ALL_ATTRIB_BITS );
00686     glColor4f( 1.0, 0.0, 0.0, 1.0 );
00687     drawText( 80, 15, QString("time: %1  step: %2")
00688         .arg(world()->elapsedTime())
00689         .arg((int)(world()->elapsedTime()/world()->timeStep()))
00690     );
00691     glPopAttrib();
00692 }
00693 
00694 void RenderWorld::drawWithNames() {
00695     QMutexLocker locker(&mutex);
00696 
00697     for( int i=0; i< graphics().size(); i++ ) {
00698         glPushName(i);
00699         graphics()[i]->render( (QGLContext*)( QGLContext::currentContext() ) );
00700         glPopName();
00701     }
00702 }
00703 
00704 void RenderWorld::postSelection(const QPoint& point) {
00705     QMutexLocker locker(&mutex);
00706 
00707     UNUSED_PARAM( point );
00708     int i = selectedName();
00709     if ( i != -1 && i != currentSelected ) {
00710         graphics()[qMax(0,currentSelected)]->object()->setColor( Qt::white );
00711         graphics()[i]->object()->setColor( Qt::yellow );
00712         currentSelected = i;
00713     }
00714 }
00715 
00716 void RenderWorld::keyPressEvent(QKeyEvent *e) {
00717     QMutexLocker locker(&mutex);
00718 
00719     // Get event modifiers key
00720 #if QT_VERSION < 0x040000
00721     // Bug in Qt : use 0x0f00 instead of Qt::KeyButtonMask with Qt versions < 3.1
00722     const Qt::ButtonState modifiers = (Qt::ButtonState)(e->state() & Qt::KeyButtonMask);
00723 #else
00724     const Qt::KeyboardModifiers modifiers = e->modifiers();
00725 #endif
00726 
00727     // A simple switch on e->key() is not sufficient if we want to take state key into account.
00728     // With a switch, it would have been impossible to separate 'F' from 'CTRL+F'.
00729     // That's why we use imbricated if...else and a "handled" boolean.
00730     bool handled = false;
00731     if ((e->key()==Qt::Key_Left) && (modifiers==Qt::NoButton)) {
00732         // rotate camera
00733         Quaternion qcur = camera()->orientation();
00734         Quaternion qnew = qcur * Quaternion( Vec(0,1,0), 3.1415/30 );
00735         camera()->setOrientation( qnew );
00736         handled = true;
00737         updateGL();
00738     } else if ((e->key()==Qt::Key_Right) && (modifiers==Qt::NoButton)) {
00739         // rotate camera
00740         Quaternion qcur = camera()->orientation();
00741         Quaternion qnew = qcur * Quaternion( Vec(0,1,0), -3.1415/30 );
00742         camera()->setOrientation( qnew );
00743         handled = true;
00744         updateGL();
00745     }
00746     if ((e->key()==Qt::Key_Up) && (modifiers==Qt::NoButton)) {
00747         // rotate camera
00748         Quaternion qcur = camera()->orientation();
00749         Quaternion qnew = qcur * Quaternion( Vec(1,0,0), 3.1415/30 );
00750         camera()->setOrientation( qnew );
00751         handled = true;
00752         updateGL();
00753     } else if ((e->key()==Qt::Key_Down) && (modifiers==Qt::NoButton)) {
00754         // rotate camera
00755         Quaternion qcur = camera()->orientation();
00756         Quaternion qnew = qcur * Quaternion( Vec(1,0,0), -3.1415/30 );
00757         camera()->setOrientation( qnew );
00758         handled = true;
00759         updateGL();
00760     }
00761     //--- Context Menu (not accessible with right-click, because right button as different meaning)
00762     if ((e->key()==Qt::Key_M) && (modifiers==Qt::NoButton)) {
00763         contextMenu( mapToGlobal(QPoint(10,10)) );
00764         handled = true;
00765     }
00766 
00767     if (!handled) {
00768         QGLViewer::keyPressEvent(e);
00769     }
00770 }
00771 
00772 void RenderWorld::resourceChanged(QString name, ResourceChangeType changeType)
00773 {
00774     QMutexLocker locker(&mutex);
00775 
00776     // Calling parent function
00777     RenderWObjectContainer::resourceChanged(name, changeType);
00778 
00779     if (world() == NULL) {
00780         return;
00781     }
00782 
00783     // Connecting slots and updating world dimensions
00784 
00785     // These connections are direct because syncronization is done with Mutexes
00786     connect( world(), SIGNAL( removedObject( WObject* ) ),
00787              this, SLOT( slotRemoveObject( WObject* ) ), Qt::DirectConnection );
00788     connect( world(), SIGNAL( addedObject( WObject* ) ),
00789              this, SLOT( slotAddObject( WObject* ) ), Qt::DirectConnection );
00790     connect( world(), SIGNAL( resized() ),
00791              this, SLOT( onWorldResize() ), Qt::DirectConnection );
00792 
00793     connect( world(), SIGNAL( advanced() ),
00794              this, SLOT( update() ) );
00795 
00796     // We can't call onWorldResize because that function tries to take a lock (and we can't from inside resourceChanged)
00797     MyCameraConstraint* tmp = dynamic_cast<MyCameraConstraint*>( camera()->frame()->constraint() );
00798     if ( tmp != NULL ) {
00799         wVector min, max;
00800         world()->size( min, max );
00801         setSceneBoundingBox( qglviewer::Vec( min[0], min[1], min[2] ),
00802                              qglviewer::Vec( max[0], max[1], max[2] ) );
00803         tmp->minP = qglviewer::Vec( min[0], min[1], min[2] );
00804         tmp->maxP = qglviewer::Vec( max[0], max[1], max[2] );
00805     }
00806 
00807     // Forcing renderworld update (we cannot call update() because here we could be in a different thread)
00808     QCoreApplication::postEvent(this, new ForceRenderWorldUpdateEvent());
00809 }
00810 
00811 void RenderWorld::drawDOF( PhyDOF* dof, bool drawAxes ) {
00812     // Used inside draw(), no need to lock here
00813 
00814     if ( dof->joint() == NULL ) return;
00815 
00816     //--- FIX ME: this method handles only rotational joints :-(
00817     if ( dof->translate() ) return;
00818 
00819     wMatrix mat;
00820     mat.x_ax = dof->xAxis();
00821     mat.y_ax = dof->yAxis();
00822     mat.z_ax = dof->axis();
00823     mat.w_pos = dof->centre();
00824     mat.sanitifize();
00825 
00826     PhyJoint* joint = dof->joint();
00827     PhyObject* child = joint->child();
00828     PhyObject* parent= joint->parent();
00829     RenderWorld& rw = *this;
00830     wVector dims, minP, maxP;
00831 //  wVector lax = dof->axis();
00832     rw[ child ]->calculateOBB( dims, minP, maxP );
00833     real cube = (dims[0]+dims[1]+dims[2])/3.0;
00834     if ( parent ) {
00835         rw[ parent ]->calculateOBB( dims, minP, maxP );
00836         real cube2 = (dims[0]+dims[1]+dims[2])/3.0;
00837         cube = min( cube2, cube );
00838     }
00839     real len = cube * 0.70;
00840     real rad = len * 0.25;
00841 
00842     mat.w_pos = dof->centre() - dof->axis().scale(len/2.0);
00843     drawCylinder( mat, len, rad, QColor(Qt::cyan) );
00844     if ( dof->isLimited() ) {
00845         //--- draw indication about limits
00846         real lo, hi;
00847         dof->limits( lo, hi );
00848         real ang = hi-lo;
00849         mat.w_pos = wVector(0,0,0);
00850         mat = mat * wMatrix( wQuaternion( dof->axis(), lo ), wVector(0,0,0) );
00851         mat.w_pos = dof->centre() + dof->axis().scale(len/2.0);
00852         drawTorus( rad, rad*0.6, mat, ang );
00853     } else {
00854         mat.w_pos = dof->centre() + dof->axis().scale(len/2.0);
00855         drawTorus( rad, rad*0.6, mat );
00856     }
00857     //--- draw indication about current position
00858     mat.x_ax = dof->xAxis();
00859     mat.y_ax = dof->yAxis();
00860     mat.z_ax = dof->axis();
00861     mat.w_pos = wVector(0,0,0);
00862     mat.sanitifize();
00863     mat = mat * wMatrix( wQuaternion( dof->axis(), dof->position()-0.05 ), wVector(0,0,0) );
00864     mat.w_pos = dof->centre() + dof->axis().scale(len/2.0);
00865     drawTorus( rad, rad*0.55f, mat, 0.1f, Qt::green );
00866 
00867     if ( drawAxes ) {
00868         drawArrow( dof->centre(), dof->centre()+dof->xAxis().scale(len*1.2), rad*0.4, 12, Qt::magenta );
00869         drawArrow( dof->centre(), dof->centre()+dof->yAxis().scale(len*1.2), rad*0.5, 12, Qt::yellow );
00870     }
00871 }
00872 
00873 void RenderWorld::drawKineChains() {
00874     // Used inside draw(), no need to lock here
00875 
00876     if (world() == NULL) {
00877         return;
00878     }
00879 
00880     foreach( PhyJoint* jn, world()->joints()) {
00881         //if ( ! jn->isEnabled() ) continue;
00882         float dist1 = 0.0f, dist2 = 0.0f;
00883         wVector start = jn->centre();
00884         wVector end1 = jn->child()->matrix().w_pos;
00885         wVector end2 = start;
00886         dist1 = wVector::distance( start, end1 );
00887         if ( jn->parent() ) {
00888             end2 = jn->parent()->matrix().w_pos;
00889             dist2 = wVector::distance( start, end2 );
00890         }
00891         real rad = (dist1 + dist2) * 0.04;
00892         drawCylinder( start, end1, rad*0.6 );
00893         drawCylinder( start, end2, rad*0.6 );
00894 
00895         QVector<PhyDOF*> ds = jn->dofs();
00896         for( int k=0; k<ds.size(); k++ ) {
00897             drawDOF( ds[k], true );
00898         }
00899     }
00900 }
00901 
00902 void RenderWorld::customEvent(QEvent* event)
00903 {
00904     if (event->type() == QEvent::User) {
00905         // Forcing RenderWorld update
00906         update();
00907     }
00908 }
00909 
00910 void RenderWObjectContainer::drawSphere( wVector pos, real radius ) {
00911     GLUquadricObj *pObj;
00912     
00913     // set the color
00914     glShadeModel( GL_SMOOTH );
00915     glColor4f( 0.0f, 1.0f, 0.0f, 1.0f );
00916     
00917     wMatrix mat = wMatrix::identity();
00918     mat.w_pos = pos;
00919     mat.x_ax = mat.x_ax.scale( radius );
00920     mat.y_ax = mat.y_ax.scale( radius );
00921     mat.z_ax = mat.z_ax.scale( radius );
00922     glPushMatrix();
00923     GLMultMatrix(&mat[0][0]);
00924     
00925     // Get a new Quadric off the stack
00926     pObj = gluNewQuadric();
00927     // Get a new Quadric off the stack
00928     gluQuadricTexture(pObj, true);
00929     gluSphere(pObj, 1.0f, 20, 20);
00930     
00931     gluDeleteQuadric(pObj);
00932     glPopMatrix();
00933 }
00934 
00935 void RenderWObjectContainer::drawCylinder( wVector axis, wVector centre, float h, float rad, QColor c ) {
00936     GLUquadricObj *pObj;
00937     
00938     glPushMatrix();
00939     
00940     // set the color
00941     glShadeModel( GL_SMOOTH );
00942     glColor4f( c.redF(), c.greenF(), c.blueF(), c.alphaF() );
00943     
00944     wVector recentre = axis.scale( -h*0.5 ) + centre;
00945     glTranslatef( recentre[0], recentre[1], recentre[2] );
00946     
00947     Vec xg(0,0,1);
00948     Vec ax( axis[0], axis[1], axis[2] );
00949     Quaternion quad( xg, ax );
00950     glMultMatrixd( quad.matrix() );
00951         
00952     // Get a new Quadric off the stack
00953     pObj = gluNewQuadric();             
00954     gluQuadricTexture(pObj, true);                      
00955     gluCylinder(pObj, rad, rad, h, 20, 2);
00956     
00957     // render the caps
00958     gluQuadricOrientation(pObj, GLU_INSIDE);
00959     gluDisk(pObj, 0.0f, rad, 20, 1);
00960     
00961     glTranslatef (0.0f, 0.0f, h);
00962     gluQuadricOrientation(pObj, GLU_OUTSIDE);
00963     gluDisk(pObj, 0.0f, rad, 20, 1);
00964 
00965     gluDeleteQuadric(pObj);
00966     glPopMatrix();
00967 }
00968 
00969 void RenderWObjectContainer::drawCylinder( wVector start, wVector end, float rad, QColor c ) {
00970     float h = wVector::distance( start, end );
00971     if ( h < 0.0001 ) return;
00972 
00973     GLUquadricObj *pObj;
00974     
00975     glPushMatrix();
00976     
00977     // set the color
00978     glShadeModel( GL_SMOOTH );
00979     glColor4f( c.redF(), c.greenF(), c.blueF(), c.alphaF() );
00980     
00981     wVector zaxis = end - start;
00982     zaxis.normalize();
00983     wMatrix tm = wMatrix::grammSchmidt( zaxis );
00984     tm.w_pos = start;
00985     GLMultMatrix( &tm[0][0] );
00986         
00987     // Get a new Quadric off the stack
00988     pObj = gluNewQuadric();             
00989     gluQuadricTexture(pObj, true);                      
00990     gluCylinder(pObj, rad, rad, h, 20, 2);
00991     
00992     // render the caps
00993     gluQuadricOrientation(pObj, GLU_INSIDE);
00994     gluDisk(pObj, 0.0f, rad, 20, 1);
00995     
00996     glTranslatef (0.0f, 0.0f, h);
00997     gluQuadricOrientation(pObj, GLU_OUTSIDE);
00998     gluDisk(pObj, 0.0f, rad, 20, 1);
00999     
01000     gluDeleteQuadric(pObj); 
01001     glPopMatrix();
01002 }
01003 
01004 void RenderWObjectContainer::drawCylinder( const wMatrix& mat, float h, float rad, QColor c ) {
01005     GLUquadricObj *pObj;
01006     
01007     glPushMatrix();
01008     
01009     // set the color
01010     glShadeModel( GL_SMOOTH );
01011     glColor4f( c.redF(), c.greenF(), c.blueF(), c.alphaF() );
01012     
01013     GLMultMatrix(&mat[0][0]);
01014         
01015     // Get a new Quadric off the stack
01016     pObj = gluNewQuadric();             
01017     gluQuadricTexture(pObj, true);                      
01018     gluCylinder(pObj, rad, rad, h, 20, 2);
01019     
01020     // render the caps
01021     gluQuadricOrientation(pObj, GLU_INSIDE);
01022     gluDisk(pObj, 0.0f, rad, 20, 1);
01023     
01024     glTranslatef (0.0f, 0.0f, h);
01025     gluQuadricOrientation(pObj, GLU_OUTSIDE);
01026     gluDisk(pObj, 0.0f, rad, 20, 1);
01027     
01028     gluDeleteQuadric(pObj); 
01029     glPopMatrix();
01030 }
01031 
01032 void RenderWObjectContainer::drawWireBox( wVector dims, wMatrix matrix ) {
01033     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
01034     glPushMatrix();
01035     
01036     // set the color
01037     glShadeModel( GL_SMOOTH );
01038     glColor4f( 1.0f, 1.0f, 0.0f, 1.0f );
01039     
01040     float hdx = (dims[0]/2.0);
01041     float hdy = (dims[1]/2.0);
01042     float hdz = (dims[2]/2.0);
01043     GLMultMatrix(&matrix[0][0]);
01044     
01045     // the cube will just be drawn as six quads for the sake of simplicity
01046     // for each face, we specify the quad's normal (for lighting), then
01047     // specify the quad's 4 vertices and associated texture coordinates
01048     glBegin(GL_QUADS);
01049     // front
01050     glNormal3f(0.0, 0.0, 1.0);
01051     glVertex3f(-hdx, -hdy, hdz);
01052     glVertex3f( hdx, -hdy, hdz);
01053     glVertex3f( hdx,  hdy, hdz);
01054     glVertex3f(-hdx,  hdy, hdz);
01055     
01056     // back
01057     glNormal3f(0.0, 0.0, -1.0);
01058     glVertex3f( hdx, -hdy, -hdz);
01059     glVertex3f(-hdx, -hdy, -hdz);
01060     glVertex3f(-hdx,  hdy, -hdz);
01061     glVertex3f( hdx,  hdy, -hdz);
01062     
01063     // top
01064     glNormal3f(0.0, 1.0, 0.0);
01065     glVertex3f(-hdx,  hdy,  hdz);
01066     glVertex3f( hdx,  hdy,  hdz);
01067     glVertex3f( hdx,  hdy, -hdz);
01068     glVertex3f(-hdx,  hdy, -hdz);
01069     
01070     // bottom
01071     glNormal3f(0.0, -1.0, 0.0);
01072     glVertex3f(-hdx, -hdy, -hdz);
01073     glVertex3f( hdx, -hdy, -hdz);
01074     glVertex3f( hdx, -hdy,  hdz);
01075     glVertex3f(-hdx, -hdy,  hdz);
01076     
01077     // left
01078     glNormal3f(-1.0, 0.0, 0.0);
01079     glVertex3f(-hdx, -hdy, -hdz);
01080     glVertex3f(-hdx, -hdy,  hdz);
01081     glVertex3f(-hdx,  hdy,  hdz);
01082     glVertex3f(-hdx,  hdy, -hdz);
01083     
01084     // right
01085     glNormal3f(1.0, 0.0, 0.0);
01086     glVertex3f(hdx,  -hdy,  hdz);
01087     glVertex3f(hdx,  -hdy, -hdz);
01088     glVertex3f(hdx,   hdy, -hdz);
01089     glVertex3f(hdx,   hdy,  hdz);
01090     glEnd();
01091     
01092     glPopMatrix();
01093 }
01094 
01095 void RenderWObjectContainer::drawWireBox( wVector min, wVector max, const wMatrix& matrix ) {
01096     glPushMatrix();
01097     GLMultMatrix(&matrix[0][0]);
01098     drawWireBox( min, max );
01099     glPopMatrix();
01100 }
01101 
01102 void RenderWObjectContainer::drawWireBox( wVector min, wVector max ) {
01103     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
01104     glPushMatrix();
01105     
01106     // set the color
01107     glShadeModel( GL_SMOOTH );
01108     glColor4f( 1.0f, 1.0f, 0.0f, 1.0f );
01109     
01110     float dx = fabs( max[0]-min[0] );
01111     float dy = fabs( max[1]-min[1] );
01112     float dz = fabs( max[2]-min[2] );
01113     float hdx = (dx/2.0);
01114     float hdy = (dy/2.0);
01115     float hdz = (dz/2.0);
01116     float minX = qMin(min[0], max[0]);
01117     float minY = qMin(min[1], max[1]);
01118     float minZ = qMin(min[2], max[2]);
01119     glTranslatef( minX+hdx, minY+hdy, minZ+hdz );
01120     
01121     // the cube will just be drawn as six quads for the sake of simplicity
01122     // for each face, we specify the quad's normal (for lighting), then
01123     // specify the quad's 4 vertices and associated texture coordinates
01124     glBegin(GL_QUADS);
01125     // front
01126     glNormal3f(0.0, 0.0, 1.0);
01127     glVertex3f(-hdx, -hdy, hdz);
01128     glVertex3f( hdx, -hdy, hdz);
01129     glVertex3f( hdx,  hdy, hdz);
01130     glVertex3f(-hdx,  hdy, hdz);
01131     
01132     // back
01133     glNormal3f(0.0, 0.0, -1.0);
01134     glVertex3f( hdx, -hdy, -hdz);
01135     glVertex3f(-hdx, -hdy, -hdz);
01136     glVertex3f(-hdx,  hdy, -hdz);
01137     glVertex3f( hdx,  hdy, -hdz);
01138     
01139     // top
01140     glNormal3f(0.0, 1.0, 0.0);
01141     glVertex3f(-hdx,  hdy,  hdz);
01142     glVertex3f( hdx,  hdy,  hdz);
01143     glVertex3f( hdx,  hdy, -hdz);
01144     glVertex3f(-hdx,  hdy, -hdz);
01145     
01146     // bottom
01147     glNormal3f(0.0, -1.0, 0.0);
01148     glVertex3f(-hdx, -hdy, -hdz);
01149     glVertex3f( hdx, -hdy, -hdz);
01150     glVertex3f( hdx, -hdy,  hdz);
01151     glVertex3f(-hdx, -hdy,  hdz);
01152     
01153     // left
01154     glNormal3f(-1.0, 0.0, 0.0);
01155     glVertex3f(-hdx, -hdy, -hdz);
01156     glVertex3f(-hdx, -hdy,  hdz);
01157     glVertex3f(-hdx,  hdy,  hdz);
01158     glVertex3f(-hdx,  hdy, -hdz);
01159     
01160     // right
01161     glNormal3f(1.0, 0.0, 0.0);
01162     glVertex3f(hdx,  -hdy,  hdz);
01163     glVertex3f(hdx,  -hdy, -hdz);
01164     glVertex3f(hdx,   hdy, -hdz);
01165     glVertex3f(hdx,   hdy,  hdz);
01166     glEnd();
01167     
01168     glPopMatrix();
01169 }
01170 
01171 void RenderWObjectContainer::drawTorus( real outRad, real innRad, const wMatrix& matrix, real angle, QColor c ) {
01172     glPushMatrix();
01173     
01174     // set the color
01175     glShadeModel( GL_SMOOTH );
01176     glColor4f( c.redF(), c.greenF(), c.blueF(), c.alphaF() );
01177 
01178     GLMultMatrix(&matrix[0][0]);
01179 
01180     int numc = 8;
01181     int numt = 25;
01182     int i, j, k;
01183     double s, t, x, y, z, twopi;
01184     real tubeRad = (outRad-innRad)/2.0;
01185     real toruRad = outRad - tubeRad;
01186     twopi = 2 * PI_GRECO;
01187     for (i = 0; i < numc; i++) {
01188         glBegin(GL_QUAD_STRIP);
01189         for (j = 0; j <= numt; j++) {
01190             for (k = 1; k >= 0; k--) {
01191                 s = (i + k) % numc + 0.5;
01192                 t = j; //% numt;
01193 
01194                 x = (toruRad+tubeRad*cos(s*twopi/numc))*cos(t*(angle)/numt);
01195                 y = (toruRad+tubeRad*cos(s*twopi/numc))*sin(t*(angle)/numt);
01196                 z = tubeRad * sin(s * twopi / numc);
01197                 glVertex3f(x, y, z);
01198             }
01199         }
01200         glEnd();
01201     }
01202 
01203     glPopMatrix();
01204 }
01205 
01206 void RenderWObjectContainer::drawTorus( wVector axis, wVector centre, real outRad, real innRad, real angle ) {
01207     glPushMatrix();
01208     
01209     // set the color
01210     glShadeModel( GL_SMOOTH );
01211     glColor4f( 1.0f, 0.0f, 0.0f, 1.0f );
01212 
01213     wMatrix tm = wMatrix::grammSchmidt( axis );
01214     tm.w_pos = centre;
01215     GLMultMatrix( &tm[0][0] );
01216 
01217     int numc = 8;
01218     int numt = 25;
01219     int i, j, k;
01220     double s, t, x, y, z, twopi;
01221     real tubeRad = (outRad-innRad)/2.0;
01222     real toruRad = outRad - tubeRad;
01223     twopi = 2 * PI_GRECO;
01224     for (i = 0; i < numc; i++) {
01225         glBegin(GL_QUAD_STRIP);
01226         for (j = 0; j <= numt; j++) {
01227             for (k = 1; k >= 0; k--) {
01228                 s = (i + k) % numc + 0.5;
01229                 t = j; //% numt;
01230 
01231                 x = (toruRad+tubeRad*cos(s*twopi/numc))*cos(t*(angle)/numt);
01232                 y = (toruRad+tubeRad*cos(s*twopi/numc))*sin(t*(angle)/numt);
01233                 z = tubeRad * sin(s * twopi / numc);
01234                 glVertex3f(x, y, z);
01235             }
01236         }
01237         glEnd();
01238     }
01239 
01240     glPopMatrix();
01241 }
01242 
01243 void RenderWorld::drawArrow( const wVector& start, const wVector& end, float radius, int nbSubdivisions, QColor c ) {
01244     glPushMatrix();
01245     glColor4f( c.redF(), c.greenF(), c.blueF(), c.alphaF() );
01246 
01247     wVector zaxis = end - start;
01248     real len = zaxis.norm();
01249     zaxis.normalize();
01250     wMatrix tm = wMatrix::grammSchmidt( zaxis );
01251     tm.w_pos = start;
01252     GLMultMatrix( &tm[0][0] );
01253 
01254     QGLViewer::drawArrow( len, radius, nbSubdivisions );
01255 
01256     glPopMatrix();
01257 }
01258 
01259 } // end namespace farsa