osg 自己写地面操作器

本文介绍了一种名为地面操作器的设计方案,该方案继承自osg自身的轨迹球操作器,并进行了特定的功能增强。地面操作器能够确保在缩放和平移过程中,视点始终保持在设定的高度上,同时保持地面与视线平行,提供更加稳定的视觉体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



地面操作器,继承自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;
}


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TheDeaf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值