osg 不同节点间的碰撞检查

本文介绍了一种检测视图中节点碰撞的方法。首先通过包围盒判断节点是否可能碰撞,然后利用线段进行精确检测。文章提供了关键代码实现,并讨论了不同情况下的检测结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目标是,视图中有很多节点,当两个节点间有碰撞,那么能够检测出来。(其中不存在运动模型)

问题简化为检查两个节点是否碰撞。

解决思路是首先判断两个节点的包围盒(对象比较细长,不适合用包围球)是否有相交,如果没有相交,那么这两个节点肯定没有碰撞。如果包围盒有相交,然后用第一个节点上所有线段构成相交测试器(osgUtil::IntersectVisitor)与第二个节点进行相交测试,如果有相交,那么说明这两个节点是有碰撞的,如果没有相交,还要继续判断一下,因为有可能刚好第二个节点相交在第一个节点没有线段的位置。这个时候再取第二个节点上所有线段构成相交测试器与第一个节点进行相交测试。这次如果还是没有相交,那么说明这两个节点是没有碰撞的。

关键点1:获取一个节点的所有线段。思路为遍历节点上的所有顶点信息取出线段来。容易出错的是如果这个节点有位置变换,那么取顶点的时候需要考虑到。(其中假定所有节点都是由osg::Geometry构建出来,不考虑osg::ShapeDrawable)

关键代码如下


void GetAllLineSegment( osg::Geometry *pGeometry, osgUtil::IntersectVisitor *pIv, osg::Matrixd &matParent )
{
	if (pGeometry == NULL)
	{
		return;
	}osg::ShapeDrawable
	osg::ref_ptr<osg::Vec3Array> vertices = dynamic_cast<osg::Vec3Array*>(pGeometry->getVertexArray());
	if (vertices.valid())
	{
		unsigned int uPriNum = pGeometry->getNumPrimitiveSets();
		for (unsigned int i = 0; i < uPriNum; i++)
		{
			const osg::PrimitiveSet *pPriSet = pGeometry->getPrimitiveSet(i);

			for( unsigned int j = 0; j < pPriSet->getNumIndices()-1; j++ )
			{
				unsigned int iIndex1 = pPriSet->index(j);
				unsigned int iIndex2 = pPriSet->index(j+1);
				osg::ref_ptr<osg::LineSegment> ls = new osg::LineSegment();
				ls->set(vertices->at(iIndex1) * matParent, vertices->at(iIndex2) * matParent);
				pIv->addLineSegment(ls);
			}
		}
	}
}
void GetAllLineSegment( osg::Node *pNode, osgUtil::IntersectVisitor *pIv, osg::Matrixd &matParent )
{
	osg::Geode *pGeode = dynamic_cast<osg::Geode*>(pNode);
	if (pGeode)
	{
		unsigned int iDrawNum = pGeode->getNumDrawables();
		for(unsigned int i = 0; i < iDrawNum; i++ )
		{
			osg::Drawable *pDrawable = pGeode->getDrawable(i);
			GetAllLineSegment(pDrawable->asGeometry(), pIv, matParent);
		}
	}
	else 
	{
		osg::MatrixTransform *pMatrix = dynamic_cast<osg::MatrixTransform*>(pNode);
		if (pMatrix)
		{
			osg::Matrixd mat = pMatrix->getMatrix();
			osg::Matrixd matNow = mat * matParent;
			unsigned int uNum = pMatrix->getNumChildren();
			for (unsigned int i = 0; i < uNum; i++)
			{
				osg::Node *pChild = pMatrix->getChild(i);
				GetAllLineSegment(pChild, pIv, matNow);
			}
		}
		else
		{
			osg::Group *pGroup = dynamic_cast<osg::Group*>(pNode);
			if (pGroup)
			{
				unsigned int uNum = pGroup->getNumChildren();
				for (unsigned int i = 0; i < uNum; i++)
				{
					osg::Node *pChild = pGroup->getChild(i);
					GetAllLineSegment(pChild, pIv, matParent);
				}
			}
		}
	}
}


然后就是碰撞检查的关键代码了:


void HitDetection()
{
	unsigned int uNum = m_root->getNumChildren();
	unsigned int i = 0;
	for (i = 0; i < uNum-1; i++)
	{
		osg::Node *pNode1 = m_root->getChild(i);
		if (pNode1)
		{
			// 获取包围盒
			osg::ComputeBoundsVisitor boundvisitor ;  
			pNode1->accept(boundvisitor);  
			osg::BoundingBox& bb1 = boundvisitor.getBoundingBox(); 
			osg::ref_ptr<osgUtil::IntersectVisitor> iv = new osgUtil::IntersectVisitor();
			GetAllLineSegment(pNode1, iv);
			//iv->addLineSegment(new osg::LineSegment(osg::Vec3(50.0,20.0, 0.0), osg::Vec3(50, 50,50)));
			unsigned int j = i + 1;
			for (; j < uNum; j++)
			{
				osg::Node *pNode2 = m_root->getChild(j);
				if (pNode2)
				{
					osg::ComputeBoundsVisitor boundvisitor2 ;  
					pNode2->accept(boundvisitor2);
					osg::BoundingBox& bb2 = boundvisitor2.getBoundingBox();
					if (bb1.intersects(bb2))
					{
						// 包围盒有相交
						// 判断两个第一个节点的所有线是否和第二个节点有相交
						pNode2->accept(*iv);
						if (iv->hits())
						{
							//  有相交,下面的代码是获取相交点
							osgUtil::IntersectVisitor::LineSegmentHitListMap &mapList = iv->getSegHitList();
							osgUtil::IntersectVisitor::LineSegmentHitListMap::iterator mapIter = mapList.begin();
							for (mapIter = mapList.begin(); mapIter != mapList.end(); mapIter++)
							{
								osgUtil::IntersectVisitor::HitList hitList = mapIter->second;
								osgUtil::IntersectVisitor::HitList::iterator iter = hitList.begin();
								for (; iter != hitList.end(); iter++)
								{
									osg::Vec3 ptInter = iter->getWorldIntersectPoint();
									int cc = 0;
								}
							}
						}
						else
						{
							// 没有相交,用第二个节点的线段与第一个节点进行检查
							osg::ref_ptr<osgUtil::IntersectVisitor> iv2 = new osgUtil::IntersectVisitor();
							GetAllLineSegment(pNode2, iv2);
							pNode1->accept(*iv2);
							if (iv2->hits())
							{
								// 有相交
							}
						}

					}
				}
			}
		}
	}
}

其中经过测试发现,如果线段就在测试面上,那么相交测试结果为不相交,如果线段只有一个端点在面上,那么相交测试能够成功,但是如果线段与面垂直,那么相交结果点为两个,如果不垂直,那么相交结果点为一个就是那个端点。





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

TheDeaf

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值