OSG交互:选中场景模型并高亮显示

这篇具有很好参考价值的文章主要介绍了OSG交互:选中场景模型并高亮显示。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、目的

  可以在osg视图中选中指定模型实体,并高亮显示。共分为两种,一种鼠标点选,一种框选。

2、鼠标点选

2.1 功能说明

  1. 生成两组对象,一组cow对象可以被选中,另一组robot不能被选中;
  2. 点击cow对象被选中高亮,点击robot被选中不高亮;
  3. 点击空白处,弹出“select nothing!”提示未选择任何实体;

备注:
  存在bug:当点击一个cow时,通过osg::PositionAttitudeTransform矩阵变换节点生成的另一个牛也被高亮,后续抽空来研究原因解决处理。

2.2 效果

效果如下:
osg点选,OSG/OSGEarth开发,交互,osg,选中高亮

2.3 源码

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/LineWidth>
#include <osgViewer/Viewer>
#include <osgViewer/CompositeViewer>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers>  
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/LineWidth>
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers> 
#include <osgViewer/CompositeViewer> 
#include <osg/PositionAttitudeTransform> 
#include <osg/MatrixTransform> 
#include <osgFX/Scribe> 
#include <osgParticle/PrecipitationEffect> 
#include <osg/NodeCallback> 
#include <osg/DrawPixels> 
#include <osg/ShapeDrawable>
#include <osg/ComputeBoundsVisitor>
#include <osgGA/TrackballManipulator>
#include <osgGA/StateSetManipulator>
#include <osg/GraphicsContext>
#include <osgViewer/GraphicsWindow>
#include <iostream>
#include <osgFX/Scribe>
#include <osgFX/Outline>
#include <osgViewer/ViewerEventHandlers>

#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")
#pragma comment(lib, "osgSimd.lib")
#pragma comment(lib, "osgFXd.lib")

class nodePick :public osgGA::GUIEventHandler
{
	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
	{
		osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*> (&aa);
		switch (ea.getEventType())
		{
		case osgGA::GUIEventAdapter::PUSH:
		{
			osgUtil::LineSegmentIntersector::Intersections intersections;
			osg::ref_ptr<osg::Node> node = new osg::Node();
			osg::ref_ptr<osg::Group> parent = new osg::Group();
			osg::ref_ptr<osg::Group> group0 = dynamic_cast<osg::Group*>(viewer->getSceneData()->asGroup()->getChild(0));
			if (viewer->computeIntersections(ea.getX(), ea.getY(), intersections))
			{
				//得到选择的节点
				osgUtil::LineSegmentIntersector::Intersection intersection = *intersections.begin();
				osg::NodePath& nodePath = intersection.nodePath;
				node = nodePath.back();

				// 判定所属的分组
				osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(nodePath[2]);
				if (group->getName() == "groupCow")
				{
					//点击节点切换高亮
					parent = dynamic_cast<osg::Group*> (nodePath[nodePath.size() - 2]);//当前选择节点的父节点
					osgFX::Outline* ot = dynamic_cast<osgFX::Outline*>(parent.get());
					//若ot不存在(未高亮) (node->parent)=>(node->outline->parent)
					if (!ot) 
					{
						osg::ref_ptr<osgFX::Outline> outline = new osgFX::Outline();
						outline->setColor(osg::Vec4(1, 1, 0, 1));
						outline->setWidth(5);
						outline->addChild(node);
						parent->replaceChild(node, outline);
					}
					//若ot存在(高亮)找出当前outline的父节点(node->outline->*itr)=>(node->*itr)
					else
					{
						osg::Node::ParentList parentList = ot->getParents();
						osg::Node::ParentList::iterator itr = parentList.begin();
						(*itr)->replaceChild(ot, node);
					}
				}
			}
			else
			{
				std::cout << "select nothing!" << std::endl;
			}
		}
		default:
			return false;
		}
	}
};

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
	osg::ref_ptr<osg::Group> root = new osg::Group;

	osg::ref_ptr<osg::Group> groupCow = new osg::Group;
	groupCow->setName("groupCow");

	osg::ref_ptr<osg::Group> groupRobot = new osg::Group;
	groupRobot->setName("groupRobot");

	osg::ref_ptr<osg::Node> cowNode = osgDB::readNodeFile("D:\\OpenSceneGraph_Data\\cow.osg");
	osg::ref_ptr<osg::Node> robotNode = osgDB::readNodeFile("D:\\OpenSceneGraph_Data\\robot.osg");

	osg::ref_ptr<osg::PositionAttitudeTransform> cowpatNode = new osg::PositionAttitudeTransform();
	cowpatNode->setPosition(osg::Vec3(0, 0, 0));
	cowpatNode->addChild(cowNode.get());
	cowpatNode->setName("cow1");

	osg::ref_ptr<osg::PositionAttitudeTransform> cowpatNode2 = new osg::PositionAttitudeTransform();
	cowpatNode2->setPosition(osg::Vec3(10, 0, 0));
	cowpatNode2->addChild(cowNode.get());
	cowpatNode2->setName("cow2");

	osg::ref_ptr<osg::PositionAttitudeTransform> robotpatNode = new osg::PositionAttitudeTransform();
	robotpatNode->setPosition(osg::Vec3(0, 10, 0));
	robotpatNode->addChild(robotNode.get());

	osg::ref_ptr<osg::PositionAttitudeTransform> robotpatNode2 = new osg::PositionAttitudeTransform();
	robotpatNode2->setPosition(osg::Vec3(10, 10, 0));
	robotpatNode2->addChild(robotNode.get());

	groupCow->addChild(cowpatNode);
	groupCow->addChild(cowpatNode2);

	groupRobot->addChild(robotpatNode);
	groupRobot->addChild(robotpatNode2);

	root->addChild(groupCow);
	root->addChild(groupRobot);

	viewer->setSceneData(root.get());
	viewer->addEventHandler(new nodePick);
	viewer->addEventHandler(new osgViewer::WindowSizeHandler());//F键控制全/半屏

	viewer->run();
}

3、鼠标框选

  参照osgChina站长杨石兴的博客代码。

3.1 功能说明

  1. 生成20个圆球对象可被选中;
  2. 点击鼠标+ctrl键可以绘制框选;
  3. 框选实体变红;

备注:
  存在bug:卡顿、按F快捷键退出全屏,绘制出的框会偏离鼠标位置不随鼠标拖动、实用性不强需要改进。

3.2 效果

效果如下:
osg点选,OSG/OSGEarth开发,交互,osg,选中高亮文章来源地址https://www.toymoban.com/news/detail-754615.html

3.3 源码

#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Camera>
#include <osgGA/GUIEventHandler>
#include <osg/Geode>
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <algorithm>


//掩码为NODE_NPICK则不接受PICK,为NODE_PICK为接受PICK
#define NODE_NPICK ~0x0F
#define NODE_PICK 0x0F

//绘制一些球,用于被选择
class MySphere : public osg::Geode
{
public:
	MySphere(osg::Vec3 center, float radius)
	{
		_bSelect = false;
		_sd = new osg::ShapeDrawable(new osg::Sphere(center, radius));
		_sd->setColor(osg::Vec4(0.5, 0.5, 0.5, 1.0));
		addDrawable(_sd);
		setNodeMask(NODE_PICK);
	}

	//设置球是否被选择
	void setSelect(bool bSelect)
	{
		if (_bSelect == bSelect)
		{
			return;
		}

		_bSelect = bSelect;
		if (_bSelect)
		{
			_sd->setColor(osg::Vec4(1.0, 0.2, 0.2, 1.0));
			setNodeMask(NODE_NPICK);
		}
		else
		{
			_sd->setColor(osg::Vec4(0.5, 0.5, 0.5, 1.0));
			setNodeMask(NODE_PICK);
		}

		//重绘
		_sd->dirtyDisplayList();
	}

	osg::ShapeDrawable* _sd;
	bool _bSelect;
};

//用于选择的场景
osg::Node* g_selectNode = nullptr;

//绘制很多球
osg::Node* BuildScene()
{
	osg::Group* root = new osg::Group;

	//这些小球可以被选择
	g_selectNode = root;

	//绘制100个球
	for (int i = 0; i < 20; i++)
	{
		osg::Vec3 center(::rand() % 100, ::rand() % 100, ::rand() % 100);
		float r = ::rand() % 10 + 1.0;
		root->addChild(new MySphere(center, r));
	}

	return root;
}

//清空点选状态
void ClearSelect()
{
	for (int i = 0; i < 20; i++)
	{
		((MySphere*)g_selectNode->asGroup()->getChild(i))->setSelect(false);
	}
}

//结点的掩码,显示与隐藏
#define NODE_SHOW ~0x0
#define NODE_HIDE 0x0

//选择框做为一个全局变量,使用起来方便
osg::Geometry* g_geomSelectBox = new osg::Geometry;

//
osg::Camera* createHUD(osg::Viewport* vp)
{
	osg::Camera* camera = new osg::Camera;

	//设置投影矩阵为正交投影
	camera->setProjectionMatrix(osg::Matrix::ortho2D(0, vp->width(), 0, vp->height()));

	//设置其观察矩阵为单位矩阵,且不改变,该相机永远显示,也不用参与拣选
	camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
	camera->setViewMatrix(osg::Matrix::identity());

	//只清空深度缓存,使得其显示内容可以以主相机为背景
	camera->setClearMask(GL_DEPTH_BUFFER_BIT);

	//最后渲染,因为需要以主相机显示的内容为背景
	camera->setRenderOrder(osg::Camera::POST_RENDER);

	//不需要响应事件
	camera->setAllowEventFocus(false);

	//绘制选择框
	osg::Geode* gnode = new osg::Geode;
	camera->addChild(gnode);

	gnode->addDrawable(g_geomSelectBox);
	//设置透明
	osg::StateSet* ss = gnode->getOrCreateStateSet();
	ss->setMode(GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
	ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
	ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);

	//设置顶点
	osg::Vec3Array* vertices = new osg::Vec3Array;
	float depth = -0.1;
	vertices->push_back(osg::Vec3(0, 0, depth));
	vertices->push_back(osg::Vec3(100, 0, depth));
	vertices->push_back(osg::Vec3(100, 100, depth));
	vertices->push_back(osg::Vec3(0, 100, depth));
	g_geomSelectBox->setVertexArray(vertices);

	//设置颜色
	osg::Vec4Array* color = new osg::Vec4Array;
	color->push_back(osg::Vec4(0.8, 0.8, 0.8, 0.2));
	g_geomSelectBox->setColorArray(color, osg::Array::BIND_OVERALL);

	//绘制盒子
	g_geomSelectBox->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));

	return camera;
}

class MyEvent : public osgGA::GUIEventHandler
{
public:
	MyEvent() :osgGA::GUIEventHandler(),
		_xStart(0),
		_yStart(0)
	{
	}

	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
	{
		//左ctrl按着,又鼠标点击,则进入绘制状态
		if (ea.getEventType() == ea.PUSH) //
		{
			//判断左CTRL键是否按下
			if (ea.getModKeyMask() == ea.MODKEY_LEFT_CTRL)
			{
				_xStart = ea.getX();
				_yStart = ea.getY();
				//清空之前绘制的结果,这里仅隐藏即可
				g_geomSelectBox->setNodeMask(NODE_HIDE);

				//清空点选内容,一切重新开始
				_pickArea.clear();
				ClearSelect();

				//返回真代表后续事件处理器包括操作器不再处理该事件,就实现了拖动时场景不动
				return true;
			}
		}

		//左ctrl点下时,进入到选择状态,开始绘制选择框,且操作器不再处理鼠标拖动事件
		if (ea.getEventType() == ea.DRAG) //鼠标拖动,拖动是鼠标按键按下的时候移动鼠标
		{
			//判断左CTRL键是否按下
			if (ea.getModKeyMask() == ea.MODKEY_LEFT_CTRL)
			{
				//开始绘制,调整顶点参数就可以
				g_geomSelectBox->setNodeMask(NODE_SHOW);

				//获取顶点、更新顶点
				osg::Vec3Array* vertices = (osg::Vec3Array*)g_geomSelectBox->getVertexArray();
				int xEnd = ea.getX();
				int yEnd = ea.getY();
				float depth = -0.1;
				vertices->at(0).set(_xStart, _yStart, depth);
				vertices->at(1).set(xEnd, _yStart, depth);
				vertices->at(2).set(xEnd, yEnd, depth);
				vertices->at(3).set(_xStart, yEnd, depth);

				//重绘
				g_geomSelectBox->dirtyDisplayList();

				int xMin = _xStart > ea.getX() ? ea.getX() : _xStart;
				int xMax = _xStart < ea.getX() ? ea.getX() : _xStart;
				int yMin = _yStart > ea.getY() ? ea.getY() : _yStart;
				int yMax = _yStart < ea.getY() ? ea.getY() : _yStart;

				//将框选的区域压入到_pickArea
				for (int i = xMin; i <= xMax; i += 5)
				{
					for (int j = yMin; j <= yMax; j += 5)
					{
						if (!isPick(i, j))
						{
							_pickArea.push_back(osg::Vec2i(i, j));
							//进行pick
							osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
							osgUtil::LineSegmentIntersector::Intersections intersections;
							//只pick和NODE_PICK相&得true的
							if (view->computeIntersections(i, j, intersections, NODE_PICK))
							{
								for (osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin();
									iter != intersections.end(); iter++)
								{
									osg::NodePath np = iter->nodePath;

									MySphere* ms = dynamic_cast<MySphere*>(np.at(np.size() - 1));
									if (ms)
									{
										ms->setSelect(true);
									}
								}
							}
						}

					}
				}

				//返回真代表后续事件处理器包括操作器不再处理该事件,就实现了拖动时场景不动
				return true;
			}
		}

		return false;
	}

	//看一个点是不是pick过,返回true是已经pick过
	bool isPick(int x, int y)
	{
		for (int i = 0; i < _pickArea.size(); i++)
		{
			if ((_pickArea.at(i).x() == x) && (_pickArea.at(i).y() == y))
			{
				return true;
			}
		}

		return false;
	}

	int _xStart, _yStart;

	//鼠标按下去的时候,再拖动,就把框选的区域内的点都压进去,用于pick
	std::vector<osg::Vec2i> _pickArea;
};


int main()
{
	osgViewer::Viewer viewer;

	osg::Group* root = new osg::Group;

	root->addChild(BuildScene());

	viewer.setSceneData(root);
	viewer.realize();

	//realize之后,上下文已经被初始化,可以获得视口大小
	//在此处获得视口大小是为了创建HUD时使其视口大小与创建的一致
	osg::Viewport* vp = viewer.getCamera()->getViewport();
	root->addChild(createHUD(vp));

	viewer.addEventHandler(new MyEvent);
	viewer.addEventHandler(new osgViewer::WindowSizeHandler());//F键控制全/半屏

	return viewer.run();
}

到了这里,关于OSG交互:选中场景模型并高亮显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • opencv,opengl,osg,vulkan,webgL,opencL,cuda,osg,vtk,ogre的区别

    OpenCV OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。 它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很

    2024年02月13日
    浏览(39)
  • osg实现鼠标框选

    目录 1. 需求的提出 2. 具体实现      2.1. 禁止场景跟随鼠标转动      2.2. 矩形框前置绘制 3. 附加说明         3.1. 颜色设置说明         3.2.矩形框显示和隐藏的另一种实现        有时需要在屏幕通过按住键盘上的某个键如Ctrl键且按住鼠标左键,拖出一个矩形,实现框选

    2024年02月08日
    浏览(43)
  • osg+pbr-直射光

    算法是来自于learn opengl pbr相关部分, 只考虑如何移植到osg。 1,法线如何从局部坐标系到世界坐标系(逆转置矩阵) 2,视点通过漫游器获取, 3,视点uniform指针传递给漫游器,在漫游器中改变 为了说明正确性,先把视点设置为原点,按右方向键时正常。 可见,面向原点的

    2024年02月11日
    浏览(39)
  • OSG StatsHandler 初步学习

      osg为视景器的使用和调试提供了丰富的辅助组件,它们主要是以osg::ViewerBase的成员变量或交互事件处理器(osgGA::GUIEventHandler)的形式出现。osgViewer::StatsHandler、osg::Stats类就是其中的两个经常用到的辅助组件。   每按一下s键,视景窗口会多输出一些东西;控制台窗口也会输出

    2024年02月02日
    浏览(30)
  • OSG三维渲染引擎编程学习之八十八:“第八章:OSG文字” 之 “8.7 osgText3D”

    目录   第八章 OSG文字 8.7 osgText3D 8.7.1 osgText3D介绍 8.7.2 osgText3D实例       适当的文字信息对于显示场景信息是非常重要的。在OSG中,osgText提供了向场景中添加文字的强大功能,由于有第三方插件FreeType的支撑,可完全支持TrueType字体。       TrueType是由AppleComputer公司和Micro

    2024年02月13日
    浏览(46)
  • OpenSceneGraph几何基础教程【OSG】

    默认情况下,OSG 使用顶点数组法和显示列表法来渲染几何体。 但是,渲染策略可能会发生变化,具体取决于几何数据的呈现方式。 在本文中,我们将了解在 OSG 中处理几何体的基本技术。 OpenSceneGraph 后端的 OpenGL 使用几何图元(例如点、线、三角形和多边形面)来构建三维

    2024年02月04日
    浏览(24)
  • OSG粒子系统特效-----雨雪、爆炸、烟雾

    飞机坠毁 陨石坠落 源码: 源码:

    2024年02月07日
    浏览(37)
  • STM32LCD--基于HAL库(选中高亮?一文看懂如何玩转高亮显示)

    相关说明: 开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板) 开发环境: CubeMX+Keil5 第六届到第十二届省赛题中,高亮显示考察了四次,分别是第六、七、九、十届,第十一届和第十二届没有考察,今年考察几率仍然较大。 说明: 如何看这块LCD的坐标?将单片机 顺时针旋转

    2023年04月08日
    浏览(37)
  • CISSP-OSG-各章节书面实验整理

    1. 讨论和描述CIA 三元组。 CIA 三元组是保密性、完整性和可用性的结合。 保密性指用于确保保护数据、对象或资源的隐密性的措施。 完整性是保护数据可靠性和正确性的概念。 可用性指被授权的主体能及时不间断地访问对象。 CIA 三元组这个术语用来表示安全解决方案的三

    2024年02月06日
    浏览(30)
  • OSG文字-osgText3D(5)

            三维立体文字比二维平面文字显示效果更好,相对二维平面文字,它有非常好的立体显示效果。         在实际虚拟现实项目中,过多使用三维立体文字会降低染效率,加重渲染负担,相对平面二维文字,它占用的内存是非常大的。         osgText:: Text3D

    2024年01月19日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包