2004-05-06 19:01:16 +08:00
# include <osgGA/TerrainManipulator>
# include <osg/Quat>
# include <osg/Notify>
# include <osgUtil/IntersectVisitor>
using namespace osg ;
using namespace osgGA ;
TerrainManipulator : : TerrainManipulator ( )
{
2004-05-17 23:50:44 +08:00
_rotationMode = ELEVATION_AZIM ;
2004-06-07 22:49:39 +08:00
_distance = 1.0 ;
2004-05-17 23:50:44 +08:00
2004-05-06 19:01:16 +08:00
_thrown = false ;
}
TerrainManipulator : : ~ TerrainManipulator ( )
{
}
2004-05-17 23:50:44 +08:00
void TerrainManipulator : : setRotationMode ( RotationMode mode )
{
_rotationMode = mode ;
// need to correct rotation.
}
2004-05-06 19:01:16 +08:00
void TerrainManipulator : : setNode ( osg : : Node * node )
{
_node = node ;
2004-07-07 19:29:58 +08:00
if ( _node . get ( ) )
{
const osg : : BoundingSphere & boundingSphere = _node - > getBound ( ) ;
const float minimumDistanceScale = 0.001f ;
_minimumDistance = osg : : clampBetween (
boundingSphere . _radius * minimumDistanceScale ,
0.00001f , 1.0f ) ;
osg : : notify ( osg : : NOTICE ) < < " Setting terrain manipulator _minimumDistance to " < < _minimumDistance < < std : : endl ;
}
2004-05-06 19:01:16 +08:00
}
const osg : : Node * TerrainManipulator : : getNode ( ) const
{
return _node . get ( ) ;
}
osg : : Node * TerrainManipulator : : getNode ( )
{
return _node . get ( ) ;
}
void TerrainManipulator : : home ( const GUIEventAdapter & , GUIActionAdapter & us )
{
if ( _node . get ( ) )
{
const osg : : BoundingSphere & boundingSphere = _node - > getBound ( ) ;
computePosition ( boundingSphere . _center + osg : : Vec3 ( 0.0 , - 3.5f * boundingSphere . _radius , 0.0f ) ,
boundingSphere . _center ,
osg : : Vec3 ( 0.0f , 0.0f , 1.0f ) ) ;
us . requestRedraw ( ) ;
}
}
void TerrainManipulator : : init ( const GUIEventAdapter & , GUIActionAdapter & )
{
flushMouseEventStack ( ) ;
}
void TerrainManipulator : : getUsage ( osg : : ApplicationUsage & usage ) const
{
usage . addKeyboardMouseBinding ( " Trackball: Space " , " Reset the viewing position to home " ) ;
usage . addKeyboardMouseBinding ( " Trackball: + " , " When in stereo, increase the fusion distance " ) ;
usage . addKeyboardMouseBinding ( " Trackball: - " , " When in stereo, reduse the fusion distance " ) ;
}
bool TerrainManipulator : : handle ( const GUIEventAdapter & ea , GUIActionAdapter & us )
{
switch ( ea . getEventType ( ) )
{
case ( GUIEventAdapter : : PUSH ) :
{
flushMouseEventStack ( ) ;
addMouseEvent ( ea ) ;
if ( calcMovement ( ) ) us . requestRedraw ( ) ;
us . requestContinuousUpdate ( false ) ;
_thrown = false ;
return true ;
}
case ( GUIEventAdapter : : RELEASE ) :
{
if ( ea . getButtonMask ( ) = = 0 )
{
if ( isMouseMoving ( ) )
{
if ( calcMovement ( ) )
{
us . requestRedraw ( ) ;
us . requestContinuousUpdate ( true ) ;
_thrown = true ;
}
}
else
{
flushMouseEventStack ( ) ;
addMouseEvent ( ea ) ;
if ( calcMovement ( ) ) us . requestRedraw ( ) ;
us . requestContinuousUpdate ( false ) ;
_thrown = false ;
}
}
else
{
flushMouseEventStack ( ) ;
addMouseEvent ( ea ) ;
if ( calcMovement ( ) ) us . requestRedraw ( ) ;
us . requestContinuousUpdate ( false ) ;
_thrown = false ;
}
return true ;
}
case ( GUIEventAdapter : : DRAG ) :
{
addMouseEvent ( ea ) ;
if ( calcMovement ( ) ) us . requestRedraw ( ) ;
us . requestContinuousUpdate ( false ) ;
_thrown = false ;
return true ;
}
case ( GUIEventAdapter : : MOVE ) :
{
return false ;
}
case ( GUIEventAdapter : : KEYDOWN ) :
if ( ea . getKey ( ) = = ' ' )
{
flushMouseEventStack ( ) ;
_thrown = false ;
home ( ea , us ) ;
us . requestRedraw ( ) ;
us . requestContinuousUpdate ( false ) ;
return true ;
}
return false ;
case ( GUIEventAdapter : : FRAME ) :
if ( _thrown )
{
if ( calcMovement ( ) ) us . requestRedraw ( ) ;
}
return false ;
default :
return false ;
}
}
bool TerrainManipulator : : isMouseMoving ( )
{
if ( _ga_t0 . get ( ) = = NULL | | _ga_t1 . get ( ) = = NULL ) return false ;
static const float velocity = 0.1f ;
float dx = _ga_t0 - > getXnormalized ( ) - _ga_t1 - > getXnormalized ( ) ;
float dy = _ga_t0 - > getYnormalized ( ) - _ga_t1 - > getYnormalized ( ) ;
float len = sqrtf ( dx * dx + dy * dy ) ;
float dt = _ga_t0 - > time ( ) - _ga_t1 - > time ( ) ;
return ( len > dt * velocity ) ;
}
void TerrainManipulator : : flushMouseEventStack ( )
{
_ga_t1 = NULL ;
_ga_t0 = NULL ;
}
void TerrainManipulator : : addMouseEvent ( const GUIEventAdapter & ea )
{
_ga_t1 = _ga_t0 ;
_ga_t0 = & ea ;
}
void TerrainManipulator : : setByMatrix ( const osg : : Matrixd & matrix )
{
2004-06-12 04:57:36 +08:00
2004-05-07 19:18:18 +08:00
osg : : Vec3 lookVector ( - matrix ( 2 , 0 ) , - matrix ( 2 , 1 ) , - matrix ( 2 , 2 ) ) ;
osg : : Vec3 eye ( matrix ( 3 , 0 ) , matrix ( 3 , 1 ) , matrix ( 3 , 2 ) ) ;
osg : : notify ( INFO ) < < " eye point " < < eye < < std : : endl ;
osg : : notify ( INFO ) < < " lookVector " < < lookVector < < std : : endl ;
2004-06-12 04:57:36 +08:00
if ( ! _node )
{
_center = eye + lookVector ;
_distance = lookVector . length ( ) ;
matrix . get ( _rotation ) ;
return ;
}
2004-05-07 19:18:18 +08:00
// need to reintersect with the terrain
osgUtil : : IntersectVisitor iv ;
const osg : : BoundingSphere & bs = _node - > getBound ( ) ;
float distance = ( eye - bs . center ( ) ) . length ( ) + _node - > getBound ( ) . radius ( ) ;
2004-05-20 18:15:48 +08:00
osg : : Vec3d start_segment = eye ;
osg : : Vec3d end_segment = eye + lookVector * distance ;
2004-05-07 19:18:18 +08:00
2004-05-18 06:22:24 +08:00
//CoordinateFrame coordinateFrame = getCoordinateFrame(_center.x(), _center.y(), _center.z());
//osg::notify(INFO)<<"start="<<start_segment<<"\tend="<<end_segment<<"\tupVector="<<getUpVector(coordinateFrame)<<std::endl;
2004-05-07 19:18:18 +08:00
osg : : ref_ptr < osg : : LineSegment > segLookVector = new osg : : LineSegment ;
segLookVector - > set ( start_segment , end_segment ) ;
iv . addLineSegment ( segLookVector . get ( ) ) ;
_node - > accept ( iv ) ;
bool hitFound = false ;
if ( iv . hits ( ) )
{
osgUtil : : IntersectVisitor : : HitList & hitList = iv . getHitList ( segLookVector . get ( ) ) ;
if ( ! hitList . empty ( ) )
{
notify ( INFO ) < < " Hit terrain ok " < < std : : endl ;
2004-05-20 18:15:48 +08:00
osg : : Vec3d ip = hitList . front ( ) . getWorldIntersectPoint ( ) ;
2004-05-18 06:22:24 +08:00
2004-05-20 18:15:48 +08:00
_center = ip ;
2004-05-07 19:18:18 +08:00
_distance = ( eye - ip ) . length ( ) ;
osg : : Matrix rotation_matrix = osg : : Matrixd : : translate ( 0.0 , 0.0 , - _distance ) *
matrix *
2004-05-20 18:15:48 +08:00
osg : : Matrixd : : translate ( - _center ) ;
2004-05-07 19:18:18 +08:00
rotation_matrix . get ( _rotation ) ;
hitFound = true ;
}
}
if ( ! hitFound )
{
2004-05-20 18:15:48 +08:00
CoordinateFrame eyePointCoordFrame = getCoordinateFrame ( eye ) ;
2004-05-07 19:18:18 +08:00
// clear the intersect visitor ready for a new test
iv . reset ( ) ;
osg : : ref_ptr < osg : : LineSegment > segDowVector = new osg : : LineSegment ;
segLookVector - > set ( eye + getUpVector ( eyePointCoordFrame ) * distance ,
eye - getUpVector ( eyePointCoordFrame ) * distance ) ;
iv . addLineSegment ( segLookVector . get ( ) ) ;
_node - > accept ( iv ) ;
hitFound = false ;
if ( iv . hits ( ) )
{
osgUtil : : IntersectVisitor : : HitList & hitList = iv . getHitList ( segLookVector . get ( ) ) ;
if ( ! hitList . empty ( ) )
{
notify ( INFO ) < < " Hit terrain ok " < < std : : endl ;
2004-05-20 18:15:48 +08:00
osg : : Vec3d ip = hitList . front ( ) . getWorldIntersectPoint ( ) ;
2004-05-07 19:18:18 +08:00
2004-05-20 18:15:48 +08:00
_center = ip ;
2004-05-07 19:18:18 +08:00
_distance = ( eye - ip ) . length ( ) ;
_rotation . set ( 0 , 0 , 0 , 1 ) ;
hitFound = true ;
}
}
}
2004-05-21 07:25:26 +08:00
CoordinateFrame coordinateFrame = getCoordinateFrame ( _center ) ;
_previousUp = getUpVector ( coordinateFrame ) ;
2004-05-17 23:50:44 +08:00
clampOrientation ( ) ;
2004-05-06 19:01:16 +08:00
}
osg : : Matrixd TerrainManipulator : : getMatrix ( ) const
{
2004-05-20 18:15:48 +08:00
return osg : : Matrixd : : translate ( 0.0 , 0.0 , _distance ) * osg : : Matrixd : : rotate ( _rotation ) * osg : : Matrix : : translate ( _center ) ;
2004-05-06 19:01:16 +08:00
}
osg : : Matrixd TerrainManipulator : : getInverseMatrix ( ) const
{
2004-05-20 18:15:48 +08:00
return osg : : Matrix : : translate ( - _center ) * osg : : Matrixd : : rotate ( _rotation . inverse ( ) ) * osg : : Matrixd : : translate ( 0.0 , 0.0 , - _distance ) ;
2004-05-06 19:01:16 +08:00
}
2004-05-20 18:15:48 +08:00
void TerrainManipulator : : computePosition ( const osg : : Vec3d & eye , const osg : : Vec3d & center , const osg : : Vec3d & up )
2004-05-06 19:01:16 +08:00
{
2004-06-12 04:57:36 +08:00
if ( ! _node ) return ;
2004-05-06 19:01:16 +08:00
// compute rotation matrix
osg : : Vec3 lv ( center - eye ) ;
_distance = lv . length ( ) ;
2004-06-12 05:00:06 +08:00
_center = center ;
if ( _node . valid ( ) )
{
// compute the itersection with the scene.
osgUtil : : IntersectVisitor iv ;
2004-05-06 19:01:16 +08:00
2004-06-12 05:00:06 +08:00
osg : : ref_ptr < osg : : LineSegment > segLookVector = new osg : : LineSegment ;
segLookVector - > set ( eye , center ) ;
iv . addLineSegment ( segLookVector . get ( ) ) ;
2004-05-06 19:01:16 +08:00
2004-06-12 05:00:06 +08:00
_node - > accept ( iv ) ;
2004-05-06 19:01:16 +08:00
2004-06-12 05:00:06 +08:00
bool hitFound = false ;
if ( iv . hits ( ) )
2004-05-06 19:01:16 +08:00
{
2004-06-12 05:00:06 +08:00
osgUtil : : IntersectVisitor : : HitList & hitList = iv . getHitList ( segLookVector . get ( ) ) ;
if ( ! hitList . empty ( ) )
{
osg : : notify ( osg : : INFO ) < < " Hit terrain ok " < < std : : endl ;
osg : : Vec3d ip = hitList . front ( ) . getWorldIntersectPoint ( ) ;
2004-05-06 19:01:16 +08:00
2004-06-12 05:00:06 +08:00
_center = ip ;
_distance = ( ip - eye ) . length ( ) ;
hitFound = true ;
}
2004-05-06 19:01:16 +08:00
}
}
// note LookAt = inv(CF)*inv(RM)*inv(T) which is equivilant to:
// inv(R) = CF*LookAt.
2004-05-18 06:22:24 +08:00
osg : : Matrixd rotation_matrix = osg : : Matrixd : : lookAt ( eye , center , up ) ;
2004-05-06 19:01:16 +08:00
rotation_matrix . get ( _rotation ) ;
_rotation = _rotation . inverse ( ) ;
2004-05-17 23:50:44 +08:00
2004-05-21 07:25:26 +08:00
CoordinateFrame coordinateFrame = getCoordinateFrame ( _center ) ;
_previousUp = getUpVector ( coordinateFrame ) ;
2004-05-17 23:50:44 +08:00
clampOrientation ( ) ;
2004-05-06 19:01:16 +08:00
}
bool TerrainManipulator : : calcMovement ( )
{
// return if less then two events have been added.
if ( _ga_t0 . get ( ) = = NULL | | _ga_t1 . get ( ) = = NULL ) return false ;
2004-05-20 18:15:48 +08:00
double dx = _ga_t0 - > getXnormalized ( ) - _ga_t1 - > getXnormalized ( ) ;
double dy = _ga_t0 - > getYnormalized ( ) - _ga_t1 - > getYnormalized ( ) ;
2004-05-06 19:01:16 +08:00
// 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 )
{
2004-05-17 23:50:44 +08:00
if ( _rotationMode = = ELEVATION_AZIM_ROLL )
{
// rotate camera.
osg : : Vec3 axis ;
2004-05-20 18:15:48 +08:00
double angle ;
2004-05-17 23:50:44 +08:00
2004-05-20 18:15:48 +08:00
double px0 = _ga_t0 - > getXnormalized ( ) ;
double py0 = _ga_t0 - > getYnormalized ( ) ;
2004-05-06 19:01:16 +08:00
2004-05-20 18:15:48 +08:00
double px1 = _ga_t1 - > getXnormalized ( ) ;
double py1 = _ga_t1 - > getYnormalized ( ) ;
2004-05-06 19:01:16 +08:00
2004-05-17 23:50:44 +08:00
trackball ( axis , angle , px1 , py1 , px0 , py0 ) ;
2004-05-06 19:01:16 +08:00
2004-05-17 23:50:44 +08:00
osg : : Quat new_rotate ;
new_rotate . makeRotate ( angle , axis ) ;
_rotation = _rotation * new_rotate ;
}
else
{
osg : : Matrix rotation_matrix ;
rotation_matrix . set ( _rotation ) ;
2004-05-20 18:15:48 +08:00
osg : : Vec3d lookVector = - getUpVector ( rotation_matrix ) ;
osg : : Vec3d sideVector = getSideVector ( rotation_matrix ) ;
osg : : Vec3d upVector = getFrontVector ( rotation_matrix ) ;
2004-05-18 06:22:24 +08:00
2004-05-20 18:15:48 +08:00
CoordinateFrame coordinateFrame = getCoordinateFrame ( _center ) ;
osg : : Vec3d localUp = getUpVector ( coordinateFrame ) ;
2004-05-21 07:25:26 +08:00
//osg::Vec3d localUp = _previousUp;
2004-05-18 06:22:24 +08:00
2004-05-17 23:50:44 +08:00
2004-05-20 18:15:48 +08:00
osg : : Vec3d forwardVector = localUp ^ sideVector ;
2004-05-18 06:22:24 +08:00
sideVector = forwardVector ^ localUp ;
2004-05-17 23:50:44 +08:00
forwardVector . normalize ( ) ;
sideVector . normalize ( ) ;
osg : : Quat rotate_elevation ;
rotate_elevation . makeRotate ( dy , sideVector ) ;
2004-05-06 19:01:16 +08:00
2004-05-17 23:50:44 +08:00
osg : : Quat rotate_azim ;
2004-05-18 06:22:24 +08:00
rotate_azim . makeRotate ( - dx , localUp ) ;
2004-05-17 23:50:44 +08:00
_rotation = _rotation * rotate_elevation * rotate_azim ;
2004-05-21 07:25:26 +08:00
2004-05-17 23:50:44 +08:00
}
2004-05-06 19:01:16 +08:00
return true ;
}
else if ( buttonMask = = GUIEventAdapter : : MIDDLE_MOUSE_BUTTON | |
buttonMask = = ( GUIEventAdapter : : LEFT_MOUSE_BUTTON | GUIEventAdapter : : RIGHT_MOUSE_BUTTON ) )
{
// pan model.
2004-05-20 18:15:48 +08:00
double scale = - 0.5f * _distance ;
2004-05-06 19:01:16 +08:00
osg : : Matrix rotation_matrix ;
rotation_matrix . set ( _rotation ) ;
2004-05-17 20:08:52 +08:00
// compute look vector.
2004-05-20 18:15:48 +08:00
osg : : Vec3d lookVector = - getUpVector ( rotation_matrix ) ;
osg : : Vec3d sideVector = getSideVector ( rotation_matrix ) ;
osg : : Vec3d upVector = getFrontVector ( rotation_matrix ) ;
2004-05-06 19:01:16 +08:00
2004-05-21 07:25:26 +08:00
// CoordinateFrame coordinateFrame = getCoordinateFrame(_center);
// osg::Vec3d localUp = getUpVector(coordinateFrame);
osg : : Vec3d localUp = _previousUp ;
2004-05-18 06:22:24 +08:00
2004-05-20 18:15:48 +08:00
osg : : Vec3d forwardVector = localUp ^ sideVector ;
2004-05-18 06:22:24 +08:00
sideVector = forwardVector ^ localUp ;
2004-05-17 20:08:52 +08:00
forwardVector . normalize ( ) ;
sideVector . normalize ( ) ;
2004-05-06 19:01:16 +08:00
2004-05-20 18:15:48 +08:00
osg : : Vec3d dv = forwardVector * ( dy * scale ) + sideVector * ( dx * scale ) ;
2004-05-06 19:01:16 +08:00
2004-05-20 18:15:48 +08:00
_center + = dv ;
2004-05-17 20:08:52 +08:00
// need to recompute the itersection point along the look vector.
2004-06-12 04:57:36 +08:00
if ( _node . valid ( ) )
{
2004-05-06 19:01:16 +08:00
2004-06-12 04:57:36 +08:00
// now reorientate the coordinate frame to the frame coords.
CoordinateFrame coordinateFrame = getCoordinateFrame ( _center ) ;
2004-05-06 19:01:16 +08:00
2004-06-12 04:57:36 +08:00
// need to reintersect with the terrain
osgUtil : : IntersectVisitor iv ;
2004-05-06 20:30:53 +08:00
2004-06-12 04:57:36 +08:00
double distance = _node - > getBound ( ) . radius ( ) * 0.1f ;
osg : : Vec3d start_segment = _center + getUpVector ( coordinateFrame ) * distance ;
osg : : Vec3d end_segment = start_segment - getUpVector ( coordinateFrame ) * ( 2.0f * distance ) ;
2004-05-06 19:01:16 +08:00
2004-06-12 04:57:36 +08:00
osg : : notify ( INFO ) < < " start= " < < start_segment < < " \t end= " < < end_segment < < " \t upVector= " < < getUpVector ( coordinateFrame ) < < std : : endl ;
2004-05-06 19:01:16 +08:00
2004-06-12 04:57:36 +08:00
osg : : ref_ptr < osg : : LineSegment > segLookVector = new osg : : LineSegment ;
segLookVector - > set ( start_segment , end_segment ) ;
iv . addLineSegment ( segLookVector . get ( ) ) ;
2004-05-06 19:01:16 +08:00
2004-06-12 04:57:36 +08:00
_node - > accept ( iv ) ;
bool hitFound = false ;
if ( iv . hits ( ) )
2004-05-06 19:01:16 +08:00
{
2004-06-12 04:57:36 +08:00
osgUtil : : IntersectVisitor : : HitList & hitList = iv . getHitList ( segLookVector . get ( ) ) ;
if ( ! hitList . empty ( ) )
{
notify ( INFO ) < < " Hit terrain ok " < < std : : endl ;
osg : : Vec3d ip = hitList . front ( ) . getWorldIntersectPoint ( ) ;
_center = ip ;
2004-05-06 19:01:16 +08:00
2004-06-12 04:57:36 +08:00
hitFound = true ;
}
2004-05-06 19:01:16 +08:00
}
2004-06-12 04:57:36 +08:00
if ( ! hitFound )
{
// ??
osg : : notify ( INFO ) < < " TerrainManipulator unable to intersect with terrain. " < < std : : endl ;
}
2004-05-18 06:22:24 +08:00
2004-06-12 04:57:36 +08:00
coordinateFrame = getCoordinateFrame ( _center ) ;
osg : : Vec3d new_localUp = getUpVector ( coordinateFrame ) ;
2004-05-21 07:25:26 +08:00
2004-06-12 04:57:36 +08:00
osg : : Quat pan_rotation ;
pan_rotation . makeRotate ( localUp , new_localUp ) ;
if ( ! pan_rotation . zeroRotation ( ) )
{
_rotation = _rotation * pan_rotation ;
_previousUp = new_localUp ;
//osg::notify(osg::NOTICE)<<"Rotating from "<<localUp<<" to "<<new_localUp<<" angle = "<<acos(localUp*new_localUp/(localUp.length()*new_localUp.length()))<<std::endl;
//clampOrientation();
}
else
{
osg : : notify ( osg : : INFO ) < < " New up orientation nearly inline - no need to rotate " < < std : : endl ;
}
}
2004-05-18 06:22:24 +08:00
2004-05-06 19:01:16 +08:00
return true ;
}
else if ( buttonMask = = GUIEventAdapter : : RIGHT_MOUSE_BUTTON )
{
// zoom model.
2004-05-20 18:15:48 +08:00
double fd = _distance ;
double scale = 1.0f + dy ;
2004-06-07 22:49:39 +08:00
if ( fd * scale > _minimumDistance )
2004-05-06 19:01:16 +08:00
{
_distance * = scale ;
2004-06-07 22:49:39 +08:00
} else
{
_distance = _minimumDistance ;
2004-05-06 19:01:16 +08:00
}
return true ;
}
return false ;
}
2004-05-17 23:50:44 +08:00
void TerrainManipulator : : clampOrientation ( )
{
if ( _rotationMode = = ELEVATION_AZIM )
{
osg : : Matrix rotation_matrix ;
rotation_matrix . set ( _rotation ) ;
2004-05-20 18:15:48 +08:00
osg : : Vec3d lookVector = - getUpVector ( rotation_matrix ) ;
osg : : Vec3d upVector = getFrontVector ( rotation_matrix ) ;
2004-05-17 23:50:44 +08:00
2004-05-20 18:15:48 +08:00
CoordinateFrame coordinateFrame = getCoordinateFrame ( _center ) ;
osg : : Vec3d localUp = getUpVector ( coordinateFrame ) ;
2004-05-21 07:25:26 +08:00
//osg::Vec3d localUp = _previousUp;
2004-05-18 06:22:24 +08:00
2004-05-20 18:15:48 +08:00
osg : : Vec3d sideVector = lookVector ^ localUp ;
2004-05-17 23:50:44 +08:00
if ( sideVector . length ( ) < 0.1 )
{
osg : : notify ( osg : : NOTICE ) < < " Side vector short " < < sideVector . length ( ) < < std : : endl ;
2004-05-18 06:22:24 +08:00
sideVector = upVector ^ localUp ;
2004-05-17 23:50:44 +08:00
sideVector . normalize ( ) ;
}
2004-05-20 18:15:48 +08:00
Vec3d newUpVector = sideVector ^ lookVector ;
2004-05-17 23:50:44 +08:00
newUpVector . normalize ( ) ;
osg : : Quat rotate_roll ;
rotate_roll . makeRotate ( upVector , newUpVector ) ;
2004-05-21 07:25:26 +08:00
if ( ! rotate_roll . zeroRotation ( ) )
{
_rotation = _rotation * rotate_roll ;
}
2004-05-17 23:50:44 +08:00
}
}
2004-05-06 19:01:16 +08:00
/*
* 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 )
*/
2004-05-20 18:15:48 +08:00
void TerrainManipulator : : trackball ( osg : : Vec3 & axis , double & angle , double p1x , double p1y , double p2x , double p2y )
2004-05-06 19:01:16 +08:00
{
/*
* First , figure out z - coordinates for projection of P1 and P2 to
* deformed sphere
*/
osg : : Matrix rotation_matrix ( _rotation ) ;
2004-05-20 18:15:48 +08:00
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 ;
2004-05-06 19:01:16 +08:00
2004-05-20 18:15:48 +08:00
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 ) ;
2004-05-06 19:01:16 +08:00
/*
* 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 .
*/
2004-05-20 18:15:48 +08:00
double t = ( p2 - p1 ) . length ( ) / ( 2.0 * TRACKBALLSIZE ) ;
2004-05-06 19:01:16 +08:00
/*
* Avoid problems with out - of - control values . . .
*/
if ( t > 1.0 ) t = 1.0 ;
if ( t < - 1.0 ) t = - 1.0 ;
angle = inRadians ( asin ( t ) ) ;
}
/*
* Project an x , y pair onto a sphere of radius r OR a hyperbolic sheet
* if we are away from the center of the sphere .
*/
2004-05-20 18:15:48 +08:00
double TerrainManipulator : : tb_project_to_sphere ( double r , double x , double y )
2004-05-06 19:01:16 +08:00
{
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 ;
}