osgEarth拾取点、线、面(借助于osgEarth::IntersectionPicker)

进行人机交互时,经常需要拾取场景中的点、线、面,而osg中提供了LineSegmentIntersector和PolytopeIntersector两种相交检测方法。

LineSegmentIntersector的实现原理是利用射线与场景求交,因此只能检测射线与平面是否相交;

PolytopeIntersector的实现原理是构造一个五个面的视锥体,检测在场景中与视锥体相交的物体。

本文中使用osgEarth::IntersectionPicker实现点、线、面的求交。

下面的代码既可以拾取单个物体,又可以拾取多个物体,还可以取消拾取。

鼠标左键单击:拾取单个物体;

ctrl+鼠标左键单击:拾取多个物体;

鼠标右键单击:取消所有拾取到的物体;

ctrl+鼠标右键单击:取消单个拾取到的物体。

#pragma once
#include <osgGA/GUIEventHandler>
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <vector>
#include<set>
class PickHandler:public osgGA::GUIEventHandler
{
public:
	PickHandler(osgText::Text* updateText) :
		_updateText(updateText) {
		_isFirst = true;
		_pointSphere = new osg::Group;
	}
	~PickHandler() {}

	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
	//拾取
	virtual void pick(osgViewer::View* view, const osgGA::GUIEventAdapter& ea,bool IsSelect=true);

	void setLabel(const std::string& name)
	{
		if (_updateText.get()) _updateText->setText(name);
	}
protected:

private:
	osg::ref_ptr<osg::Group> _root;//作相交检测的根节点
	std::set<osg::ref_ptr<osg::Geometry>> _allGeom;//拾取到的图形
	osg::ref_ptr<osg::Group> _pointSphere;//新绘制的球形点
	osg::ref_ptr<osgText::Text>  _updateText;
	osgViewer::Viewer* _viewer;
	bool _isFirst;
	std::map<osg::Geometry*, std::vector<osg::Geode*>> _mapSphere;
};
#include "CIntersectPLF.h"
#include<osgEarth/IntersectionPicker>
#include <osg/ShapeDrawable>
#include <osg/Material>
#include <osgEarth/GLUtils>
using namespace osgEarth;
bool PickHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
	_viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
	_root = _viewer->getSceneData()->asGroup();
	if (_isFirst && _root)
	{
		_root->addChild(_pointSphere);
		_isFirst = false;
	}
	//左键单选
	if (ea.getEventType()==ea.PUSH)
	{
		if (ea.getButtonMask()==ea.LEFT_MOUSE_BUTTON)
		{
			//继续选择
			if (ea.getModKeyMask() == ea.MODKEY_LEFT_CTRL)
			{
				pick(_viewer, ea);
				return false;
			}
			else//重新选择一个
			{
				//清空原有的内容
				//清除包围球
				_pointSphere->removeChildren(0, _pointSphere->getNumChildren());
				_mapSphere.clear();
				//清除透明面片和物体
				for (auto itor=_allGeom.begin();itor!=_allGeom.end();itor++)
				{
					osg::StateSet* state = (*itor)->getOrCreateStateSet();
					state->setMode(GL_BLEND, osg::StateAttribute::ON);
				}
				_allGeom.clear();
				pick(_viewer, ea);
				return false;
			}
		}
		else if(ea.getButtonMask()==ea.RIGHT_MOUSE_BUTTON)
		{
			//单个取消
			if (ea.getModKeyMask() == ea.MODKEY_LEFT_CTRL)
			{
				pick(_viewer, ea, false);
				return false;
			}
			else//全部取消
			{
				//清空所有内容 移除球、移除透明
				_pointSphere->removeChildren(0, _pointSphere->getNumChildren());
				_mapSphere.clear();
				//清除透明面片和物体
				for (auto itor = _allGeom.begin(); itor != _allGeom.end(); itor++)
				{
					osg::StateSet* state = (*itor)->getOrCreateStateSet();
					state->setMode(GL_BLEND, osg::StateAttribute::OFF);
				}
				_allGeom.clear();
				return false;
			}
		}
		
	}
    return false;
}

void PickHandler::pick(osgViewer::View* view, const osgGA::GUIEventAdapter& ea, bool isSelect)
{
	std::string gdlist = "";
	IntersectionPicker picker(view, _root);
	IntersectionPicker::Hits hits;
	if (picker.pick(ea.getX(), ea.getY(), hits))
	{
		for (auto itor = hits.begin(); itor != hits.end(); itor++)
		{
			std::ostringstream os;
			if (!itor->nodePath.empty() && !(itor->nodePath.back()->getName().empty()))
			{
				os << "Object \"" << itor->nodePath.back()->getName() << "\"" << std::endl;
			}
			else if (itor->drawable.valid())
			{
				os << "Object \"" << itor->drawable->className() << "\"" << std::endl;
			}

			gdlist += os.str();
			osg::Node* node = itor->nodePath.back();
			if (itor->nodePath.back()->getName() == "point")
			{
				osg::Drawable* drawable = itor->drawable;
				osg::Geometry* geomTemp = itor->drawable->asGeometry();
				if (isSelect)
				{
					_allGeom.insert(drawable->asGeometry());
					auto vertex = drawable->asGeometry()->getVertexArray();
					unsigned index = vertex->getNumElements();
					const GLvoid* pointer = vertex->getDataPointer();
					osg::Vec3* p = (osg::Vec3*)pointer;
					std::vector<osg::Geode*> tmpGeode;
					for (int i = 0; i < index; i++)
					{
						osg::Sphere* shape = new osg::Sphere(p[i], 0.01);
						osg::Geode* geode = new osg::Geode();
						osg::ShapeDrawable* _shapeDrawable = new osg::ShapeDrawable(shape);
						_shapeDrawable->setDataVariance(osg::Object::DYNAMIC);
						geode->addDrawable(_shapeDrawable);
						geode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
						GLUtils::setLighting(geode->getOrCreateStateSet(), osg::StateAttribute::OFF);
						_pointSphere->addChild(geode);
						tmpGeode.push_back(geode);
					}
					_mapSphere.insert(std::pair<osg::Geometry*, std::vector<osg::Geode*>>(geomTemp, tmpGeode));
				}
				else
				{
					//恢复成未选择的状态
					auto itorGeom = _allGeom.find(geomTemp);
					if (itorGeom != _allGeom.end())
					{
						std::vector<osg::Geode*> DeleteGeode = _mapSphere[geomTemp];
						for (int i = 0; i < DeleteGeode.size(); i++)
						{
							_pointSphere->removeChild(DeleteGeode[i]);
						}
						_mapSphere.erase(geomTemp);
						_allGeom.erase(itorGeom);
					}
				}

			}
			else
			{
				osg::Geometry* geom = itor->drawable->asGeometry();
				if (isSelect)
				{
					osg::StateSet* state = geom->getOrCreateStateSet();
					state->setMode(GL_BLEND, osg::StateAttribute::ON);
					osg::ref_ptr<osg::Material> mat = new osg::Material;
					//漫发射光
					mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(1.0, 1.0, 1.0, 0.5));
					//环境光
					mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(1.0, 1.0, 1.0, 0.5));
					//设置材质的混合光颜色
					mat->setTransparency(osg::Material::FRONT_AND_BACK, 0.5);
					state->setAttributeAndModes(mat, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
					state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
					_allGeom.insert(geom);
				}
				else
				{
					auto itorGeom = _allGeom.find(geom);
					if (itorGeom != _allGeom.end())
					{
						//先从
						osg::StateSet* state = geom->getOrCreateStateSet();
						state->setMode(GL_BLEND, osg::StateAttribute::OFF);
						_allGeom.erase(geom);
					}
				}

			}
		}
	}
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值