diff --git a/VisualStudio/osgGA/osgGA.dsp b/VisualStudio/osgGA/osgGA.dsp index e8daffe7d..b1515d709 100755 --- a/VisualStudio/osgGA/osgGA.dsp +++ b/VisualStudio/osgGA/osgGA.dsp @@ -135,6 +135,10 @@ SOURCE=..\..\src\osgGA\TrackballManipulator.cpp # End Source File # Begin Source File +SOURCE=..\..\src\osgGA\TerrainManipulator.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\osgGA\Version.cpp # End Source File # End Group @@ -195,6 +199,10 @@ SOURCE=..\..\Include\osgGA\TrackballManipulator # End Source File # Begin Source File +SOURCE=..\..\Include\osgGA\TerrainManipulator +# End Source File +# Begin Source File + SOURCE=..\..\Include\osgGA\Version # End Source File # End Group diff --git a/include/osgGA/KeySwitchMatrixManipulator b/include/osgGA/KeySwitchMatrixManipulator index 21e5cba39..c32ec4a66 100644 --- a/include/osgGA/KeySwitchMatrixManipulator +++ b/include/osgGA/KeySwitchMatrixManipulator @@ -77,8 +77,8 @@ public: // Overrides from MatrixManipulator... - /** Set the coordinate frame which tells the manipulator which way is up, east and north.*/ - virtual void setCoordinateFrame(const osg::CoordinateFrame& cf) { _coordinateFrame = cf; _current->setCoordinateFrame(cf); } + /** set the coordinate frame which callback tells the manipulator which way is up, east and north.*/ + virtual void setCoordinateFrameCallback(CoordinateFrameCallback* cb); /** Set the position of the matrix manipulator using a 4x4 Matrix.*/ virtual void setByMatrix(const osg::Matrixd& matrix) { _current->setByMatrix(matrix); } diff --git a/include/osgGA/MatrixManipulator b/include/osgGA/MatrixManipulator index b6818bea8..86a0b1fd8 100644 --- a/include/osgGA/MatrixManipulator +++ b/include/osgGA/MatrixManipulator @@ -41,15 +41,34 @@ public: virtual const char* className() const { return "MatrixManipulator"; } - /** set the coordinate frame which tells the manipulator which way is up, east and north.*/ - virtual void setCoordinateFrame(const osg::CoordinateFrame& cf) { _coordinateFrame = cf; } + /** callback class to use to allow matrix manipulators to querry the application for the local coordinate frame.*/ + class CoordinateFrameCallback : public osg::Referenced + { + public: + virtual osg::CoordinateFrame getCoordinateFrame(double X, double Y, double Z) const = 0; + protected: + virtual ~CoordinateFrameCallback() {} + }; + + /** set the coordinate frame which callback tells the manipulator which way is up, east and north.*/ + virtual void setCoordinateFrameCallback(CoordinateFrameCallback* cb) { _coordinateFrameCallback = cb; } + + /** get the coordinate frame callback which tells the manipulator which way is up, east and north.*/ + CoordinateFrameCallback* getCoordinateFrameCallback() { return _coordinateFrameCallback.get(); } + + /** get the coordinate frame callback which tells the manipulator which way is up, east and north.*/ + const CoordinateFrameCallback* getCoordinateFrameCallback() const { return _coordinateFrameCallback.get(); } /** get the coordinate frame.*/ - const osg::CoordinateFrame& getCoordinateFrame() const { return _coordinateFrame; } + osg::CoordinateFrame getCoordinateFrame(double X, double Y, double Z) const + { + if (_coordinateFrameCallback.valid()) return _coordinateFrameCallback->getCoordinateFrame(X,Y,Z); + return osg::CoordinateFrame(); + } - osg::Vec3 getSideVector() const { return osg::Vec3(_coordinateFrame(0,0),_coordinateFrame(1,0),_coordinateFrame(2,0)); } - osg::Vec3 getFromVector() const { return osg::Vec3(_coordinateFrame(0,1),_coordinateFrame(1,1),_coordinateFrame(2,1)); } - osg::Vec3 getUpVector() const { return osg::Vec3(_coordinateFrame(0,2),_coordinateFrame(1,2),_coordinateFrame(2,2)); } + osg::Vec3 getSideVector(const osg::CoordinateFrame& cf) const { return osg::Vec3(cf(0,0),cf(0,1),cf(0,2)); } + osg::Vec3 getFrontVector(const osg::CoordinateFrame& cf) const { return osg::Vec3(cf(1,0),cf(1,1),cf(1,2)); } + osg::Vec3 getUpVector(const osg::CoordinateFrame& cf) const { return osg::Vec3(cf(2,0),cf(2,1),cf(2,2)); } /** set the position of the matrix manipulator using a 4x4 Matrix.*/ virtual void setByMatrix(const osg::Matrixd& matrix) = 0; @@ -106,7 +125,7 @@ protected: MatrixManipulator(); virtual ~MatrixManipulator(); - osg::CoordinateFrame _coordinateFrame; + osg::ref_ptr _coordinateFrameCallback; }; } diff --git a/include/osgGA/TerrainManipulator b/include/osgGA/TerrainManipulator new file mode 100644 index 000000000..756c8c958 --- /dev/null +++ b/include/osgGA/TerrainManipulator @@ -0,0 +1,123 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSGGA_TERRAINMANIPULATOR +#define OSGGA_TERRAINMANIPULATOR 1 + +#include +#include + +namespace osgGA{ + +class OSGGA_EXPORT TerrainManipulator : public MatrixManipulator +{ + public: + + TerrainManipulator(); + + virtual const char* className() const { return "Terrain"; } + + /** set the minimum distance (as ratio) the eye point can be zoomed in towards the + center before the center is pushed forward.*/ + void setMinimumZoomScale(float minimumZoomScale) { _minimumZoomScale=minimumZoomScale; } + + /** get the minimum distance (as ratio) the eye point can be zoomed in */ + float getMinimumZoomScale() const { return _minimumZoomScale; } + + /** 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 ~TerrainManipulator(); + + /** Reset the internal GUIEvent stack.*/ + void flushMouseEventStack(); + /** Add the current mouse GUIEvent to internal stack.*/ + void addMouseEvent(const GUIEventAdapter& ea); + + void computePosition(const osg::Vec3& eye,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(); + + 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); + + + /** Check the speed at which the mouse is moving. + If speed is below a threshold then return false, otherwise return true.*/ + bool isMouseMoving(); + + // Internal event stack comprising last three mouse events. + osg::ref_ptr _ga_t1; + osg::ref_ptr _ga_t0; + + osg::ref_ptr _node; + + float _modelScale; + float _minimumZoomScale; + + bool _thrown; + + osg::CoordinateFrame _coordinateFrame; + osg::Quat _rotation; + float _distance; + +}; + +} + +#endif + diff --git a/include/osgProducer/Viewer b/include/osgProducer/Viewer index 762be486e..6148ae7c8 100644 --- a/include/osgProducer/Viewer +++ b/include/osgProducer/Viewer @@ -53,15 +53,17 @@ class OSGPRODUCER_EXPORT Viewer : public OsgCameraGroup, public osgGA::GUIAction TRACKBALL_MANIPULATOR = 1, DRIVE_MANIPULATOR = 2, FLIGHT_MANIPULATOR = 4, - STATE_MANIPULATOR = 8, - HEAD_LIGHT_SOURCE = 16, - SKY_LIGHT_SOURCE = 32, - STATS_MANIPULATOR = 64, - VIEWER_MANIPULATOR = 128, - ESCAPE_SETS_DONE = 256, + TERRAIN_MANIPULATOR = 8, + STATE_MANIPULATOR = 16, + HEAD_LIGHT_SOURCE = 32, + SKY_LIGHT_SOURCE = 64, + STATS_MANIPULATOR = 128, + VIEWER_MANIPULATOR = 256, + ESCAPE_SETS_DONE = 512, STANDARD_SETTINGS = TRACKBALL_MANIPULATOR| DRIVE_MANIPULATOR | FLIGHT_MANIPULATOR | + TERRAIN_MANIPULATOR | STATE_MANIPULATOR | HEAD_LIGHT_SOURCE | STATS_MANIPULATOR | @@ -106,7 +108,7 @@ class OSGPRODUCER_EXPORT Viewer : public OsgCameraGroup, public osgGA::GUIAction void setCoordindateSystemNodePath(const osg::NodePath& nodePath); - const RefNodePath& getCoordindateSystemNodePath() { return _coordinateSystemNodePath; } + const RefNodePath& getCoordindateSystemNodePath() const { return _coordinateSystemNodePath; } /** Dispatch the cull and draw for each of the Camera's for this frame.*/ virtual void frame(); diff --git a/src/osgGA/DriveManipulator.cpp b/src/osgGA/DriveManipulator.cpp index 41c940185..77db2a4d7 100644 --- a/src/osgGA/DriveManipulator.cpp +++ b/src/osgGA/DriveManipulator.cpp @@ -71,12 +71,16 @@ void DriveManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us) if(_node.get()) { + const osg::BoundingSphere& boundingSphere=_node->getBound(); osg::Vec3 ep = boundingSphere._center; osg::Vec3 bp = ep; - ep -= getUpVector()* _modelScale*0.0001f; - bp -= getUpVector()* _modelScale; + + osg::CoordinateFrame cf=getCoordinateFrame(ep.x(), ep.y(), ep.z()); + + ep -= getUpVector(cf)* _modelScale*0.0001f; + bp -= getUpVector(cf)* _modelScale; // check to see if any obstruction in front. osgUtil::IntersectVisitor iv; @@ -99,11 +103,11 @@ void DriveManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us) osg::Vec3 np = hitList.front().getWorldIntersectNormal(); osg::Vec3 uv; - if (np * getUpVector()>0.0f) uv = np; + if (np * getUpVector(cf)>0.0f) uv = np; else uv = -np; ep = ip; - ep += getUpVector()*_height; + ep += getUpVector(cf)*_height; osg::Vec3 lv = uv^osg::Vec3(1.0f,0.0f,0.0f); computePosition(ep,lv,uv); @@ -117,7 +121,7 @@ void DriveManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us) if (!positionSet) { bp = ep; - bp += getUpVector()*_modelScale; + bp += getUpVector(cf)*_modelScale; osg::ref_ptr segUp = new osg::LineSegment; segUp->set(ep,bp); @@ -135,11 +139,11 @@ void DriveManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us) osg::Vec3 np = hitList.front().getWorldIntersectNormal(); osg::Vec3 uv; - if (np*getUpVector()>0.0f) uv = np; + if (np*getUpVector(cf)>0.0f) uv = np; else uv = -np; ep = ip; - ep += getUpVector()*_height; + ep += getUpVector(cf)*_height; osg::Vec3 lv = uv^osg::Vec3(1.0f,0.0f,0.0f); computePosition(ep,lv,uv); @@ -179,11 +183,13 @@ void DriveManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us) osg::Vec3 ep = _eye; + osg::CoordinateFrame cf=getCoordinateFrame(ep.x(), ep.y(), ep.z()); + Matrixd rotation_matrix; rotation_matrix.set(_rotation); osg::Vec3 sv = osg::Vec3(1.0f,0.0f,0.0f) * rotation_matrix; osg::Vec3 bp = ep; - bp -= getUpVector()*_modelScale; + bp -= getUpVector(cf)*_modelScale; // check to see if any obstruction in front. osgUtil::IntersectVisitor iv; @@ -206,7 +212,7 @@ void DriveManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us) osg::Vec3 np = hitList.front().getWorldIntersectNormal(); osg::Vec3 uv; - if (np*getUpVector()>0.0f) uv = np; + if (np*getUpVector(cf)>0.0f) uv = np; else uv = -np; ep = ip+uv*_height; @@ -222,7 +228,7 @@ void DriveManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us) if (!positionSet) { bp = ep; - bp += getUpVector()*_modelScale; + bp += getUpVector(cf)*_modelScale; osg::ref_ptr segUp = new osg::LineSegment; segUp->set(ep,bp); @@ -240,7 +246,7 @@ void DriveManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us) osg::Vec3 np = hitList.front().getWorldIntersectNormal(); osg::Vec3 uv; - if (np*getUpVector()>0.0f) uv = np; + if (np*getUpVector(cf)>0.0f) uv = np; else uv = -np; ep = ip+uv*_height; @@ -448,6 +454,8 @@ bool DriveManipulator::calcMovement() } } + osg::CoordinateFrame cf=getCoordinateFrame(_eye.x(), _eye.y(), _eye.z()); + osg::Matrix rotation_matrix; rotation_matrix.makeRotate(_rotation); @@ -535,7 +543,7 @@ bool DriveManipulator::calcMovement() // no hit on the terrain found therefore resort to a fall under // under the influence of gravity. osg::Vec3 dp = lfp; - dp -= getUpVector()* (2*_modelScale); + dp -= getUpVector(cf)* (2*_modelScale); iv.reset(); diff --git a/src/osgGA/FlightManipulator.cpp b/src/osgGA/FlightManipulator.cpp index 51efaabdb..769e6707d 100644 --- a/src/osgGA/FlightManipulator.cpp +++ b/src/osgGA/FlightManipulator.cpp @@ -257,6 +257,7 @@ bool FlightManipulator::calcMovement() float dx = _ga_t0->getXnormalized(); float dy = _ga_t0->getYnormalized(); + osg::CoordinateFrame cf=getCoordinateFrame(_eye.x(), _eye.y(), _eye.z()); osg::Matrixd rotation_matrix; rotation_matrix.makeRotate(_rotation); @@ -283,13 +284,13 @@ bool FlightManipulator::calcMovement() if (_yawMode==YAW_AUTOMATICALLY_WHEN_BANKED) { //float bank = asinf(sv.z()); - float bank = asinf(sv *getUpVector()); + float bank = asinf(sv *getUpVector(cf)); float yaw = inRadians(bank)*dt; osg::Quat yaw_rotate; //yaw_rotate.makeRotate(yaw,0.0f,0.0f,1.0f); - yaw_rotate.makeRotate(yaw,getUpVector()); + yaw_rotate.makeRotate(yaw,getUpVector(cf)); delta_rotate = delta_rotate*yaw_rotate; diff --git a/src/osgGA/GNUmakefile b/src/osgGA/GNUmakefile index 3dda74966..5fb880485 100644 --- a/src/osgGA/GNUmakefile +++ b/src/osgGA/GNUmakefile @@ -12,6 +12,7 @@ CXXFILES = \ KeySwitchMatrixManipulator.cpp\ SetSceneViewVisitor.cpp\ StateSetManipulator.cpp\ + TerrainManipulator.cpp\ TrackballManipulator.cpp\ Version.cpp\ diff --git a/src/osgGA/KeySwitchMatrixManipulator.cpp b/src/osgGA/KeySwitchMatrixManipulator.cpp index fdf79f42d..694bb2c02 100644 --- a/src/osgGA/KeySwitchMatrixManipulator.cpp +++ b/src/osgGA/KeySwitchMatrixManipulator.cpp @@ -10,7 +10,9 @@ void KeySwitchMatrixManipulator::addMatrixManipulator(int key, std::string name, _manips[key]=std::make_pair(name,osg::ref_ptr(cm)); if(!_current.valid()){ _current=cm; - _current->setNode(_current->getNode()); + _current->setNode(getNode()); + _current->setCoordinateFrameCallback(getCoordinateFrameCallback()); + _current->setByMatrix(getMatrix()); } } @@ -34,7 +36,13 @@ void KeySwitchMatrixManipulator::selectMatrixManipulator(unsigned int num) { if (_current.valid()) { - if ( !itr->second.second->getNode() ) { + if ( !itr->second.second->getCoordinateFrameCallback() ) + { + itr->second.second->setCoordinateFrameCallback(_current->getCoordinateFrameCallback()); + } + + if ( !itr->second.second->getNode() ) + { itr->second.second->setNode(_current->getNode()); } itr->second.second->setByMatrix(_current->getMatrix()); @@ -49,11 +57,21 @@ void KeySwitchMatrixManipulator::setNode(osg::Node* node) itr!=_manips.end(); ++itr) { - itr->second.second->setNode(node); } } +void KeySwitchMatrixManipulator::setCoordinateFrameCallback(CoordinateFrameCallback* cb) +{ + _coordinateFrameCallback = cb; + for(KeyManipMap::iterator itr=_manips.begin(); + itr!=_manips.end(); + ++itr) + { + itr->second.second->setCoordinateFrameCallback(cb); + } +} + MatrixManipulator* KeySwitchMatrixManipulator::getMatrixManipulator(unsigned int num) { KeyManipMap::iterator itr = _manips.find(num); diff --git a/src/osgGA/TerrainManipulator.cpp b/src/osgGA/TerrainManipulator.cpp new file mode 100644 index 000000000..962218beb --- /dev/null +++ b/src/osgGA/TerrainManipulator.cpp @@ -0,0 +1,470 @@ +#include +#include +#include +#include + +using namespace osg; +using namespace osgGA; + +TerrainManipulator::TerrainManipulator() +{ + _modelScale = 0.01f; + _minimumZoomScale = 0.05f; + _thrown = false; + + _distance = 1.0f; +} + + +TerrainManipulator::~TerrainManipulator() +{ +} + + +void TerrainManipulator::setNode(osg::Node* node) +{ + _node = node; + if (_node.get()) + { + const osg::BoundingSphere& boundingSphere=_node->getBound(); + _modelScale = boundingSphere._radius; + } +} + + +const osg::Node* TerrainManipulator::getNode() const +{ + return _node.get(); +} + + +osg::Node* TerrainManipulator::getNode() +{ + return _node.get(); +} + + +void TerrainManipulator::home(const GUIEventAdapter& ,GUIActionAdapter& us) +{ + if(_node.get()) + { + + const osg::BoundingSphere& boundingSphere=_node->getBound(); + + computePosition(boundingSphere._center+osg::Vec3( 0.0,-3.5f * boundingSphere._radius,0.0f), + boundingSphere._center, + osg::Vec3(0.0f,0.0f,1.0f)); + + us.requestRedraw(); + } + +} + + +void TerrainManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& ) +{ + flushMouseEventStack(); +} + + +void TerrainManipulator::getUsage(osg::ApplicationUsage& usage) const +{ + usage.addKeyboardMouseBinding("Trackball: Space","Reset the viewing position to home"); + usage.addKeyboardMouseBinding("Trackball: +","When in stereo, increase the fusion distance"); + usage.addKeyboardMouseBinding("Trackball: -","When in stereo, reduse the fusion distance"); +} + +bool TerrainManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us) +{ + switch(ea.getEventType()) + { + case(GUIEventAdapter::PUSH): + { + flushMouseEventStack(); + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + return true; + } + + case(GUIEventAdapter::RELEASE): + { + if (ea.getButtonMask()==0) + { + + if (isMouseMoving()) + { + if (calcMovement()) + { + us.requestRedraw(); + us.requestContinuousUpdate(true); + _thrown = true; + } + } + else + { + flushMouseEventStack(); + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + } + + } + else + { + flushMouseEventStack(); + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + } + return true; + } + + case(GUIEventAdapter::DRAG): + { + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + return true; + } + + case(GUIEventAdapter::MOVE): + { + return false; + } + + case(GUIEventAdapter::KEYDOWN): + if (ea.getKey()==' ') + { + flushMouseEventStack(); + _thrown = false; + home(ea,us); + us.requestRedraw(); + us.requestContinuousUpdate(false); + return true; + } + return false; + case(GUIEventAdapter::FRAME): + if (_thrown) + { + if (calcMovement()) us.requestRedraw(); + } + return false; + default: + return false; + } +} + + +bool TerrainManipulator::isMouseMoving() +{ + if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false; + + static const float velocity = 0.1f; + + float dx = _ga_t0->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 TerrainManipulator::flushMouseEventStack() +{ + _ga_t1 = NULL; + _ga_t0 = NULL; +} + + +void TerrainManipulator::addMouseEvent(const GUIEventAdapter& ea) +{ + _ga_t1 = _ga_t0; + _ga_t0 = &ea; +} + +void TerrainManipulator::setByMatrix(const osg::Matrixd& matrix) +{ + osg::notify(osg::WARN)<<"Ignoring TerrainManipulator::setByMatrix()"< segLookVector = new osg::LineSegment; + segLookVector->set(eye,center); + 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 terrain ok"<< std::endl; + osg::Vec3 ip = hitList.front().getWorldIntersectPoint(); + osg::Vec3 np = hitList.front().getWorldIntersectNormal(); + + _coordinateFrame = getCoordinateFrame( ip.x(), ip.y(), ip.z()); + _distance = (ip-eye).length(); + + hitFound = true; + } + } + + if (!hitFound) + { + // ?? + _coordinateFrame = getCoordinateFrame( center.x(), center.y(), center.z()); + } + + + // note LookAt = inv(CF)*inv(RM)*inv(T) which is equivilant to: + // inv(R) = CF*LookAt. + + osg::Matrixd rotation_matrix = _coordinateFrame*osg::Matrixd::lookAt(eye,center,up); + + rotation_matrix.get(_rotation); + _rotation = _rotation.inverse(); +} + + +bool TerrainManipulator::calcMovement() +{ + // return if less then two events have been added. + if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false; + + float dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized(); + float dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized(); + + + // return if there is no movement. + if (dx==0 && dy==0) return false; + + unsigned int buttonMask = _ga_t1->getButtonMask(); + if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON) + { + + // rotate camera. + + osg::Vec3 axis; + float angle; + + float px0 = _ga_t0->getXnormalized(); + float py0 = _ga_t0->getYnormalized(); + + float px1 = _ga_t1->getXnormalized(); + float py1 = _ga_t1->getYnormalized(); + + + trackball(axis,angle,px1,py1,px0,py0); + + osg::Quat new_rotate; + new_rotate.makeRotate(angle,axis); + + _rotation = _rotation*new_rotate; + + return true; + + } + else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON || + buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEventAdapter::RIGHT_MOUSE_BUTTON)) + { + + // pan model. + + float scale = -0.5f*_distance; + + osg::Matrix rotation_matrix; + rotation_matrix.set(_rotation); + + osg::Vec3 dv(dx*scale,dy*scale,0.0f); + + // _center += dv*rotation_matrix; + + // need to recompute the itersection point along the look vector. + + osg::notify(osg::NOTICE)< 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(NOTICE) << "Hit terrain ok"<< std::endl; + osg::Vec3 ip = hitList.front().getWorldIntersectPoint(); + osg::Vec3 np = hitList.front().getWorldIntersectNormal(); + + _coordinateFrame = getCoordinateFrame( ip.x(), ip.y(), ip.z()); + + hitFound = true; + } + } + + if (!hitFound) + { + // ?? + osg::notify(osg::NOTICE)<<"TerrainManipulator unable to intersect with terrain."<_modelScale*_minimumZoomScale) + { + + _distance *= scale; + + } + + return true; + + } + + return false; +} + + +/* + * This size should really be based on the distance from the center of + * rotation to the point on the object underneath the mouse. That + * point would then track the mouse as closely as possible. This is a + * simple example, though, so that is left as an Exercise for the + * Programmer. + */ +const float TRACKBALLSIZE = 0.8f; + +/* + * Ok, simulate a track-ball. Project the points onto the virtual + * trackball, then figure out the axis of rotation, which is the cross + * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) + * Note: This is a deformed trackball-- is a trackball in the center, + * but is deformed into a hyperbolic sheet of rotation away from the + * center. This particular function was chosen after trying out + * several variations. + * + * It is assumed that the arguments to this routine are in the range + * (-1.0 ... 1.0) + */ +void TerrainManipulator::trackball(osg::Vec3& axis,float& angle, float p1x, float p1y, float p2x, float p2y) +{ + /* + * First, figure out z-coordinates for projection of P1 and P2 to + * deformed sphere + */ + + osg::Matrix rotation_matrix(_rotation); + + + osg::Vec3 uv = osg::Vec3(0.0f,1.0f,0.0f)*rotation_matrix; + osg::Vec3 sv = osg::Vec3(1.0f,0.0f,0.0f)*rotation_matrix; + osg::Vec3 lv = osg::Vec3(0.0f,0.0f,-1.0f)*rotation_matrix; + + osg::Vec3 p1 = sv*p1x+uv*p1y-lv*tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y); + osg::Vec3 p2 = sv*p2x+uv*p2y-lv*tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y); + + /* + * Now, we want the cross product of P1 and P2 + */ + +// Robert, +// +// This was the quick 'n' dirty fix to get the trackball doing the right +// thing after fixing the Quat rotations to be right-handed. You may want +// to do something more elegant. +// axis = p1^p2; +axis = p2^p1; + axis.normalize(); + + /* + * Figure out how much to rotate around that axis. + */ + float t = (p2-p1).length() / (2.0*TRACKBALLSIZE); + + /* + * Avoid problems with out-of-control values... + */ + if (t > 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. + */ +float TerrainManipulator::tb_project_to_sphere(float r, float x, float 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; +} diff --git a/src/osgProducer/Viewer.cpp b/src/osgProducer/Viewer.cpp index c878ef4a9..418761751 100644 --- a/src/osgProducer/Viewer.cpp +++ b/src/osgProducer/Viewer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -169,6 +170,58 @@ public: }; +/** callback class to use to allow matrix manipulators to querry the application for the local coordinate frame.*/ +class ViewerCoordinateFrameCallback : public osgGA::MatrixManipulator::CoordinateFrameCallback +{ +public: + + ViewerCoordinateFrameCallback(Viewer* viewer): + _viewer(viewer) {} + + + virtual osg::CoordinateFrame getCoordinateFrame(double X, double Y, double Z) const + { + osg::notify(osg::NOTICE)<<"getCoordinateFrame("<getCoordindateSystemNodePath(); + + if (!refNodePath.empty()) + { + osg::Matrixd coordinateFrame; + + // have to crete a copy of the RefNodePath to create an osg::NodePath + // to allow it to be used along with the computeLocalToWorld call. + osg::NodePath tmpPath; + for(Viewer::RefNodePath::const_iterator itr=refNodePath.begin(); + itr!=refNodePath.end(); + ++itr) + { + tmpPath.push_back(const_cast(itr->get())); + } + + osg::CoordinateSystemNode* csn = dynamic_cast(tmpPath.back()); + if (csn) + { + coordinateFrame = csn->computeLocalCoordinateFrame(X,Y,Z)* osg::computeLocalToWorld(tmpPath); + } + else + { + coordinateFrame = osg::computeLocalToWorld(tmpPath); + } + return coordinateFrame; + } + else + { + return osg::CoordinateFrame(); + } + } + +protected: + virtual ~ViewerCoordinateFrameCallback() {} + Viewer* _viewer; +}; + + ////////////////////////////////////////////////////////////////////////////// // // osgProducer::Viewer implemention @@ -348,6 +401,7 @@ void Viewer::setUpViewer(unsigned int options) if (options&TRACKBALL_MANIPULATOR) addCameraManipulator(new osgGA::TrackballManipulator); if (options&FLIGHT_MANIPULATOR) addCameraManipulator(new osgGA::FlightManipulator); if (options&DRIVE_MANIPULATOR) addCameraManipulator(new osgGA::DriveManipulator); + if (options&TERRAIN_MANIPULATOR) addCameraManipulator(new osgGA::TerrainManipulator); if (options&STATE_MANIPULATOR) { @@ -524,6 +578,8 @@ bool Viewer::realize() if (_keyswitchManipulator.valid() && _keyswitchManipulator->getCurrentMatrixManipulator()) { + _keyswitchManipulator->setCoordinateFrameCallback(new ViewerCoordinateFrameCallback(this)); + osg::ref_ptr init_event = _kbmcb->createEventAdapter(); init_event->adaptFrame(0.0); @@ -548,33 +604,6 @@ void Viewer::update() osgProducer::KeyboardMouseCallback::EventQueue queue; if (_kbmcb.valid()) _kbmcb->getEventQueue(queue); - if (getKeySwitchMatrixManipulator() && !_coordinateSystemNodePath.empty()) - { - osg::Matrixd coordinateFrame; - - // have to crete a copy of the RefNodePath to create an osg::NodePath - // to allow it to be used along with the computeLocalToWorld call. - osg::NodePath tmpPath; - for(RefNodePath::iterator itr=_coordinateSystemNodePath.begin(); - itr!=_coordinateSystemNodePath.end(); - ++itr) - { - tmpPath.push_back(itr->get()); - } - - osg::CoordinateSystemNode* csn = dynamic_cast(_coordinateSystemNodePath.back().get()); - if (csn) - { - coordinateFrame = csn->computeLocalCoordinateFrame(_position[0],_position[1],_position[2])* osg::computeLocalToWorld(tmpPath); - } - else - { - coordinateFrame = osg::computeLocalToWorld(tmpPath); - } - - getKeySwitchMatrixManipulator()->setCoordinateFrame(coordinateFrame); - } - // create an event to signal the new frame. osg::ref_ptr frame_event = new osgProducer::EventAdapter; frame_event->adaptFrame(_frameStamp->getReferenceTime()); diff --git a/src/osgUtil/SceneView.cpp b/src/osgUtil/SceneView.cpp index a38068b95..9889757e6 100644 --- a/src/osgUtil/SceneView.cpp +++ b/src/osgUtil/SceneView.cpp @@ -62,7 +62,7 @@ SceneView::~SceneView() void SceneView::setDefaults() { - CullSettings::setDefaults(); + // CullSettings::setDefaults(); _projectionMatrix.makePerspective(50.0f,1.4f,1.0f,10000.0f); _viewMatrix.makeIdentity();