您的位置:首页 > 其它

【原创】碰撞检测

2008-02-29 11:02 363 查看
导读:
  【原创】碰撞检测
  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 = "  _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 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
  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 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;
  }
  五 小结
  碰撞检测非常复杂,涉及包围体和相交检验等各方面的知识,上面只是碰撞检测最简单的应用。以上思想只是个人的分析,并未得到验证,细节也并未处理,希望各位大侠给与指导。
  [本帖最后由 osg 于 2008-1-27 07:12 PM 编辑 ]
  TOP

本文转自
http://bbs.vrchina.net/viewthread.php?tid=5196&extra=page%3D1 >
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: