登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: [ogre]射线使用-碰撞检测     [回主站]     [分站链接]
标题
[ogre]射线使用-碰撞检测
clq
浏览(0) + 2010-02-22 10:42:51 发表 编辑

关键字:

[2020-03-22 01:44:45 最后更新]
射线使用-碰撞检测[zt]

来自 http://blog.sina.com.cn/s/blog_45209f340100f6ld.html

clq
2010-2-22 10:43:18 发表 编辑

OGRE手札-18 射线使用-碰撞检测(2009-07-22 21:46:38)
标签:杂谈 分类:OGRE学习

射线使用-碰撞检测

我们首先实现对鼠标右键的响应是他能够自由查看

然后实现摄像机不能穿越地面

最后单击左键增加实体,并能在单机状态下移动实体

 

首先,一如既往,提供程序运行的代码框架

#include <CEGUI/CEGUISystem.h>
#include <CEGUI/CEGUISchemeManager.h>
#include <OgreCEGUIRenderer.h>

#include "ExampleApplication.h"

class MouseQueryListener : public ExampleFrameListener, public OIS::MouseListener
{
public:

 MouseQueryListener(RenderWindow* win, Camera* cam, SceneManager *sceneManager, CEGUI::Renderer *renderer)
  : ExampleFrameListener(win, cam, false, true), mGUIRenderer(renderer)
 {
 } // MouseQueryListener

 ~MouseQueryListener()
 {


 }

 bool frameStarted(const FrameEvent &evt)
 {
  return ExampleFrameListener::frameStarted(evt);
 }

 
 bool mouseMoved(const OIS::MouseEvent &arg)
 {
  return true;
 }

 bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
 {
  return true;
 }

 bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
 {
  return true;
 }


protected:
 RaySceneQuery *mRaySceneQuery;     // The ray scene query pointer
 bool mLMouseDown, mRMouseDown;     // True if the mouse buttons are down
 int mCount;                        // The number of robots on the screen
 SceneManager *mSceneMgr;           // A pointer to the scene manager
 SceneNode *mCurrentObject;         // The newly created object
 CEGUI::Renderer *mGUIRenderer;     // CEGUI renderer
};

class MouseQueryApplication : public ExampleApplication
{
protected:
 CEGUI::OgreCEGUIRenderer *mGUIRenderer;
 CEGUI::System *mGUISystem;         // cegui system
public:
 MouseQueryApplication()
 {
 }

 ~MouseQueryApplication()
 {
 }
protected:
 void chooseSceneManager(void)
 {
  // Use the terrain scene manager.
  mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);
 }

 void createScene(void)
 {
 }

 void createFrameListener(void)
 {
  mFrameListener = new MouseQueryListener(mWindow, mCamera, mSceneMgr, mGUIRenderer);
  mFrameListener->showDebugOverlay(true);
  mRoot->addFrameListener(mFrameListener);
 }
};


#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
 // Create application object
 MouseQueryApplication app;

 try {
  app.go();
 } catch(Exception& e) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
  MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
  fprintf(stderr, "An exception has occurred: %s\n",
   e.getFullDescription().c_str());
#endif
 }

 return 0;
}

运行结果应当是一片漆黑


一、创建场景(createScene()添加代码)

       // Set ambient light创建天空
       mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
       mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

       // World geometry创建地面
       mSceneMgr->setWorldGeometry("terrain.cfg");

       // Set camera look point设置摄像机要防止摄像机一开始就在地面之下
       mCamera->setPosition(40, 100, 580);
       mCamera->pitch(Degree(-30));
       mCamera->yaw(Degree(-45));

既然我们建立了基本的世界空间,那么就要打开光标。打开光标,要使用CEGUI函数调用。

创建一个OgreCEGUIRenderer,然后创建系统对象并将刚创建的Renderer传给它。

创建mGUIRenderer时必须以最后一个参数告诉CEGUI你要用那个场景管理器

       // CEGUI setup
       mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr);
       mGUISystem = new CEGUI::System(mGUIRenderer);

      // Mouse显示光标
       CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");
       CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");

你可以运行一下你的程序,看到有天,有地,还有一个不会动的图形化光标

——————————————————————————————————————————————

二、将鼠标右键绑定到“鼠标观察”模式

 首先进行变量介绍(这些变量框架代码已经提供了)

    RaySceneQuery *mRaySceneQuery;      // 射线场景查询指针,寻找地面上的坐标


    bool mLMouseDown, mRMouseDown;     // 如果按下鼠标按钮,返回True


    int mCount;                        // 屏幕上机器人的数量,实体数


    SceneManager *mSceneMgr;           // 指向场景管理器的指针


    SceneNode *mCurrentObject;    //指向最近创建的场景节点的指针(我们将用这个“拖拽”实体)。


    CEGUI::Renderer *mGUIRenderer;     // CEGUI渲染器


MouseQueryListener的构造函数中添加代码:

        // Setup default variables
        mCount = 0;
        mCurrentObject = NULL;
        mLMouseDown = false;
        mRMouseDown = false;
        mSceneMgr = sceneManager;

        // Reduce move speed
        mMoveSpeed = 50;
        mRotateSpeed /= 500;

//为了MouseQueryListener能收到鼠标事件,我们必须把它注册为一个鼠标监听器。

        // Register this so that we get mouse events.
        mMouse->setEventCallback(this);
//用场景管理器的一个调用创建一个RaySceneQuery对象

        // Create RaySceneQuery
        mRaySceneQuery = mSceneMgr->createRayQuery(Ray());

 

//创建一个RaySceneQuery,以后我们就必须销毁它。

~MouseQueryListener()析构函数增加代码:

        // We created the query, and we are also responsible for deleting it.
        mSceneMgr->destroyQuery(mRaySceneQuery);

mouseMoved中增加代码:

      // Update CEGUI with the mouse motion
       CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);

 

 

      // If we are dragging the left mouse button.
       if (mLMouseDown)
       {
       } // if

       // If we are dragging the right mouse button.
       else if (mRMouseDown)
       {
           mCamera->yaw(Degree(-arg.state.X.rel * mRotateSpeed));
           mCamera->pitch(Degree(-arg.state.Y.rel * mRotateSpeed));
       } // else if


 

 

mousePressed中增加代码:
       //当鼠标右键按下时,隐藏光标,并设置变量mRMouseDown为true。

       // Left mouse button down
       if (id == OIS::MB_Left)
       {
           mLMouseDown = true;
       } // if

       // Right mouse button down
       else if (id == OIS::MB_Right)
       {
           CEGUI::MouseCursor::getSingleton().hide();
           mRMouseDown = true;
       } // else if

 

mouseReleased中增加代码:
        //右键抬起时,我们需要再次显示光标,并将mRMouseDown设置为false。

          // Left mouse button up
       if (id == OIS::MB_Left)
       {
           mLMouseDown = false;
       } // if

       // Right mouse button up
       else if (id == OIS::MB_Right)
       {
           CEGUI::MouseCursor::getSingleton().show();
           mRMouseDown = false;
       } // else if

试一下你的摄像机是否能受右键控制吧!

____________________________________________________________________________

三、地形碰撞检测

    在BaseFrameListener移动了摄像机后,我们要确保摄像机在地面以上10个单位处。如果它不在,我们要把它移到那儿。

frameStarted中增加代码(删除原来的return语句):

        // Process the base frame listener code.  Since we are going to be
        // manipulating the translate vector, we need this to happen first.
        if (!ExampleFrameListener::frameStarted(evt))
            return false;

//因为ExampleFrameListener的frameStarted成员函数移动摄像机,并且在此发生后我们需要在函数中安排

//我们的剩余行动。我们的目标及时找到摄像机的当前位置,并沿着它向地面发射一条射线。这被称为射线

//场景查询,它会告诉我们我们下面的地面的高度。得到了摄像机的当前位置后,我们需要创建一条射线。

//这条射线有一个起点(射线开始的地方),和一个方向(此处y轴负方向)。

 

       // Setup the scene query
       Vector3 camPos = mCamera->getPosition();
       Ray cameraRay(Vector3(camPos.x, 5000.0f, camPos.z), Vector3::NEGATIVE_UNIT_Y);

       //一旦我们创建了射线,我们就告诉RaySceneQuery对象使用它。 
       mRaySceneQuery->setRay(cameraRay);
       //使用了5000.0f高度代替了摄像机的实际位置。

 

       //现在我们需要执行查询,得到结果。查询结果是std::iterator类型的。

       // Perform the scene query
        RaySceneQueryResult &result = mRaySceneQuery->execute();
        RaySceneQueryResult::iterator itr = result.begin();

//begin方法获得迭代器的第一个元素。如果result.begin() == result.end(),那么无返回结果

 

//保证至少返回一个查询结果(itr != result.end()),那个结果是地面(itr->worldFragment)。

// Get the results, set the camera height
        if (itr != result.end() && itr->worldFragment)
        {
//worldFragment结构包含有在变量singleIntersection(一个Vector3)中射线击中地面的位置。
//我们要得到地面的高度,依靠将这个向量的Y值赋值给一个本地变量。
//一旦我们有了高度,我们就要检查摄像机是否低于这一高度,如果低于这一高度,
//那么我们要将摄像机向上移动至地面高度。
//注意,我们实际将摄像机多移动了10个单位。这样保证我们不能由于太靠近地面而看穿地面。

            Real terrainHeight = itr->worldFragment->singleIntersection.y;
            if ((terrainHeight + 10.0f) > camPos.y)
                mCamera->setPosition( camPos.x, terrainHeight + 10.0f, camPos.z );
        }

        return true;

运行一下,看你还能否“入地”!

 

_______________________________________________________________________________________

 

 


 

 四、添加对象并实现拖拽

    每次点击鼠标左键,我们将向屏幕上创建和添加对象。每次你点击、按住鼠标左键,就会创建一个对象并跟随你的光标。你可以移动对象,直到你松开鼠标左键,同时对象也锁定在那一点上。要做到这些,我们需要改变mousePressed函数。

在mousePressed()中的if语句添加代码:

       // Left mouse button down
       if (id == OIS::MB_Left)
       {
         // Setup the ray scene query, use CEGUI's mouse position
               CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
               Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width), mousePos.d_y/float(arg.state.height));
// Camera::getCameraToViewpointRay;一个将屏幕上的点击(X和Y坐标)转换成一条可供RaySceneQuery对象使用的射线的好用函数
               mRaySceneQuery->setRay(mouseRay);
               // Execute query执行查询
               RaySceneQueryResult &result = mRaySceneQuery->execute();
               RaySceneQueryResult::iterator itr = result.begin( );

               // Get results, create a node/entity on the position
               if (itr != result.end() && itr->worldFragment)
               {
       //有了worldFragment(也就是点击的位置),就可以创建对象并把它放到位。
       //解决唯一命名问题
                 char name[16];
                 sprintf( name, "Robot%d", mCount++ );
                 //用itr->worldFragment->singleIntersection作为我们的机器人的默认位置

                  Entity *ent = mSceneMgr->createEntity(name, "robot.mesh");
                   mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(String(name) + "Node", itr->worldFragment->singleIntersection);
                   mCurrentObject->attachObject(ent);
                   mCurrentObject->setScale(0.1f, 0.1f, 0.1f);//地图挺小的放小一点
               } // if

               mLMouseDown = true;
           } // if

在mousemoved()的if(mLMouseDown){}中添加代码:

if (mLMouseDown)
       {
           CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
           Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width),mousePos.d_y/float(arg.state.height));
           mRaySceneQuery->setRay(mouseRay);

           RaySceneQueryResult &result = mRaySceneQuery->execute();
           RaySceneQueryResult::iterator itr = result.begin();

           if (itr != result.end() && itr->worldFragment)
               mCurrentObject->setPosition(itr->worldFragment->singleIntersection);
       } // if

 


clq
2010-2-22 12:30:56 发表 编辑

http://blog.csdn.net/pdiy/archive/2005/03/26/331105.aspx

这个实用。

Ogre中的碰撞检测(完整版) 收藏

Ogre中的碰撞检测(完整版)

原创  韩举
http://www.hjpdiy.com
请注明转载地址

注意此例子需要Ogre 0.15 新版本RC的代码制作中

Ogre是开源的封装了DirectX和OpenGL的3D引擎

Ogre的下载和安装见http://www.uipower.com/bbs/dispbbs.asp?boardID=24&ID=225&page=1


此主题相关图片如下:

下载代码
http://www.uipower.com/bbs/dispbbs.asp?boardid=24&id=233

Ogre采用树桩管理场景中的各种"元素"(摄像机、灯光、物体等),所有的东西都挂在"树"上,不在"树"上的东西不会被渲染。
Ogre::SceneManager就是"树"的管理者,Ogre::SceneNode是从SceneManager中创建的(当然BSP和8*树的管理也和这两个类有关,这暂时不讨论)。

AABB(轴对齐包围盒)

这个东西是碰撞检测的基础(怎么总想起JJYY呢),和它类似的还有OBB(有向包围盒),由于OBB创建复杂,所以Ogre采用了AABB。

最简单的碰撞检测:

通 过Ogre::SceneNode::_getWorldAABB()可以取得这个叶子节点的AABB(Ogre::AxisAlignedBox), Ogre::AxisAlignedBox封装了对AABB的支持,该类的成员函数Ogre::AxisAlignedBox::intersects ()可以判断一个AABB和"球体、点、面以及其他面"的相交情况(碰撞情况)。

    m_SphereNode树的叶子,挂了一个"球"
    m_CubeNode树的叶子,挂了一个"正方体"

    AxisAlignedBox spbox=m_SphereNode->_getWorldAABB();
AxisAlignedBox cbbox=m_CubeNode->_getWorldAABB();
if(spbox.intersects(cbbox))
{
     //相交

}

区域查询:

简单的讲就是,查询某一区域中有什么东西,分为AABB、球体、面查询。

   //创建一个球体查询,这里的100是m_SphereNode挂着的那个球体的半径
   SphereSceneQuery * pQuery=m_SceneMgr->createSphereQuery(Sphere(m_SphereNode->getPosition(),100));
   //执行这个查询
   SceneQueryResult QResult=pQuery->execute();
   //遍历查询列表找出范围内的物体
   for (std::list<MovableObject*>::iterator iter = QResult.movables.begin(); iter != QResult.movables.end();++iter)
   {
    MovableObject* pObject=static_cast<MovableObject*>(*iter);
    if(pObject)
    {
     if(pObject->getMovableType()=="Entity")
     {
      Entity* ent = static_cast<Entity*>(pObject);
      //这里简化了操作,由于只有一个"球体"和一个"正方体",
      //所以只判断了球体和正方体的相交

      if(ent->getName()=="cube")
      {
       //改变位置防止物体重叠
       vtl=-vtl;
       m_SphereNode->translate(vtl);
       break;
      }
     }
    }
   }

相交查询

遍历所有的对象,找到一对一对的相交物体(废话呀,相交当然至少两个物体)。

    //创建相交检测
    IntersectionSceneQuery* pISQuery=m_SceneMgr->createIntersectionQuery();
    //执行查询
    IntersectionSceneQueryResult QResult=pISQuery->execute();
    //遍历查询列表找出两个相交的物体
    for (SceneQueryMovableIntersectionList::iterator iter = QResult.movables2movables.begin();
     iter != QResult.movables2movables.end();++iter)
    {
    
     SceneQueryMovableObjectPair pObject=static_cast<SceneQueryMovableObjectPair>(*iter);
     //if(pObject)
     {
      String strFirst=pObject.first->getName();
      String strSecond=pObject.second->getName();
      //下面加入你自己的两个物体相交判断代码,或者简单的用AABB的判断方法,
     }
    }


原创  韩举
http://www.hjpdiy.com
请注明转载地址



clq
2010-2-22 15:24:15 发表 编辑

"
这个Demo中使用的RayScenQuery是查找与指定Ray: updateRay;相交的world geometry。它设置Ray的起点为摄像机的位置

updateRay.setOrigin(mCamera->getPosition());

Ray的方向为垂直向下

updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y);

这样就找出了与地面的交点。从而可以确定高度,既而就可以调整摄像机的高度,模拟出颠簸的感觉。

RaySceneQueryResult::iterator i = qryResult.begin();

if (i != qryResult.end() && i->worldFragment)

{

SceneQuery::WorldFragment* wf = i->worldFragment;

    mCamera->setPosition(mCamera->getPosition().x,

           i->worldFragment->singleIntersection.y + 10,

           mCamera->getPosition().z);

 }

"

Demo_Terrain 中的射线碰撞似乎是只能在非默认的 tree.. 场景中才能使用。见下面来自

http://blog.csdn.net/yeazer0/archive/2009/12/08/4960892.aspx

的文章.


--------------------------------------------------

OGRE碰撞检测之射线查询地形

//场景射线类:场景射线查询,全局指针变量,
RaySceneQuery* raySceneQuery = 0;

//在侦听器"FrameListener"类(继承于ExampleFrameListener)中,
  //在侦渲染"FreameRendering"中执行场景射线一些列动作。

  //射线类 静态:用于更新射线。
  static Ray updateRay;
  //设置射线起点。
  updateRay.setOrigin(mCamera->getPosition());
  //设置射线方向。
  updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y);
  //将updateRay射线设置为场景查询射线。
  raySceneQuery->setRay(updateRay);
  //typedef std::vector<RaySceneQueryResultEntry> RaySceneQueryResult
  //执行场景射线查询,并将结果存于qryResult容器中。
  RaySceneQueryResult& qryResult = raySceneQuery->execute();
        //遍历qryResult容器中的射线查询。
  RaySceneQueryResult::iterator i = qryResult.begin();
  if (i != qryResult.end() && i->worldFragment)
  {   //把摄像机定在地形10个单位高的地方(y值)。
   mCamera->setPosition(mCamera->getPosition().x,
    i->worldFragment->singleIntersection.y + 10,
    mCamera->getPosition().z);
  }
  

//应用"Application"类中析构函数中,删除射线场景查询
  delete raySceneQuery;
  

  //在创建场景函数createScene中,定义raySceneQuery,定义为场景中的场景射线查询,
  //射线起点为相机原点,方向Y轴负方向,即垂直向下。
  raySceneQuery = mSceneMgr->createRayQuery(
   Ray(mCamera->getPosition(), Vector3::NEGATIVE_UNIT_Y));
   
   
//选择场景管理器,这里我们需要使用地形场景管理器
  //场景管理器置于根节点
  //TerrainSceneManager在terrainscenemanager.h中定义的一个类

  //多场景管理器能支持场景管理器把自己的类型注册为字符串ID
  //(比如“OctreeSceneManager”或“TerrainSceneManager”)。
  //这样你就可以使用迭代的方法在管理器中找到自己所需要的具体实现,
  //亦或者可以简单的使用最后一个被注册进来的管理器类型。

  //Ogre自身提供两个场景管理器类型:OctreeSceneManager和TerrainSceneManager。
  //OctreeSceneManager是一个通用的场景管理器。TerrainSceneManager是
  //一个为高度场场景优化的场景管理器。
  mSceneMgr = mRoot->createSceneManager("TerrainSceneManager");
  
  //在创建场景函数createScene中
  //将地形置于场景中,地形数据存储于.cfg文件中(默认为terrain.cfg)
  //地形数据的制作,通过高度图heightmap的运用。下面语句等同mSceneMgr -> setWorldGeometry("myterrain.cfg");
  string terrain_cfg("myterrain.cfg");
  mSceneMgr -> setWorldGeometry(terrain_cfg);


clq
2010-2-22 15:41:05 发表 编辑

      mCamera->setPosition( camPos.x, pObject->getWorldBoundingBox().getSize().y + 10, camPos.z);//保持在物体上方

getWorldBoundingBox 是Ogre中得到MovableObject的包围球包围盒的函数


总数:4 页次:1/1 首页 尾页  
总数:4 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1