第08节 实例-写个简单的操作器
缘由
应四川的群友:挑战高起点 的要求,我给大家写一个最简单的操作器,读完本文以最大程度让读者掌握在OSG中写个操作器是咋回事儿。代码在最后一个代码块,直接新建OSG工程,拷贝进去就可以运行。特别感谢这位网友,要不然真的不知道怎么为大家好呀。
另外如果您也想我来为您就某个功能写个例子,分享给大家,则只需要在本文评论区回复即可。或在本文出现的群里回复。
本节所有代码在网盘中:
注意:务必使用浏览器打开:
链接:https://pan.baidu.com/s/13gwJLwo_LbRnN3Bl2NXXXw
提取码:xrf5
功能说明
首先绘制一个场景如下,场景中间放着模型axes.osgt这个模型,它指明了xyz轴的方向,然后我们在xy方向,从-5~5,间隔0.5绘制网格,如下图所示。
然后我们操作器的功能是:点击a围绕z轴,眼睛看着(0,0,0)点,高度在5,逆时针旋转
点击d则顺时针旋转。
实现要点
1.写个类继承自osgGA::CameraManipulator,就我们的功能而言,有两个函数就可以了,一个是virtual osg::Matrixd getInverseMatrix() const,它要返回camera的ViewMatrix,也就是我说的,Camera会在事件遍历的时候,调用这个函数来设置他的ViewMatrix。所以我们要实现它。
实现它也很简单,自己写三个变量做成员函数:_eye(人头的位置), _center(人头在往哪看), _up(人头顶的朝向),自己想想自己的头,就能想明白这三个量一定,你在哪朝哪看就定了,接着直接构造ViewMatrix就可以了,代码如下:
virtual osg::Matrixd getInverseMatrix() const
{
return osg::Matrix::lookAt(_eye, _center, _up);
};
看看简单吧。那么现在的任务就是根据事件要来设置_eye了,因为根据我们的功能设想,_center始终是0,0,0,up始终是0,0,1。
2._Eye的更新在事件处理中,也就是另一个要实现的虚函数:virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us),我们定义了一个成员变量_theta,点A的时候就把它加一度,点D的时候就减一度。然后立即更新_eye的值,按半径是sqrt(55+55)来算:
//事件处理,我们要点击A就围着Z轴顺时针转动,点D就逆时针转动,转的时候始终朝0 0 0 点看着
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
{
if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
{
//若是A键
if ((ea.getKey() == 'A') || (ea.getKey() == 'a'))
{
_theta += 1.0;
}
if ((ea.getKey() == 'D') || (ea.getKey() == 'd'))
{
_theta -= 1.0;
}
_eye = osg::Vec3(_r * std::cos(osg::DegreesToRadians(_theta)), _r * std::sin(osg::DegreesToRadians(_theta)), 5.0);
}
return false;
}
3.关键代码就是上面那些。要写自己的操作器就是要看看自己按照事件怎么操作_eye,_center,_up这三个值了。多说一句,如果要让相机按着某个路径运动,可以使用AnimationPathManipulator类,它还支持将路径存在文件,然后读起来用。具体用法可以参考:applications/osgviewer/osgviewer.cpp这个程序的具体实现,主要看RecordCameraPathHandler这个类。
4.最后下面是这个示例的所有代码,大家直接拷贝就可以使用,是完整的,没有依赖的,依赖的axes.osgt是OSG自带的模型。就算读不了来只是轴不显示,也不影响其它的功能。
#include <osgViewer/viewer>
#include <osgDB/ReadFile>
#include <osg/Geode>
#include <osg/Geometry>
#include <osgGA/CameraManipulator>
class MyCameraManipulator : public osgGA::CameraManipulator
{
public:
MyCameraManipulator()
{
_theta = 0.0;
_center = osg::Vec3(0.0, 0.0, 0.0);
_up = osg::Vec3(0.0, 0.0, 1.0);
_r = std::sqrt(5 * 5 + 5 * 5);
_eye = osg::Vec3(_r*std::cos(osg::DegreesToRadians(_theta)), _r * std::sin(osg::DegreesToRadians(_theta)), 5.0);
}
//这三个纯虚函数本例不会使用
virtual void setByMatrix(const osg::Matrixd& matrix) {};
virtual void setByInverseMatrix(const osg::Matrixd& matrix) {};
virtual osg::Matrixd getMatrix() const { return osg::Matrix::identity(); };
//最关键的是这个,这个返回的就是ViewMatrix
virtual osg::Matrixd getInverseMatrix() const
{
return osg::Matrix::lookAt(_eye, _center, _up);
};
//事件处理,我们要点击A就围着Z轴顺时针转动,点D就逆时针转动,转的时候始终朝0 0 0 点看着
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
{
if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
{
//若是A键
if ((ea.getKey() == 'A') || (ea.getKey() == 'a'))
{
_theta += 1.0;
}
if ((ea.getKey() == 'D') || (ea.getKey() == 'd'))
{
_theta -= 1.0;
}
_eye = osg::Vec3(_r * std::cos(osg::DegreesToRadians(_theta)), _r * std::sin(osg::DegreesToRadians(_theta)), 5.0);
}
return false;
}
//视点位置
osg::Vec3d _eye;
//视点看向哪里
osg::Vec3d _center;
//头顶的朝向
osg::Vec3d _up;
//视点看向0 0 0的角度
float _theta;
//视点离0 0 0的距离
float _r;
};
osg::Node* createScene()
{
osg::Group* root = new osg::Group();
root->addChild(osgDB::readNodeFile("axes.osgt"));
osg::Geode* gnode = new osg::Geode;
root->addChild(gnode);
osg::Geometry* geom = new osg::Geometry;
gnode->addChild(geom);
osg::Vec3Array* vertex = new osg::Vec3Array;
geom->setVertexArray(vertex);
//沿xy平面画线,间隔0.5米,从-5,画到5
for (float i = -5; i <= 5; i += 0.5)
{
vertex->push_back(osg::Vec3(i, -5, 0));
vertex->push_back(osg::Vec3(i, 5, 0));
vertex->push_back(osg::Vec3(-5, i, 0));
vertex->push_back(osg::Vec3(5, i, 0));
}
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertex->size()));
osg::Vec4Array* color = new osg::Vec4Array();
color->push_back(osg::Vec4(0.7, 0.7, 0.7, 1.0));
geom->setColorArray(color, osg::Array::BIND_OVERALL);
geom->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
return root;
}
int main()
{
osgViewer::Viewer viewer;
viewer.setCameraManipulator(new MyCameraManipulator());
viewer.setSceneData(createScene());
return viewer.run();
}