diff --git a/include/osgGA/FirstPersonManipulator b/include/osgGA/FirstPersonManipulator new file mode 100644 index 000000000..6e612969b --- /dev/null +++ b/include/osgGA/FirstPersonManipulator @@ -0,0 +1,112 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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. + * + * FirstPersonManipulator code Copyright (C) 2010 PCJohn (Jan Peciva) + * while some pieces of code were reused from OSG. + * Thanks to company Cadwork (www.cadwork.ch) and + * Brno University of Technology (www.fit.vutbr.cz) for open-sourcing this work. +*/ + +#ifndef OSGGA_FIRST_PERSON_MANIPULATOR +#define OSGGA_FIRST_PERSON_MANIPULATOR 1 + +#include + + +namespace osgGA { + + +/** FirstPersonManipulator is base class for camera control based on position + and orientation of camera, like walk, drive, and flight manipulators. */ +class OSGGA_EXPORT FirstPersonManipulator : public StandardManipulator +{ + typedef StandardManipulator inherited; + +public: + + FirstPersonManipulator( int flags = DEFAULT_SETTINGS ); + FirstPersonManipulator( const FirstPersonManipulator& fpm, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY ); + + META_Object( osgGA, FirstPersonManipulator ); + + virtual void setByMatrix( const osg::Matrixd& matrix ); + virtual void setByInverseMatrix( const osg::Matrixd& matrix ); + virtual osg::Matrixd getMatrix() const; + virtual osg::Matrixd getInverseMatrix() const; + + virtual void setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation ); + virtual void setTransformation( const osg::Vec3d& center, const osg::Vec3d& eye, const osg::Vec3d& up ); + virtual void getTransformation( osg::Vec3d& eye, osg::Quat& rotation ) const; + virtual void getTransformation( osg::Vec3d& center, osg::Vec3d& eye, osg::Vec3d& up ) const; + + virtual void setVelocity( const double& velocity ); + inline double getVelocity() const; + virtual void setAcceleration( const double& acceleration, bool relativeToModelSize = false ); + double getAcceleration( bool *relativeToModelSize = NULL ) const; + virtual void setMaxVelocity( const double& maxVelocity, bool relativeToModelSize = false ); + double getMaxVelocity( bool *relativeToModelSize = NULL ) const; + + virtual void setWheelMovement( const double& wheelMovement, bool relativeToModelSize = false ); + double getWheelMovement( bool *relativeToModelSize = NULL ) const; + + virtual void home( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual void home( double ); + + virtual void init( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + +protected: + + virtual bool handleMouseWheel( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + + virtual bool performMovementLeftMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMouseDeltaMovement( const float dx, const float dy ); + virtual void applyAnimationStep( const double currentProgress, const double prevProgress ); + virtual bool startAnimationByMousePointerIntersection( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + + void moveForward( const double distance ); + void moveForward( const osg::Quat& rotation, const double distance ); + void moveRight( const double distance ); + void moveUp( const double distance ); + + osg::Vec3d _eye; + osg::Quat _rotation; + double _velocity; + + double _acceleration; + static int _accelerationFlagIndex; + double _maxVelocity; + static int _maxVelocityFlagIndex; + double _wheelMovement; + static int _wheelMovementFlagIndex; + + class FirstPersonAnimationData : public AnimationData { + public: + osg::Quat _startRot; + osg::Quat _targetRot; + void start( const osg::Quat& startRotation, const osg::Quat& targetRotation, const double startTime ); + }; + virtual void allocAnimationData() { _animationData = new FirstPersonAnimationData(); } +}; + + +// +// inline methods +// + +/// Returns velocity. +double FirstPersonManipulator::getVelocity() const { return _velocity; } + + +} + +#endif /* OSGGA_FIRST_PERSON_MANIPULATOR */ diff --git a/include/osgGA/FlightManipulator b/include/osgGA/FlightManipulator index a41682713..c50ac6473 100644 --- a/include/osgGA/FlightManipulator +++ b/include/osgGA/FlightManipulator @@ -1,119 +1,82 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ -#ifndef OSGGA_FLIGHTMANIPULATOR -#define OSGGA_FLIGHTMANIPULATOR 1 +#ifndef OSGGA_FLIGHT_MANIPULATOR +#define OSGGA_FLIGHT_MANIPULATOR 1 -#include -#include +#include -namespace osgGA{ -/** -FlightManipulator is a MatrixManipulator which provides flight simulator-like -updating of the camera position & orientation. By default, the left mouse -button accelerates, the right mouse button decelerates, and the middle mouse -button (or left and right simultaneously) stops dead. -*/ +namespace osgGA { -class OSGGA_EXPORT FlightManipulator : public MatrixManipulator + +/** FlightManipulator is a MatrixManipulator which provides flight simulator-like + * updating of the camera position & orientation. By default, the left mouse + * button accelerates, the right mouse button decelerates, and the middle mouse + * button (or left and right simultaneously) stops dead. + */ +class OSGGA_EXPORT FlightManipulator : public FirstPersonManipulator { - public: + typedef FirstPersonManipulator inherited; - FlightManipulator(); +public: - virtual const char* className() const { return "Flight"; } + enum YawControlMode { + YAW_AUTOMATICALLY_WHEN_BANKED, + NO_AUTOMATIC_YAW + }; - /** set the position of the matrix manipulator using a 4x4 Matrix.*/ - virtual void setByMatrix(const osg::Matrixd& matrix); + FlightManipulator( int flags = UPDATE_MODEL_SIZE | COMPUTE_HOME_USING_BBOX ); + FlightManipulator( const FlightManipulator& fpm, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY ); - /** set the position of the matrix manipulator using a 4x4 Matrix.*/ - virtual void setByInverseMatrix(const osg::Matrixd& matrix) { setByMatrix(osg::Matrixd::inverse(matrix)); } + META_Object( osgGA, FlightManipulator ); - /** get the position of the manipulator as 4x4 Matrix.*/ - virtual osg::Matrixd getMatrix() const; + virtual void setYawControlMode( YawControlMode ycm ); + inline YawControlMode getYawControlMode() 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; + virtual void home( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual void init( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual void getUsage( osg::ApplicationUsage& usage ) const; +protected: - virtual void setNode(osg::Node*); + virtual bool handleFrame( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseMove( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseDrag( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMousePush( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseRelease( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleKeyDown( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool flightHandleEvent( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); - virtual const osg::Node* getNode() const; + virtual bool performMovement(); + virtual bool performMovementLeftMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMovementMiddleMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMovementRightMouseButton( const double dt, const double dx, const double dy ); - virtual osg::Node* getNode(); - - virtual void home(const GUIEventAdapter& ea,GUIActionAdapter& us); - - virtual void init(const GUIEventAdapter& ea,GUIActionAdapter& us); - - virtual bool handle(const GUIEventAdapter& ea,GUIActionAdapter& us); - - /** Get the keyboard and mouse usage of this manipulator.*/ - virtual void getUsage(osg::ApplicationUsage& usage) const; - - enum YawControlMode { - YAW_AUTOMATICALLY_WHEN_BANKED, - NO_AUTOMATIC_YAW - }; - - /** Configure the Yaw control for the flight model. */ - void setYawControlMode(YawControlMode ycm) { _yawMode = ycm; } - - void setModelScale(double in_ms) { _modelScale = in_ms; } - double getModelScale() const { return _modelScale; } - - void setAcceleration(double in_acc) { _acceleration = in_acc; } - double getAcceleration() const { return _acceleration; } - - void setVelocity(double in_vel) { _velocity = in_vel; } - double getVelocity() const { return _velocity; } - - protected: - - virtual ~FlightManipulator(); - - /** 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(); - - - // Internal event stack comprising last two mouse events. - osg::ref_ptr _ga_t1; - osg::ref_ptr _ga_t0; - - osg::observer_ptr _node; - - double _modelScale; - double _acceleration; - double _velocity; - - YawControlMode _yawMode; - - osg::Vec3d _eye; - osg::Quat _rotation; - double _distance; + YawControlMode _yawMode; }; + +// +// inline methods +// + +/// Returns the Yaw control for the flight model. +inline FlightManipulator::YawControlMode FlightManipulator::getYawControlMode() const { return _yawMode; } + + } -#endif - +#endif /* OSGGA_FLIGHT_MANIPULATOR */ diff --git a/include/osgGA/KeySwitchMatrixManipulator b/include/osgGA/KeySwitchMatrixManipulator index d544cde8c..a844912ad 100644 --- a/include/osgGA/KeySwitchMatrixManipulator +++ b/include/osgGA/KeySwitchMatrixManipulator @@ -1,13 +1,13 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ @@ -57,7 +57,7 @@ public: /** Get the const complete list of manipulators attached to this keyswitch manipulator.*/ const KeyManipMap& getKeyManipMap() const { return _manips; } - + /** Get the current active manipulators.*/ MatrixManipulator* getCurrentMatrixManipulator() { return _current.get(); } @@ -81,10 +81,6 @@ public: // Overrides from MatrixManipulator... - /** set the minimum distance (as ratio) the eye point can be zoomed in towards the - center before the center is pushed forward.*/ - virtual void setMinimumDistance(float minimumDistance); - /** set the coordinate frame which callback tells the manipulator which way is up, east and north.*/ virtual void setCoordinateFrameCallback(CoordinateFrameCallback* cb); @@ -106,12 +102,6 @@ public: /** Get the FusionDistanceValue. Used by SceneView for setting up stereo convergence.*/ virtual float getFusionDistanceValue() const { return _current->getFusionDistanceValue(); } - /** Set the distance property. */ - void setDistance(double distance); - - /** Get the distance property. */ - double getDistance() const; - virtual void setNode(osg::Node* n); @@ -125,7 +115,7 @@ public: virtual void computeHomePosition(); - virtual void home(const GUIEventAdapter& ee,GUIActionAdapter& aa) { if (_current.valid()) _current->home(ee,aa); } + virtual void home(const GUIEventAdapter& ee,GUIActionAdapter& aa); virtual void init(const GUIEventAdapter& ee,GUIActionAdapter& aa) { if (_current.valid()) _current->init(ee,aa); } diff --git a/include/osgGA/MatrixManipulator b/include/osgGA/MatrixManipulator index 3ae435afe..afd3b2047 100644 --- a/include/osgGA/MatrixManipulator +++ b/include/osgGA/MatrixManipulator @@ -1,13 +1,13 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ @@ -36,13 +36,16 @@ amount of default functionality, for classes which wish to control OSG cameras in response to GUI events. */ -class OSGGA_EXPORT MatrixManipulator : public GUIEventHandler -{ +class OSGGA_EXPORT MatrixManipulator : public GUIEventHandler { + + typedef GUIEventHandler inherited; + public: - + // We are not using META_Object as this is abstract class. + // Use META_Object(osgGA,YourManipulator); in your descendant non-abstract classes. virtual const char* className() const { return "MatrixManipulator"; } - + /** callback class to use to allow matrix manipulators to query the application for the local coordinate frame.*/ class CoordinateFrameCallback : public osg::Referenced { @@ -51,16 +54,6 @@ public: protected: virtual ~CoordinateFrameCallback() {} }; - - - - /** set the minimum distance (as ratio) the eye point can be zoomed in towards the - center before the center is pushed forward.*/ - virtual void setMinimumDistance(float minimumDistance) { _minimumDistance=minimumDistance; } - - /** get the minimum distance (as ratio) the eye point can be zoomed in */ - float getMinimumDistance() const { return _minimumDistance; } - /** set the coordinate frame which callback tells the manipulator which way is up, east and north.*/ virtual void setCoordinateFrameCallback(CoordinateFrameCallback* cb) { _coordinateFrameCallback = cb; } @@ -70,14 +63,14 @@ public: /** 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.*/ osg::CoordinateFrame getCoordinateFrame(const osg::Vec3d& position) const { if (_coordinateFrameCallback.valid()) return _coordinateFrameCallback->getCoordinateFrame(position); return osg::CoordinateFrame(); } - + osg::Vec3d getSideVector(const osg::CoordinateFrame& cf) const { return osg::Vec3d(cf(0,0),cf(0,1),cf(0,2)); } osg::Vec3d getFrontVector(const osg::CoordinateFrame& cf) const { return osg::Vec3d(cf(1,0),cf(1,1),cf(1,2)); } osg::Vec3d getUpVector(const osg::CoordinateFrame& cf) const { return osg::Vec3d(cf(2,0),cf(2,1),cf(2,2)); } @@ -100,12 +93,6 @@ public: /** Get the FusionDistanceValue. Used by SceneView for setting up stereo convergence.*/ virtual float getFusionDistanceValue() const { return 1.0f; } - /** Set the distance parameter (used by TrackballManipulator etc.) */ - void setDistance(double /*distance*/) {} - - /** Get the distance parameter. */ - virtual double getDistance() const { return 1.0; } - /** Set the mask to use when set up intersection traversal such as used in manipulators that follow terrain or have collision detection. * The intersection traversal mask is useful for controlling what parts of the scene graph should be used for intersection purposes.*/ void setIntersectTraversalMask(unsigned int mask) { _intersectTraversalMask = mask; } @@ -134,7 +121,7 @@ public: _homeCenter = center; _homeUp = up; } - + /** Get the manually set home position. */ virtual void getHomePosition(osg::Vec3d& eye, osg::Vec3d& center, osg::Vec3d& up) const { @@ -143,36 +130,23 @@ public: up = _homeUp; } - /** Set whether the automatic compute of the home position is enabled.*/ + /** Set whether the automatic compute of the home position is enabled.*/ virtual void setAutoComputeHomePosition(bool flag) { _autoComputeHomePosition = flag; } - - /** Get whether the automatic compute of the home position is enabled.*/ + + /** Get whether the automatic compute of the home position is enabled.*/ bool getAutoComputeHomePosition() const { return _autoComputeHomePosition; } - /** Compute the home position.*/ - virtual void computeHomePosition() - { - if(getNode()) - { - const osg::BoundingSphere& boundingSphere=getNode()->getBound(); - - setHomePosition(boundingSphere._center+osg::Vec3( 0.0,-3.5f * boundingSphere._radius,0.0f), - boundingSphere._center, - //osg::Vec3(0.0f,0.0f,1.0f), - _homeUp, - _autoComputeHomePosition); - } - } - + /** Compute the home position.*/ + virtual void computeHomePosition(const osg::Camera *camera = NULL, bool useBoundingBox = false); /** - Move the camera to the default position. + Move the camera to the default position. May be ignored by manipulators if home functionality is not appropriate. */ virtual void home(const GUIEventAdapter& ,GUIActionAdapter&) {} /** - Move the camera to the default position. + Move the camera to the default position. This version does not require GUIEventAdapter and GUIActionAdapter so may be called from somewhere other than a handle() method in GUIEventHandler. Application must be aware of implications. @@ -191,14 +165,16 @@ public: protected: MatrixManipulator(); + MatrixManipulator(const MatrixManipulator& mm, const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY); + virtual ~MatrixManipulator(); - double _minimumDistance; - + std::string getManipulatorName() const; + unsigned int _intersectTraversalMask; bool _autoComputeHomePosition; - + osg::Vec3d _homeEye; osg::Vec3d _homeCenter; osg::Vec3d _homeUp; diff --git a/include/osgGA/NodeTrackerManipulator b/include/osgGA/NodeTrackerManipulator index 091309a65..6a2beebce 100644 --- a/include/osgGA/NodeTrackerManipulator +++ b/include/osgGA/NodeTrackerManipulator @@ -1,44 +1,51 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ -#ifndef OSGGA_NODETRACKERMANIPULATOR -#define OSGGA_NODETRACKERMANIPULATOR 1 +#ifndef OSGGA_NODE_TRACKER_MANIPULATOR +#define OSGGA_NODE_TRACKER_MANIPULATOR 1 -#include -#include -#include +#include -namespace osgGA{ -class OSGGA_EXPORT NodeTrackerManipulator : public MatrixManipulator +namespace osgGA { + + +class OSGGA_EXPORT NodeTrackerManipulator : public OrbitManipulator { + typedef OrbitManipulator inherited; + public: - NodeTrackerManipulator(); + NodeTrackerManipulator( int flags = DEFAULT_SETTINGS ); - virtual const char* className() const { return "NodeTrackerManipulator"; } + NodeTrackerManipulator( const NodeTrackerManipulator& om, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY ); - void setTrackNodePath(const osg::NodePath& nodePath) { _trackNodePath.setNodePath(nodePath); } - void setTrackNodePath(const osg::ObserverNodePath& nodePath) { _trackNodePath = nodePath; } - osg::ObserverNodePath& getTrackNodePath() { return _trackNodePath; } + META_Object( osgGA, NodeTrackerManipulator ); + + typedef std::vector< osg::observer_ptr > ObserverNodePath; + + void setTrackNodePath(const osg::NodePath& nodePath); + void setTrackNodePath(const ObserverNodePath& nodePath) { _trackNodePath = nodePath; } + ObserverNodePath& getTrackNodePath() { return _trackNodePath; } void setTrackNode(osg::Node* node); - osg::Node* getTrackNode(); - const osg::Node* getTrackNode() const; + osg::Node* getTrackNode() { return _trackNodePath.empty() ? 0 : _trackNodePath.back().get(); } + const osg::Node* getTrackNode() const { return _trackNodePath.empty() ? 0 : _trackNodePath.back().get(); } - enum TrackerMode + enum TrackerMode { - /** Track the center of the node's bounding sphere, but not rotations of the node. + /** Track the center of the node's bounding sphere, but not rotations of the node. * For databases which have a CoordinateSystemNode, the orientation is kept relative the coordinate frame if the center of the node. */ NODE_CENTER, @@ -50,12 +57,12 @@ class OSGGA_EXPORT NodeTrackerManipulator : public MatrixManipulator */ NODE_CENTER_AND_ROTATION }; - + void setTrackerMode(TrackerMode mode); TrackerMode getTrackerMode() const { return _trackerMode; } - enum RotationMode + enum RotationMode { /** Use a trackball style manipulation of the view direction w.r.t the tracked orientation. */ @@ -64,106 +71,43 @@ class OSGGA_EXPORT NodeTrackerManipulator : public MatrixManipulator */ ELEVATION_AZIM }; - + void setRotationMode(RotationMode mode); - RotationMode getRotationMode() const { return _rotationMode; } + RotationMode getRotationMode() const; - /** 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 stereo convergence.*/ - virtual osgUtil::SceneView::FusionDistanceMode getFusionDistanceMode() const { return osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE; } - - /** Get the FusionDistanceValue. Used by SceneView for setting up stereo 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(); - virtual void computeHomePosition(); - /** 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 ~NodeTrackerManipulator(); + virtual bool performMovementLeftMouseButton(const double dt, const double dx, const double dy); + virtual bool performMovementMiddleMouseButton(const double dt, const double dx, const double dy); + virtual bool performMovementRightMouseButton(const double dt, const double dx, const double dy); - /** Reset the internal GUIEvent stack.*/ - void flushMouseEventStack(); + osg::NodePath getNodePath() const; - /** Add the current mouse GUIEvent to internal stack.*/ - void addMouseEvent(const GUIEventAdapter& ea); + bool validateNodePath() const; + + void computeNodeWorldToLocal(osg::Matrixd& worldToLocal) const; + void computeNodeLocalToWorld(osg::Matrixd& localToWorld) const; void computeNodeCenterAndRotation(osg::Vec3d& center, osg::Quat& rotation) 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 two mouse events. - osg::ref_ptr _ga_t1; - osg::ref_ptr _ga_t0; - - osg::observer_ptr _node; - - osg::ObserverNodePath _trackNodePath; + ObserverNodePath _trackNodePath; TrackerMode _trackerMode; - RotationMode _rotationMode; - - bool _thrown; - - osg::Quat _nodeRotation; - osg::Quat _rotation; - float _distance; }; } -#endif - +#endif /* OSGGA_NODE_TRACKER_MANIPULATOR */ diff --git a/include/osgGA/OrbitManipulator b/include/osgGA/OrbitManipulator new file mode 100644 index 000000000..1df68488a --- /dev/null +++ b/include/osgGA/OrbitManipulator @@ -0,0 +1,116 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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_ORBIT_MANIPULATOR +#define OSGGA_ORBIT_MANIPULATOR 1 + +#include + + +namespace osgGA { + + +/** OrbitManipulator is base class for camera control based on focal center, + distance from the center, and orientation of distance vector to the eye. + This is the base class for trackball style manipulators.*/ +class OSGGA_EXPORT OrbitManipulator : public StandardManipulator +{ + typedef StandardManipulator inherited; + +public: + + OrbitManipulator( int flags = DEFAULT_SETTINGS ); + OrbitManipulator( const OrbitManipulator& om, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY ); + + META_Object( osgGA, OrbitManipulator ); + + virtual void setByMatrix( const osg::Matrixd& matrix ); + virtual void setByInverseMatrix( const osg::Matrixd& matrix ); + virtual osg::Matrixd getMatrix() const; + virtual osg::Matrixd getInverseMatrix() const; + + virtual void setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation ); + virtual void setTransformation( const osg::Vec3d& center, const osg::Vec3d& eye, const osg::Vec3d& up ); + virtual void getTransformation( osg::Vec3d& eye, osg::Quat& rotation ) const; + virtual void getTransformation( osg::Vec3d& center, osg::Vec3d& eye, osg::Vec3d& up ) const; + + virtual void setCenter( const osg::Vec3d& center ); + const osg::Vec3d& getCenter() const; + virtual void setRotation( const osg::Quat& rotation ); + const osg::Quat& getRotation() const; + virtual void setDistance( double distance ); + double getDistance() const; + + virtual void setTrackballSize( const double& size ); + inline double getTrackballSize() const; + virtual void setWheelZoomFactor( double wheelZoomFactor ); + inline double getWheelZoomFactor() const; + + virtual void setMinimumDistance( const double& minimumDistance, bool relativeToModelSize = NULL ); + double getMinimumDistance( bool *relativeToModelSize = NULL ) const; + + virtual osgUtil::SceneView::FusionDistanceMode getFusionDistanceMode() const; + virtual float getFusionDistanceValue() const; + +protected: + + virtual bool handleMouseWheel( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + + virtual bool performMovementLeftMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMovementMiddleMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMovementRightMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMouseDeltaMovement( const float dx, const float dy ); + virtual void applyAnimationStep( const double currentProgress, const double prevProgress ); + + virtual void rotateTrackball( const float px0, const float py0, const float px1, const float py1 ); + virtual void rotateWithFixedVertical( const float dx, const float dy ); + virtual void rotateWithFixedVertical( const float dx, const float dy, const osg::Vec3f& up ); + virtual void panModel( const float dx, const float dy, const float dz = 0.f ); + virtual void zoomModel( const float dy, bool pushForwardIfNeeded = true ); + void trackball( osg::Vec3d& axis, float& angle, float p1x, float p1y, float p2x, float p2y ); + float tb_project_to_sphere( float r, float x, float y ); + virtual bool startAnimationByMousePointerIntersection( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + + osg::Vec3d _center; + osg::Quat _rotation; + double _distance; + + double _trackballSize; + double _wheelZoomFactor; + + double _minimumDistance; + static int _minimumDistanceFlagIndex; + + class OrbitAnimationData : public AnimationData { + public: + osg::Vec3d _movement; + void start( const osg::Vec3d& movement, const double startTime ); + }; + virtual void allocAnimationData() { _animationData = new OrbitAnimationData(); } +}; + + +// +// inline functions +// + +/** Get the size of the trackball relative to the model size. */ +inline double OrbitManipulator::getTrackballSize() const { return _trackballSize; } +/** Get the mouse wheel zoom factor.*/ +inline double OrbitManipulator::getWheelZoomFactor() const { return _wheelZoomFactor; } + + +} + +#endif /* OSGGA_ORBIT_MANIPULATOR */ diff --git a/include/osgGA/StandardManipulator b/include/osgGA/StandardManipulator new file mode 100644 index 000000000..b0130a8a3 --- /dev/null +++ b/include/osgGA/StandardManipulator @@ -0,0 +1,186 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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. + * + * StandardManipulator code Copyright (C) 2010 PCJohn (Jan Peciva) + * while some pieces of code were reused from OSG. + * Thanks to company Cadwork (www.cadwork.ch) and + * Brno University of Technology (www.fit.vutbr.cz) for open-sourcing this work. +*/ + +#ifndef OSGGA_CAMERA_MANIPULATOR +#define OSGGA_CAMERA_MANIPULATOR 1 + +#include + + +namespace osgGA { + + +/** StandardManipulator class provides basic functionality + for user controlled manipulation.*/ +class OSGGA_EXPORT StandardManipulator : public MatrixManipulator +{ + typedef MatrixManipulator inherited; + +public: + + // flags + enum { + UPDATE_MODEL_SIZE = 0x01, + COMPUTE_HOME_USING_BBOX = 0x02, + PROCESS_MOUSE_WHEEL = 0x04, + SET_CENTER_ON_WHEEL_UP = 0x08, + DEFAULT_SETTINGS = UPDATE_MODEL_SIZE | COMPUTE_HOME_USING_BBOX | PROCESS_MOUSE_WHEEL + }; + + StandardManipulator( int flags = DEFAULT_SETTINGS ); + StandardManipulator( const StandardManipulator& m, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY ); + + // We are not using META_Object as this is abstract class. + // Use META_Object(osgGA,YourManipulator); in your descendant non-abstract classes. + virtual const char* className() const { return "StandardManipulator"; } + + virtual void setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation ) = 0; + virtual void setTransformation( const osg::Vec3d& center, const osg::Vec3d& eye, const osg::Vec3d& up ) = 0; + virtual void getTransformation( osg::Vec3d& eye, osg::Quat& rotation ) const = 0; + virtual void getTransformation( osg::Vec3d& center, osg::Vec3d& eye, osg::Vec3d& up ) const = 0; + + virtual void setNode( osg::Node* ); + virtual const osg::Node* getNode() const; + virtual osg::Node* getNode(); + + virtual void setVerticalAxisFixed( bool value ); + inline bool getVerticalAxisFixed() const; + + virtual void setAnimationTime( const double t ); + double getAnimationTime() const; + bool isAnimating() const; + virtual void finishAnimation(); + + virtual void home( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual void home( double ); + + virtual void init( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual void getUsage( osg::ApplicationUsage& usage ) const; + +protected: + + virtual bool handleFrame( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleResize( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseMove( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseDrag( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMousePush( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseRelease( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleKeyDown( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleKeyUp( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseWheel( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool handleMouseDeltaMovement( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + + virtual bool performMovement(); + virtual bool performMovementLeftMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMovementMiddleMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMovementRightMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMouseDeltaMovement( const float dx, const float dy ); + virtual bool performAnimationMovement( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual void applyAnimationStep( const double currentProgress, const double prevProgress ); + + void addMouseEvent( const osgGA::GUIEventAdapter& ea ); + void flushMouseEventStack(); + virtual bool isMouseMoving() const; + virtual void centerMousePointer( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + + static void rotateYawPitch( osg::Quat& rotation, const double yaw, const double pitch, + const osg::Vec3d& localUp = osg::Vec3d( 0.,0.,0.) ); + static void fixVerticalAxis( osg::Quat& rotation, const osg::Vec3d& localUp, bool disallowFlipOver ); + void fixVerticalAxis( osg::Vec3d& eye, osg::Quat& rotation, bool disallowFlipOver ); + static void fixVerticalAxis( const osg::Vec3d& forward, const osg::Vec3d& up, osg::Vec3d& newUp, + const osg::Vec3d& localUp, bool disallowFlipOver ); + virtual bool setCenterByMousePointerIntersection( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + virtual bool startAnimationByMousePointerIntersection( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ); + + // mouse state + bool _thrown; + float _mouseCenterX, _mouseCenterY; + + // internal event stack comprising last two mouse events. + osg::ref_ptr< const osgGA::GUIEventAdapter > _ga_t1; + osg::ref_ptr< const osgGA::GUIEventAdapter > _ga_t0; + + /** The approximate amount of time it is currently taking to draw a frame. + * This is used to compute the delta in translation/rotation during a thrown display update. + * It allows us to match an delta in position/rotation independent of the rendering frame rate. + */ + double _delta_frame_time; + + /** The time the last frame started. + * Used when _rate_sensitive is true so that we can match display update rate to rotation/translation rate. + */ + double _last_frame_time; + + // scene data + osg::ref_ptr< osg::Node > _node; + double _modelSize; + bool _verticalAxisFixed; + + // animation stuff + class AnimationData : public osg::Referenced { + public: + double _animationTime; + bool _isAnimating; + double _startTime; + double _phase; + AnimationData(); + void start( const double startTime ); + }; + osg::ref_ptr< AnimationData > _animationData; + virtual void allocAnimationData() { _animationData = new AnimationData(); } + + // flags + int _flags; + + // flags indicating that a value is relative to model size + int _relativeFlags; + inline bool getRelativeFlag( int index ) const; + inline void setRelativeFlag( int index, bool value ); + static int numRelativeFlagsAllocated; + static int allocateRelativeFlag(); + +}; + + +// +// inline methods +// + +inline bool StandardManipulator::getRelativeFlag( int index ) const +{ + return ( _relativeFlags & (0x01 << index) ) != 0; +} + +inline void StandardManipulator::setRelativeFlag( int index, bool value ) +{ + if( value ) _relativeFlags |= (0x01 << index); + else _relativeFlags &= (~0x01 << index); +} + +/// Returns whether manipulator preserves camera's "UP" vector. +inline bool StandardManipulator::getVerticalAxisFixed() const +{ + return _verticalAxisFixed; +} + + +} + +#endif /* OSGGA_CAMERA_MANIPULATOR */ diff --git a/include/osgGA/TerrainManipulator b/include/osgGA/TerrainManipulator index 59c9ab92d..907bcd1cf 100644 --- a/include/osgGA/TerrainManipulator +++ b/include/osgGA/TerrainManipulator @@ -1,140 +1,64 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ -#ifndef OSGGA_TERRAINMANIPULATOR -#define OSGGA_TERRAINMANIPULATOR 1 +#ifndef OSGGA_TERRAIN_MANIPULATOR +#define OSGGA_TERRAIN_MANIPULATOR 1 -#include -#include +#include -namespace osgGA{ -class OSGGA_EXPORT TerrainManipulator : public MatrixManipulator +namespace osgGA { + + +class OSGGA_EXPORT TerrainManipulator : public OrbitManipulator { - public: + typedef OrbitManipulator inherited; - TerrainManipulator(); +public: - virtual const char* className() const { return "Terrain"; } + TerrainManipulator( int flags = DEFAULT_SETTINGS ); + TerrainManipulator( const TerrainManipulator& tm, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY ); + META_Object( osgGA, TerrainManipulator ); - enum RotationMode - { - ELEVATION_HEADING_ROLL, - ELEVATION_HEADING - }; + enum RotationMode + { + ELEVATION_AZIM_ROLL, + ELEVATION_AZIM + }; - void setRotationMode(RotationMode mode); - RotationMode getRotationMode() const { return _rotationMode; } + virtual void setRotationMode(RotationMode mode); + RotationMode getRotationMode() const; + virtual void setByMatrix( const osg::Matrixd& matrix ); - /** set the position of the matrix manipulator using a 4x4 Matrix.*/ - virtual void setByMatrix(const osg::Matrixd& matrix); + virtual void setTransformation( const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up ); - /** set the position of the matrix manipulator using a 4x4 Matrix.*/ - virtual void setByInverseMatrix(const osg::Matrixd& matrix) { setByMatrix(osg::Matrixd::inverse(matrix)); } + virtual void setNode( osg::Node* node ); - /** get the position of the manipulator as 4x4 Matrix.*/ - virtual osg::Matrixd getMatrix() const; +protected: - /** 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; + virtual bool performMovementMiddleMouseButton( const double dt, const double dx, const double dy ); + virtual bool performMovementRightMouseButton( const double dt, const double dx, const double dy ); - /** Get the FusionDistanceMode. Used by SceneView for setting up stereo convergence.*/ - virtual osgUtil::SceneView::FusionDistanceMode getFusionDistanceMode() const { return osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE; } - - /** Get the FusionDistanceValue. Used by SceneView for setting up stereo convergence.*/ - virtual float getFusionDistanceValue() const { return _distance; } - - /** Set the distance of the trackball. */ - void setDistance(double distance) { _distance = distance; } - - /** Get the distance of the trackball. */ - double getDistance() 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(); - - bool intersect(const osg::Vec3d& start, const osg::Vec3d& end, osg::Vec3d& intersection) const; - - /** Reset the internal GUIEvent stack.*/ - void flushMouseEventStack(); - /** Add the current mouse GUIEvent to internal stack.*/ - void addMouseEvent(const GUIEventAdapter& ea); - - 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 two mouse events. - osg::ref_ptr _ga_t1; - osg::ref_ptr _ga_t0; - - osg::observer_ptr _node; - - RotationMode _rotationMode; - - bool _thrown; - - osg::Vec3d _center; - osg::Quat _rotation; - double _distance; - osg::Vec3d _previousUp; + bool intersect( const osg::Vec3d& start, const osg::Vec3d& end, osg::Vec3d& intersection ) const; + void clampOrientation(); + osg::Vec3d _previousUp; }; + } -#endif - +#endif /* OSGGA_TERRAIN_MANIPULATOR */ diff --git a/include/osgGA/TrackballManipulator b/include/osgGA/TrackballManipulator index 29f95471d..f27112e25 100644 --- a/include/osgGA/TrackballManipulator +++ b/include/osgGA/TrackballManipulator @@ -1,174 +1,40 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ -#ifndef OSGGA_TRACKBALLMANIPULATOR -#define OSGGA_TRACKBALLMANIPULATOR 1 +#ifndef OSGGA_TRACKBALL_MANIPULATOR +#define OSGGA_TRACKBALL_MANIPULATOR 1 -#include -#include +#include -namespace osgGA{ -class OSGGA_EXPORT TrackballManipulator : public MatrixManipulator +namespace osgGA { + + +class OSGGA_EXPORT TrackballManipulator : public OrbitManipulator { - public: - TrackballManipulator(); + typedef OrbitManipulator inherited; - virtual const char* className() const { return "Trackball"; } +public: - /** set the position of the matrix manipulator using a 4x4 Matrix.*/ - virtual void setByMatrix(const osg::Matrixd& matrix); + TrackballManipulator( int flags = DEFAULT_SETTINGS ); + TrackballManipulator( const TrackballManipulator& tm, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY ); - /** 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 stereo convergence.*/ - virtual osgUtil::SceneView::FusionDistanceMode getFusionDistanceMode() const { return osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE; } - - /** Get the FusionDistanceValue. Used by SceneView for setting up stereo 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); - virtual void home(double); - - /** 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; - - - /** set the minimum distance (as ratio) the eye point can be zoomed in towards the - center before the center is pushed forward.*/ - void setMinimumZoomScale(double minimumZoomScale) { _minimumZoomScale=minimumZoomScale; } - - /** get the minimum distance (as ratio) the eye point can be zoomed in */ - double getMinimumZoomScale() const { return _minimumZoomScale; } - - /** set the mouse scroll wheel zoom delta. - * Range -1.0 to +1.0, -ve value inverts wheel direction and zero switches off scroll wheel. */ - void setScroolWheelZoomDelta(double zoomDelta) { _zoomDelta = zoomDelta; } - - /** get the mouse scroll wheel zoom delta. */ - double getScroolWheelZoomDelta() const { return _zoomDelta; } - - /** Set the center of the trackball. */ - void setCenter(const osg::Vec3d& center) { _center = center; } - - /** Get the center of the trackball. */ - const osg::Vec3d& getCenter() const { return _center; } - - /** Set the rotation of the trackball. */ - void setRotation(const osg::Quat& rotation) { _rotation = rotation; } - - /** Get the rotation of the trackball. */ - const osg::Quat& getRotation() const { return _rotation; } - - /** Set the distance of the trackball. */ - void setDistance(double distance) { _distance = distance; } - - /** Get the distance of the trackball. */ - double getDistance() const { return _distance; } - - /** Set the size of the trackball. */ - void setTrackballSize(float size); - - /** Get the size of the trackball. */ - float getTrackballSize() const { return _trackballSize; } - - /** Set the 'allow throw' flag. Releasing the mouse button while moving the camera results in a throw. */ - void setAllowThrow(bool allowThrow) { _allowThrow = allowThrow; } - - /** Returns true if the camera can be thrown, false otherwise. This defaults to true. */ - bool getAllowThrow() const { return _allowThrow; } - protected: - - virtual ~TrackballManipulator(); - - /** 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 two mouse events. - osg::ref_ptr _ga_t1; - osg::ref_ptr _ga_t0; - - osg::observer_ptr _node; - - double _modelScale; - double _minimumZoomScale; - - bool _allowThrow; - bool _thrown; - - /** The approximate amount of time it is currently taking to draw a frame. - * This is used to compute the delta in translation/rotation during a thrown display update. - * It allows us to match an delta in position/rotation independent of the rendering frame rate. - */ - double _delta_frame_time; - - /** The time the last frame started. - * Used when _rate_sensitive is true so that we can match display update rate to rotation/translation rate. - */ - double _last_frame_time; - - osg::Vec3d _center; - osg::Quat _rotation; - double _distance; - float _trackballSize; - float _zoomDelta; + META_Object( osgGA, TrackballManipulator ); }; + } -#endif - +#endif /* OSGGA_TRACKBALL_MANIPULATOR */ diff --git a/include/osgGA/UFOManipulator b/include/osgGA/UFOManipulator index 09cd4d246..83c89c968 100644 --- a/include/osgGA/UFOManipulator +++ b/include/osgGA/UFOManipulator @@ -109,7 +109,7 @@ class OSGGA_EXPORT UFOManipulator : public osgGA::MatrixManipulator void getUsage(osg::ApplicationUsage& usage) const; /** Report the current position as LookAt vectors */ - void getCurrentPositionAsLookAt( osg::Vec3 &eye, osg::Vec3 ¢er, osg::Vec3 &up ); + void getCurrentPositionAsLookAt( osg::Vec3d& eye, osg::Vec3d& center, osg::Vec3d& up ); void setMinHeight( double in_min_height ) { _minHeightAboveGround = in_min_height; } diff --git a/src/osg/ObserverNodePath.cpp b/src/osg/ObserverNodePath.cpp index 8af7903be..912a2f9f5 100644 --- a/src/osg/ObserverNodePath.cpp +++ b/src/osg/ObserverNodePath.cpp @@ -103,7 +103,7 @@ bool ObserverNodePath::getRefNodePath(RefNodePath& refNodePath) const refNodePath[i] = _nodePath[i].lock(); if (!refNodePath[i].valid()) { - OSG_NOTICE<<"ObserverNodePath::getRefNodePath() node has been invalidated"< +#include + +using namespace osg; +using namespace osgGA; + + + +int FirstPersonManipulator::_accelerationFlagIndex = allocateRelativeFlag(); +int FirstPersonManipulator::_maxVelocityFlagIndex = allocateRelativeFlag(); +int FirstPersonManipulator::_wheelMovementFlagIndex = allocateRelativeFlag(); + + +/// Constructor. +FirstPersonManipulator::FirstPersonManipulator( int flags ) + : inherited( flags ), + _velocity( 0. ) +{ + setAcceleration( 0.25, true ); + setMaxVelocity( 0.25, true ); + setWheelMovement( 0.05, true ); + if( _flags & SET_CENTER_ON_WHEEL_UP ) + setAnimationTime( 0.2 ); +} + + +/// Constructor. +FirstPersonManipulator::FirstPersonManipulator( const FirstPersonManipulator& fpm, const CopyOp& copyOp ) + : inherited( fpm, copyOp ), + _eye( fpm._eye ), + _rotation( fpm._rotation ), + _velocity( fpm._velocity ), + _acceleration( fpm._acceleration ), + _maxVelocity( fpm._maxVelocity ), + _wheelMovement( fpm._wheelMovement ) +{ +} + + +/** Set the position of the manipulator using a 4x4 matrix.*/ +void FirstPersonManipulator::setByMatrix( const Matrixd& matrix ) +{ + // set variables + _eye = matrix.getTrans(); + _rotation = matrix.getRotate(); + + // fix current rotation + if( getVerticalAxisFixed() ) + fixVerticalAxis( _eye, _rotation, true ); +} + + +/** Set the position of the manipulator using a 4x4 matrix.*/ +void FirstPersonManipulator::setByInverseMatrix( const Matrixd& matrix ) +{ + setByMatrix( Matrixd::inverse( matrix ) ); +} + + +/** Get the position of the manipulator as 4x4 matrix.*/ +Matrixd FirstPersonManipulator::getMatrix() const +{ + return Matrixd::rotate( _rotation ) * Matrixd::translate( _eye ); +} + + +/** Get the position of the manipulator as a inverse matrix of the manipulator, + typically used as a model view matrix.*/ +Matrixd FirstPersonManipulator::getInverseMatrix() const +{ + return Matrixd::translate( -_eye ) * Matrixd::rotate( _rotation.inverse() ); +} + + +// doc in parent +void FirstPersonManipulator::setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation ) +{ + // set variables + _eye = eye; + _rotation = rotation; + + // fix current rotation + if( getVerticalAxisFixed() ) + fixVerticalAxis( _eye, _rotation, true ); +} + + +// doc in parent +void FirstPersonManipulator::getTransformation( osg::Vec3d& eye, osg::Quat& rotation ) const +{ + eye = _eye; + rotation = _rotation; +} + + +// doc in parent +void FirstPersonManipulator::setTransformation( const osg::Vec3d& center, const osg::Vec3d& eye, const osg::Vec3d& up ) +{ + // set variables + osg::Matrixd m( osg::Matrixd::lookAt( eye, center, up ) ); + _eye = eye; + _rotation = m.getRotate().inverse(); + + // fix current rotation + if( getVerticalAxisFixed() ) + fixVerticalAxis( _eye, _rotation, true ); +} + + +// doc in parent +void FirstPersonManipulator::getTransformation( osg::Vec3d& center, osg::Vec3d& eye, osg::Vec3d& up ) const +{ + center = _eye + _rotation * osg::Vec3d( 0.,0.,-1. ); + eye = _eye; + up = _rotation * osg::Vec3d( 0.,1.,0. ); +} + + +/** Sets velocity. + * + * There are no checks for maximum velocity applied. + */ +void FirstPersonManipulator::setVelocity( const double& velocity ) +{ + _velocity = velocity; +} + + +/** Sets acceleration. + * + * If acceleration effect is unwanted, it can be set to DBL_MAX. + * Then, there will be no acceleration and object will reach its + * maximum velocity immediately. + */ +void FirstPersonManipulator::setAcceleration( const double& acceleration, bool relativeToModelSize ) +{ + _acceleration = acceleration; + setRelativeFlag( _accelerationFlagIndex, relativeToModelSize ); +} + + +/// Returns acceleration speed. +double FirstPersonManipulator::getAcceleration( bool *relativeToModelSize ) const +{ + if( relativeToModelSize ) + *relativeToModelSize = getRelativeFlag( _accelerationFlagIndex ); + + return _acceleration; +} + + +/** Sets maximum velocity. + * + * If acceleration is set to DBL_MAX, there is no speeding up. + * Instead, maximum velocity is used for velocity at once without acceleration. + */ +void FirstPersonManipulator::setMaxVelocity( const double& maxVelocity, bool relativeToModelSize ) +{ + _maxVelocity = maxVelocity; + setRelativeFlag( _maxVelocityFlagIndex, relativeToModelSize ); +} + + +/// Returns maximum velocity. +double FirstPersonManipulator::getMaxVelocity( bool *relativeToModelSize ) const +{ + if( relativeToModelSize ) + *relativeToModelSize = getRelativeFlag( _maxVelocityFlagIndex ); + + return _maxVelocity; +} + + +/// Sets movement size on single wheel step. +void FirstPersonManipulator::setWheelMovement( const double& wheelMovement, bool relativeToModelSize ) +{ + _wheelMovement = wheelMovement; + setRelativeFlag( _wheelMovementFlagIndex, relativeToModelSize ); +} + + +/// Returns movement size on single wheel step. +double FirstPersonManipulator::getWheelMovement( bool *relativeToModelSize ) const +{ + if( relativeToModelSize ) + *relativeToModelSize = getRelativeFlag( _wheelMovementFlagIndex ); + + return _wheelMovement; +} + + +void FirstPersonManipulator::home( double currentTime ) +{ + inherited::home( currentTime ); + _velocity = 0.; +} + + +void FirstPersonManipulator::home( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + inherited::home( ea, us ); + _velocity = 0.; +} + + +void FirstPersonManipulator::init( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + inherited::init( ea, us ); + + // stop movement + _velocity = 0.; +} + + +// doc in parent +bool FirstPersonManipulator::handleMouseWheel( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + switch( ea.getScrollingMotion() ) { + + // mouse scroll up event + case GUIEventAdapter::SCROLL_UP: { + + if( _flags & SET_CENTER_ON_WHEEL_UP ) { + + // stop thrown animation + _thrown = false; + + if( getAnimationTime() <= 0. ) + + // center by mouse intersection (no animation) + setCenterByMousePointerIntersection( ea, us ); + + else { + + // start new animation only if there is no animation in progress + if( !isAnimating() ) + startAnimationByMousePointerIntersection( ea, us ); + + } + } + + // move forward + moveForward( isAnimating() ? dynamic_cast< FirstPersonAnimationData* >( _animationData.get() )->_targetRot : _rotation, + _wheelMovement * (getRelativeFlag( _wheelMovementFlagIndex ) ? _modelSize : 1. )); + us.requestRedraw(); + us.requestContinuousUpdate( isAnimating() || _thrown ); + return true; + } + + // mouse scroll down event + case GUIEventAdapter::SCROLL_DOWN: + moveForward( -_wheelMovement * (getRelativeFlag( _wheelMovementFlagIndex ) ? _modelSize : 1. )); + _thrown = false; + us.requestRedraw(); + us.requestContinuousUpdate( isAnimating() || _thrown ); + return true; + + // unhandled mouse scrolling motion + default: + return false; + } +} + + +// doc in parent +bool FirstPersonManipulator::performMovementLeftMouseButton( const double dt, const double dx, const double dy ) +{ + // world up vector + CoordinateFrame coordinateFrame = getCoordinateFrame( _eye ); + Vec3d localUp = getUpVector( coordinateFrame ); + + rotateYawPitch( _rotation, dx, dy, localUp ); + + return true; +} + + +bool FirstPersonManipulator::performMouseDeltaMovement( const float dx, const float dy ) +{ + // rotate camera + if( getVerticalAxisFixed() ) { + + // world up vector + CoordinateFrame coordinateFrame = getCoordinateFrame( _eye ); + Vec3d localUp = getUpVector( coordinateFrame ); + + rotateYawPitch( _rotation, dx, dy, localUp ); + + } else + + rotateYawPitch( _rotation, dx, dy ); + + return true; +} + + +/// Move camera forward by distance parameter. +void FirstPersonManipulator::moveForward( const double distance ) +{ + moveForward( _rotation, distance ); +} + + +/// Move camera forward by distance parameter. +void FirstPersonManipulator::moveForward( const Quat& rotation, const double distance ) +{ + _eye += rotation * Vec3d( 0., 0., -distance ); +} + + +/// Move camera right by distance parameter. +void FirstPersonManipulator::moveRight( const double distance ) +{ + assert( 0 && "Not implemented yet." ); +} + + +/// Move camera up by distance parameter. +void FirstPersonManipulator::moveUp( const double distance ) +{ + assert( 0 && "Not implemented yet." ); +} + + +void FirstPersonManipulator::applyAnimationStep( const double currentProgress, const double prevProgress ) +{ + FirstPersonAnimationData *ad = dynamic_cast< FirstPersonAnimationData* >( _animationData.get() ); + assert( ad ); + + // compute new rotation + _rotation.slerp( currentProgress, ad->_startRot, ad->_targetRot ); + + // fix vertical axis + if( getVerticalAxisFixed() ) + fixVerticalAxis( _eye, _rotation, false ); +} + + +// doc in parent +bool FirstPersonManipulator::startAnimationByMousePointerIntersection( + const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ) +{ + // get current transformation + osg::Vec3d prevEye; + osg::Quat prevRot; + getTransformation( prevEye, prevRot ); + + // center by mouse intersection + if( !setCenterByMousePointerIntersection( ea, us ) ) + return false; + + FirstPersonAnimationData *ad = dynamic_cast< FirstPersonAnimationData*>( _animationData.get() ); + assert( ad ); + + // setup animation data and restore original transformation + ad->start( prevRot, _rotation, ea.getTime() ); + setTransformation( _eye, prevRot ); + + return true; +} + + +void FirstPersonManipulator::FirstPersonAnimationData::start( const Quat& startRotation, const Quat& targetRotation, + const double startTime ) +{ + AnimationData::start( startTime ); + + _startRot = startRotation; + _targetRot = targetRotation; +} diff --git a/src/osgGA/FlightManipulator.cpp b/src/osgGA/FlightManipulator.cpp index 57b7be00f..c9df462e4 100644 --- a/src/osgGA/FlightManipulator.cpp +++ b/src/osgGA/FlightManipulator.cpp @@ -1,241 +1,145 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include -#include using namespace osg; using namespace osgGA; -FlightManipulator::FlightManipulator() -{ - _modelScale = 0.01f; - _acceleration = 100.0f; - _velocity = 0.0f; - _yawMode = YAW_AUTOMATICALLY_WHEN_BANKED; - - _distance = 1.0f; -} -FlightManipulator::~FlightManipulator() +/// Constructor. +FlightManipulator::FlightManipulator( int flags ) + : inherited( flags ), + _yawMode( YAW_AUTOMATICALLY_WHEN_BANKED ) { } -void FlightManipulator::setNode(osg::Node* node) +/// Constructor. +FlightManipulator::FlightManipulator( const FlightManipulator& fm, const CopyOp& copyOp ) + : inherited( fm, copyOp ), + _yawMode( fm._yawMode ) { - _node = node; - if (_node.get()) - { - const osg::BoundingSphere& boundingSphere=_node->getBound(); - _modelScale = boundingSphere._radius; - } - - if (getAutoComputeHomePosition()) computeHomePosition(); } -const osg::Node* FlightManipulator::getNode() const +void FlightManipulator::init( const GUIEventAdapter& ea, GUIActionAdapter& us ) { - return _node.get(); + inherited::init( ea, us ); + + // center mouse pointer + centerMousePointer( ea, us ); } - -osg::Node* FlightManipulator::getNode() +void FlightManipulator::home( const GUIEventAdapter& ea, GUIActionAdapter& us ) { - return _node.get(); -} + inherited::home( ea, us ); -void FlightManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us) -{ - if (getAutoComputeHomePosition()) computeHomePosition(); - - computePosition(_homeEye, _homeCenter, _homeUp); - - _velocity = 0.0; - - us.requestRedraw(); - us.requestContinuousUpdate(false); - us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2.0f,(ea.getYmin()+ea.getYmax())/2.0f); - - flushMouseEventStack(); + // center mouse pointer + centerMousePointer( ea, us ); } -void FlightManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us) +// doc in parent +bool FlightManipulator::handleFrame( const GUIEventAdapter& ea, GUIActionAdapter& us ) { - flushMouseEventStack(); + addMouseEvent( ea ); + if( performMovement() ) + us.requestRedraw(); - us.requestContinuousUpdate(false); - - _velocity = 0.0f; - - if (ea.getEventType()!=GUIEventAdapter::RESIZE) - { - us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2.0f,(ea.getYmin()+ea.getYmax())/2.0f); - } + return false; } -bool FlightManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us) +// doc in parent +bool FlightManipulator::handleMouseMove( const GUIEventAdapter& ea, GUIActionAdapter& us ) { - switch(ea.getEventType()) - { - case(GUIEventAdapter::FRAME): - addMouseEvent(ea); - if (calcMovement()) us.requestRedraw(); - return false; - - case(GUIEventAdapter::RESIZE): - init(ea,us); - us.requestRedraw(); - return true; - default: - break; - } - - if (ea.getHandled()) return false; - - switch(ea.getEventType()) - { - case(GUIEventAdapter::PUSH): - { - - addMouseEvent(ea); - us.requestContinuousUpdate(true); - if (calcMovement()) us.requestRedraw(); - return true; - } - - case(GUIEventAdapter::RELEASE): - { - - addMouseEvent(ea); - us.requestContinuousUpdate(true); - if (calcMovement()) us.requestRedraw(); - return true; - } - - case(GUIEventAdapter::DRAG): - { - - addMouseEvent(ea); - us.requestContinuousUpdate(true); - if (calcMovement()) us.requestRedraw(); - return true; - } - - case(GUIEventAdapter::MOVE): - { - - addMouseEvent(ea); - us.requestContinuousUpdate(true); - if (calcMovement()) us.requestRedraw(); - - return true; - } - - case(GUIEventAdapter::KEYDOWN): - { - if (ea.getKey()==GUIEventAdapter::KEY_Space) - { - flushMouseEventStack(); - home(ea,us); - us.requestRedraw(); - us.requestContinuousUpdate(false); - 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; - } - - default: - return false; - } -} - -void FlightManipulator::getUsage(osg::ApplicationUsage& usage) const -{ - usage.addKeyboardMouseBinding("Flight: Space","Reset the viewing position to home"); - usage.addKeyboardMouseBinding("Flight: q","Automatically yaw when banked (default)"); - usage.addKeyboardMouseBinding("Flight: a","No yaw when banked"); -} - -void FlightManipulator::flushMouseEventStack() -{ - _ga_t1 = NULL; - _ga_t0 = NULL; + return flightHandleEvent( ea, us ); } -void FlightManipulator::addMouseEvent(const GUIEventAdapter& ea) +// doc in parent +bool FlightManipulator::handleMouseDrag( const GUIEventAdapter& ea, GUIActionAdapter& us ) { - _ga_t1 = _ga_t0; - _ga_t0 = &ea; + return flightHandleEvent( ea, us ); } -void FlightManipulator::setByMatrix(const osg::Matrixd& matrix) +// doc in parent +bool FlightManipulator::handleMousePush( const GUIEventAdapter& ea, GUIActionAdapter& us ) { - _eye = matrix.getTrans(); - _rotation = matrix.getRotate(); - _distance = 1.0f; -} - -osg::Matrixd FlightManipulator::getMatrix() const -{ - return osg::Matrixd::rotate(_rotation)*osg::Matrixd::translate(_eye); -} - -osg::Matrixd FlightManipulator::getInverseMatrix() const -{ - return osg::Matrixd::translate(-_eye)*osg::Matrixd::rotate(_rotation.inverse()); -} - -void FlightManipulator::computePosition(const osg::Vec3& eye,const osg::Vec3& center,const osg::Vec3& up) -{ - osg::Vec3d lv = center-eye; - - osg::Vec3 f(lv); - f.normalize(); - osg::Vec3 s(f^up); - s.normalize(); - osg::Vec3 u(s^f); - u.normalize(); - - osg::Matrixd 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 = eye; - _distance = lv.length(); - _rotation = rotation_matrix.getRotate().inverse(); + return flightHandleEvent( ea, us ); } -bool FlightManipulator::calcMovement() +// doc in parent +bool FlightManipulator::handleMouseRelease( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + return flightHandleEvent( ea, us ); +} + + +bool FlightManipulator::handleKeyDown( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + if( inherited::handleKeyDown( ea, us ) ) + return true; + + if( ea.getKey() == 'q' ) { + + _yawMode = YAW_AUTOMATICALLY_WHEN_BANKED; + return true; + + } + else if (ea.getKey()=='a') { + + _yawMode = NO_AUTOMATIC_YAW; + return true; + + } + + return false; +} + + +bool FlightManipulator::flightHandleEvent( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + addMouseEvent( ea ); + us.requestContinuousUpdate( true ); + if( performMovement() ) + us.requestRedraw(); + + return true; +} + + +void FlightManipulator::getUsage( ApplicationUsage& usage ) const +{ + inherited::getUsage( usage ); + usage.addKeyboardMouseBinding( getManipulatorName() + ": q", "Automatically yaw when banked (default)" ); + usage.addKeyboardMouseBinding( getManipulatorName() + ": a", "No yaw when banked" ); +} + + +/** Configure the Yaw control for the flight model. */ +void FlightManipulator::setYawControlMode( YawControlMode ycm ) +{ + _yawMode = ycm; +} + + +bool FlightManipulator::performMovement() { // return if less then two events have been added. if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false; @@ -245,53 +149,46 @@ bool FlightManipulator::calcMovement() if (dt<0.0f) { - notify(INFO) << "warning dt = "<accept(cbVisitor); + osg::BoundingBox &bb = cbVisitor.getBoundingBox(); + boundingSphere.expandBy(bb); + + } + else + { + // compute bounding sphere + boundingSphere = getNode()->getBound(); + } + + // set dist to default + double dist = 3.5f * boundingSphere.radius(); + + if (camera) { + + // try to compute dist from frustrum + double left,right,bottom,top,zNear,zFar; + if (camera->getProjectionMatrixAsFrustum(left,right,bottom,top,zNear,zFar)) + { + double vertical2 = fabs(right - left) / zNear / 2.; + double horizontal2 = fabs(top - bottom) / zNear / 2.; + double dim = horizontal2 < vertical2 ? horizontal2 : vertical2; + double viewAngle = atan2(dim,1.); + dist = boundingSphere.radius() / sin(viewAngle); + } + else + // try to compute dist from ortho + if (camera->getProjectionMatrixAsOrtho(left,right,bottom,top,zNear,zFar)) + { + dist = fabs(zFar - zNear) / 2.; + } + } + + // set home position + setHomePosition(boundingSphere.center() + osg::Vec3d(0.0,-dist,0.0f), + boundingSphere.center(), + osg::Vec3d(0.0f,0.0f,1.0f), + _autoComputeHomePosition); + } +} diff --git a/src/osgGA/NodeTrackerManipulator.cpp b/src/osgGA/NodeTrackerManipulator.cpp index a064b632d..722fce6e5 100644 --- a/src/osgGA/NodeTrackerManipulator.cpp +++ b/src/osgGA/NodeTrackerManipulator.cpp @@ -1,13 +1,13 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ @@ -20,22 +20,91 @@ using namespace osg; using namespace osgGA; -NodeTrackerManipulator::NodeTrackerManipulator() +NodeTrackerManipulator::NodeTrackerManipulator( int flags ) + : inherited( flags ), + _trackerMode(NODE_CENTER_AND_ROTATION) { - _trackerMode = NODE_CENTER_AND_ROTATION; - _rotationMode = TRACKBALL; - - _distance = 1.0; - - _thrown = false; - + setVerticalAxisFixed(false); } -NodeTrackerManipulator::~NodeTrackerManipulator() +NodeTrackerManipulator::NodeTrackerManipulator( const NodeTrackerManipulator& m, const CopyOp& copyOp ) + : inherited( m, copyOp ), + _trackNodePath( m._trackNodePath ), + _trackerMode( m._trackerMode ) { } + +void NodeTrackerManipulator::setTrackNodePath(const osg::NodePath& nodePath) +{ + _trackNodePath.clear(); + _trackNodePath.reserve(nodePath.size()); + std::copy(nodePath.begin(), nodePath.end(), std::back_inserter(_trackNodePath)); +} + + +osg::NodePath NodeTrackerManipulator::getNodePath() const +{ + osg::NodePath nodePath; + for(ObserverNodePath::const_iterator itr = _trackNodePath.begin(); + itr != _trackNodePath.end(); + ++itr) + { + nodePath.push_back(const_cast(itr->get())); + } + return nodePath; +} + +bool NodeTrackerManipulator::validateNodePath() const +{ + for(ObserverNodePath::const_iterator itr = _trackNodePath.begin(); + itr != _trackNodePath.begin(); + ++itr) + { + if (*itr==0) + { + osg::notify(osg::NOTICE)<<"Warning: tracked node path has been invalidated by changes in the scene graph."<(_trackNodePath).clear(); + return false; + } + } + return true; +} + +void NodeTrackerManipulator::setTrackerMode(TrackerMode mode) +{ + _trackerMode = mode; +} + +/// Sets rotation mode. \sa setVerticalAxisFixed +void NodeTrackerManipulator::setRotationMode(RotationMode mode) +{ + setVerticalAxisFixed(mode!=TRACKBALL); + + if (getAutoComputeHomePosition()) + computeHomePosition(); +} + +/// Gets rotation mode. \sa getVerticalAxisFixed +NodeTrackerManipulator::RotationMode NodeTrackerManipulator::getRotationMode() const +{ + return getVerticalAxisFixed() ? ELEVATION_AZIM : TRACKBALL; +} + +void NodeTrackerManipulator::setNode(Node* node) +{ + inherited::setNode( node ); + + // update model size + if (_flags & UPDATE_MODEL_SIZE) + if (_node.get()) { + setMinimumDistance(clampBetween(_modelSize*0.001, 0.00001, 1.0)); + notify(INFO) << "NodeTrackerManipulator: setting minimum distance to " + << _minimumDistance << std::endl; + } +} + void NodeTrackerManipulator::setTrackNode(osg::Node* node) { if (!node) @@ -44,231 +113,48 @@ void NodeTrackerManipulator::setTrackNode(osg::Node* node) return; } - osg::NodePathList parentNodePaths = node->getParentalNodePaths(); - - if (!parentNodePaths.empty()) + osg::NodePathList nodePaths = node->getParentalNodePaths(); + if (!nodePaths.empty()) { - osg::notify(osg::INFO)<<"NodeTrackerManipulator::setTrackNode(Node*): Path set"<1) + { + osg::notify(osg::NOTICE)<<"osgGA::NodeTrackerManipualtor::setTrackNode(..) taking first parent path, ignoring others."<getName()<<"): Path set"<getName()<<")"<getBound(); - const float minimumDistanceScale = 0.001f; - _minimumDistance = osg::clampBetween( - float(boundingSphere._radius) * minimumDistanceScale, - 0.00001f,1.0f); - - osg::notify(osg::INFO)<<"Setting Tracker manipulator _minimumDistance to "<<_minimumDistance<className()<<" '"<<_trackNodePath[i]->getName()<<"'"<getBound(); - setHomePosition(boundingSphere._center+osg::Vec3( 0.0,-3.5f * boundingSphere._radius,0.0f), + setHomePosition(boundingSphere._center+osg::Vec3d( 0.0,-3.5f * boundingSphere._radius,0.0f), boundingSphere._center, - osg::Vec3(0.0f,0.0f,1.0f), + osg::Vec3d(0.0f,0.0f,1.0f), _autoComputeHomePosition); } } - -void NodeTrackerManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& ) -{ - flushMouseEventStack(); -} - - -void NodeTrackerManipulator::getUsage(osg::ApplicationUsage& usage) const -{ - usage.addKeyboardMouseBinding("NodeTracker: Space","Reset the viewing position to home"); - usage.addKeyboardMouseBinding("NodeTracker: +","When in stereo, increase the fusion distance"); - usage.addKeyboardMouseBinding("NodeTracker: -","When in stereo, reduce the fusion distance"); -} - -bool NodeTrackerManipulator::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) - { - - double timeSinceLastRecordEvent = _ga_t0.valid() ? (ea.getTime() - _ga_t0->getTime()) : DBL_MAX; - if (timeSinceLastRecordEvent>0.02) flushMouseEventStack(); - - 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 NodeTrackerManipulator::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->getTime()-_ga_t1->getTime(); - - return (len>dt*velocity); -} - - -void NodeTrackerManipulator::flushMouseEventStack() -{ - _ga_t1 = NULL; - _ga_t0 = NULL; -} - - -void NodeTrackerManipulator::addMouseEvent(const GUIEventAdapter& ea) -{ - _ga_t1 = _ga_t0; - _ga_t0 = &ea; -} - void NodeTrackerManipulator::setByMatrix(const osg::Matrixd& matrix) { osg::Vec3d eye,center,up; @@ -276,20 +162,33 @@ void NodeTrackerManipulator::setByMatrix(const osg::Matrixd& matrix) computePosition(eye,center,up); } +void NodeTrackerManipulator::computeNodeWorldToLocal(osg::Matrixd& worldToLocal) const +{ + if (validateNodePath()) + { + worldToLocal = osg::computeWorldToLocal(getNodePath()); + } +} + +void NodeTrackerManipulator::computeNodeLocalToWorld(osg::Matrixd& localToWorld) const +{ + if (validateNodePath()) + { + localToWorld = osg::computeLocalToWorld(getNodePath()); + } + +} + void NodeTrackerManipulator::computeNodeCenterAndRotation(osg::Vec3d& nodeCenter, osg::Quat& nodeRotation) const { osg::Matrixd localToWorld, worldToLocal; - osg::NodePath nodePath; - if (_trackNodePath.getNodePath(nodePath)) - { - worldToLocal = osg::computeWorldToLocal(nodePath); - localToWorld = osg::computeLocalToWorld(nodePath); - nodeCenter = osg::Vec3d(nodePath.back()->getBound().center())*localToWorld; - } + computeNodeLocalToWorld(localToWorld); + computeNodeWorldToLocal(worldToLocal); + + if (validateNodePath()) + nodeCenter = osg::Vec3d(_trackNodePath.back()->getBound().center())*localToWorld; else - { nodeCenter = osg::Vec3d(0.0f,0.0f,0.0f)*localToWorld; - } switch(_trackerMode) @@ -350,215 +249,74 @@ void NodeTrackerManipulator::computePosition(const osg::Vec3d& eye,const osg::Ve if (!_node) return; // compute rotation matrix - osg::Vec3 lv(center-eye); + osg::Vec3d lv(center-eye); _distance = lv.length(); - + osg::Matrixd lookat; lookat.makeLookAt(eye,center,up); - + _rotation = lookat.getRotate().inverse(); } -bool NodeTrackerManipulator::calcMovement() + +// doc in parent +bool NodeTrackerManipulator::performMovementLeftMouseButton( const double dt, const double dx, const double dy ) { - // 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(); - - - float distance = sqrtf(dx*dx + dy*dy); - // return if movement is too fast, indicating an error in event values or change in screen. - if (distance>0.5) - { - return false; - } - - // return if there is no movement. - if (distance==0.0f) - { - return false; - } - osg::Vec3d nodeCenter; osg::Quat nodeRotation; computeNodeCenterAndRotation(nodeCenter, nodeRotation); - unsigned int buttonMask = _ga_t1->getButtonMask(); + // rotate camera + if( getVerticalAxisFixed() ) { - if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON) - { + osg::Matrix rotation_matrix; + rotation_matrix.makeRotate(_rotation); - if (_rotationMode==TRACKBALL) - { + osg::Vec3d lookVector = -getUpVector(rotation_matrix); + osg::Vec3d sideVector = getSideVector(rotation_matrix); + osg::Vec3d upVector = getFrontVector(rotation_matrix); - // rotate camera. - osg::Vec3 axis; - double angle; + osg::Vec3d localUp(0.0f,0.0f,1.0f); - double px0 = _ga_t0->getXnormalized(); - double py0 = _ga_t0->getYnormalized(); + osg::Vec3d forwardVector = localUp^sideVector; + sideVector = forwardVector^localUp; - double px1 = _ga_t1->getXnormalized(); - double py1 = _ga_t1->getYnormalized(); + forwardVector.normalize(); + sideVector.normalize(); + osg::Quat rotate_elevation; + rotate_elevation.makeRotate(dy,sideVector); - trackball(axis,angle,px1,py1,px0,py0); + osg::Quat rotate_azim; + rotate_azim.makeRotate(-dx,localUp); - osg::Quat new_rotate; - new_rotate.makeRotate(angle,axis); + _rotation = _rotation * rotate_elevation * rotate_azim; - _rotation = _rotation*new_rotate; - } - else - { - osg::Matrix rotation_matrix; - rotation_matrix.makeRotate(_rotation); + } else + rotateTrackball( _ga_t0->getXnormalized(), _ga_t0->getYnormalized(), + _ga_t1->getXnormalized(), _ga_t1->getYnormalized() ); - osg::Vec3d lookVector = -getUpVector(rotation_matrix); - osg::Vec3d sideVector = getSideVector(rotation_matrix); - osg::Vec3d upVector = getFrontVector(rotation_matrix); - - osg::Vec3d localUp(0.0f,0.0f,1.0f); - - 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)) - { - return true; - } - else if (buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON) - { - - // zoom model. - - double fd = _distance; - double scale = 1.0f+dy; - if (fd*scale>_minimumDistance) - { - - _distance *= scale; - - } else - { - _distance = _minimumDistance; - } - - return true; - - } - - return false; + return true; } -void NodeTrackerManipulator::clampOrientation() + +// doc in parent +bool NodeTrackerManipulator::performMovementMiddleMouseButton( const double dt, const double dx, const double dy ) { + osg::Vec3d nodeCenter; + osg::Quat nodeRotation; + computeNodeCenterAndRotation(nodeCenter, nodeRotation); + + return true; } -/* - * 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 NodeTrackerManipulator::trackball(osg::Vec3& axis,double & angle, double p1x, double p1y, double p2x, double p2y) +// doc in parent +bool NodeTrackerManipulator::performMovementRightMouseButton( const double dt, const double dx, const double dy ) { - /* - * First, figure out z-coordinates for projection of P1 and P2 to - * deformed sphere - */ - - osg::Matrix rotation_matrix(_rotation); - - - osg::Vec3d uv = osg::Vec3d(0.0,1.0,0.0)*rotation_matrix; - osg::Vec3d sv = osg::Vec3d(1.0,0.0,0.0)*rotation_matrix; - osg::Vec3d lv = osg::Vec3d(0.0,0.0,-1.0)*rotation_matrix; - - osg::Vec3d p1 = sv*p1x+uv*p1y-lv*tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y); - osg::Vec3d 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. - */ - double 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)); + osg::Vec3d nodeCenter; + osg::Quat nodeRotation; + computeNodeCenterAndRotation(nodeCenter, nodeRotation); -} - - -/* - * 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 NodeTrackerManipulator::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; + return inherited::performMovementRightMouseButton(dt, dx, dy); } diff --git a/src/osgGA/OrbitManipulator.cpp b/src/osgGA/OrbitManipulator.cpp new file mode 100644 index 000000000..f7fcd8bac --- /dev/null +++ b/src/osgGA/OrbitManipulator.cpp @@ -0,0 +1,553 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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. +*/ + +#include +#include +#include + +using namespace osg; +using namespace osgGA; + + + +int OrbitManipulator::_minimumDistanceFlagIndex = allocateRelativeFlag(); + + +/// Constructor. +OrbitManipulator::OrbitManipulator( int flags ) + : inherited( flags ), + _distance( 1. ), + _trackballSize( 0.8 ) +{ + setMinimumDistance( 0.05, true ); + setWheelZoomFactor( 0.1 ); + if( _flags & SET_CENTER_ON_WHEEL_UP ) + setAnimationTime( 0.2 ); +} + + +/// Constructor. +OrbitManipulator::OrbitManipulator( const OrbitManipulator& om, const CopyOp& copyOp ) + : inherited( om, copyOp ), + _center( om._center ), + _rotation( om._rotation ), + _distance( om._distance ), + _trackballSize( om._trackballSize ), + _wheelZoomFactor( om._wheelZoomFactor ), + _minimumDistance( om._minimumDistance ) +{ +} + + +/** Set the position of the manipulator using a 4x4 matrix.*/ +void OrbitManipulator::setByMatrix( const osg::Matrixd& matrix ) +{ + _center = osg::Vec3d( 0., 0., -_distance ) * matrix; + _rotation = matrix.getRotate(); + + // fix current rotation + if( getVerticalAxisFixed() ) + fixVerticalAxis( _center, _rotation, true ); +} + + +/** Set the position of the manipulator using a 4x4 matrix.*/ +void OrbitManipulator::setByInverseMatrix( const osg::Matrixd& matrix ) +{ + setByMatrix( osg::Matrixd::inverse( matrix ) ); +} + + +/** Get the position of the manipulator as 4x4 matrix.*/ +osg::Matrixd OrbitManipulator::getMatrix() const +{ + return osg::Matrixd::translate( 0., 0., _distance ) * + osg::Matrixd::rotate( _rotation ) * + osg::Matrixd::translate( _center ); +} + + +/** Get the position of the manipulator as a inverse matrix of the manipulator, + typically used as a model view matrix.*/ +osg::Matrixd OrbitManipulator::getInverseMatrix() const +{ + return osg::Matrixd::translate( -_center ) * + osg::Matrixd::rotate( _rotation.inverse() ) * + osg::Matrixd::translate( 0.0, 0.0, -_distance ); +} + + +// doc in parent +void OrbitManipulator::setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation ) +{ + _center = eye + rotation * osg::Vec3d( 0., 0., -_distance ); + _rotation = rotation; + + // fix current rotation + if( getVerticalAxisFixed() ) + fixVerticalAxis( _center, _rotation, true ); +} + + +// doc in parent +void OrbitManipulator::getTransformation( osg::Vec3d& eye, osg::Quat& rotation ) const +{ + eye = _center - _rotation * osg::Vec3d( 0., 0., -_distance ); + rotation = _rotation; +} + + +// doc in parent +void OrbitManipulator::setTransformation( const osg::Vec3d& center, const osg::Vec3d& eye, const osg::Vec3d& up ) +{ + Vec3d lv( center - eye ); + + Vec3d f( lv ); + f.normalize(); + Vec3d s( f^up ); + s.normalize(); + Vec3d u( s^f ); + u.normalize(); + + osg::Matrixd 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 ); + + _center = center; + _distance = lv.length(); + _rotation = rotation_matrix.getRotate().inverse(); + + // fix current rotation + if( getVerticalAxisFixed() ) + fixVerticalAxis( _center, _rotation, true ); +} + + +// doc in parent +void OrbitManipulator::getTransformation( osg::Vec3d& center, osg::Vec3d& eye, osg::Vec3d& up ) const +{ + center = _center; + eye = _center + _rotation * osg::Vec3d( 0., 0., _distance ); + up = _rotation * osg::Vec3d( 0., 1., 0. ); +} + + +// doc in parent +bool OrbitManipulator::handleMouseWheel( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + switch( ea.getScrollingMotion() ) { + + // mouse scroll up event + case GUIEventAdapter::SCROLL_UP: { + + if( _flags & SET_CENTER_ON_WHEEL_UP ) { + + if( getAnimationTime() <= 0. ) + + // center by mouse intersection (no animation) + setCenterByMousePointerIntersection( ea, us ); + + else { + + // start new animation only if there is no animation in progress + if( !isAnimating() ) + startAnimationByMousePointerIntersection( ea, us ); + + } + } + + // perform zoom + zoomModel( -_wheelZoomFactor, true ); + us.requestRedraw(); + us.requestContinuousUpdate( isAnimating() || _thrown ); + return true; + } + + // mouse scroll down event + case GUIEventAdapter::SCROLL_DOWN: + zoomModel( _wheelZoomFactor, true ); + us.requestRedraw(); + us.requestContinuousUpdate( false ); + return true; + + // unhandled mouse scrolling motion + default: + return false; + } +} + + +// doc in parent +bool OrbitManipulator::performMovementLeftMouseButton( const double dt, const double dx, const double dy ) +{ + // rotate camera + if( getVerticalAxisFixed() ) + rotateWithFixedVertical( dx, dy ); + else + rotateTrackball( _ga_t0->getXnormalized(), _ga_t0->getYnormalized(), + _ga_t1->getXnormalized(), _ga_t1->getYnormalized() ); + return true; +} + + +// doc in parent +bool OrbitManipulator::performMovementMiddleMouseButton( const double dt, const double dx, const double dy ) +{ + // pan model + float scale = -0.3f * _distance; + panModel( dx*scale, dy*scale ); + return true; +} + + +// doc in parent +bool OrbitManipulator::performMovementRightMouseButton( const double dt, const double dx, const double dy ) +{ + // zoom model + zoomModel( dy, true ); + return true; +} + + +bool OrbitManipulator::performMouseDeltaMovement( const float dx, const float dy ) +{ + // rotate camera + if( getVerticalAxisFixed() ) + rotateWithFixedVertical( dx, dy ); + else + rotateTrackball( 0.f, 0.f, dx, dy ); + + return true; +} + + +void OrbitManipulator::applyAnimationStep( const double currentProgress, const double prevProgress ) +{ + OrbitAnimationData *ad = dynamic_cast< OrbitAnimationData* >( _animationData.get() ); + assert( ad ); + + // compute new center + osg::Vec3d prevCenter, prevEye, prevUp; + getTransformation( prevCenter, prevEye, prevUp ); + osg::Vec3d newCenter = osg::Vec3d(prevCenter) + (ad->_movement * (currentProgress - prevProgress)); + + // fix vertical axis + if( getVerticalAxisFixed() ) { + + CoordinateFrame coordinateFrame = getCoordinateFrame( newCenter ); + Vec3d localUp = getUpVector( coordinateFrame ); + + fixVerticalAxis( newCenter - prevEye, prevUp, prevUp, localUp, false ); + + } + + // apply new transformation + setTransformation( newCenter, prevEye, prevUp ); +} + + +bool OrbitManipulator::startAnimationByMousePointerIntersection( + const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ) +{ + // get current transformation + osg::Vec3d prevCenter, prevEye, prevUp; + getTransformation( prevCenter, prevEye, prevUp ); + + // center by mouse intersection + if( !setCenterByMousePointerIntersection( ea, us ) ) + return false; + + OrbitAnimationData *ad = dynamic_cast< OrbitAnimationData*>( _animationData.get() ); + assert( ad ); + + // setup animation data and restore original transformation + ad->start( osg::Vec3d(_center) - prevCenter, ea.getTime() ); + setTransformation( prevCenter, prevEye, prevUp ); + + return true; +} + + +void OrbitManipulator::OrbitAnimationData::start( const osg::Vec3d& movement, const double startTime ) +{ + AnimationData::start( startTime ); + + _movement = movement; +} + + +/** Performs trackball rotation based on two points given, for example, + by mouse pointer on the screen.*/ +void OrbitManipulator::rotateTrackball( const float px0, const float py0, const float px1, const float py1 ) +{ + osg::Vec3d axis; + float angle; + + trackball( axis, angle, px1, py1, px0, py0 ); + + Quat new_rotate; + new_rotate.makeRotate( angle, axis ); + + _rotation = _rotation * new_rotate; +} + + +/** Performs rotation horizontally by dx parameter and vertically by dy parameter, + while keeping UP vector.*/ +void OrbitManipulator::rotateWithFixedVertical( const float dx, const float dy ) +{ + CoordinateFrame coordinateFrame = getCoordinateFrame( _center ); + Vec3d localUp = getUpVector( coordinateFrame ); + + rotateYawPitch( _rotation, dx, dy, localUp ); +} + + +/** Performs rotation horizontally by dx parameter and vertically by dy parameter, + while keeping UP vector given by up parameter.*/ +void OrbitManipulator::rotateWithFixedVertical( const float dx, const float dy, const Vec3f& up ) +{ + rotateYawPitch( _rotation, dx, dy, up ); +} + + +/** Moves camera in x,y,z directions given in camera local coordinates.*/ +void OrbitManipulator::panModel( const float dx, const float dy, const float dz ) +{ + Matrix rotation_matrix; + rotation_matrix.makeRotate( _rotation ); + + Vec3d dv( dx, dy, dz ); + + _center += dv * rotation_matrix; +} + + +/** Changes the distance of camera to the focal center. + If pushForwardIfNeeded is true and minimumDistance is reached, + the focal center is moved forward. Otherwise, distance is limited + to its minimum value. + \sa OrbitManipulator::setMinimumDistance + */ +void OrbitManipulator::zoomModel( const float dy, bool pushForwardIfNeeded ) +{ + // scale + float scale = 1.0f + dy; + + // minimum distance + float minDist = _minimumDistance; + if( getRelativeFlag( _minimumDistanceFlagIndex ) ) + minDist *= _modelSize; + + if( _distance*scale > minDist ) + { + + // regular zoom + _distance *= scale; + + } + else + { + + if( pushForwardIfNeeded ) + { + + // push the camera forward + float scale = -_distance; + Matrixd rotation_matrix( _rotation ); + Vec3d dv = (Vec3d( 0.0f, 0.0f, -1.0f ) * rotation_matrix) * (dy * scale); + _center += dv; + + } + else + { + + // set distance on its minimum value + _distance = minDist; + + } + } +} + + +/** + * 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 OrbitManipulator::trackball( osg::Vec3d& 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::Matrixd rotation_matrix(_rotation); + + osg::Vec3d uv = Vec3d(0.0f,1.0f,0.0f)*rotation_matrix; + osg::Vec3d sv = Vec3d(1.0f,0.0f,0.0f)*rotation_matrix; + osg::Vec3d lv = Vec3d(0.0f,0.0f,-1.0f)*rotation_matrix; + + osg::Vec3d p1 = sv * p1x + uv * p1y - lv * tb_project_to_sphere(_trackballSize, p1x, p1y); + osg::Vec3d p2 = sv * p2x + uv * p2y - lv * tb_project_to_sphere(_trackballSize, p2x, p2y); + + /* + * Now, we want the cross product of P1 and 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)); + +} + + +/** + * Helper trackball method that projects 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 OrbitManipulator::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; +} + + +/** Get the FusionDistanceMode. Used by SceneView for setting up stereo convergence.*/ +osgUtil::SceneView::FusionDistanceMode OrbitManipulator::getFusionDistanceMode() const +{ + return osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE; +} + +/** Get the FusionDistanceValue. Used by SceneView for setting up stereo convergence.*/ +float OrbitManipulator::getFusionDistanceValue() const +{ + return _distance; +} + + +/** Set the center of the manipulator. */ +void OrbitManipulator::setCenter( const Vec3d& center ) +{ + _center = center; +} + + +/** Get the center of the manipulator. */ +const Vec3d& OrbitManipulator::getCenter() const +{ + return _center; +} + + +/** Set the rotation of the manipulator. */ +void OrbitManipulator::setRotation( const Quat& rotation ) +{ + _rotation = rotation; +} + + +/** Get the rotation of the manipulator. */ +const Quat& OrbitManipulator::getRotation() const +{ + return _rotation; +} + + +/** Set the distance of camera to the center. */ +void OrbitManipulator::setDistance( double distance ) +{ + _distance = distance; +} + + +/** Get the distance of the camera to the center. */ +double OrbitManipulator::getDistance() const +{ + return _distance; +} + + +/** Set the size of the trackball. Value is relative to the model size. */ +void OrbitManipulator::setTrackballSize( const double& size ) +{ + /* + * 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. + */ + _trackballSize = size; + clampBetweenRange( _trackballSize, 0.1, 1.0, "TrackballManipulator::setTrackballSize(float)" ); +} + + +/** Set the mouse wheel zoom factor. + The amount of camera movement on each mouse wheel event + is computed as the current distance to the center multiplied by this factor. + For example, value of 0.1 will short distance to center by 10% on each wheel up event.*/ +void OrbitManipulator::setWheelZoomFactor( double wheelZoomFactor ) +{ + _wheelZoomFactor = wheelZoomFactor; +} + + +/** Set the minimum distance of the eye point from the center + before the center is pushed forward.*/ +void OrbitManipulator::setMinimumDistance( const double& minimumDistance, bool relativeToModelSize ) +{ + _minimumDistance = minimumDistance; + setRelativeFlag( _minimumDistanceFlagIndex, relativeToModelSize ); +} + + +/** Get the minimum distance of the eye point from the center + before the center is pushed forward.*/ +double OrbitManipulator::getMinimumDistance( bool *relativeToModelSize ) const +{ + if( relativeToModelSize ) + *relativeToModelSize = getRelativeFlag( _minimumDistanceFlagIndex ); + + return _minimumDistance; +} diff --git a/src/osgGA/SphericalManipulator.cpp b/src/osgGA/SphericalManipulator.cpp index 392b57f57..7ee57b051 100644 --- a/src/osgGA/SphericalManipulator.cpp +++ b/src/osgGA/SphericalManipulator.cpp @@ -247,7 +247,7 @@ void SphericalManipulator::addMouseEvent(const GUIEventAdapter& ea) //-------------------------------------------------------------------------------------------------- void SphericalManipulator::setByMatrix(const osg::Matrixd& matrix) { - _center=osg::Vec3(0,0,-_distance)*matrix; + _center=osg::Vec3d(0,0,-_distance)*matrix; _heading=atan2(-matrix(0,0),matrix(0,1)); @@ -373,7 +373,7 @@ bool SphericalManipulator::calcMovement() osg::Matrix rotation_matrix; rotation_matrix=osg::Matrixd::rotate(_elevation,-1,0,0)*osg::Matrixd::rotate(PI_2+_heading,0,0,1); - osg::Vec3 dv(throwScale*dx*scale,0,throwScale*dy*scale); + osg::Vec3d dv(throwScale*dx*scale,0,throwScale*dy*scale); _center += dv*rotation_matrix; return true; diff --git a/src/osgGA/StandardManipulator.cpp b/src/osgGA/StandardManipulator.cpp new file mode 100644 index 000000000..919058ed9 --- /dev/null +++ b/src/osgGA/StandardManipulator.cpp @@ -0,0 +1,792 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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. +*/ + +#include +#include +#include + +using namespace osg; +using namespace osgGA; +using namespace osgUtil; + + + +/** \fn void StandardManipulator::setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation ); + Sets manipulator by eye position and eye orientation.*/ + +/** \fn void StandardManipulator::setTransformation( const osg::Vec3d& center, const osg::Vec3d& eye, const osg::Vec3d& up ); + Sets manipulator by focal center, eye position, and up vector.*/ + +/** \fn void StandardManipulator::getTransformation( osg::Vec3d& eye, osg::Quat& rotation ); + Gets manipulator's eye position and eye orientation.*/ + +/** \fn void StandardManipulator::getTransformation( osg::Vec3d& center, osg::Vec3d& eye, osg::Vec3d& up ); + Gets manipulator's focal center, eye position, and up vector.*/ + + +int StandardManipulator::numRelativeFlagsAllocated = 0; + + +int StandardManipulator::allocateRelativeFlag() +{ + return numRelativeFlagsAllocated++; +} + + +/// Constructor. +StandardManipulator::StandardManipulator( int flags ) + : inherited(), + _thrown( false ), + _mouseCenterX(0.0f), _mouseCenterY(0.0f), + _delta_frame_time(0.01), _last_frame_time(0.0), + _modelSize( 0. ), + _verticalAxisFixed( true ), + _flags( flags ), + _relativeFlags( 0 ) +{ +} + + +/// Constructor. +StandardManipulator::StandardManipulator( const StandardManipulator& uim, const CopyOp& copyOp ) + : inherited( uim, copyOp ), + _thrown( uim._thrown ), + _mouseCenterX(0.0f), _mouseCenterY(0.0f), + _ga_t1( dynamic_cast< GUIEventAdapter* >( copyOp( uim._ga_t1.get() ) ) ), + _ga_t0( dynamic_cast< GUIEventAdapter* >( copyOp( uim._ga_t0.get() ) ) ), + _delta_frame_time(0.01), _last_frame_time(0.0), + _modelSize( uim._modelSize ), + _verticalAxisFixed( uim._verticalAxisFixed ), + _flags( uim._flags ), + _relativeFlags( uim._relativeFlags ) +{ +} + + +/** Attach a node to the manipulator. + Automatically detaches previously attached node. + setNode(NULL) detaches previously attached nodes. + Is ignored by manipulators which do not require a reference model.*/ +void StandardManipulator::setNode( Node* node ) +{ + _node = node; + + // update model size + if( _node.get() ) { + const BoundingSphere& boundingSphere = _node->getBound(); + _modelSize = boundingSphere.radius(); + } else + _modelSize = 0.; + + // compute home position + if( getAutoComputeHomePosition() ) + computeHomePosition( NULL, ( _flags & COMPUTE_HOME_USING_BBOX ) != 0 ); +} + + +/** Return node if attached.*/ +const Node* StandardManipulator::getNode() const +{ + return _node.get(); +} + + +/** Return node if attached.*/ +Node* StandardManipulator::getNode() +{ + return _node.get(); +} + + +/** Makes manipulator to keep camera's "UP" vector. + * + * In general, fixed up vector makes camera control more user friendly. + * + * To change up vector, use MatrixManipulator::setCoordinateFrameCallback.*/ +void StandardManipulator::setVerticalAxisFixed( bool value ) +{ + _verticalAxisFixed = value; +} + + +/// Sets manipulator animation time when centering on mouse wheel up is enabled. +void StandardManipulator::setAnimationTime( const double t ) +{ + if( t <= 0. ) { + finishAnimation(); + _animationData = NULL; + return; + } + + if( !_animationData ) + allocAnimationData(); + + _animationData->_animationTime = t; +} + + +/// Returns manipulator animation time when centering on mouse wheel up is enabled. +double StandardManipulator::getAnimationTime() const +{ + if( _animationData ) + return _animationData->_animationTime; + else + return 0.; +} + + +/// Returns whether manipulator is performing animation at the moment. +bool StandardManipulator::isAnimating() const +{ + if( _animationData ) + return _animationData->_isAnimating; + else + return false; +} + + +void StandardManipulator::finishAnimation() +{ + if( !isAnimating() ) + return; + + applyAnimationStep( 1., _animationData->_phase ); +} + + +/** Move the camera to the default position. + + The user should probably want to use home(GUIEventAdapter&, GUIActionAdapter&) + instead to set manipulator to the home position. This method does not trigger + any redraw processing or updates continuous update processing. + + StandardManipulator implementation only updates its internal structures and + recomputes its home position if autoComputeHomePosition is set. + Descendant classes are expected to update camera position.*/ +void StandardManipulator::home( double /*currentTime*/ ) +{ + if( getAutoComputeHomePosition() ) + computeHomePosition( NULL, ( _flags & COMPUTE_HOME_USING_BBOX ) != 0 ); + + _thrown = false; + setTransformation( _homeCenter, _homeEye, _homeUp ); + flushMouseEventStack(); +} + + +/** Move the camera to the default position. + + If autoComputeHomePosition is on, home position is computed. + The computation considers camera fov and model size and + positions camera far enough to fit the model to the screen. + + StandardManipulator implementation only updates its internal data. + If home position is expected to be supported by the descendant manipulator, + it has to reimplement the method to update manipulator transformation.*/ +void StandardManipulator::home( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + if( getAutoComputeHomePosition() ) { + const Camera *camera = us.asView() ? us.asView()->getCamera() : NULL; + computeHomePosition( camera, ( _flags & COMPUTE_HOME_USING_BBOX ) != 0 ); + } + + _thrown = false; + setTransformation( _homeCenter, _homeEye, _homeUp ); + + us.requestRedraw(); + us.requestContinuousUpdate( false ); + flushMouseEventStack(); +} + + +/** Start/restart the manipulator.*/ +void StandardManipulator::init( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + flushMouseEventStack(); + + // stop animation + _thrown = false; + us.requestContinuousUpdate(false); +} + + +/** Handles events. Returns true if handled, false otherwise.*/ +bool StandardManipulator::handle( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + switch( ea.getEventType() ) { + + case GUIEventAdapter::FRAME: + return handleFrame( ea, us ); + + case GUIEventAdapter::RESIZE: + return handleResize( ea, us ); + + default: + break; + } + + if( ea.getHandled() ) + return false; + + switch( ea.getEventType() ) { + + case GUIEventAdapter::MOVE: + return handleMouseMove( ea, us ); + + case GUIEventAdapter::DRAG: + return handleMouseDrag( ea, us ); + + case GUIEventAdapter::PUSH: + return handleMousePush( ea, us ); + + case GUIEventAdapter::RELEASE: + return handleMouseRelease( ea, us ); + + case GUIEventAdapter::KEYDOWN: + return handleKeyDown( ea, us ); + + case GUIEventAdapter::KEYUP: + return handleKeyUp( ea, us ); + + case GUIEventAdapter::SCROLL: + if( _flags & PROCESS_MOUSE_WHEEL ) + return handleMouseWheel( ea, us ); + else + return false; + + default: + return false; + } +} + + +/// Handles GUIEventAdapter::FRAME event. +bool StandardManipulator::handleFrame( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + double current_frame_time = ea.getTime(); + + _delta_frame_time = current_frame_time - _last_frame_time; + _last_frame_time = current_frame_time; + + if( _thrown && performMovement() ) + { + us.requestRedraw(); + } + + if( _animationData && _animationData->_isAnimating ) + { + performAnimationMovement( ea, us ); + } + + return false; +} + +/// Handles GUIEventAdapter::RESIZE event. +bool StandardManipulator::handleResize( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + init( ea, us ); + us.requestRedraw(); + + return true; +} + + +/// Handles GUIEventAdapter::MOVE event. +bool StandardManipulator::handleMouseMove( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + return false; +} + + +/// Handles GUIEventAdapter::DRAG event. +bool StandardManipulator::handleMouseDrag( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + addMouseEvent( ea ); + + if( performMovement() ) + us.requestRedraw(); + + us.requestContinuousUpdate( false ); + _thrown = false; + + return true; +} + + +/// Handles GUIEventAdapter::PUSH event. +bool StandardManipulator::handleMousePush( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + flushMouseEventStack(); + addMouseEvent( ea ); + + if( performMovement() ) + us.requestRedraw(); + + us.requestContinuousUpdate( false ); + _thrown = false; + + return true; +} + + +/// Handles GUIEventAdapter::RELEASE event. +bool StandardManipulator::handleMouseRelease( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + if( ea.getButtonMask() == 0 ) { + + double timeSinceLastRecordEvent = _ga_t0.valid() ? (ea.getTime() - _ga_t0->getTime()) : DBL_MAX; + if( timeSinceLastRecordEvent > 0.02 ) + flushMouseEventStack(); + + if( isMouseMoving() ) { + + if( performMovement() ) { + + us.requestRedraw(); + us.requestContinuousUpdate( true ); + _thrown = true; + + } + + return true; + } + } + + flushMouseEventStack(); + addMouseEvent( ea ); + if( performMovement() ) + us.requestRedraw(); + us.requestContinuousUpdate( false ); + _thrown = false; + + return true; +} + + +/// Handles GUIEventAdapter::KEYDOWN event. +bool StandardManipulator::handleKeyDown( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + if( ea.getKey() == GUIEventAdapter::KEY_Space ) { + + flushMouseEventStack(); + _thrown = false; + home(ea,us); + return true; + } + + return false; +} + + +/// Handles GUIEventAdapter::KEYUP event. +bool StandardManipulator::handleKeyUp( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + return false; +} + + +/// Handles GUIEventAdapter::SCROLL event. +bool StandardManipulator::handleMouseWheel( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + return false; +} + + +/** Get the keyboard and mouse usage of the manipulator.*/ +void StandardManipulator::getUsage( ApplicationUsage& usage ) const +{ + usage.addKeyboardMouseBinding( getManipulatorName() + ": Space", "Reset the viewing position to home" ); +} + + +/// Make movement step of manipulator. Returns true if any movement was made. +bool StandardManipulator::performMovement() +{ + // return if less then two events have been added + if( _ga_t0.get() == NULL || _ga_t1.get() == NULL ) + return false; + + // get delta time + double dt = _ga_t0->getTime() - _ga_t1->getTime(); + if( dt < 0. ) { + notify( INFO ) << "Manipulator warning: dt = " << dt << std::endl; + dt = 0.; + } + + // get deltaX and deltaY + 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; + + + // call appropriate methods + unsigned int buttonMask = _ga_t1->getButtonMask(); + if( buttonMask == GUIEventAdapter::LEFT_MOUSE_BUTTON ) + + return performMovementLeftMouseButton( dt, dx, dy ); + + else if( buttonMask == GUIEventAdapter::MIDDLE_MOUSE_BUTTON || + buttonMask == (GUIEventAdapter::LEFT_MOUSE_BUTTON | GUIEventAdapter::RIGHT_MOUSE_BUTTON) ) + + return performMovementMiddleMouseButton( dt, dx, dy ); + + else if( buttonMask == GUIEventAdapter::RIGHT_MOUSE_BUTTON ) + + return performMovementRightMouseButton( dt, dx, dy ); + + + return false; +} + + +/** Make movement step of manipulator. + This method implements movement for left mouse button.*/ +bool StandardManipulator::performMovementLeftMouseButton( const double dt, const double dx, const double dy ) +{ + return false; +} + + +/** Make movement step of manipulator. + This method implements movement for middle mouse button + or combination of left and right mouse button pressed together.*/ +bool StandardManipulator::performMovementMiddleMouseButton( const double dt, const double dx, const double dy ) +{ + return false; +} + + +/** Make movement step of manipulator. + This method implements movement for right mouse button.*/ +bool StandardManipulator::performMovementRightMouseButton( const double dt, const double dx, const double dy ) +{ + return false; +} + + +bool StandardManipulator::handleMouseDeltaMovement( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + float dx = ea.getX() - _mouseCenterX; + float dy = ea.getY() - _mouseCenterY; + + if( dx == 0.f && dy == 0.f ) + return false; + + addMouseEvent( ea ); + centerMousePointer( ea, us ); + + return performMouseDeltaMovement( dx, dy ); +} + + +bool StandardManipulator::performMouseDeltaMovement( const float dx, const float dy ) +{ + return false; +} + + +bool StandardManipulator::performAnimationMovement( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + double f = (ea.getTime() - _animationData->_startTime) / _animationData->_animationTime; + if( f >= 1. ) { + f = 1.; + _animationData->_isAnimating = false; + if( !_thrown ) + us.requestContinuousUpdate( false ); + } + + applyAnimationStep( f, _animationData->_phase ); + + _animationData->_phase = f; + us.requestRedraw(); + + return _animationData->_isAnimating; +} + + +void StandardManipulator::applyAnimationStep( const double currentProgress, const double prevProgress ) +{ +} + + +void StandardManipulator::centerMousePointer( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + _mouseCenterX = (ea.getXmin() + ea.getXmax()) / 2.0f; + _mouseCenterY = (ea.getYmin() + ea.getYmax()) / 2.0f; + us.requestWarpPointer( _mouseCenterX, _mouseCenterY ); +} + + +/** Add the current mouse GUIEvent to internal stack.*/ +void StandardManipulator::addMouseEvent( const GUIEventAdapter& ea ) +{ + _ga_t1 = _ga_t0; + _ga_t0 = &ea; +} + + +/** Reset the internal GUIEvent stack.*/ +void StandardManipulator::flushMouseEventStack() +{ + _ga_t1 = NULL; + _ga_t0 = NULL; +} + + +/** Check the speed at which the mouse is moving. + If speed is below a threshold then return false, otherwise return true.*/ +bool StandardManipulator::isMouseMoving() const +{ + 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->getTime()-_ga_t1->getTime(); + + return (len>dt*velocity); +} + + +/** Update rotation by yaw and pitch. + * + * localUp parameter defines either camera's "UP" vector + * that will be preserved during rotation, or it can be zero (0,0,0) to specify + * that camera's "UP" vector will be not preserved and free rotation will be made.*/ +void StandardManipulator::rotateYawPitch( Quat& rotation, const double yaw, const double pitch, + const Vec3d& localUp ) +{ + bool verticalAxisFixed = (localUp != Vec3d( 0.,0.,0. )); + + // fix current rotation + if( verticalAxisFixed ) + fixVerticalAxis( rotation, localUp, true ); + + // rotations + Quat rotateYaw( -yaw, verticalAxisFixed ? localUp : rotation * Vec3d( 0.,1.,0. ) ); + Quat rotatePitch; + Quat newRotation; + Vec3d cameraRight( rotation * Vec3d( 1.,0.,0. ) ); + + double my_dy = pitch; + int i = 0; + + do { + + // rotations + rotatePitch.makeRotate( my_dy, cameraRight ); + newRotation = rotation * rotateYaw * rotatePitch; + + // update vertical axis + if( verticalAxisFixed ) + fixVerticalAxis( newRotation, localUp, false ); + + // check for viewer's up vector to be more than 90 degrees from "up" axis + Vec3d newCameraUp = newRotation * Vec3d( 0.,1.,0. ); + if( newCameraUp * localUp > 0. ) { + + // apply new rotation + rotation = newRotation; + return; + + } + + my_dy /= 2.; + if( ++i == 20 ) { + rotation = rotation * rotateYaw; + return; + } + + } while( true ); +} + + +void StandardManipulator::fixVerticalAxis( Vec3d& eye, Quat& rotation, bool disallowFlipOver ) +{ + CoordinateFrame coordinateFrame = getCoordinateFrame( eye ); + Vec3d localUp = getUpVector( coordinateFrame ); + + fixVerticalAxis( rotation, localUp, disallowFlipOver ); +} + + +/** The method corrects the rotation to make impression of fixed up direction. + * Technically said, it makes the roll component of the rotation equal to zero. + * + * Rotation parameter is the rotation to be fixed, localUp is UP vector, and + * disallowFlipOver when set on true avoids pitch rotation component to grow + * over +/- 90 degrees. If this happens and disallowFlipOver is true, + * right and up camera vectors are negated (changing roll by 180 degrees), + * making pitch once again between -90..+90 degrees limits.*/ +void StandardManipulator::fixVerticalAxis( Quat& rotation, const Vec3d& localUp, bool disallowFlipOver ) +{ + // camera direction vectors + Vec3d cameraUp = rotation * Vec3d( 0.,1.,0. ); + Vec3d cameraRight = rotation * Vec3d( 1.,0.,0. ); + Vec3d cameraForward = rotation * Vec3d( 0.,0.,-1. ); + + // computed directions + Vec3d newCameraRight1 = cameraForward ^ localUp; + Vec3d newCameraRight2 = cameraUp ^ localUp; + Vec3d newCameraRight = (newCameraRight1.length2() > newCameraRight2.length2()) ? + newCameraRight1 : newCameraRight2; + if( newCameraRight * cameraRight < 0. ) + newCameraRight = -newCameraRight; + + assert( newCameraRight.length2() > 0. ); + + // vertical axis correction + Quat rotationVerticalAxisCorrection; + rotationVerticalAxisCorrection.makeRotate( cameraRight, newCameraRight ); + + // rotate camera + rotation *= rotationVerticalAxisCorrection; + + if( disallowFlipOver ) { + + // make viewer's up vector to be always less than 90 degrees from "up" axis + Vec3d newCameraUp = newCameraRight ^ cameraForward; + if( newCameraUp * localUp < 0. ) + rotation = Quat( PI, Vec3d( 0.,0.,1. ) ) * rotation; + + } +} + + +/** The method corrects the rotation to make impression of fixed up direction. + * Technically said, it makes the roll component of the rotation equal to zero. + * + * forward and up parameter is the forward and up vector of the camera. + * newUp will receive corrected UP camera vector. localUp is UP vector + * that is used for vertical correction. + * disallowFlipOver when set on true avoids pitch rotation component to grow + * over +/- 90 degrees. If this happens and disallowFlipOver is true, + * right and up camera vectors are negated (changing roll by 180 degrees), + * making pitch once again between -90..+90 degrees limits.*/ +void StandardManipulator::fixVerticalAxis( const osg::Vec3d& forward, const osg::Vec3d& up, osg::Vec3d& newUp, + const osg::Vec3d& localUp, bool disallowFlipOver ) +{ + // right direction + osg::Vec3d right1 = forward ^ localUp; + osg::Vec3d right2 = up ^ localUp; + osg::Vec3d right = (right1.length2() > right2.length2()) ? right1 : right2; + + // newUp + // note: do not use up and other parameters from now as they may point + // to the same variable as newUp parameter + newUp = right ^ forward; + assert( newUp.length2() > 0. ); + newUp.normalize(); +} + +bool StandardManipulator::setCenterByMousePointerIntersection( const GUIEventAdapter& ea, GUIActionAdapter& us ) +{ + osg::View* view = us.asView(); + if( !view ) + return false; + + Camera *camera = view->getCamera(); + if( !camera ) + return false; + + // prepare variables + float x = ( ea.getX() - ea.getXmin() ) / ( ea.getXmax() - ea.getXmin() ); + float y = ( ea.getY() - ea.getYmin() ) / ( ea.getYmax() - ea.getYmin() ); + LineSegmentIntersector::CoordinateFrame cf; + Viewport *vp = camera->getViewport(); + if( vp ) { + cf = Intersector::WINDOW; + x *= vp->width(); + y *= vp->height(); + } else + cf = Intersector::PROJECTION; + + // perform intersection computation + ref_ptr< LineSegmentIntersector > picker = new LineSegmentIntersector( cf, x, y ); + IntersectionVisitor iv( picker.get() ); + camera->accept( iv ); + + // return on no intersections + if( !picker->containsIntersections() ) + return false; + + // get all intersections + LineSegmentIntersector::Intersections& intersections = picker->getIntersections(); + + // get current transformation + osg::Vec3d eye, oldCenter, up; + getTransformation( oldCenter, eye, up ); + + // new center + osg::Vec3d newCenter = (*intersections.begin()).getWorldIntersectPoint(); + + // make vertical axis correction + if( getVerticalAxisFixed() ) { + + CoordinateFrame coordinateFrame = getCoordinateFrame( newCenter ); + Vec3d localUp = getUpVector( coordinateFrame ); + + fixVerticalAxis( newCenter - eye, up, up, localUp, true ); + + } + + // set the new center + setTransformation( newCenter, eye, up ); + + + // warp pointer + // note: this works for me on standard camera on GraphicsWindowEmbedded and Qt, + // while it was necessary to implement requestWarpPointer like follows: + // + // void QOSGWidget::requestWarpPointer( float x, float y ) + // { + // osgViewer::Viewer::requestWarpPointer( x, y ); + // QCursor::setPos( this->mapToGlobal( QPoint( int( x+.5f ), int( y+.5f ) ) ) ); + // } + // + // Additions of .5f are just for the purpose of rounding. + centerMousePointer( ea, us ); + + return true; +} + + +/** Makes mouse pointer intersection test with the geometry bellow the pointer + and starts animation to center camera to look at the closest hit bellow the mouse pointer. + + If there is a hit, animation is started and true is returned. + Otherwise, the method returns false.*/ +bool StandardManipulator::startAnimationByMousePointerIntersection( + const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us ) +{ + return false; +} + + +StandardManipulator::AnimationData::AnimationData() + :_isAnimating( false ) +{ +} + + +void StandardManipulator::AnimationData::start( const double startTime ) +{ + _isAnimating = true; + _startTime = startTime; + _phase = 0.; +} diff --git a/src/osgGA/TerrainManipulator.cpp b/src/osgGA/TerrainManipulator.cpp index dc7bde477..c569a5f06 100644 --- a/src/osgGA/TerrainManipulator.cpp +++ b/src/osgGA/TerrainManipulator.cpp @@ -12,239 +12,63 @@ */ #include -#include -#include -#include #include +#include using namespace osg; using namespace osgGA; -TerrainManipulator::TerrainManipulator() -{ - _rotationMode =ELEVATION_HEADING; - _distance = 1.0; - - _thrown = false; - -} -TerrainManipulator::~TerrainManipulator() +/// Constructor. +TerrainManipulator::TerrainManipulator( int flags ) + : inherited( flags ) { } -void TerrainManipulator::setRotationMode(RotationMode mode) +/// Constructor. +TerrainManipulator::TerrainManipulator( const TerrainManipulator& tm, const CopyOp& copyOp ) + : inherited( tm, copyOp ), + _previousUp( tm._previousUp ) { - _rotationMode = mode; - - // need to correct rotation. -} - -void TerrainManipulator::setNode(osg::Node* node) -{ - _node = node; - - if (_node.get()) - { - const osg::BoundingSphere& boundingSphere=_node->getBound(); - const float minimumDistanceScale = 0.001f; - _minimumDistance = osg::clampBetween( - float(boundingSphere._radius) * minimumDistanceScale, - 0.00001f,1.0f); - - OSG_NOTIFY(osg::INFO)<<"Setting terrain manipulator _minimumDistance to "<<_minimumDistance< lsi = new osgUtil::LineSegmentIntersector(start,end); + inherited::setNode( node ); - osgUtil::IntersectionVisitor iv(lsi.get()); - iv.setTraversalMask(_intersectTraversalMask); - - _node->accept(iv); - - if (lsi->containsIntersections()) - { - intersection = lsi->getIntersections().begin()->getWorldIntersectPoint(); - return true; - } - return false; + // update model size + if( _flags & UPDATE_MODEL_SIZE ) + if( _node.get() ) { + setMinimumDistance( clampBetween( _modelSize * 0.001, 0.00001, 1.0 ) ); + notify( INFO ) << "TerrainManipulator: setting _minimumDistance to " + << _minimumDistance << std::endl; + } } -void TerrainManipulator::home(const GUIEventAdapter& ,GUIActionAdapter& us) -{ - if (getAutoComputeHomePosition()) computeHomePosition(); - - computePosition(_homeEye, _homeCenter, _homeUp); - us.requestRedraw(); -} - - -void TerrainManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& ) -{ - flushMouseEventStack(); -} - - -void TerrainManipulator::getUsage(osg::ApplicationUsage& usage) const -{ - usage.addKeyboardMouseBinding("Terrain: Space","Reset the viewing position to home"); - usage.addKeyboardMouseBinding("Terrain: +","When in stereo, increase the fusion distance"); - usage.addKeyboardMouseBinding("Terrain: -","When in stereo, reduce the fusion distance"); -} - -bool TerrainManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us) +void TerrainManipulator::setByMatrix(const Matrixd& matrix) { - switch(ea.getEventType()) - { - case(GUIEventAdapter::FRAME): - if (_thrown) - { - if (calcMovement()) us.requestRedraw(); - } - return false; - default: - break; - } + Vec3d lookVector(- matrix(2,0),-matrix(2,1),-matrix(2,2)); + Vec3d eye(matrix(3,0),matrix(3,1),matrix(3,2)); - - if (ea.getHandled()) return false; - - - 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()== GUIEventAdapter::KEY_Space) - { - flushMouseEventStack(); - _thrown = false; - home(ea,us); - us.requestRedraw(); - us.requestContinuousUpdate(false); - return true; - } - 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->getTime()-_ga_t1->getTime(); - - 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::Vec3d lookVector(- matrix(2,0),-matrix(2,1),-matrix(2,2)); - osg::Vec3d eye(matrix(3,0),matrix(3,1),matrix(3,2)); - - OSG_NOTIFY(INFO)<<"eye point "<getBound(); + const BoundingSphere& bs = _node->getBound(); float distance = (eye-bs.center()).length() + _node->getBound().radius(); - osg::Vec3d start_segment = eye; - osg::Vec3d end_segment = eye + lookVector*distance; - - osg::Vec3d ip; + Vec3d start_segment = eye; + Vec3d end_segment = eye + lookVector*distance; + + Vec3d ip; bool hitFound = false; if (intersect(start_segment, end_segment, ip)) { @@ -270,9 +94,9 @@ void TerrainManipulator::setByMatrix(const osg::Matrixd& matrix) _distance = (eye-ip).length(); - osg::Matrixd rotation_matrix = osg::Matrixd::translate(0.0,0.0,-_distance)* - matrix* - osg::Matrixd::translate(-_center); + Matrixd rotation_matrix = Matrixd::translate(0.0,0.0,-_distance)* + matrix* + Matrixd::translate(-_center); _rotation = rotation_matrix.getRotate(); @@ -304,26 +128,17 @@ void TerrainManipulator::setByMatrix(const osg::Matrixd& matrix) clampOrientation(); } -osg::Matrixd TerrainManipulator::getMatrix() const -{ - return osg::Matrixd::translate(0.0,0.0,_distance)*osg::Matrixd::rotate(_rotation)*osg::Matrixd::translate(_center); -} -osg::Matrixd TerrainManipulator::getInverseMatrix() const -{ - return osg::Matrixd::translate(-_center)*osg::Matrixd::rotate(_rotation.inverse())*osg::Matrixd::translate(0.0,0.0,-_distance); -} - -void TerrainManipulator::computePosition(const osg::Vec3d& eye,const osg::Vec3d& center,const osg::Vec3d& up) +void TerrainManipulator::setTransformation( const osg::Vec3d& center, const osg::Vec3d& eye, const osg::Vec3d& up ) { if (!_node) return; // compute rotation matrix - osg::Vec3d lv(center-eye); + Vec3d lv(center-eye); _distance = lv.length(); _center = center; - OSG_NOTIFY(osg::INFO) << "In compute"<< std::endl; + notify(INFO) << "In compute"<< std::endl; if (_node.valid()) { @@ -331,15 +146,15 @@ void TerrainManipulator::computePosition(const osg::Vec3d& eye,const osg::Vec3d& double distance = lv.length(); double maxDistance = distance+2*(eye-_node->getBound().center()).length(); - osg::Vec3d farPosition = eye+lv*(maxDistance/distance); - osg::Vec3d endPoint = center; + Vec3d farPosition = eye+lv*(maxDistance/distance); + Vec3d endPoint = center; for(int i=0; !hitFound && i<2; ++i, endPoint = farPosition) { // compute the intersection with the scene. - - osg::Vec3d ip; + + Vec3d ip; if (intersect(eye, endPoint, ip)) { _center = ip; @@ -353,7 +168,7 @@ void TerrainManipulator::computePosition(const osg::Vec3d& eye,const osg::Vec3d& // note LookAt = inv(CF)*inv(RM)*inv(T) which is equivalent to: // inv(R) = CF*LookAt. - osg::Matrixd rotation_matrix = osg::Matrixd::lookAt(eye,center,up); + Matrixd rotation_matrix = Matrixd::lookAt(eye,center,up); _rotation = rotation_matrix.getRotate().inverse(); @@ -364,216 +179,149 @@ void TerrainManipulator::computePosition(const osg::Vec3d& eye,const osg::Vec3d& } -bool TerrainManipulator::calcMovement() +bool TerrainManipulator::intersect( const Vec3d& start, const Vec3d& end, Vec3d& intersection ) const { - // return if less then two events have been added. - if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false; + ref_ptr lsi = new osgUtil::LineSegmentIntersector(start,end); - double dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized(); - double dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized(); + osgUtil::IntersectionVisitor iv(lsi.get()); + iv.setTraversalMask(_intersectTraversalMask); + _node->accept(iv); - // 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) + if (lsi->containsIntersections()) { - - if (_rotationMode==ELEVATION_HEADING_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.makeRotate(_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::Matrixd rotation_matrix; - rotation_matrix.makeRotate(_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 intersection point along the look vector. - - bool hitFound = false; - - if (_node.valid()) - { - - // now reorientate the coordinate frame to the frame coords. - CoordinateFrame coordinateFrame = getCoordinateFrame(_center); - - // need to reintersect with the terrain - double distance = _node->getBound().radius()*0.25f; - - osg::Vec3d ip1; - osg::Vec3d ip2; - bool hit_ip1 = intersect(_center, _center + getUpVector(coordinateFrame) * distance, ip1); - bool hit_ip2 = intersect(_center, _center - getUpVector(coordinateFrame) * distance, ip2); - if (hit_ip1) - { - if (hit_ip2) - { - _center = (_center-ip1).length2() < (_center-ip2).length2() ? - ip1 : - ip2; - - hitFound = true; - } - else - { - _center = ip1; - hitFound = true; - } - } - else if (hit_ip2) - { - _center = ip2; - hitFound = true; - } - - if (!hitFound) - { - // ?? - OSG_NOTIFY(INFO)<<"TerrainManipulator unable to intersect with terrain."<getIntersections().begin()->getWorldIntersectPoint(); return true; } - else if (buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON) - { - - // zoom model. - - double fd = _distance; - double scale = 1.0f+dy; - if (fd*scale>_minimumDistance) - { - - _distance *= scale; - - } else - { - _distance = _minimumDistance; - } - - return true; - - } - return false; } + +bool TerrainManipulator::performMovementMiddleMouseButton( const double dt, const double dx, const double dy ) +{ + // pan model. + double scale = -0.3f*_distance; + + Matrixd rotation_matrix; + rotation_matrix.makeRotate(_rotation); + + + // compute look vector. + Vec3d lookVector = -getUpVector(rotation_matrix); + Vec3d sideVector = getSideVector(rotation_matrix); + Vec3d upVector = getFrontVector(rotation_matrix); + + // CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + // Vec3d localUp = getUpVector(coordinateFrame); + Vec3d localUp = _previousUp; + + Vec3d forwardVector =localUp^sideVector; + sideVector = forwardVector^localUp; + + forwardVector.normalize(); + sideVector.normalize(); + + Vec3d dv = forwardVector * (dy*scale) + sideVector * (dx*scale); + + _center += dv; + + // need to recompute the intersection point along the look vector. + + bool hitFound = false; + + if (_node.valid()) + { + + // now reorientate the coordinate frame to the frame coords. + CoordinateFrame coordinateFrame = getCoordinateFrame(_center); + + // need to reintersect with the terrain + double distance = _node->getBound().radius()*0.25f; + + Vec3d ip1; + Vec3d ip2; + bool hit_ip1 = intersect(_center, _center + getUpVector(coordinateFrame) * distance, ip1); + bool hit_ip2 = intersect(_center, _center - getUpVector(coordinateFrame) * distance, ip2); + if (hit_ip1) + { + if (hit_ip2) + { + _center = (_center-ip1).length2() < (_center-ip2).length2() ? + ip1 : + ip2; + + hitFound = true; + } + else + { + _center = ip1; + hitFound = true; + } + } + else if (hit_ip2) + { + _center = ip2; + hitFound = true; + } + + if (!hitFound) + { + // ?? + notify(INFO)<<"TerrainManipulator unable to intersect with terrain."< 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 TerrainManipulator::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; -} diff --git a/src/osgGA/TrackballManipulator.cpp b/src/osgGA/TrackballManipulator.cpp index da31ba17b..37e6fcdb1 100644 --- a/src/osgGA/TrackballManipulator.cpp +++ b/src/osgGA/TrackballManipulator.cpp @@ -1,488 +1,20 @@ #include -#include -#include -#include using namespace osg; using namespace osgGA; -TrackballManipulator::TrackballManipulator() -{ - _modelScale = 0.01f; - _minimumZoomScale = 0.05f; - _allowThrow = true; - _thrown = false; - _distance = 1.0f; - _trackballSize = 0.8f; - _zoomDelta = 0.1f; + +/// Constructor. +TrackballManipulator::TrackballManipulator( int flags ) + : inherited( flags ) +{ + setVerticalAxisFixed( false ); } -TrackballManipulator::~TrackballManipulator() +/// Constructor. +TrackballManipulator::TrackballManipulator( const TrackballManipulator& tm, const CopyOp& copyOp ) + : inherited( tm, copyOp ) { } - - -void TrackballManipulator::setNode(osg::Node* node) -{ - _node = node; - if (_node.get()) - { - const osg::BoundingSphere& boundingSphere=_node->getBound(); - _modelScale = boundingSphere._radius; - } - if (getAutoComputeHomePosition()) computeHomePosition(); -} - - -const osg::Node* TrackballManipulator::getNode() const -{ - return _node.get(); -} - - -osg::Node* TrackballManipulator::getNode() -{ - return _node.get(); -} - - -void TrackballManipulator::home(double /*currentTime*/) -{ - if (getAutoComputeHomePosition()) computeHomePosition(); - computePosition(_homeEye, _homeCenter, _homeUp); - _thrown = false; -} - -void TrackballManipulator::home(const GUIEventAdapter& ea ,GUIActionAdapter& us) -{ - home(ea.getTime()); - us.requestRedraw(); - us.requestContinuousUpdate(false); -} - - -void TrackballManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& ) -{ - flushMouseEventStack(); -} - - -void TrackballManipulator::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, reduce the fusion distance"); -} - -bool TrackballManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us) -{ - switch(ea.getEventType()) - { - case(GUIEventAdapter::FRAME): - { - double current_frame_time = ea.getTime(); - - _delta_frame_time = current_frame_time - _last_frame_time; - _last_frame_time = current_frame_time; - - if (_thrown && _allowThrow) - { - if (calcMovement()) us.requestRedraw(); - } - return false; - } - default: - break; - } - - if (ea.getHandled()) return false; - - 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) - { - - double timeSinceLastRecordEvent = _ga_t0.valid() ? (ea.getTime() - _ga_t0->getTime()) : DBL_MAX; - if (timeSinceLastRecordEvent>0.02) flushMouseEventStack(); - - if (isMouseMoving()) - { - if (calcMovement()) - { - us.requestRedraw(); - us.requestContinuousUpdate(true); - _thrown = _allowThrow; - } - } - 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): - case(GUIEventAdapter::SCROLL): - { - addMouseEvent(ea); - if (calcMovement()) us.requestRedraw(); - us.requestContinuousUpdate(false); - _thrown = false; - return true; - } - - case(GUIEventAdapter::MOVE): - { - return false; - } - - case(GUIEventAdapter::KEYDOWN): - if (ea.getKey()== GUIEventAdapter::KEY_Space) - { - flushMouseEventStack(); - _thrown = false; - home(ea,us); - return true; - } - return false; - case(GUIEventAdapter::FRAME): - if (_thrown) - { - if (calcMovement()) us.requestRedraw(); - } - return false; - default: - return false; - } -} - - -bool TrackballManipulator::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->getTime()-_ga_t1->getTime(); - - return (len>dt*velocity); -} - - -void TrackballManipulator::flushMouseEventStack() -{ - _ga_t1 = NULL; - _ga_t0 = NULL; -} - - -void TrackballManipulator::addMouseEvent(const GUIEventAdapter& ea) -{ - _ga_t1 = _ga_t0; - _ga_t0 = &ea; -} - -void TrackballManipulator::setByMatrix(const osg::Matrixd& matrix) -{ - _center = osg::Vec3(0.0f,0.0f,-_distance)*matrix; - _rotation = matrix.getRotate(); -} - -osg::Matrixd TrackballManipulator::getMatrix() const -{ - return osg::Matrixd::translate(0.0,0.0,_distance)*osg::Matrixd::rotate(_rotation)*osg::Matrixd::translate(_center); -} - -osg::Matrixd TrackballManipulator::getInverseMatrix() const -{ - return osg::Matrixd::translate(-_center)*osg::Matrixd::rotate(_rotation.inverse())*osg::Matrixd::translate(0.0,0.0,-_distance); -} - -void TrackballManipulator::computePosition(const osg::Vec3& eye,const osg::Vec3& center,const osg::Vec3& up) -{ - - osg::Vec3 lv(center-eye); - - 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); - - _center = center; - _distance = lv.length(); - _rotation = rotation_matrix.getRotate().inverse(); -} - - -bool TrackballManipulator::calcMovement() -{ - // mouse scroll is only a single event - if (_ga_t0.get()==NULL) return false; - - float dx=0.0f; - float dy=0.0f; - unsigned int buttonMask=osgGA::GUIEventAdapter::NONE; - - if (_ga_t0->getEventType()==GUIEventAdapter::SCROLL) - { - switch (_ga_t0->getScrollingMotion()) { - case osgGA::GUIEventAdapter::SCROLL_UP: - dy = _zoomDelta; - break; - case osgGA::GUIEventAdapter::SCROLL_DOWN: - dy = -_zoomDelta; - break; - case osgGA::GUIEventAdapter::SCROLL_LEFT: - case osgGA::GUIEventAdapter::SCROLL_RIGHT: - // pass - break; - case osgGA::GUIEventAdapter::SCROLL_2D: - // normalize scrolling delta - dx = _ga_t0->getScrollingDeltaX() / ((_ga_t0->getXmax()-_ga_t0->getXmin()) * 0.5f); - dy = _ga_t0->getScrollingDeltaY() / ((_ga_t0->getYmax()-_ga_t0->getYmin()) * 0.5f); - - dx *= _zoomDelta; - dy *= _zoomDelta; - break; - default: - break; - } - buttonMask=GUIEventAdapter::SCROLL; - } - else - { - - if (_ga_t1.get()==NULL) return false; - dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized(); - dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized(); - float distance = sqrtf(dx*dx + dy*dy); - - // return if movement is too fast, indicating an error in event values or change in screen. - if (distance>0.5) - { - return false; - } - - // return if there is no movement. - if (distance==0.0f) - { - return false; - } - - buttonMask = _ga_t1->getButtonMask(); - } - - - double throwScale = (_thrown && _ga_t0.valid() && _ga_t1.valid()) ? - _delta_frame_time / (_ga_t0->getTime() - _ga_t1->getTime()) : - 1.0; - - 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 * throwScale,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.3f * _distance * throwScale; - - osg::Matrix rotation_matrix; - rotation_matrix.makeRotate(_rotation); - - osg::Vec3 dv(dx*scale,dy*scale,0.0f); - - _center += dv*rotation_matrix; - - return true; - - } - else if ((buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON) || (buttonMask==GUIEventAdapter::SCROLL)) - { - - // zoom model. - - float fd = _distance; - float scale = 1.0f+ dy * throwScale; - if (fd*scale>_modelScale*_minimumZoomScale) - { - - _distance *= scale; - - } - else - { - - // notify(DEBUG_INFO) << "Pushing forward"< 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 TrackballManipulator::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/osgGA/UFOManipulator.cpp b/src/osgGA/UFOManipulator.cpp index 56cddeba6..89ceda58d 100644 --- a/src/osgGA/UFOManipulator.cpp +++ b/src/osgGA/UFOManipulator.cpp @@ -112,7 +112,7 @@ void UFOManipulator::setByMatrix( const osg::Matrixd &mat ) _position.set( _inverseMatrix(3,0), _inverseMatrix(3,1), _inverseMatrix(3,2 )); osg::Matrix R(_inverseMatrix); R(3,0) = R(3,1) = R(3,2) = 0.0; - _direction = osg::Vec3(0,0,-1) * R; // camera up is +Z, regardless of CoordinateFrame + _direction = osg::Vec3d(0,0,-1) * R; // camera up is +Z, regardless of CoordinateFrame _stop(); } @@ -125,7 +125,7 @@ void UFOManipulator::setByInverseMatrix( const osg::Matrixd &invmat) _position.set( _inverseMatrix(3,0), _inverseMatrix(3,1), _inverseMatrix(3,2 )); osg::Matrix R(_inverseMatrix); R(3,0) = R(3,1) = R(3,2) = 0.0; - _direction = osg::Vec3(0,0,-1) * R; // camera up is +Z, regardless of CoordinateFrame + _direction = osg::Vec3d(0,0,-1) * R; // camera up is +Z, regardless of CoordinateFrame _stop(); } @@ -155,8 +155,8 @@ void UFOManipulator::computeHomePosition() osg::CoordinateFrame cf( getCoordinateFrame(bs.center()) ); // not sure what position to use here osg::Vec3d upVec( getUpVector(cf) ); - osg::Vec3 A = bs.center() + (upVec*(bs.radius()*2)); - osg::Vec3 B = bs.center() + (-upVec*(bs.radius()*2)); + osg::Vec3d A = bs.center() + (upVec*(bs.radius()*2)); + osg::Vec3d B = bs.center() + (-upVec*(bs.radius()*2)); if( (B-A).length() == 0.0) { @@ -180,7 +180,7 @@ void UFOManipulator::computeHomePosition() } - osg::Vec3 p(bs.center() + upVec*( ground + _minHeightAboveGround*1.25 ) ); + osg::Vec3d p(bs.center() + upVec*( ground + _minHeightAboveGround*1.25 ) ); setHomePosition( p, p + getFrontVector(cf), upVec ); } @@ -462,7 +462,7 @@ void UFOManipulator::_frame( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionA } { - osg::Vec3 _sideVec = _direction * osg::Matrix::rotate( -M_PI*0.5, upVec); + osg::Vec3d _sideVec = _direction * osg::Matrix::rotate( -M_PI*0.5, upVec); _position += ((_direction * _forwardSpeed) + (_sideVec * _sideSpeed) + @@ -577,7 +577,7 @@ void UFOManipulator::_stop() _directionRotationRate = 0.0; } -void UFOManipulator::getCurrentPositionAsLookAt( osg::Vec3 &eye, osg::Vec3 ¢er, osg::Vec3 &up ) +void UFOManipulator::getCurrentPositionAsLookAt( osg::Vec3d& eye, osg::Vec3d& center, osg::Vec3d& up ) { eye = _position; center = _position + _direction;