Added UFO manipulator

This commit is contained in:
Don BURNS 2005-03-11 06:09:16 +00:00
parent 537fa6c604
commit a7ac8f5992
3 changed files with 677 additions and 0 deletions

View File

@ -0,0 +1,165 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef OSGGA_UFO_MANIPULATOR_DEF
#define OSGGA_UFO_MANIPULATOR_DEF 1
#include <iostream>
#include <Producer/Keyboard>
#include <osgGA/MatrixManipulator>
#include <osg/Node>
#include <osg/Matrix>
/**
\class UFOManipulator
\brief A UFO manipulator driven with keybindings.
The UFOManipulator is better suited for applications that employ
architectural walk-throughs, or situations where the eyepoint motion
model must move slowly, deliberately and well controlled.
The UFO Manipulator allows the following movements with the listed
Key combinations:
\param UpArrow Acceleration forward.
\param DownArrow Acceleration backward (or deceleration forward).
\param LeftArrow Rotate view and direction of travel to the left.
\param RightArrow Rotate view and direction of travel to the right.
\param SpaceBar Brake. Gradually decelerates linear and rotational movement.
\param Shift/UpArrow Accelerate up.
\param Shift/DownArrow Accelerate down.
\param Shift/LeftArrow Accelerate (linearly) left.
\param Shift/RightArrow Accelerate (linearly) right.
\param Shift/SpaceBar Instant brake. Immediately stop all linear and rotational movement.
When the Shift key is released, up, down, linear left and/or linear right movement is decelerated.
\param Ctrl/UpArrow Rotate view (but not direction of travel) up.
\param Ctrl/DownArrow Rotate view (but not direction of travel) down.
\param Ctrl/LeftArrow Rotate view (but not direction of travel) left.
\param Ctrl/RightArrow Rotate view (but not direction of travel) right.
\param Ctrl/Return Straightens out the view offset.
*/
namespace osgGA {
class OSGGA_EXPORT UFOManipulator : public osgGA::MatrixManipulator
{
public:
/** Default constructor */
UFOManipulator();
/** return className
\return returns constant "UFOManipulator"
*/
virtual const char* className() const;
/** Set the current position with a matrix
\param matrix A viewpoint matrix.
*/
virtual void setByMatrix( const osg::Matrix &matrix ) ;
/** Set the current position with the invers matrix
\param invmatrix The inverse of a viewpoint matrix
*/
virtual void setByInverseMatrix( const osg::Matrix &invmat);
/** Get the current viewmatrix */
virtual osg::Matrix getMatrix() const;
/** Get the current inverse view matrix */
virtual osg::Matrix getInverseMatrix() const ;
/** Set the subgraph this manipulator is driving the eye through.
\param node root of subgraph
*/
virtual void setNode(osg::Node*);
/** Get the root node of the subgraph this manipulator is driving the eye through (const)*/
virtual const osg::Node* getNode() const;
/** Get the root node of the subgraph this manipulator is driving the eye through */
virtual osg::Node* getNode();
/** Computes the home position based on the extents and scale of the
scene graph rooted at node */
virtual void computeHomePosition();
/** Sets the viewpoint matrix to the home position */
virtual void home(const osgGA::GUIEventAdapter&, osgGA::GUIActionAdapter&) ;
/** Handles incoming osgGA events */
bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter &aa);
/** Reports Usage parameters to the application */
void getUsage(osg::ApplicationUsage& usage) const;
protected:
private:
osg::ref_ptr<osg::Node> _node;
float _viewAngle;
osg::Matrix _matrix;
osg::Matrix _inverseMatrix;
osg::Matrix _offset;
double _minHeightAboveGround;
double _minDistanceInFront;
double _speedEpsilon;
double _forwardSpeed;
double _sideSpeed;
double _upSpeed;
double _speedAccelerationFactor;
double _speedDecelerationFactor;
bool _decelerateUpSideRate;
double _directionRotationEpsilon;
double _directionRotationRate;
double _directionRotationAcceleration;
double _directionRotationDeceleration;
double _viewOffsetDelta;
double _pitchOffsetRate;
double _pitchOffset;
double _yawOffsetRate;
double _yawOffset;
double _t0;
double _dt;
osg::Vec3 _direction;
osg::Vec3 _position;
bool _shift;
bool _ctrl;
bool _decelerateOffsetRate;
bool _straightenOffset;
void _home();
void _stop();
void _keyDown( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &);
void _keyUp( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter &);
void _frame(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter &);
void _adjustPosition();
};
}
#endif

View File

@ -8,6 +8,7 @@ CXXFILES = \
DriveManipulator.cpp\
EventVisitor.cpp\
FlightManipulator.cpp\
UFOManipulator.cpp\
GUIEventAdapter.cpp\
GUIEventHandler.cpp\
GUIEventHandlerVisitor.cpp\

View File

@ -0,0 +1,511 @@
#include <osgGA/UFOManipulator>
#include <osgUtil/IntersectVisitor>
using namespace osgGA;
UFOManipulator::UFOManipulator():
_t0(0.0),
_shift(false),
_ctrl(false)
{
_minHeightAboveGround = 2.0;
_minDistanceInFront = 5.0;
_speedAccelerationFactor = 0.4;
_speedDecelerationFactor = 0.90;
_directionRotationRate = 0.0;
_directionRotationAcceleration = M_PI*0.00005;
_directionRotationDeceleration = 0.90;
_speedEpsilon = 0.02;
_directionRotationEpsilon = 0.0001;
_viewOffsetDelta = M_PI * 0.0025;
_pitchOffsetRate = 0.0;
_pitchOffset = 0.0;
_yawOffsetRate = 0.0;
_yawOffset = 0.0;
_offset.makeIdentity();
_decelerateOffsetRate = true;
_straightenOffset = false;
_direction.set( 0,1,0);
_stop();
}
void UFOManipulator::setNode( osg::Node *node )
{
_node = node;
if (getAutoComputeHomePosition())
computeHomePosition();
_home();
}
const osg::Node* UFOManipulator::getNode() const
{
return _node.get();
}
osg::Node* UFOManipulator::getNode()
{
return _node.get();
}
const char* UFOManipulator::className() const
{
return "UFOManipulator";
}
void UFOManipulator::setByMatrix( const osg::Matrix &mat )
{
_inverseMatrix = mat;
_matrix.invert( _inverseMatrix );
}
void UFOManipulator::setByInverseMatrix( const osg::Matrix &invmat)
{
_matrix = invmat;
_inverseMatrix.invert( _matrix );
}
osg::Matrix UFOManipulator::getMatrix() const
{
return (_offset * _matrix);
}
osg::Matrix UFOManipulator::getInverseMatrix() const
{
return (_inverseMatrix * _offset);
}
void UFOManipulator::computeHomePosition()
{
if( !_node.valid() )
return;
osg::BoundingSphere bs = _node->getBound();
/*
* Find the ground - Assumption: The ground is the hit of an intersection
* from a line segment extending from above to below the database at its
* horizontal center, that intersects the database closest to zero. */
osgUtil::IntersectVisitor iv;
osg::ref_ptr<osg::LineSegment> seg = new osg::LineSegment;
osg::Vec3 A = bs.center() + (osg::Vec3(0,0,1)*(bs.radius()*2));
osg::Vec3 B = bs.center() + (osg::Vec3(0,0,-1)*(bs.radius()*2));
if( (B-A).length() == 0.0)
{
puts( "DOH" ); fflush(stdout);
return;
}
/*
seg->set( bs.center() + (osg::Vec3(0,0,1)*(bs.radius()*2)),
bs.center() + (osg::Vec3(0,0,-1)*(bs.radius()*2)) );
*/
seg->set( A, B );
iv.addLineSegment( seg.get() );
_node->accept(iv);
// start with it high
double ground = bs.radius() * 3;
if (iv.hits())
{
osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(seg.get());
for(osgUtil::IntersectVisitor::HitList::iterator hitr=hitList.begin();
hitr!=hitList.end(); ++hitr)
{
osg::Vec3d ip = hitList.front().getWorldIntersectPoint();
if( fabs(ip[3]) < ground )
ground = ip[3];
}
}
else
{
osg::notify(osg::WARN)<<"UFOManipulator : I can't find the ground!"<<std::endl;
ground = 0.0;
}
osg::Vec3 p(bs.center()[0], bs.center()[1], ground + (_minHeightAboveGround*1.25) );
setHomePosition( p, p + osg::Vec3(0,1,0), osg::Vec3(0,0,1) );
}
void UFOManipulator::home(const osgGA::GUIEventAdapter&, osgGA::GUIActionAdapter&)
{
_home();
}
void UFOManipulator::_home()
{
if (getAutoComputeHomePosition())
computeHomePosition();
_position = _homeEye;
_direction = _homeCenter - _homeEye;
_direction.normalize();
_directionRotationRate = 0.0;
_inverseMatrix.makeLookAt( _homeEye, _homeCenter, _homeUp );
_matrix.invert( _matrix );
_offset.makeIdentity();
_forwardSpeed = 0.0;
_sideSpeed = 0.0;
_upSpeed = 0.0;
}
bool UFOManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter &aa)
{
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::KEYUP):
_keyUp( ea, aa );
return false;
break;
case(osgGA::GUIEventAdapter::KEYDOWN):
_keyDown(ea, aa);
return false;
break;
case(osgGA::GUIEventAdapter::FRAME):
_frame(ea,aa);
return false;
break;
default:
return false;
}
return false;
}
void UFOManipulator::getUsage(osg::ApplicationUsage& usage) const
{
usage.addKeyboardMouseBinding("UFO Manipulator: <SpaceBar>", "Reset the viewing angle to 0.0");
usage.addKeyboardMouseBinding("UFO Manipulator: <UpArrow>", "Acceleration forward.");
usage.addKeyboardMouseBinding("UFO Manipulator: <DownArrow>", "Acceleration backward (or deceleration forward");
usage.addKeyboardMouseBinding("UFO Manipulator: <LeftArrow>", "Rotate view and direction of travel to the left.");
usage.addKeyboardMouseBinding("UFO Manipulator: <RightArrow>", "Rotate view and direction of travel to the right.");
usage.addKeyboardMouseBinding("UFO Manipulator: <SpaceBar>", "Brake. Gradually decelerates linear and rotational movement.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/UpArrow>", "Accelerate up.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/DownArrow>", "Accelerate down.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/LeftArrow>", "Accelerate (linearly) left.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/RightArrow>","Accelerate (linearly) right.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/SpaceBar>", "Instant brake. Immediately stop all linear and rotational movement.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/UpArrow>", "Rotate view (but not direction of travel) up.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/DownArrow>", "Rotate view (but not direction of travel) down.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/LeftArrow>", "Rotate view (but not direction of travel) left.");
usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/RightArrow>", "Rotate view (but not direction of travel) right.");
}
void UFOManipulator::_keyUp( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter & )
{
switch( ea.getKey() )
{
case Producer::KeyChar_Control_L:
case Producer::KeyChar_Control_R:
_ctrl = false;
_decelerateOffsetRate = true;
_straightenOffset = false;
break;
case Producer::KeyChar_Shift_L:
case Producer::KeyChar_Shift_R:
_shift = false;
_decelerateUpSideRate = true;
break;
}
}
void UFOManipulator::_keyDown( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter & )
{
switch( ea.getKey() )
{
case Producer::KeyChar_Control_L:
case Producer::KeyChar_Control_R:
_ctrl = true;
break;
case Producer::KeyChar_Shift_L :
case Producer::KeyChar_Shift_R :
_shift = true;
break;
case Producer::KeyChar_Up:
if( _ctrl )
{
_pitchOffsetRate -= _viewOffsetDelta;
_decelerateOffsetRate = false;
}
else
{
if( _shift )
{
_upSpeed += _speedAccelerationFactor;
_decelerateUpSideRate = false;
}
else
_forwardSpeed += _speedAccelerationFactor;
}
break;
case Producer::KeyChar_Down:
if( _ctrl )
{
_pitchOffsetRate += _viewOffsetDelta;
_decelerateOffsetRate = false;
}
else
{
if( _shift )
{
_upSpeed -= _speedAccelerationFactor;
_decelerateUpSideRate = false;
}
else
_forwardSpeed -= _speedAccelerationFactor;
}
break;
case Producer::KeyChar_Right:
if( _ctrl )
{
_yawOffsetRate += _viewOffsetDelta;
_decelerateOffsetRate = false;
}
else
{
if(_shift)
{
_sideSpeed += _speedAccelerationFactor;
_decelerateUpSideRate = false;
}
else
_directionRotationRate -= _directionRotationAcceleration;
}
break;
case Producer::KeyChar_Left:
if( _ctrl )
{
_yawOffsetRate -= _viewOffsetDelta;
_decelerateOffsetRate = false;
}
else
{
if(_shift)
{
_sideSpeed -= _speedAccelerationFactor;
_decelerateUpSideRate = false;
}
else
_directionRotationRate += _directionRotationAcceleration;
}
break;
case Producer::KeyChar_Return:
if( _ctrl )
{
_straightenOffset = true;
}
break;
case ' ':
if( _shift )
{
_stop();
}
else
{
if( fabs(_forwardSpeed) > 0.0 )
{
_forwardSpeed *= _speedDecelerationFactor;
if( fabs(_forwardSpeed ) < _speedEpsilon )
_forwardSpeed = 0.0;
}
if( fabs(_sideSpeed) > 0.0 )
{
_sideSpeed *= _speedDecelerationFactor;
if( fabs( _sideSpeed ) < _speedEpsilon )
_sideSpeed = 0.0;
}
if( fabs(_upSpeed) > 0.0 )
{
_upSpeed *= _speedDecelerationFactor;
if( fabs( _upSpeed ) < _speedEpsilon )
_sideSpeed = 0.0;
}
if( fabs(_directionRotationRate ) > 0.0 )
{
_directionRotationRate *= _directionRotationDeceleration;
if( fabs( _directionRotationRate ) < _directionRotationEpsilon )
_directionRotationRate = 0.0;
}
}
break;
case 'H':
_home();
break;
}
}
void UFOManipulator::_frame( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter & )
{
double t1 = ea.time();
if( _t0 == 0.0 )
{
_t0 = ea.time();
_dt = 0.0;
}
else
{
_dt = t1 - _t0;
_t0 = t1;
}
if( fabs( _directionRotationRate ) > _directionRotationEpsilon )
{
_direction = _direction * osg::Matrix::rotate( _directionRotationRate, osg::Vec3(0,0,1));
}
{
osg::Vec3 _sideVec = _direction * osg::Matrix::rotate( -M_PI*0.5, osg::Vec3(0,0,1));
_position += ((_direction * _forwardSpeed) +
(_sideVec * _sideSpeed) +
(osg::Vec3(0,0,1) * _upSpeed))
* _dt;
}
_pitchOffset += _pitchOffsetRate * _dt;
if( _pitchOffset >= M_PI || _pitchOffset < -M_PI )
_pitchOffset *= -1;
_yawOffset += _yawOffsetRate * _dt;
if( _yawOffset >= M_PI || _yawOffset < -M_PI )
_yawOffset *= -1;
_offset = osg::Matrix::rotate( _yawOffset, osg::Vec3(0,1,0),
_pitchOffset, osg::Vec3(1,0,0),
0.0, osg::Vec3(0,0,1));
_adjustPosition();
_inverseMatrix.makeLookAt( _position, _position + _direction, osg::Vec3(0,0,1));
_matrix.invert(_inverseMatrix);
if( _decelerateUpSideRate )
{
_upSpeed *= 0.98;
_sideSpeed *= 0.98;
}
if( _decelerateOffsetRate )
{
_yawOffsetRate *= 0.98;
_pitchOffsetRate *= 0.98;
}
if( _straightenOffset )
{
_pitchOffsetRate = 0.0;
_yawOffsetRate = 0.0;
_pitchOffset *= 0.99;
_yawOffset *= 0.99;
if( fabs(_pitchOffset ) < 0.01 )
_pitchOffset = 0.0;
if( fabs(_yawOffset ) < 0.01 )
_pitchOffset = 0.0;
if( _pitchOffset == 0.0 && _yawOffset == 0.0 )
_straightenOffset = false;
}
}
void UFOManipulator::_adjustPosition()
{
if( !_node.valid() )
return;
osgUtil::IntersectVisitor iv;
// Forward line segment at 3 times our intersect distance
osg::ref_ptr<osg::LineSegment> segForward = new osg::LineSegment;
segForward->set(_position, _position + (_direction * (_minDistanceInFront * 3.0)) );
iv.addLineSegment( segForward.get() );
// Down line segment at 3 times our intersect distance
osg::ref_ptr<osg::LineSegment> segDown = new osg::LineSegment;
segDown->set( _position,
_position - (osg::Vec3(0,0, _minHeightAboveGround*3)));
iv.addLineSegment( segDown.get() );
_node->accept(iv);
if (iv.hits())
{
// Check intersects infront.
{
osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segForward.get());
if (!hitList.empty())
{
osg::Vec3d ip = hitList.front().getWorldIntersectPoint();
double d = (ip - _position).length();
if( d < _minDistanceInFront )
{
osg::Vec3 op = _position;
_position = ip + (_direction * -_minDistanceInFront);
_stop();
}
}
}
// Check intersects below.
{
osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segDown.get());
if (!hitList.empty())
{
osg::Vec3d ip = hitList.front().getWorldIntersectPoint();
if( _position[2] - ip[2] < _minHeightAboveGround )
_position[2] = ip[2] + _minHeightAboveGround;
}
}
}
}
void UFOManipulator::_stop()
{
_forwardSpeed = 0.0;
_sideSpeed = 0.0;
_upSpeed = 0.0;
_directionRotationRate = 0.0;
}