OSG 碰撞检测之多面体求交器代码解读(PloytopeIntersector)
2015-10-16 15:49
591 查看
转载地址http://www.cppblog.com/acmiyou/archive/2009/08/02/91942.html
关于碰撞检测,始终是物理系统在图形学运用上的一个比较复杂的问题.碰撞检测做的好不好.完全决定一个场景漫游的逼真性.
这几天,在坐城市汽车仿真处理上,对于驱动汽车运动时候,对于汽车的碰撞检测问题困扰了我相当的久.始终没能做到很好.这当中我现在使用的汽车包围体对场景进行求交测试时候,用到了OSG中PolytopeIntersector.
对于一个Polytope 应当是多个平面组成的一个空间区域,对于OSG求交器当中,这个多面体各个平面的正面必须都是属于这个区域内的? 怎么解释呢.平面的法线应当是指向该空间区域内部.比如说一个四面体.其内部的四个面应当是正面(法线朝向的方向那个面.) 这是多面体求交器使用的一个关键.(这在之后的代码解读当中会一并解释).
对于OSG场景求交测试,是必然要用到访问器的(Vistor.这个应当在在另辟一篇文章才能详述清楚,它的使用原理,因此这里我们暂时先用着.) 对于求交器使用到的访问器(Vistor)应当是交集访问器(IntersectionVistor).
因此,我们在定义上则应当是如下:
再对于访问操作之后,我们就可以获得所返回的交集了.
固然,这些只是相对于简单的操作.而我们是想要深入到了解在root->accept(iv)之后到底做了什么事情?它到底如何求得了我们想要的数据? 那现在开始我们的代码解读之旅……当然这其中,我想有必要略去一些关系到Vistor的内容.因为这些详述起来,不是简短的能够说的清楚...
现在我们定位到: osgUtil/IntersectionVisitor.cpp 第226行:
因为geode是叶子节点,最后肯定都会请求到它,并访问..其中的代码我们将能够非常直观的看出它将要干嘛?对于geode下的所有可绘制图元进行求交.因此我们现在将转到 intersect函数
定位到: include/osgUtil/IntersectionVisitor.h 第245行:
关于_intersectorStack 是个求交器的集合,我们在构造的时候将PolytopeIntersector传入后将会被加入到这个集合当中..因此 这将会回到PolytopeIntersector中的intersect函数..因此,我们又得重新打开polytope那个文件咯..
定位到 osgUtil/PolyIntersector.cpp 第547行..
我们可以看到再用 PolytopePrimitiveIntersector 构造了一个func 后(并设置多面体,和参考平面) 对于drawable进行访问操作?似乎又回到vistor...? 其实这个只是类似的操作,但还算不上vistor..暂时当作类似的对待吧..虽然Vistor模式在OSG中的运用非常的多..而且几乎处处都会用到..这个时候我们将要进入一个关键时刻,因为我们知道.在osg中drawable里头已经是最后的顶点等所有数据存放的地方.drawable其实只是个抽象类.这里我只会简单的通过它的一个特例:Geometry
来讲述这一段内容..
所以现在 我们将定位在osg/Geometry.cpp 第2199行:
对于这个操作,我们暂时只看不存在索引数据的..因为相对于来讲原理总是一样的.后面的只是多了一些步骤将顶点数据取出..好了.我们回到正题.
functor.setVecterArray() 很直观的明白,将顶点数据存到fuctor里.以便于在之后functor操作.
其后最主要的还是在于对于drawable里的每个primitiveset 进行接受fuctor访问操作 (*itr)->accept(functor);
我们知道.primitiveset里头拥有的数据是顶点最后绘制的规则. 相当于我们在OPENGL当中使用glBegin() glEnd()一样指定最后基础图元生成的规则.而我们所要求交集的目的在于获得跟这些基础图元的交集.因此.我们有必要继续往下深究.我们还没有嗅到最终结果,还不能够放弃.
好了 继续..PrimitiveSet又是一个虚类.因此,我们有必要挑个实体类来深究.就选DrawArray吧. DrawArray指定一个MODE,顶点的起始位置,以及参与绘制的顶点的总数目..
MODE 就相当于 GL_LINES GL_TRIANGLES 等等.我们再次回到代码来说吧.
这次我们将定位在: osg/PrimitiveSet.cpp 第43行:
很简单...
所以最终的结果 都将回到fuctor里头进行交集运算的处理..._mode _first _count 将拥有的规则送往fuctor..
在追究了这么多之后,我们又需要回到functor里头.这个functor 是什么呢? 还记得我之前说的使用PolytopePrimitiveIntersector 构造了一个func对不? 所有的关键将在那里揭开....最后的结果总还是深藏于原来的最初的起点位置..不过我想还真不枉绕了一圈...
在我们回到func 之前我们还需要深究下functor.drawArrays() 这个函数到底做了什么? 因为在PolytopePrimitiveIntersector当初我们并未发现有这个函数.PolytopePrimitiveIntersector这个类是在PolytopeIntersector.cpp文件当中定义的.它只有一大堆的operator()操作...因此我们需要回到构造它的那个functor()里头..
现在我将定位到 include/osg/TemplatePrimitiveFunctor.h 第90行..
对于此,我们暂时只观看最简单的GL_TRIANGLES
,对于三角形的每三个点将会绘制一个三角形.因此每次只取三个顶点,将它传递给当前构造的func0>operator()处理.这就是为什么 func里头全部是都是operator()操作了..
我们弄明白这些之后,马上回到PolytopePrimitiveIntersector 最后的结果.令人期待啊...
PolytopePrimitiveIntersector中的operator()支持很多种类型,.参数的不同,一个点(points)(两个点)lines,三个点(Triangles),四个点(quads)
最后定位在三角形的处理上: osgUtil/PolytopeIntersector.cpp 第208行.
这段代码相当的长,但是看起来非常的好理解.这里我也将解释为什么对于多面体在定义的时候法线很重要了? 我想我有必要将这部分代码全部解读清楚..这部分是关键.
现在将做最后的代码解读工作 selector_mask 当前操作平面编号的标记 inside_mask 标记三角形在哪些平面的正面?即所说在区域内..对于所有平面,将进行如下操作:
1. d1 d2 d3 分别求得 ax+by+cz+d < = > 0
[ax+by+cz >0 表示点在正面这边,=0 表示点平面上,<0则表示在背面这边]
若三个点都在某个平面的背面..那说明这个三角形肯定在这个多面体的区域外.则结束..
若三个点都在某个平面的正面,则做标记并继续其他平面.
2. 若不是以上两种情况,那分别判断v1v2 v1v3 v2v3这三条线段的与平面的交点.并加入至候选顶点列表当中.
在对所有平面都进行操作之后,需要判断几种情况我们可以考虑?
第一.三角形刚好在多面体内部.
第二.可能这些交点落在其他平面的背面了.
第三 可能三条边与平面是存在交点.但是多面体的组成的闭合区域却刚好穿过三角形内部.这个时候必须对平面的交线与三角形求交点..
所以这三个部分完全概括了上面的代码?是的.我想这个部分并不需要我讲的有多么详细了.很容易理解的.
其后,我还想深究下最后这个交集会存放到哪里去了?我们最终该如何使用获得交集才能够更好被我们所利用?
addIntersection(_index, _candidates);
对于每处理一个三角形 _index 都会在开头部分自增..因此 对于Intersections中的每一个交集的点都针对于同一个三角形..(对于别的同理可得?) 也就是说_index表示在primitiveSet当中.这个三角形是第几个三角形.(三角形序号)
最后,我们再次回到我们最开始进入这么大段篇幅讨论的起始位置吧?还记得否?我们第二个intersect()函数..就是PolytopeIntersector类中的..因为我们最后的结果总会回归到我们需要的地方.所以我们现在得回到那里去取得我们最终获得的数据/.
对于从func中获得的交集.我们将需要将它变成我们所需要的数据.我将一一解释最终我们得到的每个数据的含义:
hit.distance // 表示从当前这个交集的所有顶点的中心点到参考平面的距离.
hit.primitiveIndex //表示之前我们说的这个图元在PrimitiveSet中的序号.
hit.nodepath //表示这个从根结点到当前这个geode的路径..因为我们知道在vistor中我们有pushNodepath() popNodePath()来保存这个路径操作..所以这个路径是从vistor中获得的.
hit.drawable //当然是我们保存着当前这个交集是对于哪个drawable.
hit.matrix //表示当前这个drawable应当在世界坐标系的变换矩阵.我们可以使用point*matrix 来得到获得点在世界坐标系下的位置..
hit.localIntersecotPoint //表示所有交点的中心点.
hit.intersectorPoint //所有交点的一个数组..目前最多的顶点个数应该是6.. enum { MaxNumIntesectionPoints=6 };
hit.numintersectorPoint // 所有顶点的个数..
我想这个解读过程到此应当结束了...继续学习ING.....
关于碰撞检测,始终是物理系统在图形学运用上的一个比较复杂的问题.碰撞检测做的好不好.完全决定一个场景漫游的逼真性.
这几天,在坐城市汽车仿真处理上,对于驱动汽车运动时候,对于汽车的碰撞检测问题困扰了我相当的久.始终没能做到很好.这当中我现在使用的汽车包围体对场景进行求交测试时候,用到了OSG中PolytopeIntersector.
对于一个Polytope 应当是多个平面组成的一个空间区域,对于OSG求交器当中,这个多面体各个平面的正面必须都是属于这个区域内的? 怎么解释呢.平面的法线应当是指向该空间区域内部.比如说一个四面体.其内部的四个面应当是正面(法线朝向的方向那个面.) 这是多面体求交器使用的一个关键.(这在之后的代码解读当中会一并解释).
对于OSG场景求交测试,是必然要用到访问器的(Vistor.这个应当在在另辟一篇文章才能详述清楚,它的使用原理,因此这里我们暂时先用着.) 对于求交器使用到的访问器(Vistor)应当是交集访问器(IntersectionVistor).
因此,我们在定义上则应当是如下:
/** 创建一个多面体求交器*/ osgUtil::PolytopeIntersector* pI =new osgUtil::PolytopeIntersector(poly); /** 构造一个交集访问器*/ osgUtil::IntersectionVisitor iv(pI); 对于求交集 则应当对于场景根节点做请求访问的操作..这当中可能需要避开自身节点等一些不必要的节点等. /** 设置避开自身节点*/ _model->setNodeMask(0x0); /** 根节点请求访问操作*/ root->accept(iv); /** 恢复自身节点的NodeMask*/ _model->setNodeMask(0xffffffff);对于setNodeMask()避开节点等.我想应当在Vistor中在详述..
再对于访问操作之后,我们就可以获得所返回的交集了.
if(pI->containsIntersections()) { typedef osgUtil::PolytopeIntersector::Intersections inters; for(inters::iterator it=pI->getIntersections().begin();\ it!=pI->getIntersections().end();it++) /** ……*/ }
固然,这些只是相对于简单的操作.而我们是想要深入到了解在root->accept(iv)之后到底做了什么事情?它到底如何求得了我们想要的数据? 那现在开始我们的代码解读之旅……当然这其中,我想有必要略去一些关系到Vistor的内容.因为这些详述起来,不是简短的能够说的清楚...
现在我们定位到: osgUtil/IntersectionVisitor.cpp 第226行:
void IntersectionVisitor::apply(osg::Geode& geode) { // osg::notify(osg::NOTICE)<<"apply(Geode&)"<<std::endl; if (!enter(geode)) return; // osg::notify(osg::NOTICE)<<"inside apply(Geode&)"<<std::endl; for(unsigned int i=0; i<geode.getNumDrawables(); ++i) { intersect( geode.getDrawable(i) ); } leave(); }
因为geode是叶子节点,最后肯定都会请求到它,并访问..其中的代码我们将能够非常直观的看出它将要干嘛?对于geode下的所有可绘制图元进行求交.因此我们现在将转到 intersect函数
定位到: include/osgUtil/IntersectionVisitor.h 第245行:
inline void intersect(osg::Drawable* drawable) { _intersectorStack.back()->intersect(*this, drawable); }
关于_intersectorStack 是个求交器的集合,我们在构造的时候将PolytopeIntersector传入后将会被加入到这个集合当中..因此 这将会回到PolytopeIntersector中的intersect函数..因此,我们又得重新打开polytope那个文件咯..
定位到 osgUtil/PolyIntersector.cpp 第547行..
void PolytopeIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable) { /** 之上部分省略……*/ osg::TemplatePrimitiveFunctor<PolytopeIntersectorUtils::PolytopePrimitiveIntersector> func; func.setPolytope( _polytope, _referencePlane ); func.setDimensionMask( _dimensionMask ); drawable->accept(func); /** 之下部分省略……*/ }
我们可以看到再用 PolytopePrimitiveIntersector 构造了一个func 后(并设置多面体,和参考平面) 对于drawable进行访问操作?似乎又回到vistor...? 其实这个只是类似的操作,但还算不上vistor..暂时当作类似的对待吧..虽然Vistor模式在OSG中的运用非常的多..而且几乎处处都会用到..这个时候我们将要进入一个关键时刻,因为我们知道.在osg中drawable里头已经是最后的顶点等所有数据存放的地方.drawable其实只是个抽象类.这里我只会简单的通过它的一个特例:Geometry
来讲述这一段内容..
所以现在 我们将定位在osg/Geometry.cpp 第2199行:
void Geometry::accept(PrimitiveFunctor& functor) const { if (!_vertexData.array.valid() || _vertexData.array->getNumElements()==0) return; if (!_vertexData.indices.valid()) { switch(_vertexData.array->getType()) { case(Array::Vec2ArrayType): functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec2*>(_vertexData.array->getDataPointer())); break; case(Array::Vec3ArrayType): functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec3*>(_vertexData.array->getDataPointer())); break; case(Array::Vec4ArrayType): functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec4*>(_vertexData.array->getDataPointer())); break; case(Array::Vec2dArrayType): functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec2d*>(_vertexData.array->getDataPointer())); break; case(Array::Vec3dArrayType): functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec3d*>(_vertexData.array->getDataPointer())); break; case(Array::Vec4dArrayType): functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec4d*>(_vertexData.array->getDataPointer())); break; default: notify(WARN)<<"Warning: Geometry::accept(PrimitiveFunctor&) cannot handle Vertex Array type"<<_vertexData.array->getType()<<std::endl; return; } for(PrimitiveSetList::const_iterator itr=_primitives.begin(); itr!=_primitives.end(); ++itr) { (*itr)->accept(functor); } } /** *//** 后面对于存在索引数组的暂时省略……*/ }
对于这个操作,我们暂时只看不存在索引数据的..因为相对于来讲原理总是一样的.后面的只是多了一些步骤将顶点数据取出..好了.我们回到正题.
functor.setVecterArray() 很直观的明白,将顶点数据存到fuctor里.以便于在之后functor操作.
其后最主要的还是在于对于drawable里的每个primitiveset 进行接受fuctor访问操作 (*itr)->accept(functor);
我们知道.primitiveset里头拥有的数据是顶点最后绘制的规则. 相当于我们在OPENGL当中使用glBegin() glEnd()一样指定最后基础图元生成的规则.而我们所要求交集的目的在于获得跟这些基础图元的交集.因此.我们有必要继续往下深究.我们还没有嗅到最终结果,还不能够放弃.
好了 继续..PrimitiveSet又是一个虚类.因此,我们有必要挑个实体类来深究.就选DrawArray吧. DrawArray指定一个MODE,顶点的起始位置,以及参与绘制的顶点的总数目..
MODE 就相当于 GL_LINES GL_TRIANGLES 等等.我们再次回到代码来说吧.
这次我们将定位在: osg/PrimitiveSet.cpp 第43行:
很简单...
void DrawArrays::accept(PrimitiveFunctor& functor) const { functor.drawArrays(_mode,_first,_count); }
所以最终的结果 都将回到fuctor里头进行交集运算的处理..._mode _first _count 将拥有的规则送往fuctor..
在追究了这么多之后,我们又需要回到functor里头.这个functor 是什么呢? 还记得我之前说的使用PolytopePrimitiveIntersector 构造了一个func对不? 所有的关键将在那里揭开....最后的结果总还是深藏于原来的最初的起点位置..不过我想还真不枉绕了一圈...
在我们回到func 之前我们还需要深究下functor.drawArrays() 这个函数到底做了什么? 因为在PolytopePrimitiveIntersector当初我们并未发现有这个函数.PolytopePrimitiveIntersector这个类是在PolytopeIntersector.cpp文件当中定义的.它只有一大堆的operator()操作...因此我们需要回到构造它的那个functor()里头..
现在我将定位到 include/osg/TemplatePrimitiveFunctor.h 第90行..
对于此,我们暂时只观看最简单的GL_TRIANGLES
,对于三角形的每三个点将会绘制一个三角形.因此每次只取三个顶点,将它传递给当前构造的func0>operator()处理.这就是为什么 func里头全部是都是operator()操作了..
我们弄明白这些之后,马上回到PolytopePrimitiveIntersector 最后的结果.令人期待啊...
PolytopePrimitiveIntersector中的operator()支持很多种类型,.参数的不同,一个点(points)(两个点)lines,三个点(Triangles),四个点(quads)
最后定位在三角形的处理上: osgUtil/PolytopeIntersector.cpp 第208行.
这段代码相当的长,但是看起来非常的好理解.这里我也将解释为什么对于多面体在定义的时候法线很重要了? 我想我有必要将这部分代码全部解读清楚..这部分是关键.
void operator()(const Vec3_type v1, const Vec3_type v2, const Vec3_type v3, bool treatVertexDataAsTemporary) { ++_index; if ((_dimensionMask & PolytopeIntersector::DimTwo) == 0) return; PlaneMask selector_mask = 0x1; PlaneMask inside_mask = 0x0; _candidates.clear(); for(PlaneList::const_iterator it=_planes.begin(); it!=_planes.end(); ++it, selector_mask <<= 1) { const osg::Plane& plane=*it; const value_type d1=plane.distance(v1); const value_type d2=plane.distance(v2); const value_type d3=plane.distance(v3); const bool d1IsNegative = (d1<0.0f); const bool d2IsNegative = (d2<0.0f); const bool d3IsNegative = (d3<0.0f); if (d1IsNegative && d2IsNegative && d3IsNegative) return; // triangle outside if (!d1IsNegative && !d2IsNegative && !d3IsNegative) { inside_mask |= selector_mask; continue; // completly inside } // edge v1-v2 intersects if (d1==0.0f) { _candidates.push_back( CandList_t::value_type(selector_mask, v1) ); } else if (d2==0.0f) { _candidates.push_back( CandList_t::value_type(selector_mask, v2) ); } else if (d1IsNegative && !d2IsNegative) { _candidates.push_back( CandList_t::value_type(selector_mask, (v1-(v2-v1)*(d1/(-d1+d2))) ) ); } else if (!d1IsNegative && d2IsNegative) { _candidates.push_back( CandList_t::value_type(selector_mask, (v1+(v2-v1)*(d1/(d1-d2))) ) ); } // edge v1-v3 intersects if (d3==0.0f) { _candidates.push_back( CandList_t::value_type(selector_mask, v3) ); } else if (d1IsNegative && !d3IsNegative) { _candidates.push_back( CandList_t::value_type(selector_mask, (v1-(v3-v1)*(d1/(-d1+d3))) ) ); } else if (!d1IsNegative && d3IsNegative) { _candidates.push_back( CandList_t::value_type(selector_mask, (v1+(v3-v1)*(d1/(d1-d3))) ) ); } // edge v2-v3 intersects if (d2IsNegative && !d3IsNegative) { _candidates.push_back( CandList_t::value_type(selector_mask, (v2-(v3-v2)*(d2/(-d2+d3))) ) ); } else if (!d2IsNegative && d3IsNegative) { _candidates.push_back( CandList_t::value_type(selector_mask, (v2+(v3-v2)*(d2/(d2-d3))) ) ); } } if (_plane_mask==inside_mask) { // triangle lies inside of all planes _candidates.push_back( CandList_t::value_type(_plane_mask, v1) ); _candidates.push_back( CandList_t::value_type(_plane_mask, v2) ); _candidates.push_back( CandList_t::value_type(_plane_mask, v3) ); addIntersection(_index, _candidates); return; } if (_candidates.empty() && _planes.size()<3) return; unsigned int numCands=checkCandidatePoints(inside_mask); if (numCands>0) { addIntersection(_index, _candidates); return; } // handle case where the polytope goes through the triangle // without containing any point of it LinesList& lines=getPolytopeLines(); _candidates.clear(); // check all polytope lines against the triangle // use algorithm from "Real-time rendering" (second edition) pp.580 const Vec3_type e1=v2-v1; const Vec3_type e2=v3-v1; for (LinesList::const_iterator it=lines.begin(); it!=lines.end(); ++it) { const PlanesLine& line=*it; Vec3_type p=line.dir^e2; const value_type a=e1*p; if (osg::absolute(a)<eps()) continue; const value_type f=1.0f/a; const Vec3_type s=(line.pos-v1); const value_type u=f*(s*p); if (u<0.0f || u>1.0f) continue; const Vec3_type q=s^e1; const value_type v=f*(line.dir*q); if (v<0.0f || u+v>1.0f) continue; const value_type t=f*(e2*q); _candidates.push_back(CandList_t::value_type(line.mask, line.pos+line.dir*t)); } numCands=checkCandidatePoints(inside_mask); if (numCands>0) { addIntersection(_index, _candidates); return; } }
现在将做最后的代码解读工作 selector_mask 当前操作平面编号的标记 inside_mask 标记三角形在哪些平面的正面?即所说在区域内..对于所有平面,将进行如下操作:
1. d1 d2 d3 分别求得 ax+by+cz+d < = > 0
[ax+by+cz >0 表示点在正面这边,=0 表示点平面上,<0则表示在背面这边]
若三个点都在某个平面的背面..那说明这个三角形肯定在这个多面体的区域外.则结束..
若三个点都在某个平面的正面,则做标记并继续其他平面.
2. 若不是以上两种情况,那分别判断v1v2 v1v3 v2v3这三条线段的与平面的交点.并加入至候选顶点列表当中.
在对所有平面都进行操作之后,需要判断几种情况我们可以考虑?
第一.三角形刚好在多面体内部.
第二.可能这些交点落在其他平面的背面了.
第三 可能三条边与平面是存在交点.但是多面体的组成的闭合区域却刚好穿过三角形内部.这个时候必须对平面的交线与三角形求交点..
所以这三个部分完全概括了上面的代码?是的.我想这个部分并不需要我讲的有多么详细了.很容易理解的.
其后,我还想深究下最后这个交集会存放到哪里去了?我们最终该如何使用获得交集才能够更好被我们所利用?
addIntersection(_index, _candidates);
对于每处理一个三角形 _index 都会在开头部分自增..因此 对于Intersections中的每一个交集的点都针对于同一个三角形..(对于别的同理可得?) 也就是说_index表示在primitiveSet当中.这个三角形是第几个三角形.(三角形序号)
最后,我们再次回到我们最开始进入这么大段篇幅讨论的起始位置吧?还记得否?我们第二个intersect()函数..就是PolytopeIntersector类中的..因为我们最后的结果总会回归到我们需要的地方.所以我们现在得回到那里去取得我们最终获得的数据/.
for(PolytopeIntersectorUtils::Intersections::const_iterator it=func.intersections.begin(); it!=func.intersections.end(); ++it) { const PolytopeIntersectorUtils::PolytopeIntersection& intersection = *it; Intersection hit; hit.distance = intersection._distance; hit.maxDistance = intersection._maxDistance; hit.primitiveIndex = intersection._index; hit.nodePath = iv.getNodePath(); hit.drawable = drawable; hit.matrix = iv.getModelMatrix(); osg::Vec3 center; for (unsigned int i=0; i<intersection._numPoints; ++i) { center += intersection._points[i]; } center /= float(intersection._numPoints); hit.localIntersectionPoint = center; hit.numIntersectionPoints = intersection._numPoints; std::copy(&intersection._points[0], &intersection._points[intersection._numPoints], &hit.intersectionPoints[0]); insertIntersection(hit); }
对于从func中获得的交集.我们将需要将它变成我们所需要的数据.我将一一解释最终我们得到的每个数据的含义:
hit.distance // 表示从当前这个交集的所有顶点的中心点到参考平面的距离.
hit.primitiveIndex //表示之前我们说的这个图元在PrimitiveSet中的序号.
hit.nodepath //表示这个从根结点到当前这个geode的路径..因为我们知道在vistor中我们有pushNodepath() popNodePath()来保存这个路径操作..所以这个路径是从vistor中获得的.
hit.drawable //当然是我们保存着当前这个交集是对于哪个drawable.
hit.matrix //表示当前这个drawable应当在世界坐标系的变换矩阵.我们可以使用point*matrix 来得到获得点在世界坐标系下的位置..
hit.localIntersecotPoint //表示所有交点的中心点.
hit.intersectorPoint //所有交点的一个数组..目前最多的顶点个数应该是6.. enum { MaxNumIntesectionPoints=6 };
hit.numintersectorPoint // 所有顶点的个数..
我想这个解读过程到此应当结束了...继续学习ING.....
相关文章推荐
- MyEclipse快捷键大全
- Java io详解(-)
- 基于JDK 8的Dubbo Admin
- 比较Java数组,ArrayList,LinkedList,Vector 性能比较
- 解读Python编程中的命名空间与作用域
- c# 委托delegate 编写计算器
- Maven管理java工程
- Eclipse 设置一行代码宽度
- php 语言包的实现
- Java学习篇之---Spring Scope prototype与singleton区别
- c++模板与泛型编程基础
- ASP.NET Web大文件下载的实现思路及代码
- JAVA中异常处理小结
- 从0开始学java——JSP&Servlet——web容器搜索class的路径顺序
- C语言经典面试题
- ruby on rails
- ASP.NET JSON的序列化和反序列化 之 Newtonsoft.Json
- eclipse 中设置注释模板
- spring 监听器 IntrospectorCleanupListener
- win10系统升级提示错误代码0x80070006-0x2000c该怎么解决