您的位置:首页 > 其它

OSG碰撞检测

2010-03-29 13:04 483 查看
http://bbs.vrchina.net/viewthread.php?tid=5196&extra=page%3D1

OSG碰撞检测

碰撞检测涉及到虚拟仿真的各个方面,这里我主要介绍两方面的应用:

一 判断摄像机是否与前面的物体相撞

基本原理如下图:

首先确定摄像机的旧位置和新位置(此处的新位置是假设前方没有障碍物所行进到的新位置),然后利用两点创建一条线段,然后判断这条线段与模型是否有交点,如果存在交点,则取交点作为新位置(或者取交点稍前面的一点作为新位置)。







1.jpg (16.32 KB)

2008-1-27 07:07 PM



二 摄像机按照地形来进行漫游:

如果不进行碰撞检测的话,可能会出现一下两种情况:

第一种情况,摄像机穿地形而过:







2.JPG (11.76 KB)

2008-1-27 07:12 PM

第二种情况, 摄像机处于悬空状态







3.JPG (9.14 KB)

2008-1-27 07:12 PM

对于第一种情况,摄像机肯定会与地形发生碰撞,但检测到碰撞之后,摄像机到达的位置

是否位于地形法线上方的合适高度,需要进行另一次碰撞检测,如下图所示:







4.JPG (9.3 KB)

2008-1-27 07:12 PM



绿色代表摄像机移动到的最终位置,从而保证了摄像机与地形之间的高度是固定的。

对于第二种情况,可能在第一种情况的碰撞检测中检测不到碰撞,那么需要进行在一次碰撞检测,







5.JPG (10.57 KB)

2008-1-27 07:12 PM





绿色代表摄像机移动到的最终位置,从而保证了摄像机与地形之间的高度是固定的。

三 重要的类和函数

osg::LineSegmen

表示一个线段的类,包括一个起点和一个终点

osgUtil::IntersectVisiotr

接受线段的类,用于判别与节点的交集。

其中的函数addLineSegment(line.get())用来添加一条线段到列表当中

osgUtil::IntersectVisitor::HitList

可以得到相交点的具体位置,从而计算出距离

四 代码示例

bool DriveManipulator::calcMovement()

{

//如果少于两个事件则返回错误

if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false;

double dt = _ga_t0->getTime()-_ga_t1->getTime();

if (dt<0.0f)

{

notify(INFO) << "warning dt = "<<dt<< std::endl;

dt = 0.0f;

}

switch(_speedMode)

{

case(USE_MOUSE_Y_FOR_SPEED):

{//切换到鼠标上下控制速度模式

double dy = _ga_t0->getYnormalized();

_velocity = _modelScale*0.2f*dy;

break;

}

case(USE_MOUSE_BUTTONS_FOR_SPEED):

{//切换到鼠标按键控制速度模式

unsigned int buttonMask = _ga_t1->getButtonMask();

if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON)

{ //左键加速

_velocity += dt*_modelScale*0.01;

}

else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON ||

buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEventAdapter::RIGHT_MOUSE_BUTTON))

{ //中键停止

_velocity = 0.0;

}

else if (buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON)

{ //右键减速

_velocity -= dt*_modelScale*0.01;

}

break;

}

}

osg::CoordinateFrame cf=getCoordinateFrame(_eye);

osg::Matrix rotation_matrix;

rotation_matrix.makeRotate(_rotation);

osg::Vec3d up = osg::Vec3d(0.0,1.0,0.0) * rotation_matrix;

osg::Vec3d lv = osg::Vec3d(0.0,0.0,-1.0) * rotation_matrix;

osg::Vec3d sv = osg::Vec3d(1.0,0.0,0.0) * rotation_matrix;

//旋转摄像机

double dx = _ga_t0->getXnormalized();

double yaw = -inDegrees(dx*50.0f*dt);



#ifdef KEYBOARD_PITCH

double pitch_delta = 0.5;

if (_pitchUpKeyPressed) _pitch += pitch_delta*dt;

if (_pitchDownKeyPressed) _pitch -= pitch_delta*dt;

#endif

#if defined(ABOSULTE_PITCH)

// abosolute pitch

double dy = _ga_t0->getYnormalized();

_pitch = -dy*0.5;

#elif defined(INCREMENTAL_PITCH)

// incremental pitch

double dy = _ga_t0->getYnormalized();

_pitch += dy*dt;

#endif



osg::Quat yaw_rotation;

yaw_rotation.makeRotate(yaw,up);

_rotation *= yaw_rotation;

rotation_matrix.makeRotate(_rotation);

sv = osg::Vec3d(1.0,0.0,0.0) * rotation_matrix;

if (fabs(_velocity*dt)>1e-8)

{

double distanceToMove = _velocity*dt;

double signedBuffer;

if (distanceToMove>=0.0) signedBuffer=_buffer;

else signedBuffer=-_buffer;

//检查前方是否有障碍物

osgUtil::IntersectVisitor iv;

iv.setTraversalMask(_intersectTraversalMask);

osg::ref_ptr<osg::LineSegment> segForward = new osg::LineSegment;

segForward->set(_eye,_eye+lv*(signedBuffer+distanceToMove));

iv.addLineSegment(segForward.get());

_node->accept(iv);

//如果检测到碰撞

if (iv.hits())

{

osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segForward.get());

if (!hitList.empty())

{

// notify(INFO) << "Hit obstruction"<< std::endl;

//取得碰撞交点

osg::Vec3d ip = hitList.front().getWorldIntersectPoint();

//计算移动距离

distanceToMove = (ip-_eye).length()-_buffer;

_velocity = 0.0;

}

}

// 检查前进到的点是不是高于地形一个固定值

osg::Vec3d fp = _eye+lv*distanceToMove;

osg::Vec3d lfp = fp-up*_height*5;

iv.reset();

osg::ref_ptr<osg::LineSegment> segNormal = new osg::LineSegment;

segNormal->set(fp,lfp);

iv.addLineSegment(segNormal.get());

_node->accept(iv);

//第二次进行碰撞检测

if (iv.hits())

{

osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segNormal.get());

if (!hitList.empty())

{

//notify(INFO) << "Hit terrain ok"<< std::endl;

osg::Vec3d ip = hitList.front().getWorldIntersectPoint();

osg::Vec3d np = hitList.front().getWorldIntersectNormal();

if (up*np>0.0) up = np;

else up = -np;

_eye = ip+up*_height;

lv = up^sv;

computePosition(_eye,_eye+lv,up);

return true;

}

}



//第二次没有检测到碰撞,那么进行第三次碰撞检测

osg::Vec3d dp = lfp;

dp -= getUpVector(cf)* (2*_modelScale);

iv.reset();

osg::ref_ptr<osg::LineSegment> segFall = new osg::LineSegment;

segFall->set(lfp,dp);

iv.addLineSegment(segFall.get());

_node->accept(iv);

if (iv.hits())

{

osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segFall.get());

if (!hitList.empty())

{

//notify(INFO) << "Hit terrain on decent ok"<< std::endl;

osg::Vec3d ip = hitList.front().getWorldIntersectPoint();

osg::Vec3d np = hitList.front().getWorldIntersectNormal();

if (up*np>0.0) up = np;

else up = -np;

_eye = ip+up*_height;

lv = up^sv;

computePosition(_eye,_eye+lv,up);

return true;

}

}

lv *= (_velocity*dt);

_eye += lv;

}

return true;

}

五 小结

碰撞检测非常复杂,涉及包围体和相交检验等各方面的知识,上面只是碰撞检测最简单的应用。以上思想只是个人的分析,并未得到验证,细节也并未处理,希望各位大侠给与指导。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: