您的位置:首页 > 其它

osg 不同节点间的碰撞检查

2015-06-03 17:51 197 查看
目标是,视图中有很多节点,当两个节点间有碰撞,那么能够检测出来。(其中不存在运动模型)

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

解决思路是首先判断两个节点的包围盒(对象比较细长,不适合用包围球)是否有相交,如果没有相交,那么这两个节点肯定没有碰撞。如果包围盒有相交,然后用第一个节点上所有线段构成相交测试器(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())
{
// 有相交
}
}

}
}
}
}
}
}


其中经过测试发现,如果线段就在测试面上,那么相交测试结果为不相交,如果线段只有一个端点在面上,那么相交测试能够成功,但是如果线段与面垂直,那么相交结果点为两个,如果不垂直,那么相交结果点为一个就是那个端点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: