From Jan Perciva with changes from Robert Osfield, "I am submitting improved osgGA camera manipulators.
Changes: - new mouse wheel zoom/movement/center functionality - ability to fix vertical axis (important for CAD) - possibility to specify values as absolute values or relative to model size - kind of backward compatibility by flags passed to constructor - and much more - restructuring classes to use kind of hierarchy and standard way of event processing (handle methods). This way, there is much more code reusability and it is more easy to develop new kinds of manipulators. Briefly, the new architecture keeps MatrixManipulator as base abstract class. StandardManipulator is the feature-rich standard manipulator with two main descendant classes: OrbitManipulator and FirstPersonManipulator. OrbitManipulator is base class for all trackball style manipulators, based on center, rotation and distance from center. FirstPersonManipulator is base for walk or fly style manipulators, using position and rotation for camera manipulation. " Changes by Robert: Replaced osg::Vec3 by osg::Vec3d, introduced DEFAULT_SETTINGS enum and usage. Added frame time member variables in prep for improving throw animation when vysync is off.
This commit is contained in:
@@ -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<osg::Node*>(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."<<std::endl;
|
||||
const_cast<ObserverNodePath&>(_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"<<std::endl;
|
||||
setTrackNodePath(parentNodePaths[0]);
|
||||
if (nodePaths.size()>1)
|
||||
{
|
||||
osg::notify(osg::NOTICE)<<"osgGA::NodeTrackerManipualtor::setTrackNode(..) taking first parent path, ignoring others."<<std::endl;
|
||||
}
|
||||
|
||||
osg::notify(osg::INFO)<<"NodeTrackerManipulator::setTrackNode(Node*"<<node<<" "<<node->getName()<<"): Path set"<<std::endl;
|
||||
_trackNodePath.clear();
|
||||
setTrackNodePath( nodePaths.front() );
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::notify(osg::NOTICE)<<"NodeTrackerManipulator::setTrackNode(Node*): Unable to set tracked node due to empty parental path."<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
osg::Node* NodeTrackerManipulator::getTrackNode()
|
||||
{
|
||||
osg::NodePath nodePath;
|
||||
if (_trackNodePath.getNodePath(nodePath)) return nodePath.back();
|
||||
else return 0;
|
||||
}
|
||||
|
||||
const osg::Node* NodeTrackerManipulator::getTrackNode() const
|
||||
{
|
||||
osg::NodePath nodePath;
|
||||
if (_trackNodePath.getNodePath(nodePath)) return nodePath.back();
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void NodeTrackerManipulator::setTrackerMode(TrackerMode mode)
|
||||
{
|
||||
_trackerMode = mode;
|
||||
}
|
||||
|
||||
void NodeTrackerManipulator::setRotationMode(RotationMode mode)
|
||||
{
|
||||
_rotationMode = mode;
|
||||
if (getAutoComputeHomePosition()) computeHomePosition();
|
||||
}
|
||||
|
||||
void NodeTrackerManipulator::setNode(osg::Node* node)
|
||||
{
|
||||
_node = node;
|
||||
|
||||
if (_node.get())
|
||||
osg::notify(osg::INFO)<<"setTrackNode("<<node->getName()<<")"<<std::endl;
|
||||
for(unsigned int i=0; i<_trackNodePath.size(); ++i)
|
||||
{
|
||||
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 Tracker manipulator _minimumDistance to "<<_minimumDistance<<std::endl;
|
||||
osg::notify(osg::INFO)<<" "<<_trackNodePath[i]->className()<<" '"<<_trackNodePath[i]->getName()<<"'"<<std::endl;
|
||||
}
|
||||
if (getAutoComputeHomePosition()) computeHomePosition();
|
||||
|
||||
}
|
||||
|
||||
const osg::Node* NodeTrackerManipulator::getNode() const
|
||||
{
|
||||
return _node.get();
|
||||
}
|
||||
|
||||
|
||||
osg::Node* NodeTrackerManipulator::getNode()
|
||||
{
|
||||
return _node.get();
|
||||
}
|
||||
|
||||
|
||||
void NodeTrackerManipulator::home(const GUIEventAdapter& ,GUIActionAdapter& us)
|
||||
{
|
||||
if (getAutoComputeHomePosition()) computeHomePosition();
|
||||
|
||||
computePosition(_homeEye, _homeCenter, _homeUp);
|
||||
us.requestRedraw();
|
||||
}
|
||||
|
||||
void NodeTrackerManipulator::computeHomePosition()
|
||||
{
|
||||
osg::NodePath nodePath;
|
||||
_trackNodePath.getNodePath(nodePath);
|
||||
osg::Node* node = _trackNodePath.empty() ? getNode() : _trackNodePath.back().get();
|
||||
|
||||
osg::Node* node = nodePath.empty() ? getNode() : nodePath.back();
|
||||
|
||||
if(node)
|
||||
{
|
||||
const osg::BoundingSphere& boundingSphere=node->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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user