diff --git a/VisualStudio/osgGA/osgGA.dsp b/VisualStudio/osgGA/osgGA.dsp index 05f083a0e..58fcc5650 100755 --- a/VisualStudio/osgGA/osgGA.dsp +++ b/VisualStudio/osgGA/osgGA.dsp @@ -139,6 +139,10 @@ SOURCE=..\..\src\osgGA\TrackballManipulator.cpp # End Source File # Begin Source File +SOURCE=..\..\src\osgGA\TrackerManipulator.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\osgGA\Version.cpp # End Source File # End Group @@ -203,6 +207,10 @@ SOURCE=..\..\Include\osgGA\TrackballManipulator # End Source File # Begin Source File +SOURCE=..\..\Include\osgGA\TrackerManipulator +# End Source File +# Begin Source File + SOURCE=..\..\Include\osgGA\Version # End Source File # End Group diff --git a/examples/osglightpoint/osglightpoint.cpp b/examples/osglightpoint/osglightpoint.cpp index 93928eacb..247fe776c 100644 --- a/examples/osglightpoint/osglightpoint.cpp +++ b/examples/osglightpoint/osglightpoint.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include #include @@ -59,7 +59,10 @@ osg::Node* createLightPointsDatabase() end._position.set(500.0f,-500.0f,0.0f); end._color.set(1.0f,1.0f,1.0f,1.0f); - osg::Transform* transform = new osg::Transform; + osg::MatrixTransform* transform = new osg::MatrixTransform; + + transform->setDataVariance(osg::Object::STATIC); + transform->setMatrix(osg::Matrix::scale(0.1,0.1,0.1)); osg::Vec3 start_delta(0.0f,10.0f,0.0f); osg::Vec3 end_delta(0.0f,10.0f,1.0f); diff --git a/examples/osgsimulation/osgsimulation.cpp b/examples/osgsimulation/osgsimulation.cpp index 96ae6a286..cc993adc3 100644 --- a/examples/osgsimulation/osgsimulation.cpp +++ b/examples/osgsimulation/osgsimulation.cpp @@ -18,6 +18,8 @@ #include #include +#include + // for the grid data.. #include "../osghangglide/terrain_coords.h" @@ -59,6 +61,8 @@ osg::Node* createMovingModel(const osg::Vec3& center, float radius) osg::Node* glider = osgDB::readNodeFile("glider.osg"); if (glider) { + glider->setName("glider"); + const osg::BoundingSphere& bs = glider->getBound(); float size = radius/bs.radius()*0.3f; @@ -81,6 +85,8 @@ osg::Node* createMovingModel(const osg::Vec3& center, float radius) osg::Node* cessna = osgDB::readNodeFile("cessna.osg"); if (cessna) { + cessna->setName("cessna"); + const osg::BoundingSphere& bs = cessna->getBound(); osgText::Text* text = new osgText::Text; @@ -245,6 +251,29 @@ void build_world(osg::Group *root) } } +class FindNamedNodeVisitor : public osg::NodeVisitor +{ +public: + FindNamedNodeVisitor(const std::string& name): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _name(name) {} + + virtual void apply(osg::Node& node) + { + if (node.getName()==_name) + { + _foundNodes.push_back(&node); + } + traverse(node); + } + + typedef std::vector< osg::ref_ptr > NodeList; + + std::string _name; + NodeList _foundNodes; +}; + + ////////////////////////////////////////////////////////////////////////////// // main() @@ -268,6 +297,8 @@ int main(int argc, char **argv) // set up the value with sensible default event handlers. viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS); + + // get details on keyboard and mouse bindings used by the viewer. viewer.getUsage(*arguments.getApplicationUsage()); @@ -294,6 +325,23 @@ int main(int argc, char **argv) // add a viewport to the viewer and attach the scene graph. viewer.setSceneData(root); + + FindNamedNodeVisitor fnnv("cessna"); + root->accept(fnnv); + + if (!fnnv._foundNodes.empty()) + { + osgGA::TrackerManipulator* tm = new osgGA::TrackerManipulator; + tm->setTrackNode(fnnv._foundNodes[0].get()); + + std::cout<<"Found "< +#include + +namespace osgGA{ + +class OSGGA_EXPORT TrackerManipulator : public MatrixManipulator +{ + public: + + TrackerManipulator(); + + virtual const char* className() const { return "Tracker"; } + + void setTrackNode(osg::Node* node) { _trackNode = node; } + osg::Node* getTrackNode() { return _trackNode.get(); } + const osg::Node* getTrackNode() const { return _trackNode.get(); } + + + enum RotationMode + { + ELEVATION_AZIM_ROLL, + ELEVATION_AZIM, + }; + + void setRotationMode(RotationMode mode); + RotationMode getRotationMode() const { return _rotationMode; } + + + /** set the position of the matrix manipulator using a 4x4 Matrix.*/ + virtual void setByMatrix(const osg::Matrixd& matrix); + + /** set the position of the matrix manipulator using a 4x4 Matrix.*/ + virtual void setByInverseMatrix(const osg::Matrixd& matrix) { setByMatrix(osg::Matrixd::inverse(matrix)); } + + /** get the position of the manipulator as 4x4 Matrix.*/ + virtual osg::Matrixd getMatrix() const; + + /** get the position of the manipulator as a inverse matrix of the manipulator, typically used as a model view matrix.*/ + virtual osg::Matrixd getInverseMatrix() const; + + /** Get the FusionDistanceMode. Used by SceneView for setting up setereo convergence.*/ + virtual osgUtil::SceneView::FusionDistanceMode getFusionDistanceMode() const { return osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE; } + + /** Get the FusionDistanceValue. Used by SceneView for setting up setereo convergence.*/ + virtual float getFusionDistanceValue() const { return _distance; } + + /** Attach a node to the manipulator. + Automatically detaches previously attached node. + setNode(NULL) detaches previously nodes. + Is ignored by manipulators which do not require a reference model.*/ + virtual void setNode(osg::Node*); + + /** Return node if attached.*/ + virtual const osg::Node* getNode() const; + + /** Return node if attached.*/ + virtual osg::Node* getNode(); + + /** Move the camera to the default position. + May be ignored by manipulators if home functionality is not appropriate.*/ + virtual void home(const GUIEventAdapter& ea,GUIActionAdapter& us); + + /** Start/restart the manipulator.*/ + virtual void init(const GUIEventAdapter& ea,GUIActionAdapter& us); + + /** handle events, return true if handled, false otherwise.*/ + virtual bool handle(const GUIEventAdapter& ea,GUIActionAdapter& us); + + /** Get the keyboard and mouse usage of this manipulator.*/ + virtual void getUsage(osg::ApplicationUsage& usage) const; + + protected: + + virtual ~TrackerManipulator(); + + /** Reset the internal GUIEvent stack.*/ + void flushMouseEventStack(); + /** Add the current mouse GUIEvent to internal stack.*/ + void addMouseEvent(const GUIEventAdapter& ea); + + osg::Vec3d computeCenter() const; + + void computePosition(const osg::Vec3d& eye,const osg::Vec3d& lv,const osg::Vec3d& 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(); + + void trackball(osg::Vec3& axis,double& angle, double p1x, double p1y, double p2x, double p2y); + double tb_project_to_sphere(double r, double x, double y); + + + /** Check the speed at which the mouse is moving. + If speed is below a threshold then return false, otherwise return true.*/ + bool isMouseMoving(); + + + void clampOrientation(); + + + // Internal event stack comprising last three mouse events. + osg::ref_ptr _ga_t1; + osg::ref_ptr _ga_t0; + + osg::ref_ptr _node; + osg::ref_ptr _trackNode; + + RotationMode _rotationMode; + + bool _thrown; + + osg::Vec3d _center; + osg::Quat _rotation; + float _distance; + osg::Vec3d _previousUp; + +}; + +} + +#endif + diff --git a/include/osgProducer/Viewer b/include/osgProducer/Viewer index efbbc057a..943abc7bd 100644 --- a/include/osgProducer/Viewer +++ b/include/osgProducer/Viewer @@ -54,12 +54,12 @@ class OSGPRODUCER_EXPORT Viewer : public OsgCameraGroup, public osgGA::GUIAction DRIVE_MANIPULATOR = 2, FLIGHT_MANIPULATOR = 4, TERRAIN_MANIPULATOR = 8, - STATE_MANIPULATOR = 16, - HEAD_LIGHT_SOURCE = 32, - SKY_LIGHT_SOURCE = 64, - STATS_MANIPULATOR = 128, - VIEWER_MANIPULATOR = 256, - ESCAPE_SETS_DONE = 512, + STATE_MANIPULATOR = 32, + HEAD_LIGHT_SOURCE = 64, + SKY_LIGHT_SOURCE = 128, + STATS_MANIPULATOR = 256, + VIEWER_MANIPULATOR = 512, + ESCAPE_SETS_DONE = 1024, STANDARD_SETTINGS = TRACKBALL_MANIPULATOR| DRIVE_MANIPULATOR | FLIGHT_MANIPULATOR | diff --git a/src/osgGA/GNUmakefile b/src/osgGA/GNUmakefile index 5fb880485..661043a04 100644 --- a/src/osgGA/GNUmakefile +++ b/src/osgGA/GNUmakefile @@ -13,6 +13,7 @@ CXXFILES = \ SetSceneViewVisitor.cpp\ StateSetManipulator.cpp\ TerrainManipulator.cpp\ + TrackerManipulator.cpp\ TrackballManipulator.cpp\ Version.cpp\ diff --git a/src/osgGA/TrackerManipulator.cpp b/src/osgGA/TrackerManipulator.cpp new file mode 100644 index 000000000..e1379235d --- /dev/null +++ b/src/osgGA/TrackerManipulator.cpp @@ -0,0 +1,747 @@ +#include +#include +#include +#include +#include + +using namespace osg; +using namespace osgGA; + + +class CollectParentPaths : public osg::NodeVisitor +{ +public: + CollectParentPaths() : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_PARENTS) {} + + virtual void apply(osg::Node& node) + { + if (node.getNumParents()==0) + { + _nodePaths.push_back(getNodePath()); + } + traverse(node); + } + + osg::NodePath _nodePath; + typedef std::vector< osg::NodePath > NodePathList; + NodePathList _nodePaths; +}; + +TrackerManipulator::TrackerManipulator() +{ + _rotationMode =ELEVATION_AZIM; + _distance = 1.0; + + _thrown = false; + +} + + +TrackerManipulator::~TrackerManipulator() +{ +} + + +void TrackerManipulator::setRotationMode(RotationMode mode) +{ + _rotationMode = mode; + + // need to correct rotation. +} + +void TrackerManipulator::setNode(osg::Node* node) +{ + _node = node; + + if (_node.get()) + { + const osg::BoundingSphere& boundingSphere=_node->getBound(); + const float minimumDistanceScale = 0.001f; + _minimumDistance = osg::clampBetween( + boundingSphere._radius * minimumDistanceScale, + 0.00001f,1.0f); + + osg::notify(osg::INFO)<<"Setting Tracker manipulator _minimumDistance to "<<_minimumDistance<getXnormalized()-_ga_t1->getXnormalized(); + float dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized(); + float len = sqrtf(dx*dx+dy*dy); + float dt = _ga_t0->time()-_ga_t1->time(); + + return (len>dt*velocity); +} + + +void TrackerManipulator::flushMouseEventStack() +{ + _ga_t1 = NULL; + _ga_t0 = NULL; +} + + +void TrackerManipulator::addMouseEvent(const GUIEventAdapter& ea) +{ + _ga_t1 = _ga_t0; + _ga_t0 = &ea; +} + +void TrackerManipulator::setByMatrix(const osg::Matrixd& matrix) +{ + + osg::Vec3 lookVector(- matrix(2,0),-matrix(2,1),-matrix(2,2)); + osg::Vec3 eye(matrix(3,0),matrix(3,1),matrix(3,2)); + + osg::notify(INFO)<<"eye point "<getBound(); + float distance = (eye-bs.center()).length() + _node->getBound().radius(); + osg::Vec3d start_segment = eye; + osg::Vec3d end_segment = eye + lookVector*distance; + + //CoordinateFrame coordinateFrame = getCoordinateFrame(_center.x(), _center.y(), _center.z()); + //osg::notify(INFO)<<"start="< segLookVector = new osg::LineSegment; + segLookVector->set(start_segment,end_segment); + iv.addLineSegment(segLookVector.get()); + + _node->accept(iv); + + bool hitFound = false; + if (iv.hits()) + { + osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segLookVector.get()); + if (!hitList.empty()) + { + notify(INFO) << "Hit Tracker ok A"<< std::endl; + osg::Vec3d ip = hitList.front().getWorldIntersectPoint(); + + _center = ip; + + _distance = (eye-ip).length(); + + osg::Matrix rotation_matrix = osg::Matrixd::translate(0.0,0.0,-_distance)* + matrix* + osg::Matrixd::translate(-_center); + + rotation_matrix.get(_rotation); + + hitFound = true; + } + } + + if (!hitFound) + { + CoordinateFrame eyePointCoordFrame = getCoordinateFrame( eye ); + + // clear the intersect visitor ready for a new test + iv.reset(); + + osg::ref_ptr segDowVector = new osg::LineSegment; + segLookVector->set(eye+getUpVector(eyePointCoordFrame)*distance, + eye-getUpVector(eyePointCoordFrame)*distance); + iv.addLineSegment(segLookVector.get()); + + _node->accept(iv); + + hitFound = false; + if (iv.hits()) + { + osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segLookVector.get()); + if (!hitList.empty()) + { + notify(INFO) << "Hit Tracker ok B"<< std::endl; + osg::Vec3d ip = hitList.front().getWorldIntersectPoint(); + + _center = ip; + + _distance = (eye-ip).length(); + + _rotation.set(0,0,0,1); + + hitFound = true; + } + } + } + + CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + _previousUp = getUpVector(coordinateFrame); + + clampOrientation(); +} + +osg::Vec3d TrackerManipulator::computeCenter() const +{ + if (_trackNode.valid()) + { + CollectParentPaths cpp; + + TrackerManipulator* non_const_this = const_cast(this); + + non_const_this->_trackNode->accept(cpp); + + if (!cpp._nodePaths.empty()) + { + osg::Matrix localToWorld; + localToWorld = osg::computeLocalToWorld(cpp._nodePaths[0]); + + return _trackNode->getBound().center() * localToWorld; + + } + } + return osg::Vec3d(0.0,0.0,0.0); +} + +osg::Matrixd TrackerManipulator::getMatrix() const +{ + return osg::Matrixd::translate(0.0,0.0,_distance)*osg::Matrixd::rotate(_rotation)*osg::Matrix::translate(computeCenter()); +} + +osg::Matrixd TrackerManipulator::getInverseMatrix() const +{ + return osg::Matrix::translate(-computeCenter())*osg::Matrixd::rotate(_rotation.inverse())*osg::Matrixd::translate(0.0,0.0,-_distance); +} + +void TrackerManipulator::computePosition(const osg::Vec3d& eye,const osg::Vec3d& center,const osg::Vec3d& up) +{ + if (!_node) return; + + // compute rotation matrix + osg::Vec3 lv(center-eye); + _distance = lv.length(); + _center = center; + + osg::notify(osg::INFO) << "In compute"<< std::endl; + + if (_node.valid()) + { + bool hitFound = false; + + float distance = lv.length(); + float maxDistance = distance+2*(eye-_node->getBound().center()).length(); + osg::Vec3 farPosition = eye+lv*(maxDistance/distance); + osg::Vec3 endPoint = center; + for(int i=0; + !hitFound && i<2; + ++i, endPoint = farPosition) + { + // compute the itersection with the scene. + osgUtil::IntersectVisitor iv; + + osg::ref_ptr segLookVector = new osg::LineSegment; + segLookVector->set(eye,endPoint ); + iv.addLineSegment(segLookVector.get()); + + _node->accept(iv); + + if (iv.hits()) + { + osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segLookVector.get()); + if (!hitList.empty()) + { + osg::notify(osg::INFO) << "Hit Tracker ok C"<< std::endl; + osg::Vec3d ip = hitList.front().getWorldIntersectPoint(); + + _center = ip; + _distance = (ip-eye).length(); + + hitFound = true; + } + } + } + } + + // note LookAt = inv(CF)*inv(RM)*inv(T) which is equivilant to: + // inv(R) = CF*LookAt. + + osg::Matrixd rotation_matrix = osg::Matrixd::lookAt(eye,center,up); + + rotation_matrix.get(_rotation); + _rotation = _rotation.inverse(); + + CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + _previousUp = getUpVector(coordinateFrame); + + clampOrientation(); +} + +bool TrackerManipulator::calcMovement() +{ + // return if less then two events have been added. + if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false; + + double dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized(); + double dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized(); + + + // return if there is no movement. + if (dx==0 && dy==0) return false; + + + if (_trackNode.valid()) + { + osg::notify(osg::NOTICE)<<"_trackNode="<<_trackNode->getName()<accept(cpp); + + osg::notify(osg::NOTICE)<<"number of nodepaths = "<getBound().center() * localToWorld; + + + } + } + + unsigned int buttonMask = _ga_t1->getButtonMask(); + if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON) + { + + if (_rotationMode==ELEVATION_AZIM_ROLL) + { + // rotate camera. + osg::Vec3 axis; + double angle; + + double px0 = _ga_t0->getXnormalized(); + double py0 = _ga_t0->getYnormalized(); + + double px1 = _ga_t1->getXnormalized(); + double py1 = _ga_t1->getYnormalized(); + + + trackball(axis,angle,px1,py1,px0,py0); + + osg::Quat new_rotate; + new_rotate.makeRotate(angle,axis); + + _rotation = _rotation*new_rotate; + } + else + { + osg::Matrix rotation_matrix; + rotation_matrix.set(_rotation); + + osg::Vec3d lookVector = -getUpVector(rotation_matrix); + osg::Vec3d sideVector = getSideVector(rotation_matrix); + osg::Vec3d upVector = getFrontVector(rotation_matrix); + + CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + osg::Vec3d localUp = getUpVector(coordinateFrame); + //osg::Vec3d localUp = _previousUp; + + + osg::Vec3d forwardVector = localUp^sideVector; + sideVector = forwardVector^localUp; + + forwardVector.normalize(); + sideVector.normalize(); + + osg::Quat rotate_elevation; + rotate_elevation.makeRotate(dy,sideVector); + + osg::Quat rotate_azim; + rotate_azim.makeRotate(-dx,localUp); + + _rotation = _rotation * rotate_elevation * rotate_azim; + + } + + return true; + + } + else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON || + buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEventAdapter::RIGHT_MOUSE_BUTTON)) + { + + // pan model. + double scale = -0.3f*_distance; + + osg::Matrix rotation_matrix; + rotation_matrix.set(_rotation); + + + // compute look vector. + osg::Vec3d lookVector = -getUpVector(rotation_matrix); + osg::Vec3d sideVector = getSideVector(rotation_matrix); + osg::Vec3d upVector = getFrontVector(rotation_matrix); + + // CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + // osg::Vec3d localUp = getUpVector(coordinateFrame); + osg::Vec3d localUp = _previousUp; + + osg::Vec3d forwardVector =localUp^sideVector; + sideVector = forwardVector^localUp; + + forwardVector.normalize(); + sideVector.normalize(); + + osg::Vec3d dv = forwardVector * (dy*scale) + sideVector * (dx*scale); + + _center += dv; + + // need to recompute the itersection point along the look vector. + + if (_node.valid()) + { + + // now reorientate the coordinate frame to the frame coords. + CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + + // need to reintersect with the Tracker + osgUtil::IntersectVisitor iv; + + double distance = _node->getBound().radius()*0.1f; + osg::Vec3d start_segment = _center + getUpVector(coordinateFrame) * distance; + osg::Vec3d end_segment = start_segment - getUpVector(coordinateFrame) * (2.0f*distance); + + osg::notify(INFO)<<"start="< segLookVector = new osg::LineSegment; + segLookVector->set(start_segment,end_segment); + iv.addLineSegment(segLookVector.get()); + + _node->accept(iv); + + bool hitFound = false; + if (iv.hits()) + { + osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segLookVector.get()); + if (!hitList.empty()) + { + notify(INFO) << "Hit Tracker ok"<< std::endl; + osg::Vec3d ip = hitList.front().getWorldIntersectPoint(); + _center = ip; + + hitFound = true; + } + } + + if (!hitFound) + { + // ?? + osg::notify(INFO)<<"TrackerManipulator unable to intersect with Tracker."<_minimumDistance) + { + + _distance *= scale; + + } else + { + _distance = _minimumDistance; + } + + return true; + + } + + return false; +} + +void TrackerManipulator::clampOrientation() +{ + if (_rotationMode==ELEVATION_AZIM) + { + osg::Matrix rotation_matrix; + rotation_matrix.set(_rotation); + + osg::Vec3d lookVector = -getUpVector(rotation_matrix); + osg::Vec3d upVector = getFrontVector(rotation_matrix); + + CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + osg::Vec3d localUp = getUpVector(coordinateFrame); + //osg::Vec3d localUp = _previousUp; + + osg::Vec3d sideVector = lookVector ^ localUp; + + if (sideVector.length()<0.1) + { + osg::notify(osg::INFO)<<"Side vector short "< 1.0) t = 1.0; + if (t < -1.0) t = -1.0; + angle = inRadians(asin(t)); + +} + + +/* + * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet + * if we are away from the center of the sphere. + */ +double TrackerManipulator::tb_project_to_sphere(double r, double x, double y) +{ + float d, t, z; + + d = sqrt(x*x + y*y); + /* Inside sphere */ + if (d < r * 0.70710678118654752440) + { + z = sqrt(r*r - d*d); + } /* On hyperbola */ + else + { + t = r / 1.41421356237309504880; + z = t*t / d; + } + return z; +}