Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

gfx/TrackballManipulator.cpp

Go to the documentation of this file.
00001 #include <gfx/TrackballManipulator>
00002 
00003 #include <osg/Quat>
00004 #include <osg/Notify>
00005 
00006 using namespace osg;
00007 using namespace osgGA;
00008 
00009 using gfx::TrackballManipulator;
00010 
00011 
00012 TrackballManipulator::TrackballManipulator()
00013 {
00014     _modelScale = 0.01f;
00015     _minimumZoomScale = 0.05f;
00016     _thrown = false;
00017 
00018     _distance = 1.0f;
00019 }
00020 
00021 
00022 TrackballManipulator::~TrackballManipulator()
00023 {
00024 }
00025 
00026 
00027 void TrackballManipulator::setNode(osg::Node* node)
00028 {
00029     _node = node;
00030     if (_node.get())
00031     {
00032         const osg::BoundingSphere& boundingSphere=_node->getBound();
00033         _modelScale = boundingSphere._radius;
00034     }
00035 }
00036 
00037 
00038 const osg::Node* TrackballManipulator::getNode() const
00039 {
00040     return _node.get();
00041 }
00042 
00043 
00044 osg::Node* TrackballManipulator::getNode()
00045 {
00046     return _node.get();
00047 }
00048 
00049 
00050                                  /*ea*/
00051 void TrackballManipulator::home(const GUIEventAdapter& ,GUIActionAdapter& us)
00052 {
00053     if(_node.get())
00054     {
00055 
00056         const osg::BoundingSphere& boundingSphere=_node->getBound();
00057 
00058         computePosition(osg::Vec3( 0.0,-3.5f * _modelScale,0.0f),
00059                         osg::Vec3(0,0,0),
00060                         osg::Vec3(0.0f,0.0f,1.0f));
00061 
00062         us.requestRedraw();
00063     }
00064 
00065 }
00066 
00067 
00068 void TrackballManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& )
00069 {
00070     flushMouseEventStack();
00071 }
00072 
00073 
00074 void TrackballManipulator::getUsage(osg::ApplicationUsage& usage) const
00075 {
00076     usage.addKeyboardMouseBinding("Trackball: Space","Reset the viewing position to home");
00077     usage.addKeyboardMouseBinding("Trackball: +","When in stereo, increase the fusion distance");
00078     usage.addKeyboardMouseBinding("Trackball: -","When in stereo, reduse the fusion distance");
00079 }
00080 
00081 bool TrackballManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us)
00082 {
00083     switch(ea.getEventType())
00084     {
00085         case(GUIEventAdapter::PUSH):
00086         {
00087             flushMouseEventStack();
00088             addMouseEvent(ea);
00089             if (calcMovement()) us.requestRedraw();
00090             us.requestContinuousUpdate(false);
00091             _thrown = false;
00092             return true;
00093         }
00094 
00095         case(GUIEventAdapter::RELEASE):
00096         {
00097             if (ea.getButtonMask()==0)
00098             {
00099 
00100                 if (isMouseMoving())
00101                 {
00102                     if (calcMovement())
00103                     {
00104                         us.requestRedraw();
00105                         us.requestContinuousUpdate(true);
00106                         _thrown = true;
00107                     }
00108                 }
00109                 else
00110                 {
00111                     flushMouseEventStack();
00112                     addMouseEvent(ea);
00113                     if (calcMovement()) us.requestRedraw();
00114                     us.requestContinuousUpdate(false);
00115                     _thrown = false;
00116                 }
00117 
00118             }
00119             else
00120             {
00121                 flushMouseEventStack();
00122                 addMouseEvent(ea);
00123                 if (calcMovement()) us.requestRedraw();
00124                 us.requestContinuousUpdate(false);
00125                 _thrown = false;
00126             }
00127             return true;
00128         }
00129 
00130         case(GUIEventAdapter::DRAG):
00131         {
00132             addMouseEvent(ea);
00133             if (calcMovement()) us.requestRedraw();
00134             us.requestContinuousUpdate(false);
00135             _thrown = false;
00136             return true;
00137         }
00138 
00139         case(GUIEventAdapter::MOVE):
00140         {
00141             return false;
00142         }
00143 
00144         case(GUIEventAdapter::KEYDOWN):
00145             if (ea.getKey()==' ')
00146             {
00147                 flushMouseEventStack();
00148                 _thrown = false;
00149                 home(ea,us);
00150                 us.requestRedraw();
00151                 us.requestContinuousUpdate(false);
00152                 return true;
00153             }
00154             return false;
00155         case(GUIEventAdapter::FRAME):
00156             if (_thrown)
00157             {
00158                 if (calcMovement()) us.requestRedraw();
00159                 return true;
00160             }
00161             return false;
00162         default:
00163             return false;
00164     }
00165 }
00166 
00167 
00168 bool TrackballManipulator::isMouseMoving()
00169 {
00170     if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false;
00171 
00172     static const float velocity = 0.1f;
00173 
00174     float dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized();
00175     float dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized();
00176     float len = sqrtf(dx*dx+dy*dy);
00177     float dt = _ga_t0->time()-_ga_t1->time();
00178 
00179     return (len>dt*velocity);
00180 }
00181 
00182 
00183 void TrackballManipulator::flushMouseEventStack()
00184 {
00185     _ga_t1 = NULL;
00186     _ga_t0 = NULL;
00187 }
00188 
00189 
00190 void TrackballManipulator::addMouseEvent(const GUIEventAdapter& ea)
00191 {
00192     _ga_t1 = _ga_t0;
00193     _ga_t0 = &ea;
00194 }
00195 
00196 void TrackballManipulator::setByMatrix(const osg::Matrixd& matrix)
00197 {
00198     _center = osg::Vec3(0.0f,0.0f,-_distance)*matrix;
00199     matrix.get(_rotation);
00200 }
00201 
00202 osg::Matrixd TrackballManipulator::getMatrix() const
00203 {
00204     return osg::Matrixd::translate(0.0,0.0,_distance)*osg::Matrixd::rotate(_rotation)*osg::Matrixd::translate(_center);
00205 }
00206 
00207 osg::Matrixd TrackballManipulator::getInverseMatrix() const
00208 {
00209     return osg::Matrixd::translate(-_center)*osg::Matrixd::rotate(_rotation.inverse())*osg::Matrixd::translate(0.0,0.0,-_distance);
00210 }
00211 
00212 void TrackballManipulator::computePosition(const osg::Vec3& eye,const osg::Vec3& center,const osg::Vec3& up)
00213 { 
00214     osg::Vec3 e(eye);
00215     if (e.z() < 0) e.z() = 0; // keep eye above ground
00216   
00217     osg::Vec3 lv(center-e);
00218 
00219     osg::Vec3 f(lv);
00220     f.normalize();
00221     osg::Vec3 s(f^up);
00222     s.normalize();
00223     osg::Vec3 u(s^f);
00224     u.normalize();
00225     
00226     osg::Matrix rotation_matrix(s[0],     u[0],     -f[0],     0.0f,
00227                                 s[1],     u[1],     -f[1],     0.0f,
00228                                 s[2],     u[2],     -f[2],     0.0f,
00229                                 0.0f,     0.0f,     0.0f,      1.0f);
00230                    
00231     _center = center;
00232     _distance = lv.length();
00233     rotation_matrix.get(_rotation);
00234     _rotation = _rotation.inverse();
00235 }
00236 
00237 
00238 bool TrackballManipulator::calcMovement()
00239 {
00240 
00241     // return if less then two events have been added.
00242     if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false;
00243 
00244     float dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized();
00245     float dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized();
00246 
00247 
00248     // return if there is no movement.
00249     if (dx==0 && dy==0) return false;
00250 
00251     unsigned int buttonMask = _ga_t1->getButtonMask();
00252     if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON)
00253     {
00254 
00255         // rotate camera.
00256 
00257         osg::Vec3 axis;
00258         float angle;
00259 
00260         float px0 = _ga_t0->getXnormalized();
00261         float py0 = _ga_t0->getYnormalized();
00262         
00263         float px1 = _ga_t1->getXnormalized();
00264         float py1 = _ga_t1->getYnormalized();
00265         
00266 
00267         trackball(axis,angle,px1,py1,px0,py0);
00268 
00269         osg::Quat new_rotate;
00270         new_rotate.makeRotate(angle,axis);
00271         
00272         _rotation = _rotation*new_rotate;
00273 
00274         return true;
00275 
00276     }
00277     else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON ||
00278         buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEventAdapter::RIGHT_MOUSE_BUTTON))
00279     {
00280 
00281         // pan model.
00282 
00283         float scale = -0.5f*_distance;
00284 
00285         osg::Matrix rotation_matrix;
00286         rotation_matrix.set(_rotation);
00287 
00288         osg::Vec3 dv(dx*scale,dy*scale,0.0f);
00289 
00290         _center += dv*rotation_matrix;
00291         
00292         return true;
00293 
00294     }
00295     else if (buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON)
00296     {
00297 
00298         // zoom model.
00299 
00300         float fd = _distance;
00301         float scale = 1.0f+dy;
00302         if (fd*scale>_modelScale*_minimumZoomScale)
00303         {
00304 
00305             _distance *= scale;
00306 
00307         }
00308         else
00309         {
00310 
00311             // notify(DEBUG_INFO) << "Pushing forward"<<std::endl;
00312             // push the camera forward.
00313             float scale = -fd;
00314 
00315             osg::Matrix rotation_matrix(_rotation);
00316 
00317             osg::Vec3 dv = (osg::Vec3(0.0f,0.0f,-1.0f)*rotation_matrix)*(dy*scale);
00318 
00319             _center += dv;
00320 
00321         }
00322 
00323         return true;
00324 
00325     }
00326 
00327     return false;
00328 }
00329 
00330 
00331 /*
00332  * This size should really be based on the distance from the center of
00333  * rotation to the point on the object underneath the mouse.  That
00334  * point would then track the mouse as closely as possible.  This is a
00335  * simple example, though, so that is left as an Exercise for the
00336  * Programmer.
00337  */
00338 const float TRACKBALLSIZE = 0.8f;
00339 
00340 /*
00341  * Ok, simulate a track-ball.  Project the points onto the virtual
00342  * trackball, then figure out the axis of rotation, which is the cross
00343  * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
00344  * Note:  This is a deformed trackball-- is a trackball in the center,
00345  * but is deformed into a hyperbolic sheet of rotation away from the
00346  * center.  This particular function was chosen after trying out
00347  * several variations.
00348  *
00349  * It is assumed that the arguments to this routine are in the range
00350  * (-1.0 ... 1.0)
00351  */
00352 void TrackballManipulator::trackball(osg::Vec3& axis,float& angle, float p1x, float p1y, float p2x, float p2y)
00353 {
00354     /*
00355      * First, figure out z-coordinates for projection of P1 and P2 to
00356      * deformed sphere
00357      */
00358 
00359     osg::Matrix rotation_matrix(_rotation);
00360 
00361 
00362     osg::Vec3 uv = osg::Vec3(0.0f,1.0f,0.0f)*rotation_matrix;
00363     osg::Vec3 sv = osg::Vec3(1.0f,0.0f,0.0f)*rotation_matrix;
00364     osg::Vec3 lv = osg::Vec3(0.0f,0.0f,-1.0f)*rotation_matrix;
00365 
00366     osg::Vec3 p1 = sv*p1x+uv*p1y-lv*tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y);
00367     osg::Vec3 p2 = sv*p2x+uv*p2y-lv*tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y);
00368 
00369     /*
00370      *  Now, we want the cross product of P1 and P2
00371      */
00372 
00373 // Robert,
00374 //
00375 // This was the quick 'n' dirty  fix to get the trackball doing the right 
00376 // thing after fixing the Quat rotations to be right-handed.  You may want
00377 // to do something more elegant.
00378 //   axis = p1^p2;
00379 axis = p2^p1;
00380     axis.normalize();
00381 
00382     /*
00383      *  Figure out how much to rotate around that axis.
00384      */
00385     float t = (p2-p1).length() / (2.0*TRACKBALLSIZE);
00386 
00387     /*
00388      * Avoid problems with out-of-control values...
00389      */
00390     if (t > 1.0) t = 1.0;
00391     if (t < -1.0) t = -1.0;
00392     angle = inRadians(asin(t));
00393 
00394 }
00395 
00396 
00397 /*
00398  * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
00399  * if we are away from the center of the sphere.
00400  */
00401 float TrackballManipulator::tb_project_to_sphere(float r, float x, float y)
00402 {
00403     float d, t, z;
00404 
00405     d = sqrt(x*x + y*y);
00406                                  /* Inside sphere */
00407     if (d < r * 0.70710678118654752440)
00408     {
00409         z = sqrt(r*r - d*d);
00410     }                            /* On hyperbola */
00411     else
00412     {
00413         t = r / 1.41421356237309504880;
00414         z = t*t / d;
00415     }
00416     return z;
00417 }

Generated on Thu Jul 29 15:56:19 2004 for OpenSim by doxygen 1.3.6