相关知识
OSG交运算、选取和碰撞
计算几何中基础几何
解析参考原文
osgUtil/IntersectionVisitor.cpp
void IntersectionVisitor::apply(osg::Geode& geode)
- geode是叶子节点,求交运算中最后一定会访问该函数
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();
}
- enter是调用求交访问器中设置的求交器的enter方法
// IntersectionVisitor
typedef std::list< osg::ref_ptr<Intersector> > IntersectorStack;
IntersectorStack _intersectorStack;
inline bool enter(const osg::Node& node) { return _intersectorStack.empty() ? false : _intersectorStack.back()->enter(node); }
- 须知class OSGUTIL_EXPORT PolytopeIntersector : public Intersector
bool PolytopeIntersector::enter(const osg::Node& node)
{
if (reachedLimit()) return false;// 由下可知当LIMIT_ONE 或 已经有交集了就false
// 只有在启用cull或者当前node被求交器的几何体contains才进行求交判断,即true
return !node.isCullingActive() || _polytope.contains( node.getBound() );
}
inline bool Intersector::reachedLimit() { return _intersectionLimit == LIMIT_ONE && containsIntersections(); }
virtual bool PolytopeIntersector::containsIntersections() { return !getIntersections().empty(); }
- 之后便用intersect,对geode节点下的所有可绘制图元进行求交测试
- 是调用求交访问器中设置的求交器的intersect方法
inline void intersect(osg::Drawable* drawable) { _intersectorStack.back()->intersect(*this, drawable); }
void PolytopeIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable)
{
if (reachedLimit()) return;
if ( !_polytope.contains( drawable->getBoundingBox() ) ) return;
osg::ref_ptr<PolytopeIntersectorUtils::Settings> settings = new PolytopeIntersectorUtils::Settings;
settings->_polytopeIntersector = this;
settings->_iv = &iv;
settings->_drawable = drawable;
settings->_limitOneIntersection = (_intersectionLimit == LIMIT_ONE_PER_DRAWABLE || _intersectionLimit == LIMIT_ONE);
settings->_primitiveMask = _primitiveMask;
osg::KdTree* kdTree = iv.getUseKdTreeWhenAvailable() ? dynamic_cast<osg::KdTree*>(drawable->getShape()) : 0;
// 重点看以下,PolytopePrimitiveIntersector 构造了一个func 后(并设置多面体,和参考平面)
//然后再设置到drawable中,类似vistor的操作
if (getPrecisionHint()==USE_DOUBLE_CALCULATIONS)
{
osg::TemplatePrimitiveFunctor<PolytopeIntersectorUtils::IntersectFunctor<osg::Vec3d> > intersector;
intersector._settings = settings;
if (kdTree) kdTree->intersect(intersector, kdTree->getNode(0));
else drawable->accept(intersector);
}
else
{
osg::TemplatePrimitiveFunctor<PolytopeIntersectorUtils::IntersectFunctor<osg::Vec3f> > intersector;
intersector._settings = settings;
if (kdTree) kdTree->intersect(intersector, kdTree->getNode(0));
else drawable->accept(intersector);
}
}
- 在osg中drawable里头已经是最后的顶点等所有数据存放的地方.drawable其实只是个抽象类,可以简单的通过它的一个特例:Geometry 来讲述这一段内容:
- 实际上就是将顶点数据取出来,然后functor.setVertexArray,以便之后的functor操作。
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::Vec3ArrayType):
functor.setVertexArray(_vertexData.array->getNumElements(),static_cast<const Vec3*>(_vertexData.array->getDataPointer()));
/** 部分省略……*/
}
for(PrimitiveSetList::const_iterator itr=_primitives.begin();
itr!=_primitives.end();
++itr)
{
(*itr)->accept(functor);
}
}
/** 后面对于存在索引数组的暂时省略……*/
}
- 其后最主要的还是在于对于drawable里的每个primitiveset 进行接受fuctor访问操作 (*itr)->accept(functor)。
- 我们知道.primitiveset里头拥有的数据是顶点最后绘制的规则,而我们所要求交集的目的在于获得跟这些基础图元的交集。
- PrimitiveSet又是一个虚类.因此,我们选一个实体类:DrawArray来深究
void DrawArrays::accept(PrimitiveFunctor& functor) const
{
functor.drawArrays(_mode,_first,_count);
}
所以最终的结果 都将回到fuctor里头进行交集运算的处理…_mode _first _count 将拥有的规则送往functor,然后又回到functor里面的操作,而functor就是在
void PolytopeIntersector::intersect
中设置的IntersectFunctor
(详见PolytopeIntersector.cpp)。
但是首先我们需要先了解上面的drawArrays
函数,该函数定义在osg::TemplatePrimitiveFunctor
中:
virtual void drawArrays(GLenum mode,GLint first,GLsizei count)
{
if (_vertexArrayPtr==0 || count==0) return;
switch(mode)
{
case(GL_TRIANGLES): {
const Vec3* vlast = &_vertexArrayPtr[first+count];
for(const Vec3* vptr=&_vertexArrayPtr[first];vptr<vlast;vptr+=3)
this->operator()(*(vptr),*(vptr+1),*(vptr+2),_treatVertexDataAsTemporary);
break;
}
/** *//** 后面的其他省略…… */
}
可见实际上drawArrays就是在调用IntersectFunctor的各种operator,支持很多种类型,.参数的不同,包括一个点(points)(两个点)lines,三个点(Triangles),四个点(quads)。
我们可以从最基础的三角形的处理上进行深究:
// handle triangles
void operator()(const osg::Vec3& v0, const osg::Vec3& v1, const osg::Vec3& v2, bool /*treatVertexDataAsTemporary*/)
{
if (_settings->_limitOneIntersection && _hit) return;
if ((_settings->_primitiveMask&PolytopeIntersector::TRIANGLE_PRIMITIVES)==0)
{
++_primitiveIndex;
return;
}
src.clear();
src.push_back(v0);
src.push_back(v1);
src.push_back(v2);
src.push_back(v0);
if (contains())
{
addIntersection();
}
++_primitiveIndex;
}
bool contains()
{
const osg::Polytope& polytope = _settings->_polytopeIntersector->getPolytope();
const osg::Polytope::PlaneList& planeList = polytope.getPlaneList();
osg::Polytope::ClippingMask resultMask = polytope.getCurrentMask();
if (!resultMask) return true;
osg::Polytope::ClippingMask selector_mask = 0x1;
for(osg::Polytope::PlaneList::const_iterator pitr = planeList.begin();
pitr != planeList.end();
++pitr)
{
if (resultMask&selector_mask)
{
//OSG_NOTICE<<"Polytope::contains() Plane testing"<<std::endl;
dest.clear();
const osg::Plane& plane = *pitr;
typename Vertices::iterator vitr = src.begin();
vec_type* v_previous = &(*(vitr++));
value_type d_previous = plane.distance(*v_previous);
for(; vitr != src.end(); ++vitr)
{
vec_type* v_current = &(*vitr);
value_type d_current = plane.distance(*v_current);
// 代表点在几何体的里面,请回想多面体各个平面的正面必须都是属于这个区域内的,
//即平面的法线应当是指向该空间区域内部
//就是这个原因!
if (d_previous>=0.0)
{
dest.push_back(*v_previous);
}
// 一个在外一个在内
if (d_previous*d_current<0.0)
{
// edge crosses plane so insert the vertex between them.
value_type distance = d_previous-d_current;
value_type r_current = d_previous/distance;
vec_type v_new = (*v_previous)*(1.0-r_current) + (*v_current)*r_current;
dest.push_back(v_new);
}
d_previous = d_current;
v_previous = v_current;
}
if (d_previous>=0.0)
{
dest.push_back(*v_previous);
}
if (dest.size()<=1)
{
// OSG_NOTICE<<"Polytope::contains() All points on triangle culled, dest.size()="<<dest.size()<<std::endl;
return false;
}
dest.swap(src);
}
else
{
// OSG_NOTICE<<"Polytope::contains() Plane disabled"<<std::endl;
}
selector_mask <<= 1;
}
//OSG_NOTICE<<"Polytope::contains() triangle within Polytope, src.size()="<<src.size()<<std::endl;
return true;
}
- 在
void PolytopeIntersector::intersect
中,含有settings->_polytopeIntersector = this;
,所以实际上是将intersection,即求交结果设置到使用的多面体求交器中,即每一个drawable里的每个primitiveset都会进行求交,都有可能获得intersection。 - primitiveset就是opengl中GL_LINES,GL_TRIANGLES等绘制规则。
void addIntersection()
{
vec_type center(0.0,0.0,0.0);
double maxDistance = -DBL_MAX;
const osg::Plane& referencePlane = _settings->_polytopeIntersector->getReferencePlane();
for(typename Vertices::iterator itr = src.begin();
itr != src.end();
++itr)
{
center += *itr;
double d = referencePlane.distance(*itr);
if (d>maxDistance) maxDistance = d;
}
center /= value_type(src.size());
PolytopeIntersector::Intersection intersection;
intersection.primitiveIndex = _primitiveIndex;
intersection.distance = referencePlane.distance(center);
intersection.maxDistance = maxDistance;
intersection.nodePath = _settings->_iv->getNodePath();
intersection.drawable = _settings->_drawable;
intersection.matrix = _settings->_iv->getModelMatrix();
intersection.localIntersectionPoint = center;
if (src.size()<PolytopeIntersector::Intersection::MaxNumIntesectionPoints) intersection.numIntersectionPoints = src.size();
else intersection.numIntersectionPoints = PolytopeIntersector::Intersection::MaxNumIntesectionPoints;
for(unsigned int i=0; i<intersection.numIntersectionPoints; ++i)
{
intersection.intersectionPoints[i] = src[i];
}
// OSG_NOTICE<<"intersection "<<src.size()<<" center="<<center<<std::endl;
//
_settings->_polytopeIntersector->insertIntersection(intersection);
_hit = true;
// OSG_NOTICE<<"addIntersection() center="<<center<<std::endl;
}