地面操作器,继承自osg自身的轨迹球操作器(osgGA::OrbitManipulator),参考osgEarth的操作器的实现。
特性:
放大缩小视点始终在指定的地面高度上。(旋转中心点一直在那个高度的平面上)
地面始终与视线平行,即在操作过程中地面上的物体不会歪。
放大到指定的视线距离后,距离不再放大;(即距离地面,实际上是距离视点一定距离后不再升高,改为后退)。
缩小到一定距离后,距离不再缩小;(即距离视点一定距离后不再变小,继续缩放的话,就是前进)。
下面给出类的实现
// 文件名:GroundManipulator.h
// 作者:mingjun;日期:2015-11-05
// 描述: 地面操作器
//
//
// 历史记录
/
#ifndef _GROUNDMANIPULATOR_H_
#define _GROUNDMANIPULATOR_H_
class CGroundManipulator : public osgGA::OrbitManipulator
{
typedef OrbitManipulator inherited;
public:
COCSGroundManipulator();
virtual ~COCSGroundManipulator();
// 见父类
virtual void setCenter( const osg::Vec3d& center );
// 见父类
virtual void setByMatrix( const osg::Matrixd& matrix );
// 见父类
virtual void zoomModel( const float dy, bool pushForwardIfNeeded = true );
// 见父类
virtual bool performMovementLeftMouseButton( const double eventTimeDelta, const double dx, const double dy );
// 见父类
virtual bool performMovementMiddleMouseButton( const double eventTimeDelta, const double dx, const double dy );
// 见父类
virtual void panModel( const float dx, const float dy, const float dz = 0.f );
// 见父类
virtual void rotateTrackball( const float px0, const float py0,
const float px1, const float py1, const float scale );
public:
// 地面法向
osg::Vec3d GetUpVector() const;
void SetUpVector(const osg::Vec3d &vecUp);
// 最大距离
void SetMaxDistance(double dDistance);
double GetMaxDistance() const;
private:
osg::Vec3d _upVector; // 垂直地面向上的向量
double _maxDistance; // 定义一个最大距离,当超过这个距离,就不在放大了
};
#endif
类的实现:
里面设置的视点距离地面高度为5000;假定y轴为地面法向设置的。如果你的不是则哪里需要修改一下。
// 文件名:GroundManipulator.cpp
// 作者:mingjun;日期:2015-11-05
// 描述:
//
//
// 历史记录
/
#include "StdAfx.h"
#include "GroundManipulator.h"
COCSGroundManipulator::COCSGroundManipulator()
:inherited(DEFAULT_SETTINGS)
{
_upVector = osg::Vec3d(0.0, 1.0, 0.0);
setVerticalAxisFixed( false );
setAllowThrow(false);
// 设置其放大缩小方式与cad一样
double dOld = getWheelZoomFactor();
setWheelZoomFactor(-1.0 * dOld);
// 设置当缩放到这个大小后,在缩放的时候,同时移动center
setMinimumDistance( 1000, false );
SetMaxDistance(100000.0);
}
COCSGroundManipulator::~COCSGroundManipulator()
{
}
osg::Vec3d COCSGroundManipulator::GetUpVector() const
{
return _upVector;
}
void COCSGroundManipulator::SetUpVector( const osg::Vec3d &vecUp )
{
_upVector = vecUp;
}
void COCSGroundManipulator::setByMatrix( const osg::Matrixd& matrix )
{
_center = osg::Vec3d( 0., 0., -_distance ) * matrix;
_center.y() = 5000.0;
// 应该让y轴投影到视平面上后,是竖直的 计算上比较麻烦,所以直接给一个
osg::Vec3d f = -getUpVector(matrix);//( 1.0, -1.0, 0.0 );
//f.normalize();
osg::Vec3d s = getSideVector(matrix);//( 0.0, 0.0, 1.0 );
osg::Vec3d u(_upVector);//u( 1.0, 1.0, 0.0 );
s = f^u;
//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 );
_rotation = rotation_matrix.getRotate().inverse();
}
void COCSGroundManipulator::zoomModel( const float dy, bool pushForwardIfNeeded /*= true */ )
{
// dy 放大为正,缩小为负
// scale
float scale = 1.0f + dy;
// minimum distance
float minDist = _minimumDistance;
if( getRelativeFlag( _minimumDistanceFlagIndex ) )
minDist *= _modelSize;
double dResult = _distance * scale;
if( dResult > minDist )
{
if ( dResult < GetMaxDistance())
{
_distance = dResult;
}
else
{
float scale = -_distance;
osg::Matrix rotation_matrix;
rotation_matrix.makeRotate( _rotation );
osg::Vec3d sideVector = getSideVector(rotation_matrix);
osg::Vec3d localUp(0.0f,1.0f,0.0f);
osg::Vec3d forwardVector =localUp^sideVector;
forwardVector.normalize();
osg::Vec3d dv = forwardVector * (dy * scale);
_center += dv;
}
// regular zoom
//_distance *= scale;
}
else
{
if( pushForwardIfNeeded )
{
// push the camera forward
float scale = -_distance;
osg::Matrix rotation_matrix;
rotation_matrix.makeRotate( _rotation );
osg::Vec3d sideVector = getSideVector(rotation_matrix);
osg::Vec3d localUp(0.0f,1.0f,0.0f);
osg::Vec3d forwardVector =localUp^sideVector;
forwardVector.normalize();
osg::Vec3d dv = forwardVector * (dy * scale);
_center += dv;
}
else
{
// set distance on its minimum value
_distance = minDist;
}
}
}
void COCSGroundManipulator::panModel( const float dx, const float dy, const float dz )
{
double scaley = 1;//0.001 * _distance;
double scalex = 1;//0.0003 * _distance;
osg::Matrix 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);
osg::Vec3d localUp(_upVector);
osg::Vec3d forwardVector =localUp^sideVector;
sideVector = forwardVector^localUp;
forwardVector.normalize();
sideVector.normalize();
osg::Vec3d dv = forwardVector * (dy*scaley) + sideVector * (dx*scalex);
_center += dv;
}
void COCSGroundManipulator::rotateTrackball( const float px0, const float py0,
const float px1, const float py1, const float scale )
{
double dx = px0 - px1;
double dy = py0 - py1;
if ( ::fabs(dx) > ::fabs(dy) )
dy = 0.0;
else
dx = 0.0;
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);
osg::Vec3d localUp(_upVector);
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;
}
bool COCSGroundManipulator::performMovementLeftMouseButton( const double eventTimeDelta,
const double dx, const double dy )
{
// pan model
float scale = -0.3f * _distance * getThrowScale( eventTimeDelta );
panModel( dx*scale, dy*scale );
return true;
}
bool COCSGroundManipulator::performMovementMiddleMouseButton( const double eventTimeDelta,
const double dx, const double dy )
{
rotateTrackball( _ga_t0->getXnormalized(), _ga_t0->getYnormalized(),
_ga_t1->getXnormalized(), _ga_t1->getYnormalized(),
getThrowScale( eventTimeDelta ) );
return true;
}
void COCSGroundManipulator::setCenter( const osg::Vec3d& center )
{
_center = center;
_center.y() = 5000.0;
}
void COCSGroundManipulator::SetMaxDistance(double dValue)
{
_maxDistance = dValue;
}
double COCSGroundManipulator::GetMaxDistance() const
{
return _maxDistance;
}