OSG场景交互/事件与漫游

第一部分见这里

3.场景交互与场景漫游

osgGA库

为用户提供各种事件处理及操作处理,类似控制windows窗口。
主要组成:(最主要是事件适配器和动作适配器)

  • 事件适配器osgGA::GUIEventAdapter,而事件处理器osgGA::GUIEventHandler用它的实例来接收更新
    且包含了一系列的枚举事件类型:
    在这里插入图片描述

  • 动作适配器osgGA::GUIActionAdapter,且事件处理器osgGA::GUIEventHandler用它的实例来向系统发送请求
    主要包含系统执行的动作

  • 操作器/控制器osgGA::CameraManipulator
    它提供的机构包括矩阵变换和事件处理,编写自己的操作器需要派生自它,并且重载矩阵变换函数和时间处理函数。

  • 事件处理器osgGA::GUIEventHandler
    可以提供窗口系统的GUI事件接口。有很多派生自它已经写好的事件处理器。
    编写自己的事件处理器,从而实现交互。主要步骤:
    1.派生自osgGA::GUIEventHandler的新类
    2.重载有三个重载: virtual bool handle (const GUIEventAdapter &, GUIActionAdapter &);
    3.将事件处理器压入处理器列表(addEventHandler),否则的话所写的事件处理器将不会执行。

如果返回值为true,则系统会认为该事件已经处理(响应一个事件可能需要多个事件处理器),就不再传递给下一个事件处理器(即之后的都不会响应了);如果返回false,会继续传递给下一个事件处理器。

可以参考场景漫游代码中,按键盘wasd等都是返回true,因为这个事件不需要再响应别的)。
但是鼠标拖动事件,判断按下鼠标左边事件,并且处理之后返回false,因为这个事件要在继续判断并响应拖动事件。

需要注意事件不要重复。

事件响应osgGA::GUIEventHandler

osgGA::GUIEventHandler为事件处理器,可以提供窗口系统的GUI事件接口。
在这里插入图片描述
首先操作系统响应外设,并把响应的存到事件队列里面去,然后程序每一帧取得这些事件。
在这里插入图片描述

virtual bool handle (const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us);
// 其实第二个参数就是个viewer

在这里插入图片描述在这里插入图片描述

事件响应示例(多键响应)

class TestInput : public osgGA::GUIEventHandler
{
public:
	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
	{
		// 参数aa就是viewer
		osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
		switch (ea.getEventType())
		{
		case osgGA::GUIEventAdapter::KEYDOWN:
			keyMap_[ea.getKey()] = true;// 创建
			if ((keyMap_['w'] == true) || (keyMap_['a'] == true))
			{
				std::cout << "左向角走"<< std::endl;
			}
			break;
		case osgGA::GUIEventAdapter::KEYUP:
			keyMap_[ea.getKey()] = false;
			break;
		case osgGA::GUIEventAdapter::PUSH:
			if ((ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON))
			{
				std::cout << "左键点击" << ea.getX() << "  " << ea.getY() << std::endl;// 左下角0,0
			}
		case osgGA::GUIEventAdapter::DRAG:
			std::cout << "拖拽了"<< std::endl; 
		case osgGA::GUIEventAdapter::SCROLL:
			if (ea.getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_UP)
			{
				// osg很多关于节点的设置流程都是如下
				osg::ref_ptr<osg::MatrixTransform> mx = new osg::MatrixTransform;
				mx->addChild(viewer->getSceneData());
				mx->setMatrix(osg::Matrix::rotate(0.1, osg::Vec3f(1.0, 0.0, 0.0)));
				viewer->setSceneData(mx);
			}
			else if (ea.getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_DOWN)
			{
			}
			else
			{
			}
		default:
			break;
		}
		return false;
	}

private:
	std::map<int,bool> keyMap_; // 模仿GraphicsWindowWin32源码
};

int main()
{
....
viewer->addEventHandler(new TestInput);
....
}

抓图示例(补

场景漫游

操作器/控制器Manipulator可以方便我们实现通过适当的矩阵变换,获得各种移动或者渲染效果。
编写自己的操作器步骤:

  1. 写一个派生自osgGA::CameraManipulator的类。
  2. 重载handle(),添加事件处理函数和指定执行相关动作。
  3. 重载相关矩阵变换函数。
// 设置当前视口
virtual void setByMatrix (const osg::Matrixd &matrix);

// 设置当前视口
virtual void setByInverseMatrix (const osg::Matrixd &matrix);

virtual osg::Matrixd getMatrix () const;
virtual osg::Matrixd getInverseMatrix () const;
  1. 进行碰撞检测。
  2. 关联该操作到当前视图场景中viewer->setCameraManipulator(camera);,否则不会启动。

自定义操作器场景漫游示例(键盘,鼠标)

在这里插入图片描述
在这里插入图片描述

class TravelManipulator: public osgGA::CameraManipulator
{
public:
	TravelManipulator();
	// 设置当前视口
	virtual void setByMatrix (const osg::Matrixd &matrix);

	// 设置当前视口
	virtual void setByInverseMatrix (const osg::Matrixd &matrix);

	virtual osg::Matrixd getMatrix () const;
	virtual osg::Matrixd getInverseMatrix () const;

	virtual bool handle (const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us);
	void ChangePostion(osg::Vec3d &delta);
private:
	// 视点
	osg::Vec3 m_vPosition;
	// 朝向
	osg::Vec3 m_vRotation;
	// 步长
	int m_nStep;
	// 旋转步长
	float m_fRotateStep;
	// 记录坐标
	int m_iLeftX;
	int m_iLeftY;
	bool m_bLeftDown;
};

TravelManipulator::TravelManipulator()
{	
	m_vPosition = osg::Vec3(0,0,50);// 视点,即相机的位置在z轴正方向上
	m_vRotation = osg::Vec3(/*osg::PI_2*/0,0,0);// 如果相机位置距离原点太远,且进行旋转,很容易和预期的不一样。。。
	m_nStep= 1;
	m_fRotateStep= 1.0;
	m_bLeftDown = false;ji
}

// 注意要按照缩放旋转平移的顺序来。
osg::Matrixd TravelManipulator::getMatrix () const
{
	osg::Matrixd mat;
	mat.makeTranslate(m_vPosition);
	return osg::Matrixd::rotate(m_vPosition[0],osg::X_AXIS,m_vPosition[1],osg::Y_AXIS,m_vPosition[2],osg::Z_AXIS)*mat;
}

osg::Matrixd TravelManipulator::getInverseMatrix () const
{
	osg::Matrixd mat;
	mat.makeTranslate(m_vPosition);
	return osg::Matrixd::inverse(osg::Matrixd::rotate(m_vPosition[0],osg::X_AXIS,m_vPosition[1],osg::Y_AXIS,m_vPosition[2],osg::Z_AXIS)*mat);
}

bool TravelManipulator::handle (const GUIEventAdapter &ea, GUIActionAdapter &us)
{
	switch(ea.getEventType())
	{
		case osgGA::GUIEventAdapter::KEYDOWN:
		{	
			if(ea.getKey()=='w')
			{
				ChangePostion(osg::Vec3d(m_nStep*cosf(osg::PI_2+m_vRotation[2]),m_nStep*sinf(osg::PI_2+m_vRotation[2]),0));
				return true;
				//m_vPosition[2]  +=2; // 视点/相机沿z轴正方向移动,即物体向后移动
			}
			else if(ea.getKey()=='s')
			{	
				ChangePostion(osg::Vec3d(-m_nStep*cosf(osg::PI_2+m_vRotation[2]),-m_nStep*sinf(osg::PI_2+m_vRotation[2]),0));
				return true;
			}
			else if(ea.getKey()=='a')
			{	
				ChangePostion(osg::Vec3d(-m_nStep*sinf(osg::PI_2+m_vRotation[2]),m_nStep*cosf(osg::PI_2+m_vRotation[2]),0));
				return true;
			}
			else if(ea.getKey()=='d')
			{	
				ChangePostion(osg::Vec3d(m_nStep*sinf(osg::PI_2+m_vRotation[2]),-m_nStep*cosf(osg::PI_2+m_vRotation[2]),0));
				return true;
			}
			else if(ea.getKey()==osgGA::GUIEventAdapter::KEY_Left)
			{	
				m_vRotation[2] += 0.2;
				return true;
			}
			else if(ea.getKey()==osgGA::GUIEventAdapter::KEY_Right)
			{	
				m_vRotation[2] -= 0.2;
				return true;
			}
			else if(ea.getKey()==osgGA::GUIEventAdapter::KEY_Home)
			{	
				ChangePostion(osg::Vec3d(0,0,m_nStep));
				return true;
			}
			else if(ea.getKey()==osgGA::GUIEventAdapter::KEY_End)
			{	
				ChangePostion(osg::Vec3d(0,0,-m_nStep));
				return true;
			}
			else
			{}
		}	
		case osgGA::GUIEventAdapter::PUSH:
		{
			if(ea.getButton()==osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
			{
				m_iLeftX = ea.getX();
				m_iLeftY = ea.getY();
				m_bLeftDown = true;
			}
			return false;
		}
		case osgGA::GUIEventAdapter::DRAG:
		{
			if(m_bLeftDown)
			{
				int delX = ea.getX()- m_iLeftX ;
				int delY = ea.getY()- m_iLeftY ;
				m_vRotation[2] -= osg::DegreesToRadians(0.005*delX );
				m_vRotation[0] += osg::DegreesToRadians(0.005*delY );
				if(m_vRotation[0] <=0)
					m_vRotation[0]=0;
				if(m_vRotation[0] >=OSG::PI)
					m_vRotation[0]=OSG::PI;
			}
		}
		case osgGA::GUIEventAdapter::RELEASE:
		{
			if(ea.getButton()==osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
			{
				m_bLeftDown = false;
			}
		}
	return false;
	}
}

void TravelManipulator::ChangePostion(osg::Vec3d &delta)
{
	m_vPosition +=delta;
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
	osg::ref_ptr<osg::Group> group = new osg::Group();
	group->addChild(osgDB::ReadNodeFile("glider.osg"));
	viewer->setSceneData(group.get());

	// 关联控制器到场景中
	viewer->setCameraManipulator(new TravelManipulator);
	
	return viewer->run();
}


如果第一次将控制器关联到场景后,只有清屏色,没有渲染的物体,可以慢慢调m_vPosition 和m_vRotation ,直到看到物体(一般将相机向上移动即可)。

路径漫游(补

可以按照指定的路径进行漫游(路径漫游),或者指定物体按照指定的路径进行运动(路径动画)。
osgGA::AnimationPathManipulator

路径漫游示例(补

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值