407 lines
11 KiB
C++
407 lines
11 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 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 taken from OSG.
|
|
* Thanks to company Cadwork (www.cadwork.ch) and
|
|
* Brno University of Technology (www.fit.vutbr.cz) for open-sourcing this work.
|
|
*/
|
|
|
|
#include <osgGA/FirstPersonManipulator>
|
|
#include <cassert>
|
|
|
|
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( 1.0, true );
|
|
setMaxVelocity( 0.25, true );
|
|
setWheelMovement( 0.05, true );
|
|
if( _flags & SET_CENTER_ON_WHEEL_FORWARD_MOVEMENT )
|
|
setAnimationTime( 0.2 );
|
|
}
|
|
|
|
|
|
/// Constructor.
|
|
FirstPersonManipulator::FirstPersonManipulator( const FirstPersonManipulator& fpm, const CopyOp& copyOp )
|
|
: osg::Object(fpm, copyOp),
|
|
osg::Callback(fpm, 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& eye, const osg::Vec3d& center, 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& eye, osg::Vec3d& center, 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 )
|
|
{
|
|
osgGA::GUIEventAdapter::ScrollingMotion sm = ea.getScrollingMotion();
|
|
|
|
// handle centering
|
|
if( _flags & SET_CENTER_ON_WHEEL_FORWARD_MOVEMENT )
|
|
{
|
|
|
|
if( ((sm == GUIEventAdapter::SCROLL_DOWN) && (_wheelMovement > 0.)) ||
|
|
((sm == GUIEventAdapter::SCROLL_UP) && (_wheelMovement < 0.)) )
|
|
{
|
|
|
|
// 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 );
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
FirstPersonAnimationData *ad = dynamic_cast< FirstPersonAnimationData*>( _animationData.get() );
|
|
if (!ad) return false;
|
|
|
|
switch( sm )
|
|
{
|
|
|
|
// mouse scroll up event
|
|
case GUIEventAdapter::SCROLL_UP:
|
|
{
|
|
// move forward
|
|
moveForward( isAnimating() ? ad->_targetRot : _rotation,
|
|
-_wheelMovement * (getRelativeFlag( _wheelMovementFlagIndex ) ? _modelSize : 1. ));
|
|
us.requestRedraw();
|
|
us.requestContinuousUpdate( isAnimating() || _thrown );
|
|
return true;
|
|
}
|
|
|
|
// mouse scroll down event
|
|
case GUIEventAdapter::SCROLL_DOWN:
|
|
{
|
|
// move backward
|
|
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 /*eventTimeDelta*/, 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 )
|
|
{
|
|
_eye += _rotation * Vec3d( distance, 0., 0. );
|
|
}
|
|
|
|
|
|
/// Move camera up by distance parameter.
|
|
void FirstPersonManipulator::moveUp( const double distance )
|
|
{
|
|
_eye += _rotation * Vec3d( 0., distance, 0. );
|
|
}
|
|
|
|
|
|
void FirstPersonManipulator::applyAnimationStep( const double currentProgress, const double /*prevProgress*/ )
|
|
{
|
|
FirstPersonAnimationData *ad = dynamic_cast< FirstPersonAnimationData* >( _animationData.get() );
|
|
if (!ad) return;
|
|
|
|
// 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() );
|
|
if (!ad) return false;
|
|
|
|
// 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;
|
|
}
|