1、目的
可以在osg视图中选中指定模型实体,并高亮显示。共分为两种,一种鼠标点选,一种框选。
2、鼠标点选
2.1 功能说明
- 生成两组对象,一组cow对象可以被选中,另一组robot不能被选中;
- 点击cow对象被选中高亮,点击robot被选中不高亮;
- 点击空白处,弹出“select nothing!”提示未选择任何实体;
备注:
存在bug:当点击一个cow时,通过osg::PositionAttitudeTransform矩阵变换节点生成的另一个牛也被高亮,后续抽空来研究原因解决处理。
2.2 效果
效果如下:
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 功能说明
- 生成20个圆球对象可被选中;
- 点击鼠标+ctrl键可以绘制框选;
- 框选实体变红;
备注:
存在bug:卡顿、按F快捷键退出全屏,绘制出的框会偏离鼠标位置不随鼠标拖动、实用性不强需要改进。文章来源:https://www.toymoban.com/news/detail-754615.html
3.2 效果
效果如下:
文章来源地址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模板网!