文章

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;
}

代码解析

  1. 包含头文件:我们首先包含了必要的头文件。osgViewer/Viewer是OSG的主要类,用于创建和运行一个3D场景。osgDB/ReadFile用于从文件中读取模型。osgFX/Outline是一个特效节点,用于在模型上添加轮廓。
  2. 创建事件处理器:我们创建了一个名为ClickHandler的事件处理器类,它继承自osgGA::GUIEventHandler。这个类有一个成员变量_node,用于存储我们要操作的节点。它还有一个handle方法,这个方法会在发生GUI事件(如鼠标点击)时被调用。在这个方法中,我们检查事件的类型,如果是鼠标点击事件,就切换模型的轮廓显示状态。
  3. 加载模型:在main函数中,我们首先使用osgDB::readNodeFile函数加载了一个模型。这个函数的参数是模型文件的路径。
  4. 创建轮廓节点:然后,我们创建了一个osgFX::Outline节点,并设置了轮廓的颜色为黄色。我们将模型添加到这个节点中,这样模型就会显示轮廓。
  5. 创建场景:我们创建了一个osg::Group节点作为场景的根节点,并将轮廓节点添加到根节点中。
  6. 创建并运行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)功能来检查鼠标是否点击到了模型。

  1. 创建拾取器:我们首先获取了鼠标点击的位置,然后创建了一个osgUtil::LineSegmentIntersector对象。这个对象是一个拾取器,它会创建一条从摄像机出发,经过鼠标点击位置的线段。
  2. 执行拾取操作:然后,我们创建了一个osgUtil::IntersectionVisitor对象,并将拾取器添加到这个访问器中。我们将这个访问器传递给摄像机,摄像机会将访问器传递给场景中的所有节点。在这个过程中,拾取器会检查线段是否与场景中的任何节点相交。
  3. 处理拾取结果:如果拾取器检测到了相交的节点,我们就获取这些节点,并检查它们是否是我们的模型节点。如果是,就切换模型的轮廓显示状态。
本文由作者按照 CC BY 4.0 进行授权