查看: 4743|回复: 11

OSG 碰撞检测之多面体求交器代码解读(PloytopeIntersector)(上)

[复制链接]

该用户从未签到

发表于 2009-8-2 15:41:03 | 显示全部楼层 |阅读模式
本帖最后由 acmiyou 于 2009-8-2 15:45 编辑

关于碰撞检测,始终是物理系统在图形学运用上的一个比较复杂的问题.碰撞检测做的好不好.完全决定一个场景漫游的逼真性.
     这几天,在坐城市汽车仿真处理上,对于驱动汽车运动时候,对于汽车的碰撞检测问题困扰了我相当的久.始终没能做到很好.这当中我现在使用的汽车包围体对场景进行求交测试时候,用到了OSG中PolytopeIntersector.
     对于一个Polytope 应当是多个平面组成的一个空间区域,对于OSG求交器当中,这个多面体各个平面的正面必须都是属于这个区域内的? 怎么解释呢.平面的法线应当是指向该空间区域内部.比如说一个四面体.其内部的四个面应当是正面(法线朝向的方向那个面.) 这是多面体求交器使用的一个关键.(这在之后的代码解读当中会一并解释).

     对于OSG场景求交测试,是必然要用到访问器的(Vistor.这个应当在在另辟一篇文章才能详述清楚,它的使用原理,因此这里我们暂时先用着.) 对于求交器使用到的访问器(Vistor)应当是交集访问器(IntersectionVistor).

    因此,我们在定义上则应当是如下:
  1. /**//** 创建一个多面体求交器*/
  2.     osgUtil::PolytopeIntersector* pI =new osgUtil::PolytopeIntersector(poly);
  3.     /**//** 构造一个交集访问器*/
  4.     osgUtil::IntersectionVisitor iv(pI);    对于求交集 则应当对于场景根节点做请求访问的操作..这当中可能需要避开自身节点等一些不必要的节点等.
  5.     /**//** 设置避开自身节点*/
  6.     _model->setNodeMask(0x0);
  7.     /**//** 根节点请求访问操作*/
  8.     root->accept(iv);
  9.     /**//** 恢复自身节点的NodeMask*/
  10.     _model->setNodeMask(0xffffffff);
复制代码
对于setNodeMask()避开节点等.我想应当在Vistor中在详述..
     再对于访问操作之后,我们就可以获得所返回的交集了.
  1.          if(pI->containsIntersections())
  2.     {
  3.         typedef osgUtil::PolytopeIntersector::Intersections inters;
  4.         for(inters::iterator it=pI->getIntersections().begin();\
  5.             it!=pI->getIntersections().end();it++)
  6.             /**//** ……*/
  7.     }
复制代码
固然,这些只是相对于简单的操作.而我们是想要深入到了解在root->accept(iv)之后到底做了什么事情?它到底如何求得了我们想要的数据? 那现在开始我们的代码解读之旅……当然这其中,我想有必要略去一些关系到Vistor的内容.因为这些详述起来,不是简短的能够说的清楚...


     现在我们定位到: osgUtil/IntersectionVisitor.cpp 第226行:
  1. void IntersectionVisitor::apply(osg::Geode& geode)
  2. {
  3.     // osg::notify(osg::NOTICE)<<"apply(Geode&)"<<std::endl;

  4.     if (!enter(geode)) return;

  5.     // osg::notify(osg::NOTICE)<<"inside apply(Geode&)"<<std::endl;

  6.     for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
  7.     {
  8.         intersect( geode.getDrawable(i) );
  9.     }

  10.     leave();
  11. }
复制代码
因为geode是叶子节点,最后肯定都会请求到它,并访问..其中的代码我们将能够非常直观的看出它将要干嘛?
对于geode下的所有可绘制图元进行求交.因此我们现在将转到 intersect函数

    定位到: include/osgUtil/IntersectionVisitor.h 第245行:
  1. inline void intersect(osg::Drawable* drawable) { _intersectorStack.back()->intersect(*this, drawable); }
复制代码
关于_intersectorStack 是个求交器的集合,我们在构造的时候将PolytopeIntersector传入后将会被加入到这个集合当中..因此 这将会回到PolytopeIntersector中的intersect函数..因此,我们又得重新打开polytope那个文件咯..
    定位到 osgUtil/PolyIntersector.cpp 第547行..
  1. void PolytopeIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable)
  2. {
  3.     /**//** 之上部分省略……*/
  4. osg::TemplatePrimitiveFunctor<PolytopeIntersectorUtils::PolytopePrimitiveIntersector> func;
  5.     func.setPolytope( _polytope, _referencePlane );
  6.     func.setDimensionMask( _dimensionMask );
  7.     drawable->accept(func);
  8.     /**//** 之下部分省略……*/
  9. }
复制代码
我们可以看到再用 PolytopePrimitiveIntersector 构造了一个func 后(并设置多面体,和参考平面) 对于drawable进行访问操作?似乎又回到vistor...? 其实这个只是类似的操作,但还算不上vistor..暂时当作类似的对待吧..虽然Vistor模式在OSG中的运用非常的多..而且几乎处处都会用到..这个时候我们将要进入一个关键时刻,因为我们知道.在osg中drawable里头已经是最后的顶点等所有数据存放的地方.drawable其实只是个抽象类.这里我只会简单的通过它的一个特例:Geometry 来讲述这一段内容..
所以现在  我们将定位在osg/Geometry.cpp 第2199行:
  1. void Geometry::accept(PrimitiveFunctor& functor) const
  2. {
  3.     if (!_vertexData.array.valid() || _vertexData.array->getNumElements()==0) return;

  4.     if (!_vertexData.indices.valid())
  5.     {
  6.         switch(_vertexData.array->getType())
  7.         {
  8.            /** 部分省略……*/
  9.         case(Array::Vec3ArrayType):
  10.             functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec3*>(_vertexData.array->getDataPointer()));
  11.          /** 部分省略……*/
  12.         }
  13.         for(PrimitiveSetList::const_iterator itr=_primitives.begin();
  14.             itr!=_primitives.end();
  15.             ++itr)
  16.         {
  17.             (*itr)->accept(functor);
  18.         }
  19.      }
  20.     /** 后面对于存在索引数组的暂时省略……*/
  21. }
复制代码
对于这个操作,我们暂时只看不存在索引数据的..因为相对于来讲原理总是一样的.后面的只是多了一些步骤将顶点数据取出..好了.我们回到正题.
       functor.setVecterArray() 很直观的明白,将顶点数据存到fuctor里.以便于在之后functor操作.
后续(下篇)

该用户从未签到

发表于 2009-8-2 18:55:13 | 显示全部楼层
嗯,加精华支持~~

该用户从未签到

发表于 2009-8-3 22:06:31 | 显示全部楼层
支持一下~~

该用户从未签到

发表于 2009-11-5 10:45:42 | 显示全部楼层
赞一个!

不过我觉得效率是一个问题,一是 visitor, 二是 PrimitiveFunctor.
对于 static geometry, 不会 transform 的结点,分开处理会好点

该用户从未签到

发表于 2009-11-5 11:50:50 | 显示全部楼层
效率上的影响应该说还是比较轻微的。当然访问器会比顺序访问和递归要慢上一点点,不过通常来说可以忽略。

该用户从未签到

发表于 2009-11-9 08:28:02 | 显示全部楼层
很顶斑竹

该用户从未签到

发表于 2009-12-22 12:53:16 | 显示全部楼层
学习了

该用户从未签到

发表于 2011-3-17 09:04:14 | 显示全部楼层
分析的挺透彻的!

该用户从未签到

发表于 2011-3-17 13:05:18 | 显示全部楼层
支持加精啊!

该用户从未签到

发表于 2013-3-25 20:48:05 | 显示全部楼层
谢谢

该用户从未签到

发表于 2013-6-3 17:47:47 | 显示全部楼层
多谢楼主
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表