目录
1. 需求的提出
osg下如何将物体拉近拉远?有两种方法:
- 保持相机不动,将物体拉近或拉远。
- 保持物体不动,将相机拉近或拉远。
2. 第1种方法
在将物体也即模型加入到视图时,先要将物体加入到MatrixTranform节点中去,如下:
osg::ref_ptr<osg::MatrixTransform> spMatrixTransform = new osg::MatrixTransform;
spMatrixTransform ->addChild(m_spNode);
然后调用tranlate拉近或拉远物体,代码如下:
osg::ref_ptr< osg::Node> spRootNode = m_pViewer->getSceneData();
if (nullptr == spRootNode)
{
return;
}
osg::ref_ptr<osg::Transform> spTransformNode = spRootNode->asTransform();
if (nullptr == spTransformNode)
{
return;
}
osg::ref_ptr<osg::MatrixTransform> spMatrixTransformNode = spTransformNode->asMatrixTransform();
if (nullptr == spMatrixTransformNode)
{
return;
}
static int a = 500;
osg::Matrix curMatrix = spMatrixTransformNode->getMatrix();
curMatrix *= osg::Matrix::translate(0, a, 0); // 更改a值也即y值实现拉近拉远
spMatrixTransformNode->setMatrix(curMatrix);
可以写个事件处理类,在该类中实现按下键盘a键,拉远物体;按下键盘s键拉近物体,整个实现代码如下:
#include<osgViewer/Viewer>
#include<osgGA/GUIEventHandler>
#include<osgDB/ReadFile>
#include<osg/MatrixTransform>
using namespace osgGA;
class moveNearOrFarHandler : public GUIEventHandler
{
public:
moveNearOrFarHandler(const osg::ref_ptr< osg::MatrixTransform>& matTrans)
{
_matTrans = matTrans;
}
private:
virtual bool handle(osgGA::Event* event, osg::Object* object, osg::NodeVisitor* nv) override
{
auto pGUIEvent = (GUIEventAdapter*)event;
if (GUIEventAdapter::KEYDOWN == pGUIEvent->getEventType())
{
auto keyValue = pGUIEvent->getKey();
if ( ('a' == keyValue) || ('A' == keyValue) )
{
const osg::Matrix& mat = _matTrans->getMatrix();
_matTrans->setMatrix( mat * osg::Matrix::translate(0.0, _moveOffset, 0.0));
}
else if ( ('s' == keyValue) || ('S' == keyValue) )
{
const osg::Matrix& mat = _matTrans->getMatrix();
_matTrans->setMatrix(mat * osg::Matrix::translate(0.0, -_moveOffset, 0.0));
}
}
return false;
}
private:
osg::ref_ptr< osg::MatrixTransform > _matTrans;
float _moveOffset{0.5}; // 每次移动的距离
};
int main(int argc, char *argv[])
{
auto pCowNode = osgDB::readNodeFile("cow.osg");
if (nullptr == pCowNode)
{
OSG_WARN << "node is null!";
return 1;
}
auto pRoot = new osg::Group;
auto pMatrixTrans = new osg::MatrixTransform;
pMatrixTrans->addChild(pCowNode);
pRoot->addChild(pMatrixTrans);
osgViewer::Viewer viewer;
viewer.addEventHandler(new moveNearOrFarHandler(pMatrixTrans));
viewer.setSceneData(pRoot);
return viewer.run();
}
注意:考虑到大小写的问题,如果按下a、s键没反应,请先按一下shift键,再按a、s键试试。
3. 第2种方法
从osgGA::CameraManipulator类派生一个自己的操控器,重载操控器的Handle函数,在该函数中更改视点的位置,具体代码参见:自定义一个简单的操控器类 博文。
4. 其它方法
如果相机操控器支持鼠标滚轮滚动时,场景能随滚轮滚动而拉近拉远,则可以模拟鼠标滚轮滚动来使场景拉近拉远。如 osgGA::TrackballManipulator操控器,如下代码就是模拟滚轮滚动从而使场景拉近拉远:
#include<osgViewer/Viewer>
#include<osgGA/GUIEventHandler>
#include<osgDB/ReadFile>
#include<osg/MatrixTransform>
#include<osgGA/TrackballManipulator>
using namespace osgGA;
class moveNearOrFarHandler : public GUIEventHandler
{
private:
virtual bool handle(const GUIEventAdapter&ea, GUIActionAdapter&aa) override
{
if (GUIEventAdapter::KEYDOWN == ea.getEventType())
{
auto keyValue = ea.getKey();
if ( ('a' == keyValue) || ('A' == keyValue) )
{
// 模拟鼠标滚轮朝向人滚动,以便场景离人显得更近些
auto pViewer = (osgViewer::Viewer*)(&aa);
pViewer->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_DOWN);
}
else if ( ('s' == keyValue) || ('S' == keyValue) )
{
// 模拟鼠标滚轮背向人滚动,以便场景离人显得更远些
auto pViewer = (osgViewer::Viewer*)(&aa);
pViewer->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_UP);
}
}
return false;
}
};
int main(int argc, char* argv[])
{
auto pCowNode = osgDB::readNodeFile("cow.osg");
if (nullptr == pCowNode)
{
OSG_WARN << "node is null!";
return 1;
}
auto pRoot = new osg::Group;
pRoot->addChild(pCowNode );
osgViewer::Viewer viewer;
viewer.addEventHandler(new moveNearOrFarHandler());
viewer.setSceneData(pRoot);
// 设置操控器为跟踪球操控器,这句代码也可以不要,没有设置操控器,则默认为跟踪球操控器
viewer.setCameraManipulator(new osgGA::TrackballManipulator);
return viewer.run();
}