From fc5f07acaa2566daa89f8a7202e1e068d59e06c3 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 29 Aug 2002 20:41:19 +0000 Subject: [PATCH] Fixed the numerical accuracy problems in the Drive and Flight Manipulators. --- include/osgGA/DriveManipulator | 18 +++-- include/osgGA/FlightManipulator | 9 +++ include/osgGA/TrackballManipulator | 8 +- src/osgGA/DriveManipulator.cpp | 124 +++++++++++++++++++---------- src/osgGA/FlightManipulator.cpp | 84 ++++++++++++++++--- src/osgGA/TrackballManipulator.cpp | 2 + 6 files changed, 186 insertions(+), 59 deletions(-) diff --git a/include/osgGA/DriveManipulator b/include/osgGA/DriveManipulator index 322525373..be71a7dbf 100644 --- a/include/osgGA/DriveManipulator +++ b/include/osgGA/DriveManipulator @@ -24,15 +24,15 @@ public: virtual ~DriveManipulator(); - virtual void setNode(osg::Node*); + virtual void setNode(osg::Node*); - virtual const osg::Node* getNode() const; + virtual const osg::Node* getNode() const; - virtual osg::Node* getNode(); + virtual osg::Node* getNode(); - virtual void home(const GUIEventAdapter& ea,GUIActionAdapter& us); + virtual void home(const GUIEventAdapter& ea,GUIActionAdapter& us); - virtual void init(const GUIEventAdapter& ea,GUIActionAdapter& us); + virtual void init(const GUIEventAdapter& ea,GUIActionAdapter& us); virtual bool handle(const GUIEventAdapter& ea,GUIActionAdapter& us); @@ -44,6 +44,11 @@ private: /** Add the current mouse GUIEvent to internal stack.*/ void addMouseEvent(const GUIEventAdapter& ea); + void computeLocalDataFromCamera(); + + void computeCameraFromLocalData(); + void computeCameraFromLocalData(const osg::Vec3& lv,const osg::Vec3& up); + /** For the give mouse movement calculate the movement of the camera. Return true is camera has moved and a redraw is required.*/ bool calcMovement(); @@ -66,6 +71,9 @@ private: SpeedControlMode _speedMode; + osg::Vec3 _eye; + osg::Quat _rotation; + float _distance; }; } diff --git a/include/osgGA/FlightManipulator b/include/osgGA/FlightManipulator index 42a83048f..0a67f0ebb 100644 --- a/include/osgGA/FlightManipulator +++ b/include/osgGA/FlightManipulator @@ -50,10 +50,15 @@ private: /** Add the current mouse GUIEvent to internal stack.*/ void addMouseEvent(const GUIEventAdapter& ea); + void computeLocalDataFromCamera(); + + void computeCameraFromLocalData(); + /** For the give mouse movement calculate the movement of the camera. Return true is camera has moved and a redraw is required.*/ bool calcMovement(); + // Internal event stack comprising last three mouse events. osg::ref_ptr _ga_t1; osg::ref_ptr _ga_t0; @@ -64,6 +69,10 @@ private: float _velocity; YawControlMode _yawMode; + + osg::Vec3 _eye; + osg::Quat _rotation; + float _distance; }; diff --git a/include/osgGA/TrackballManipulator b/include/osgGA/TrackballManipulator index 4344fc63b..71160792c 100644 --- a/include/osgGA/TrackballManipulator +++ b/include/osgGA/TrackballManipulator @@ -46,14 +46,14 @@ class OSGGA_EXPORT TrackballManipulator : public CameraManipulator /** Add the current mouse GUIEvent to internal stack.*/ void addMouseEvent(const GUIEventAdapter& ea); + void computeLocalDataFromCamera(); + + void computeCameraFromLocalData(); + /** For the give mouse movement calculate the movement of the camera. Return true is camera has moved and a redraw is required.*/ bool calcMovement(); - void computeLocalDataFromCamera(); - - void computeCameraFromLocalData(); - void trackball(osg::Vec3& axis,float& angle, float p1x, float p1y, float p2x, float p2y); float tb_project_to_sphere(float r, float x, float y); diff --git a/src/osgGA/DriveManipulator.cpp b/src/osgGA/DriveManipulator.cpp index 31bd7c834..cd1301153 100644 --- a/src/osgGA/DriveManipulator.cpp +++ b/src/osgGA/DriveManipulator.cpp @@ -161,6 +161,7 @@ void DriveManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us) flushMouseEventStack(); + computeLocalDataFromCamera(); } @@ -258,6 +259,7 @@ void DriveManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us) us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2,(ea.getYmin()+ea.getYmax())/2); + computeLocalDataFromCamera(); } @@ -369,6 +371,59 @@ void DriveManipulator::addMouseEvent(const GUIEventAdapter& ea) _ga_t0 = &ea; } +void DriveManipulator::computeLocalDataFromCamera() +{ + // maths from gluLookAt/osg::Matrix::makeLookAt + osg::Vec3 f(_camera->getCenterPoint()-_camera->getEyePoint()); + f.normalize(); + osg::Vec3 s(f^_camera->getUpVector()); + s.normalize(); + osg::Vec3 u(s^f); + u.normalize(); + + osg::Matrix rotation_matrix(s[0], u[0], -f[0], 0.0f, + s[1], u[1], -f[1], 0.0f, + s[2], u[2], -f[2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + + _eye = _camera->getEyePoint(); + _distance = _camera->getLookDistance(); + _rotation.set(rotation_matrix); + _rotation = _rotation.inverse(); + +} + +void DriveManipulator::computeCameraFromLocalData() +{ + osg::Matrix new_rotation; + new_rotation.makeRotate(_rotation); + + osg::Vec3 up = osg::Vec3(0.0f,1.0f,0.0) * new_rotation; + osg::Vec3 center = (osg::Vec3(0.0f,0.0f,-_distance) * new_rotation) + _eye; + + _camera->setLookAt(_eye,center,up); +} + +void DriveManipulator::computeCameraFromLocalData(const osg::Vec3& lv,const osg::Vec3& up) +{ + osg::Vec3 f(lv); + f.normalize(); + osg::Vec3 s(f^up); + s.normalize(); + osg::Vec3 u(s^f); + u.normalize(); + + osg::Matrix rotation_matrix(s[0], u[0], -f[0], 0.0f, + s[1], u[1], -f[1], 0.0f, + s[2], u[2], -f[2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + + _rotation.set(rotation_matrix); + _rotation = _rotation.inverse(); + + computeCameraFromLocalData(); +} + bool DriveManipulator::calcMovement() { @@ -421,36 +476,29 @@ bool DriveManipulator::calcMovement() } } - // rotate the camera. - osg::Vec3 center = _camera->getEyePoint(); - osg::Vec3 uv = _camera->getUpVector(); + osg::Matrix rotation_matrix; + rotation_matrix.makeRotate(_rotation); + + osg::Vec3 up = osg::Vec3(0.0f,1.0f,0.0) * rotation_matrix; + osg::Vec3 lv = osg::Vec3(0.0f,0.0f,-1.0f) * rotation_matrix; + // rotate the camera. float mx = (_ga_t0->getXmin()+_ga_t0->getXmax())/2.0f; float dx = _ga_t0->getX()-mx; float yaw = -inDegrees(dx*0.1f*dt); - osg::Matrix mat; - mat.makeTranslate(-center.x(),-center.y(),-center.z()); - mat *= Matrix::rotate(yaw,uv.x(),uv.y(),uv.z()); - mat *= Matrix::translate(center.x(),center.y(),center.z()); + osg::Quat yaw_rotation; + yaw_rotation.makeRotate(yaw,up); - center = _camera->getEyePoint(); - uv = _camera->getUpVector(); - - _camera->transformLookAt(mat); - - // get the new forward (look) vector. - osg::Vec3 sv = _camera->getSideVector(); - osg::Vec3 lv = _camera->getCenterPoint()-_camera->getEyePoint(); - float lookDistance = lv.length(); - lv.normalize(); + _rotation *= yaw_rotation; + rotation_matrix.makeRotate(_rotation); + osg::Vec3 sv = osg::Vec3(1.0f,0.0f,0.0f) * rotation_matrix; // movement is big enough the move the eye point along the look vector. if (fabsf(_velocity*dt)>1e-8) { - osg::Vec3 ep = _camera->getEyePoint(); float distanceToMove = _velocity*dt; float signedBuffer; @@ -460,7 +508,7 @@ bool DriveManipulator::calcMovement() // check to see if any obstruction in front. osgUtil::IntersectVisitor iv; osg::ref_ptr segForward = osgNew osg::LineSegment; - segForward->set(ep,ep+lv*(signedBuffer+distanceToMove)); + segForward->set(_eye,_eye+lv*(signedBuffer+distanceToMove)); iv.addLineSegment(segForward.get()); _node->accept(iv); @@ -472,15 +520,15 @@ bool DriveManipulator::calcMovement() { // notify(INFO) << "Hit obstruction"<< std::endl; osg::Vec3 ip = hitList.front().getWorldIntersectPoint(); - distanceToMove = (ip-ep).length()-_buffer; + distanceToMove = (ip-_eye).length()-_buffer; _velocity = 0.0f; } } // check to see if forward point is correct height above terrain. - osg::Vec3 fp = ep+lv*distanceToMove; - osg::Vec3 lfp = fp-uv*_height*5; + osg::Vec3 fp = _eye+lv*distanceToMove; + osg::Vec3 lfp = fp-up*_height*5; iv.reset(); @@ -499,15 +547,13 @@ bool DriveManipulator::calcMovement() osg::Vec3 ip = hitList.front().getWorldIntersectPoint(); osg::Vec3 np = hitList.front().getWorldIntersectNormal(); - if (uv*np>0.0f) uv = np; - else uv = -np; + if (up*np>0.0f) up = np; + else up = -np; - ep = ip+uv*_height; - lv = uv^sv; - osg::Vec3 lp = ep+lv*lookDistance; + _eye = ip+up*_height; + lv = up^sv; - _camera->setLookAt(ep,lp,uv); - _camera->ensureOrthogonalUpVector(); + computeCameraFromLocalData(lv,up); return true; @@ -538,15 +584,13 @@ bool DriveManipulator::calcMovement() osg::Vec3 ip = hitList.front().getWorldIntersectPoint(); osg::Vec3 np = hitList.front().getWorldIntersectNormal(); - if (uv*np>0.0f) uv = np; - else uv = -np; + if (up*np>0.0f) up = np; + else up = -np; - ep = ip+uv*_height; - lv = uv^sv; - osg::Vec3 lp = ep+lv*lookDistance; + _eye = ip+up*_height; + lv = up^sv; - _camera->setLookAt(ep,lp,uv); - _camera->ensureOrthogonalUpVector(); + computeCameraFromLocalData(lv,up); return true; } @@ -555,13 +599,13 @@ bool DriveManipulator::calcMovement() // no collision with terrain has been found therefore track horizontally. lv *= (_velocity*dt); - ep += lv; - osg::Vec3 lp = _camera->getCenterPoint()+lv; + + _eye += lv; - _camera->setLookAt(ep,lp,uv); - _camera->ensureOrthogonalUpVector(); } + computeCameraFromLocalData(); + return true; } diff --git a/src/osgGA/FlightManipulator.cpp b/src/osgGA/FlightManipulator.cpp index 3a83c67b1..189ea7fab 100644 --- a/src/osgGA/FlightManipulator.cpp +++ b/src/osgGA/FlightManipulator.cpp @@ -10,6 +10,8 @@ FlightManipulator::FlightManipulator() _modelScale = 0.01f; _velocity = 0.0f; _yawMode = YAW_AUTOMATICALLY_WHEN_BANKED; + + _distance = 1.0f; } @@ -59,6 +61,8 @@ void FlightManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us) us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2,(ea.getYmin()+ea.getYmax())/2); + computeLocalDataFromCamera(); + flushMouseEventStack(); } @@ -76,6 +80,7 @@ void FlightManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us) us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2,(ea.getYmin()+ea.getYmax())/2); + computeLocalDataFromCamera(); } @@ -141,6 +146,16 @@ bool FlightManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us) _camera->setFusionDistanceRatio(_camera->getFusionDistanceRatio()/1.25f); return true; } + else if (ea.getKey()=='q') + { + _yawMode = YAW_AUTOMATICALLY_WHEN_BANKED; + return true; + } + else if (ea.getKey()=='a') + { + _yawMode = NO_AUTOMATIC_YAW; + return true; + } return false; case(GUIEventAdapter::FRAME): @@ -173,6 +188,39 @@ void FlightManipulator::addMouseEvent(const GUIEventAdapter& ea) } +void FlightManipulator::computeLocalDataFromCamera() +{ + // maths from gluLookAt/osg::Matrix::makeLookAt + osg::Vec3 f(_camera->getCenterPoint()-_camera->getEyePoint()); + f.normalize(); + osg::Vec3 s(f^_camera->getUpVector()); + s.normalize(); + osg::Vec3 u(s^f); + u.normalize(); + + osg::Matrix rotation_matrix(s[0], u[0], -f[0], 0.0f, + s[1], u[1], -f[1], 0.0f, + s[2], u[2], -f[2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + + _eye = _camera->getEyePoint(); + _distance = _camera->getLookDistance(); + _rotation.set(rotation_matrix); + _rotation = _rotation.inverse(); + +} + +void FlightManipulator::computeCameraFromLocalData() +{ + osg::Matrix new_rotation; + new_rotation.makeRotate(_rotation); + + osg::Vec3 up = osg::Vec3(0.0f,1.0f,0.0) * new_rotation; + osg::Vec3 center = (osg::Vec3(0.0f,0.0f,-_distance) * new_rotation) + _eye; + + _camera->setLookAt(_eye,center,up); +} + bool FlightManipulator::calcMovement() { _camera->setFusionDistanceMode(osg::Camera::PROPORTIONAL_TO_SCREEN_DISTANCE); @@ -217,29 +265,45 @@ bool FlightManipulator::calcMovement() float dx = _ga_t0->getX()-mx; float dy = _ga_t0->getY()-my; - osg::Vec3 center = _camera->getEyePoint(); - osg::Vec3 sv = _camera->getSideVector(); - osg::Vec3 lv = _camera->getLookVector(); + osg::Matrix rotation_matrix; + rotation_matrix.makeRotate(_rotation); + + osg::Vec3 up = osg::Vec3(0.0f,1.0f,0.0) * rotation_matrix; + osg::Vec3 lv = osg::Vec3(0.0f,0.0f,-1.0f) * rotation_matrix; + + osg::Vec3 sv = lv^up; + sv.normalize(); float pitch = inDegrees(dy*0.15f*dt); float roll = inDegrees(dx*0.1f*dt); - osg::Matrix mat; - mat.makeTranslate(-center); - mat *= Matrix::rotate(pitch,sv.x(),sv.y(),sv.z()); - mat *= Matrix::rotate(roll,lv.x(),lv.y(),lv.z()); + osg::Quat delta_rotate; + + osg::Quat roll_rotate; + osg::Quat pitch_rotate; + + pitch_rotate.makeRotate(pitch,sv.x(),sv.y(),sv.z()); + roll_rotate.makeRotate(roll,lv.x(),lv.y(),lv.z()); + + delta_rotate = pitch_rotate*roll_rotate; + if (_yawMode==YAW_AUTOMATICALLY_WHEN_BANKED) { float bank = asinf(sv.z()); float yaw = inRadians(bank)*dt; - mat *= Matrix::rotate(yaw,0.0f,0.0f,1.0f); + + osg::Quat yaw_rotate; + yaw_rotate.makeRotate(yaw,0.0f,0.0f,1.0f); + + delta_rotate = delta_rotate*yaw_rotate; } lv *= (_velocity*dt); - mat *= Matrix::translate(center+lv); + _eye += lv; + _rotation = _rotation*delta_rotate; - _camera->transformLookAt(mat); + computeCameraFromLocalData(); return true; } diff --git a/src/osgGA/TrackballManipulator.cpp b/src/osgGA/TrackballManipulator.cpp index f10068bbe..bfdef6704 100644 --- a/src/osgGA/TrackballManipulator.cpp +++ b/src/osgGA/TrackballManipulator.cpp @@ -10,6 +10,8 @@ TrackballManipulator::TrackballManipulator() _modelScale = 0.01f; _minimumZoomScale = 0.05f; _thrown = false; + + _distance = 1.0f; }