OSG编程指南<二十二>:基于OSG+Qt制作三维可视化软件客户端

本文档介绍如何结合OSG与Qt构建一个三维可视化软件,涵盖模型导入导出、场景管理、高亮显示、节点添加删除等功能。通过在Qt中嵌入OSG视图窗口,实现视图更新、变形处理、模型显示隐藏等关键技巧。
摘要由CSDN通过智能技术生成

1、概述

  经过前面很多章节课程的学习,我们基本上对于OSG的基础知识已经有了大概的了解。有些人可能已经迫不及待想要开发自己的三维可视化软件了。本节我们就制作一个简版的三维可视化软件。效果包括如下:

1) 利用开源库Assimpsdk实现目前多种主流三维格式的导入导出;
2)OSG场景管理器,可以管理场景数据;
3)鼠标双击高亮模型,显示模型属性信息;
4)动态添加场景模型节点和删除节点;
5)控制场景模型的显示隐藏;
6)双击场景节点自动聚焦视点至模型并高亮显示;

2、效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、关键技术点

3.1 Qt中嵌入OSG视图窗口

  利用UI设计器拖入QOpenGLWidget 控件,然后右键提升为osgQOpenGLWidget 类,进行初始化即可;

connect(ui.openGLWidget, &osgQOpenGLWidget::initialized, this, &QAssimpWidget::slotInitOsgWindow);
void QAssimpWidget::slotInitOsgWindow()
{
	m_pOsgViewer = ui.openGLWidget->getOsgViewer();
	m_pRootNode = new osg::Group;
	m_pRootNode->setName("root");
	m_pRootNode->addChild(createCornerAxis());
	m_pOsgViewer->setSceneData(m_pRootNode);
	m_pOsgViewer->setCameraManipulator(new osgGA::TrackballManipulator());
	m_pOsgViewer->addEventHandler(new CustomNodePick);
}

在这里插入图片描述

3.2 OSG视图更新

  当OsgViewer视图中新增模型之后,我们需要重新计算场景的视点并进行更新,参照OSG源码的处理如下:

void QAssimpWidget::updataViewer()
{
	// 计算新的场景外包
	osg::BoundingSphere boundingSphere;
	osg::ComputeBoundsVisitor cbVisitor;
	m_pRootNode->accept(cbVisitor);
	osg::BoundingBox& bb = cbVisitor.getBoundingBox();
	……………………………省略…………
	double radius = osg::maximum(double(boundingSphere.radius()), 1e-6);
	// 更新相机位置,俯视角度观察模型
	m_pOsgViewer->setCameraManipulator(NULL);
	osgGA::CameraManipulator* pManipulator = new osgGA::TrackballManipulator();
	m_pOsgViewer->setCameraManipulator(pManipulator);
}

3.3 OSG视图变形处理

  当OsgViewer视图中新增模型之后,由于设计的视图宽高比例不一定为4:3,会出现模型变形的情况,也需要进行处理。

void QAssimpWidget::updataViewer()
{
	//根据分辨率确定合适的投影来保证显示的图形不变形
	double fovy, aspectRatio, zNear, zFar;
	m_pOsgViewer->getCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio, zNear, zFar);
	double newAspectRatio = double(ui.openGLWidget->width()) / double(ui.openGLWidget->height());
	double aspectRatioChange = newAspectRatio / aspectRatio;
	if (aspectRatioChange != 1.0)
	{
		//设置投影矩阵
		m_pOsgViewer->getCamera()->getProjectionMatrix() *= osg::Matrix::scale(1.0 / aspectRatioChange, 1.0, 1.0);
	}
}

3.4 OSG清空场景

  当Osg视图进行清理时候,需要删除所有数据。

// 清空场景
	while (true)
	{
		int nCount = m_pRootNode->getNumChildren();
		if (nCount == 1)
		{
			break;
		}

		int nIndex = m_pRootNode->getNumChildren() - 1;
		if (m_pRootNode->getChild(nIndex)->getName() == "Axis")
		{
			continue;
		}

		m_pRootNode->removeChild(nIndex);
	}

3.5 OSG控制模型显示隐藏

  当Osg视图进行场景节点勾选和取消时,可以控制模型数据的显示和隐藏。

for (unsigned int i = 0; i < m_pRootNode->getNumChildren(); i++)
{
	if (m_pRootNode->getChild(i)->getName() == sGuid.toStdString())
	{
		int nNodeMask = m_pRootNode->getChild(i)->getNodeMask();
		m_pRootNode->getChild(i)->setNodeMask((nNodeMask == 0) ? 1 : 0);
		break;
	}
}

3.6 OSG添加删除节点

  Osg视图添加及删除节点。

//添加
{
		osg::ref_ptr<osg::Node> pNode = osgDB::readNodeFile(sFileName.toStdString());
		pNode->setName(sGuid);
		m_pRootNode->addChild(pOutline);
}

//删除
	QList<QTreeWidgetItem*> selectedItems = getAllSelectedItems(ui.treeWidget);
	for (QTreeWidgetItem* pItem : selectedItems) 
	{
		QString sGuid = pItem->data(0, Qt::UserRole + 1).toString();
		for (unsigned int i = 0; i < m_pRootNode->getNumChildren(); i++)
		{
			if (m_pRootNode->getChild(i)->getName() == sGuid.toStdString())
			{
				m_pRootNode->removeChild(i);
				break;
			}
		}
	}

3.7 OSG双击模型高亮显示

  Osg视图中双击模型可以选中模型高亮。重载osgGA::GUIEventHandler的DOUBLECLICK消息,利用射线求交方式得到模型,并把模型轮廓osgFX::Outline显示出来。
在这里插入图片描述

// 处理鼠标点击选中模型高亮
class CustomNodePick :public osgGA::GUIEventHandler
{
	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
	{
		osgViewer::Viewer* pViewer = dynamic_cast<osgViewer::Viewer*> (&aa);
		switch (ea.getEventType())
		{
		case osgGA::GUIEventAdapter::DOUBLECLICK:
		{
			osgUtil::LineSegmentIntersector::Intersections intersections;
			osg::ref_ptr<osg::Node> pNode = new osg::Node();
			if (pViewer->computeIntersections(ea.getX(), ea.getY(), intersections))
			{
				//得到选择的节点
				osgUtil::LineSegmentIntersector::Intersection intersection = *intersections.begin();
				osg::NodePath& nodePath = intersection.nodePath;
				pNode = nodePath[nodePath.size() - 2];
				std::string sName = pNode->getName();
				sName += "outline";

				osg::ref_ptr<osg::Group> pGroup = dynamic_cast<osg::Group*>(pViewer->getSceneData());
				for (unsigned int i = 0; i < pGroup->getNumChildren(); i++)
				{
					if (pGroup->getChild(i)->getName() == sName)
					{
						int nNodeMask = pGroup->getChild(i)->getNodeMask();
						pGroup->getChild(i)->setNodeMask((nNodeMask == 0) ? 1 : 0);
						break;
					}
				}
			}
			else
			{
				osg::ref_ptr<osg::Group> pGroup = dynamic_cast<osg::Group*>(pViewer->getSceneData());
				for (unsigned int i = 0; i < pGroup->getNumChildren(); i++)
				{
					std::string sName = pGroup->getChild(i)->getName();
					if (sName.find("outline") != std::string::npos)
					{
						pGroup->getChild(i)->setNodeMask(0);
					}
				}
			}
		}

		default:
		return false;
		}
	}
};

3.8 OSG模型定位

  Osg视图中双击模型可以将模型聚焦到当前视图中来。

		if (pNode)
		{
			// 更新相机位置
			osg::BoundingSphere boundingSphere = pNode->getBound();
			double radius = osg::maximum(double(boundingSphere.radius()), 1e-6);
			double dist = 3.5f * radius;
			m_pOsgViewer->setCameraManipulator(NULL);
			osgGA::CameraManipulator* pManipulator = new osgGA::TrackballManipulator();
			pManipulator->setHomePosition(boundingSphere.center() + osg::Vec3d(0, -dist, 0), boundingSphere.center(), osg::Vec3d(0.0f, 0.0f, 1.0f));
			m_pOsgViewer->setCameraManipulator(pManipulator);
		}

4、源码下载

源码下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欧特克_Glodon

很高兴能帮助到您!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值