ManipulatorNode.cc

00001 /*===========================================================================*\
00002  *                                                                           *
00003  *                              OpenFlipper                                  *
00004  *      Copyright (C) 2001-2009 by Computer Graphics Group, RWTH Aachen      *
00005  *                           www.openflipper.org                             *
00006  *                                                                           *
00007  *---------------------------------------------------------------------------*
00008  *  This file is part of OpenFlipper.                                        *
00009  *                                                                           *
00010  *  OpenFlipper is free software: you can redistribute it and/or modify      *
00011  *  it under the terms of the GNU Lesser General Public License as           *
00012  *  published by the Free Software Foundation, either version 3 of           *
00013  *  the License, or (at your option) any later version with the              *
00014  *  following exceptions:                                                    *
00015  *                                                                           *
00016  *  If other files instantiate templates or use macros                       *
00017  *  or inline functions from this file, or you compile this file and         *
00018  *  link it with other files to produce an executable, this file does        *
00019  *  not by itself cause the resulting executable to be covered by the        *
00020  *  GNU Lesser General Public License. This exception does not however       *
00021  *  invalidate any other reasons why the executable file might be            *
00022  *  covered by the GNU Lesser General Public License.                        *
00023  *                                                                           *
00024  *  OpenFlipper is distributed in the hope that it will be useful,           *
00025  *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
00026  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
00027  *  GNU Lesser General Public License for more details.                      *
00028  *                                                                           *
00029  *  You should have received a copy of the GNU LesserGeneral Public          *
00030  *  License along with OpenFlipper. If not,                                  *
00031  *  see <http://www.gnu.org/licenses/>.                                      *
00032  *                                                                           *
00033 \*===========================================================================*/
00034 
00035 /*===========================================================================*\
00036  *                                                                           *
00037  *   $Revision: 6998 $                                                       *
00038  *   $Author: kremer $                                                      *
00039  *   $Date: 2009-09-07 12:07:43 +0200 (Mo, 07. Sep 2009) $                   *
00040  *                                                                           *
00041 \*===========================================================================*/
00042 
00043 
00044 
00045 
00046 //=============================================================================
00047 //
00048 //  CLASS ManipulatorNode - IMPLEMENTATION
00049 //
00050 //=============================================================================
00051 
00052 
00053 //== INCLUDES =================================================================
00054 
00055 
00056 #include "ManipulatorNode.hh"
00057 #include "../Math/GLMatrixT.hh"
00058 #include "../GL/gl.hh"
00059 
00060 #include <QMouseEvent>
00061 #include <QEvent>
00062 #include <float.h>
00063 
00064 
00065 //== NAMESPACES ===============================================================
00066 
00067 
00068 namespace ACG {
00069 namespace SceneGraph {
00070 
00071 
00072 //== IMPLEMENTATION ==========================================================
00073 
00074 
00075 const Vec4f cylinder_color (0.8, 0.4, 0.4, 1.0);
00076 const Vec4f sphere_color   (0.8, 0.4, 0.4, 1.0);
00077 const Vec4f select_color   (1.0, 0.1, 0.1, 1.0);
00078 const float SCALE_CONST  =  5.0;
00079 
00080 
00081 //----------------------------------------------------------------------------
00082 
00083 
00084 ManipulatorNode::
00085 ManipulatorNode( BaseNode* _parent, const std::string& _name )
00086   : TransformNode(_parent, _name),
00087     draw_cylinder_(false),
00088     direction_(1.0,0.0,0.0),
00089     cylinder_(0),
00090     cylinder_radius_(1),
00091     cylinder_height_(10),
00092     cylinder_slices_(30),
00093     cylinder_stacks_(10),
00094     cylinder_clicked_(false),
00095     sphere_clicked_(false),
00096     touched_(false)
00097 {
00098 }
00099 
00100 
00101 //----------------------------------------------------------------------------
00102 
00103 
00104 ManipulatorNode::
00105 ~ManipulatorNode()
00106 {
00107   if (cylinder_)
00108     gluDeleteQuadric(cylinder_);
00109 }
00110 
00111 
00112 //----------------------------------------------------------------------------
00113 
00114 
00115 void
00116 ManipulatorNode::
00117 setIdentity()
00118 {
00119   direction_ = rotation().transform_vector( direction_ );
00120   TransformNode::setIdentity();
00121 }
00122 
00123 
00124 //----------------------------------------------------------------------------
00125 
00126 
00127 void
00128 ManipulatorNode::
00129 setup_cylinder_system(GLState& _state)
00130 {
00131   _state.translate(center()[0], center()[1], center()[2]);
00132   _state.mult_matrix(inverse_scale (), scale ()); // Adapt scaling
00133 
00134   // rotation axis & angle
00135   Vec3d z(0.0, 0.0, 1.0);
00136   double scal_prod = (direction_ | z);
00137   Vec3d  axis      = z % direction_;
00138   double norm      = axis.norm();
00139   double angle;
00140 
00141   if (norm > FLT_MIN)
00142   {
00143     axis /= norm;
00144     if (scal_prod >  1.0) scal_prod =  1.0;
00145     if (scal_prod < -1.0) scal_prod = -1.0;
00146     angle = 180.0 / M_PI * acos(scal_prod);
00147   }
00148   else
00149   {
00150     axis  = Vec3d(1.0, 0.0, 0.0);
00151     angle = (scal_prod > 0.0) ? 0.0 : 180.0;
00152   }
00153 
00154   _state.rotate(angle, axis[0], axis[1], axis[2]);
00155 }
00156 
00157 
00158 void
00159 ManipulatorNode::
00160 setup_sphere_system(GLState& _state)
00161 {
00162   setup_cylinder_system(_state);
00163 
00164   _state.translate(0, 0, cylinder_height_+ 2*cylinder_radius_);
00165 }
00166 
00167 
00168 //----------------------------------------------------------------------------
00169 
00170 
00171 void
00172 ManipulatorNode::draw(GLState& _state, unsigned int /* _drawMode */ )
00173 {
00174   if (draw_cylinder_)
00175   {
00176     glEnable(GL_LIGHTING);
00177     glShadeModel(GL_FLAT);
00178 
00179 
00180     // backup colors
00181     Vec4f backup_diffuse  = _state.diffuse_color();
00182     Vec4f backup_specular = _state.specular_color();
00183 
00184 
00185     // draw cylinder
00186 
00187     if (!cylinder_)
00188     {
00189       cylinder_ = gluNewQuadric();
00190       gluQuadricDrawStyle(cylinder_, GLU_FILL);
00191       gluQuadricNormals(cylinder_, GLU_SMOOTH);
00192     }
00193 
00194     _state.push_modelview_matrix();
00195     setup_cylinder_system(_state);
00196 
00197     if( cylinder_clicked_)
00198     {
00199       _state.set_diffuse_color(select_color * 0.6f);
00200       _state.set_specular_color(select_color * 0.8f);
00201     }
00202     else
00203     {
00204       _state.set_diffuse_color(cylinder_color * 0.6f);
00205       _state.set_specular_color(cylinder_color * 0.8f);
00206     }
00207 
00208         // Zylinder in die X-Achse
00209         /*glPushMatrix();
00210         glLoadIdentity();
00211         glRotatef(-90, 0.0, 1.0, 0.0);*/
00212 
00213     glShadeModel(GL_SMOOTH);
00214     gluCylinder(cylinder_,
00215                 cylinder_radius_,
00216                 cylinder_radius_,
00217                 cylinder_height_,
00218                 cylinder_slices_,
00219                 cylinder_stacks_);
00220 
00221         //glPopMatrix();
00222 
00223 
00224     // BIG wireframe sphere
00225     if( sphere_clicked_)
00226     {
00227       _state.set_diffuse_color(select_color * 0.6f);
00228       _state.set_specular_color(select_color * 0.0f);
00229       glShadeModel(GL_SMOOTH);
00230       glutWireSphere(cylinder_height_+4*cylinder_radius_, 20, 20);
00231     }
00232 
00233     _state.pop_modelview_matrix();
00234 
00235 
00236 
00237 
00238     // draw sphere
00239 
00240     _state.push_modelview_matrix();
00241     setup_sphere_system(_state);
00242 
00243     if( sphere_clicked_)
00244     {
00245       _state.set_diffuse_color(select_color * 0.6f);
00246       _state.set_specular_color(select_color * 0.8f);
00247     }
00248     else
00249     {
00250       _state.set_diffuse_color(sphere_color * 0.6f);
00251       _state.set_specular_color(sphere_color * 0.8f);
00252     }
00253 
00254     glShadeModel(GL_SMOOTH);
00255     glutSolidSphere(2*cylinder_radius_, 20, 20);
00256 
00257     _state.pop_modelview_matrix();
00258 
00259 
00260 
00261 
00262     // restore
00263     _state.set_diffuse_color(backup_diffuse);
00264     _state.set_specular_color(backup_specular);
00265   }
00266 }
00267 
00268 
00269 //----------------------------------------------------------------------------
00270 
00271 
00272 void
00273 ManipulatorNode::mouseEvent(GLState& _state, QMouseEvent* _event)
00274 {
00275   Vec3d         oldPoint3D;
00276   Vec2i         newPoint2D(_event->pos().x(), _event->pos().y());
00277   Vec3d         newPoint3D;
00278   double        old_axis_hit, new_axis_hit;
00279 
00280 
00281   switch (_event->type())
00282   {
00283     case QEvent::MouseButtonPress:
00284     {
00285       // hit sphere ?
00286       sphere_clicked_ = hitSphere(_state, newPoint2D);
00287 
00288       // hit cylinder ?
00289       cylinder_clicked_ = mapToCylinder(_state, newPoint2D, new_axis_hit);
00290 
00291       // If the user clicked on the manipulator, remember it
00292       if ( sphere_clicked_ || cylinder_clicked_)
00293          touched_ = true;
00294 
00295       // select only sphere or cylinder
00296       if(sphere_clicked_ && cylinder_clicked_)
00297         cylinder_clicked_ = false;
00298 
00299       oldPoint2D_   = newPoint2D;
00300       break;
00301     }
00302 
00303 
00304     case QEvent::MouseButtonRelease:
00305     {
00306       sphere_clicked_   = false;
00307       cylinder_clicked_ = false;
00308       break;
00309     }
00310 
00311 
00312     case QEvent::MouseButtonDblClick:
00313     {
00314       draw_cylinder_ = !draw_cylinder_;
00315       break;
00316     }
00317 
00318 
00319     case QEvent::MouseMove:
00320     {
00321       // IF sphere clicked rotate or change direction
00322       if(sphere_clicked_)
00323       {
00324         bool hit0 = mapToSphere(_state, newPoint2D,  newPoint3D);
00325         bool hit1 = mapToSphere(_state, oldPoint2D_, oldPoint3D);
00326 
00327 
00328         if (hit0 && hit1)
00329         {
00330           // change direction
00331           if(_event->modifiers() & Qt::ShiftModifier)
00332           {
00333 
00334              // calculate new cylinder direction
00335              direction_.normalize();
00336              newPoint3D.normalize();
00337              oldPoint3D.normalize();
00338 
00339              direction_ += newPoint3D - oldPoint3D;
00340 
00341           }
00342           // rotate
00343           else
00344           {
00345             Vec3d axis = oldPoint3D % newPoint3D;
00346             double cos_angle = ( oldPoint3D | newPoint3D );
00347 
00348             if (fabs(cos_angle) < 1.0)
00349               rotate(acos(cos_angle)*180.0/M_PI, axis);
00350           }
00351         }
00352         else sphere_clicked_ = false;
00353       }
00354 
00355 
00356       // cylinder clicked  change scaling or change translation
00357       if(cylinder_clicked_)
00358       {
00359         mapToCylinder(_state, oldPoint2D_, old_axis_hit);
00360         mapToCylinder(_state, newPoint2D,  new_axis_hit);
00361 
00362         // scale
00363         if(_event->modifiers() & Qt::ShiftModifier)
00364         {
00365           scale(1.0 + (new_axis_hit - old_axis_hit) /
00366                 (cylinder_height_ * SCALE_CONST));
00367         }
00368 
00369         // twist
00370         else if(_event->modifiers() & (Qt::ControlModifier |
00371                                    Qt::AltModifier))
00372         {
00373           rotate( 45.0 * (new_axis_hit-old_axis_hit) / cylinder_height_,
00374                   direction_);
00375 
00376         }
00377 
00378         // translate
00379         else
00380         {
00381           translate(new_axis_hit - old_axis_hit);
00382         }
00383       }
00384 
00385       break;
00386     }
00387 
00388     default: // avoid warning
00389       break;
00390   }
00391 
00392 
00393   // save old Point
00394   oldPoint2D_   = newPoint2D;
00395 }
00396 
00397 
00398 //----------------------------------------------------------------------------
00399 
00400 
00401 bool ManipulatorNode::hitSphere( GLState& _state,
00402                                  const Vec2i& _v2)
00403 {
00404   // Qt -> GL coordinate systems
00405   unsigned int x = _v2[0];
00406   unsigned int y = _state.context_height() - _v2[1];
00407 
00408 
00409   // get ray from eye through pixel, in sphere coords
00410   Vec3d origin, direction;
00411 
00412   _state.set_updateGL(false);
00413   _state.push_modelview_matrix();
00414 
00415   setup_sphere_system(_state);
00416   _state.scale(2*cylinder_radius_);
00417 
00418   _state.viewing_ray(x, y, origin, direction);
00419 
00420   _state.pop_modelview_matrix();
00421   _state.set_updateGL(true);
00422 
00423 
00424 
00425   // calc sphere-ray intersection
00426   // (sphere is centered at origin, has radius 1)
00427   double a = direction.sqrnorm(),
00428         b = 2.0 * (origin | direction),
00429         c = origin.sqrnorm() - 1.0,
00430         d = b*b - 4.0*a*c;
00431 
00432   return (d >= 0.0);
00433 }
00434 
00435 
00436 //----------------------------------------------------------------------------
00437 
00438 
00439 bool
00440 ManipulatorNode::mapToSphere( GLState& _state,
00441                               const Vec2i& _v2,
00442                               Vec3d& _v3 )
00443 {
00444   // Qt -> GL coordinate systems
00445   unsigned int x = _v2[0];
00446   unsigned int y = _state.context_height() - _v2[1];
00447 
00448 
00449 
00450   // get ray from eye through pixel (trackball coords)
00451   // *no* rotation, points have to be in world coords
00452   Vec3d origin, direction;
00453 
00454   _state.set_updateGL(false);
00455   _state.push_modelview_matrix();
00456 
00457   _state.translate(center()[0], center()[1], center()[2]);
00458   _state.mult_matrix(inverse_scale (), scale ());
00459   _state.scale(cylinder_height_ + 4*cylinder_radius_);
00460 
00461   _state.viewing_ray(x, y, origin, direction);
00462 
00463   _state.pop_modelview_matrix();
00464   _state.set_updateGL(true);
00465 
00466 
00467 
00468   // calc sphere-ray intersection
00469   // (sphere is centered at origin, has radius 1)
00470   double a = direction.sqrnorm(),
00471         b = 2.0 * (origin | direction),
00472         c = origin.sqrnorm() - 1.0,
00473         d = b*b - 4.0*a*c,
00474         t;
00475 
00476   if      (d <  0.0)  return false;
00477   else if (d == 0.0)  t = -b / (2.0*a);
00478   else
00479   {
00480     a = 1.0 / (2.0*a);
00481     d = sqrt(d);
00482     double t1 = (-b - d) * a;
00483     double t2 = (-b + d) * a;
00484     t = (t1 < t2) ? t1 : t2;
00485   }
00486 
00487 
00488 
00489   // map back to world coords
00490   _v3 = origin + direction*t;
00491 
00492   return true;
00493 }
00494 
00495 
00496 //----------------------------------------------------------------------------
00497 
00498 
00499 bool
00500 ManipulatorNode::mapToCylinder(GLState&       _state,
00501                                const Vec2i&   _v1,
00502                                double&         _axis_hit)
00503 {
00504   // Qt -> GL coordinate systems
00505   unsigned int x = _v1[0];
00506   unsigned int y = _state.context_height() - _v1[1];
00507 
00508 
00509   // get ray from eye through pixel (cylinder coords)
00510   Vec3d origin, direction;
00511 
00512   _state.set_updateGL(false);
00513   _state.push_modelview_matrix();
00514 
00515   setup_cylinder_system(_state);
00516 
00517   _state.viewing_ray(x, y, origin, direction);
00518 
00519   _state.pop_modelview_matrix();
00520   _state.set_updateGL(true);
00521 
00522 
00523 
00524   // get cylinder axis ray: it's in its own coord system!
00525   const Vec3d origin2(0,0,0), direction2(0,0,1);
00526 
00527 
00528   // compute pseude-intersection
00529   Vec3d normal = (direction % direction2).normalize();
00530   Vec3d vd = ((origin2 - origin) % direction);
00531   _axis_hit = (normal | vd);
00532 
00533   double orthodistance = fabsf( ( origin2 - origin ) | normal);
00534 
00535 
00536   // hit cylinder ?
00537   return((orthodistance < cylinder_radius_) &&
00538          (_axis_hit >= 0)                   &&
00539          (_axis_hit <= cylinder_height_));
00540 }
00541 
00542 
00543 //----------------------------------------------------------------------------
00544 
00545 
00546 void ManipulatorNode::pick(GLState& _state, PickTarget _target) {
00547         if (_target == PICK_FACE || _target == PICK_ANYTHING) {
00548                 if (draw_cylinder_) {
00549 
00550                         _state.pick_set_maximum(2);
00551 
00552                         // cylinder
00553                         _state.push_modelview_matrix();
00554                         setup_cylinder_system(_state);
00555                         //glLoadName(1);
00556                         _state.pick_set_name(0);
00557                         gluCylinder(cylinder_, cylinder_radius_, cylinder_radius_, cylinder_height_, cylinder_slices_,
00558                                 cylinder_stacks_);
00559                         _state.pop_modelview_matrix();
00560 
00561                         // sphere
00562                         _state.push_modelview_matrix();
00563                         setup_sphere_system(_state);
00564                         //glLoadName(2);
00565                         _state.pick_set_name(1);
00566                         glutSolidSphere(2* cylinder_radius_ , 20, 20);
00567                         _state.pop_modelview_matrix();
00568                 }
00569         }
00570 }
00571 
00572 
00573 //----------------------------------------------------------------------------
00574 
00575 
00576 void
00577 ManipulatorNode::set_direction(Vec3d& _v)
00578 {
00579   direction_ = inverse_rotation().transform_vector(_v.normalize());
00580 }
00581 
00582 
00583 //----------------------------------------------------------------------------
00584 
00585 
00586 Vec3d
00587 ManipulatorNode::direction() const
00588 {
00589   return rotation().transform_vector(direction_);
00590 }
00591 
00592 
00593 
00594 //=============================================================================
00595 } // namespace SceneGraph
00596 } // namespace ACG
00597 //=============================================================================

acg pic Project OpenFlipper, ©  Computer Graphics Group, RWTH Aachen. Documentation generated using doxygen .