说明
- OSG 入门看的,大佬绕道
- 示例来源于《OSG程序设计教程》
- 没有此电子书的小伙伴们,我已上传至CSDN
- 代码已经整理,并且一些错误也得到更正
1. 交互过程
2. 交互详解
函数功能:鼠标右键单击时牛和飞机都隐藏,鼠标左键双击时牛和飞机都显示,按键盘上面的 LEFT(方向左键) 键,显示牛, 按键盘上面的 RIGHT(方向右键) 键显示飞机。其中显示与隐藏结点我们使用的是 setNodeMask(bool)
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/Node>
class UseEventHandler : public osgGA::GUIEventHandler {
public:
// 实现虚函数,virtual 可以不写
// 这里有两个极其重要的参数,
// 一个是 const osgGA::GUIEventAdapter,这里 const 不能不写,
// 因为是虚函数,参数必须与父类中虚函数相应一致,该参数是用来识别各种事件类型的参数
// 第二个参数 osgGA::GUIActionAdapter,它是控制显示的参数,
// 其实最重要的是它是 Viewer 的祖父类,由它可以得到 viewer,在操作中有体现
virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) {
// 由参数二 osgGA::GUIActionAdapter 得到了 viewer
osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer *>(&aa);
if (!viewer) return false; // 没有创建成功的话直接退出
switch (ea.getEventType()) {
case osgGA::GUIEventAdapter::KEYDOWN: // 键盘事件
if (ea.getKey() == 0xFF51) { // 键盘左键
viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(0);
viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(1);
}
if (ea.getKey() == 0xFF53) { // 键盘右键
viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(0);
viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(1);
}
break;
case osgGA::GUIEventAdapter::PUSH: // 单击
if (ea.getButton() == 4) { // 右键
viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(0);
viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(0);
}
break;
case osgGA::GUIEventAdapter::DOUBLECLICK: // 双击
if (ea.getButton() == 1) { // 左键
viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(1);
viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(1);
}
break;
default:
break;
}
return false;
}
};
void main() {
osgViewer::Viewer viewer;
osg::ref_ptr<osg::Group> root = new osg::Group();
root->addChild(osgDB::readNodeFile("cow.osg"));
root->addChild(osgDB::readNodeFile("glider.osg"));
viewer.setSceneData(root.get());
viewer.addEventHandler(new UseEventHandler);
viewer.realize();
viewer.run();
}
- OSG 中常用的事件类型
代码 | 值 | 事件类型 |
---|---|---|
NONE | 0 | 无事件 |
PUSH | 1 | 鼠标某键按下 |
RELEASE | 2 | 鼠标某键弹起 |
DOUBLECLICK | 4 | 鼠标某键双击 |
DRAG | 8 | 鼠标某键拖动 |
MOVE | 16 | 鼠标移动 |
KEYDOWN | 32 | 键盘上某键按下 |
KEYUP | 64 | 键盘上某键弹起 |
FRAME | 128 | 应该是鼠标每帧 |
RESIZE | 256 | 窗口大小改变时会有的事件 |
SCROLL | 512 | 鼠标轮滚动 |
PEN_PRESSURE | 1024 | 手写板的某事件 |
PEN_PROXIMITY_ENTER | 2048 | 手写板的某事件 |
PEN_PROXIMITY_LEAVE | 4096 | 手写板的某事件 |
CLOSE_WINDOWS | 8192 | 关闭窗口 |
QUIT_APPLICATION | 16384 | 退出程序 |
USER | 32768 | 用户定义 |
一般视为 1 代表左键,2 代表中键,4 代表右键
- OSG 中支持的键盘码值
3. pick
- 程序功能:场景是一飞机和一头加白边的牛。点击牛后牛白边会消失,为了简单起见没有做成单击消失,单击再出来这种效果。给物体加白边用的是 osgFX::Scribe 类,它是专门做这个的。要加边只要把结点加入到它下面,然后它再加到原先该结点要加的结点下面就加边了,看示例代码就清楚了
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/Group>
#include <osgFX/Scribe>
#include <osgGA/GUIEventHandler>
#include <osgUtil/LineSegmentIntersector> // 新增头文件
class CPickHandler : public osgGA::GUIEventHandler {
public:
CPickHandler(osgViewer::Viewer *viewer) : mViewer(viewer) {}
virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) {
switch (ea.getEventType()) {
case(osgGA::GUIEventAdapter::PUSH):
if (ea.getButton() == 1) { // 左键按下
// 传入的两个参数分别为点击时的 XY 的屏幕坐标
// 在屏幕上坐标是从 0\0 到 1\1 因此从原理上可以表示无限个点
Pick(ea.getX(), ea.getY()); // 调用函数
}
return true;
}
return false;
}
protected:
void Pick(float x, float y) {
// 申请一个相交测试的结果集,判断屏幕与场景相交后,得出的结果集放入此中
osgUtil::LineSegmentIntersector::Intersections intersections;
if (mViewer->computeIntersections(x, y, intersections)) {
// 申请一个结果集遍历器,遍历该结果集
for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr) {
// 注意这里取的是 back()
if (!hitr->nodePath.empty() && !(hitr->nodePath.back()->getName().empty())) {
// 得到遍历器中的 nodepath,以此可以判断该 path 中是否有想要的结点
const osg::NodePath &np = hitr->nodePath;
// 如果结果集中有所需要的结点,则设置隐藏该结点
// 其中有一个动态转换,如果可以转换成功则左值不为 NULL,否则为 NULL。
for (int i = np.size() - 1; i >= 0; --i) {
// 添加一个 scribe 结点,该结点下的模型会被加白描线高亮显示
osgFX::Scribe *sc = dynamic_cast<osgFX::Scribe *>(np[i]);
if (sc != NULL) {
if (sc->getNodeMask() != 0)
sc->setNodeMask(0);
}
}
}
}
}
}
osgViewer::Viewer *mViewer; // mViewer
};
int main(int, char **) {
osgViewer::Viewer viewer;
osg::ref_ptr<osg::Group> root = new osg::Group();
root->addChild(osgDB::readNodeFile("cessna.osg"));
osg::ref_ptr<osg::Node> cow = osgDB::readNodeFile("cow.osg");
osg::ref_ptr<osgFX::Scribe> sc = new osgFX::Scribe();
sc->addChild(cow.get());
root->addChild(cow.get());
root->addChild(sc.get());
viewer.setSceneData(root.get());
viewer.addEventHandler(new CPickHandler(&viewer));
viewer.realize();
viewer.run();
return 0;
}
这个说白了就是单击鼠标左键去除白边