6.osg之实现高亮
我们将使用两种方法来实现这个功能:一种是当鼠标点击时,无论是否点击到模型,都会切换模型的轮廓显示;另一种是只有当鼠标点击到模型时,才会切换模型的轮廓显示。
方法一:鼠标点击切换轮廓显示
首先,我们来看第一种方法。以下是完整的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgFX/Outline>
class ClickHandler : public osgGA::GUIEventHandler
{
public:
ClickHandler(osg::Node* node) : _node(node) {}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&)
{
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::PUSH):
{
osgFX::Outline* outline = dynamic_cast<osgFX::Outline*>(_node->asGroup()->getChild(0));
if (outline)
{
outline->setEnabled(!outline->getEnabled());
}
return false;
}
default:
return false;
}
}
private:
osg::ref_ptr<osg::Node> _node;
};
int main(int argc, char** argv)
{
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cow.osg");
osg::ref_ptr<osgFX::Outline> outline = new osgFX::Outline;
outline->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); // 设置轮廓颜色为黄色
outline->addChild(model.get());
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(outline.get());
osgViewer::Viewer viewer;
viewer.addEventHandler(new ClickHandler(root.get())); // 添加点击事件处理器
viewer.setSceneData(root.get());
viewer.run();
return 0;
}
代码解析
- 包含头文件:我们首先包含了必要的头文件。
osgViewer/Viewer
是OSG的主要类,用于创建和运行一个3D场景。osgDB/ReadFile
用于从文件中读取模型。osgFX/Outline
是一个特效节点,用于在模型上添加轮廓。 - 创建事件处理器:我们创建了一个名为
ClickHandler
的事件处理器类,它继承自osgGA::GUIEventHandler
。这个类有一个成员变量_node
,用于存储我们要操作的节点。它还有一个handle
方法,这个方法会在发生GUI事件(如鼠标点击)时被调用。在这个方法中,我们检查事件的类型,如果是鼠标点击事件,就切换模型的轮廓显示状态。 - 加载模型:在
main
函数中,我们首先使用osgDB::readNodeFile
函数加载了一个模型。这个函数的参数是模型文件的路径。 - 创建轮廓节点:然后,我们创建了一个
osgFX::Outline
节点,并设置了轮廓的颜色为黄色。我们将模型添加到这个节点中,这样模型就会显示轮廓。 - 创建场景:我们创建了一个
osg::Group
节点作为场景的根节点,并将轮廓节点添加到根节点中。 - 创建并运行viewer:我们创建了一个
osgViewer::Viewer
对象,并添加了我们的事件处理器。然后,我们将场景设置到viewer中,并运行viewer。
方法二:鼠标点击模型切换轮廓显示
接下来,我们来看第二种方法。以下是完整的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgFX/Outline>
#include <osgUtil/LineSegmentIntersector>
class ClickHandler : public osgGA::GUIEventHandler
{
public:
ClickHandler(osg::Node* node, osgViewer::Viewer* viewer) : _node(node), _viewer(viewer) {}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&)
{
switch (ea.getEventType())
{
case(osgGA::GUIEventAdapter::PUSH):
{
float x = ea.getX();
float y = ea.getY();
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector =
new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y);
osgUtil::IntersectionVisitor iv(intersector.get());
_viewer->getCamera()->accept(iv);
if (intersector->containsIntersections())
{
osg::NodePath nodePath = intersector->getFirstIntersection().nodePath;
for (osg::NodePath::iterator it = nodePath.begin(); it != nodePath.end(); ++it)
{
osgFX::Outline* outline = dynamic_cast<osgFX::Outline*>(*it);
if (outline)
{
outline->setEnabled(!outline->getEnabled());
break;
}
}
}
return false;
}
default:
return false;
}
}
private:
osg::ref_ptr<osg::Node> _node;
osgViewer::Viewer* _viewer;
};
int main(int argc, char** argv)
{
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cow.osg");
osg::ref_ptr<osgFX::Outline> outline = new osgFX::Outline;
outline->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); // 设置轮廓颜色为黄色
outline->addChild(model.get());
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(outline.get());
osgViewer::Viewer viewer;
viewer.addEventHandler(new ClickHandler(root.get(), &viewer)); // 添加点击事件处理器
viewer.setSceneData(root.get());
viewer.run();
return 0;
}
代码解析
这段代码和第一种方法的代码大部分是相同的,不同的地方主要在ClickHandler
类的handle
方法中。在这个方法中,我们使用了OSG的拾取(picking)功能来检查鼠标是否点击到了模型。
- 创建拾取器:我们首先获取了鼠标点击的位置,然后创建了一个
osgUtil::LineSegmentIntersector
对象。这个对象是一个拾取器,它会创建一条从摄像机出发,经过鼠标点击位置的线段。 - 执行拾取操作:然后,我们创建了一个
osgUtil::IntersectionVisitor
对象,并将拾取器添加到这个访问器中。我们将这个访问器传递给摄像机,摄像机会将访问器传递给场景中的所有节点。在这个过程中,拾取器会检查线段是否与场景中的任何节点相交。 - 处理拾取结果:如果拾取器检测到了相交的节点,我们就获取这些节点,并检查它们是否是我们的模型节点。如果是,就切换模型的轮廓显示状态。
本文由作者按照 CC BY 4.0 进行授权