现在的位置: 首页 > 综合 > 正文

Ogre中的碰撞检测

2013年03月18日 ⁄ 综合 ⁄ 共 15696字 ⁄ 字号 评论关闭

基于射线查询的方式,实现摄像机和地形的碰撞检测,防止摄像机穿透地面,这也是ogre demo中Terrian例子中的方法。

 

首先定义 RaySceneQuery* raySceneQuery = 0;

在CreateScene时候,创建场景查询

 frameRenderingQueued事件中,进行射线查询,设定摄像机位置

  raySceneQuery = mSceneMgr->createRayQuery(

            Ray(mCamera->getPosition(), Vector3::NEGATIVE_UNIT_Y));//光线的位置和方向,垂直向下

然后在

 

    bool frameRenderingQueued(const FrameEvent& evt)

    {

        if( ExampleFrameListener::frameRenderingQueued(evt) == false )

         return false;

 

        // clamp to terrain

        static Ray updateRay;

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

        updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y);

        raySceneQuery->setRay(updateRay);

        RaySceneQueryResult& qryResult = raySceneQuery->execute();

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

        if (i != qryResult.end() && i->worldFragment)//把摄像机定在地形个单位高的地方。

        {

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

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

                mCamera->getPosition().z);

        }

    

        return true;

 }

 

这样就把摄像机设在离地形高10个单位的地方。

 

Ogre 中,每个节点有AABB(轴对齐包围盒),通过包围盒,可以实现碰撞检测判断。具体实现过程如下: 通过Ogre::SceneNode::_getWorldAABB()可以取得这个叶子节点的AABB(Ogre::AxisAlignedBox),Ogre::AxisAlignedBox封装了对AABB的支持,该类的成员函数Ogre::AxisAlignedBox::intersects()可以判断一个AABB和"球体、点、面以及其他面"的相交情况(碰撞情况)。 Code m_SphereNode; //树的叶子,挂了一个"球"
m_CubeNode; //树的叶子,挂了一个"正方体"

 AxisAlignedBox spbox=m_SphereNode->_getWorldAABB(); AxisAlignedBox cbbox=m_CubeNode->_getWorldAABB();

if(spbox.intersects(cbbox)) { //相交 } 下面是测试2个节点通过AABB判断碰撞的程序代码:

Code

#ifndef __gkd4_h_

#define __gkd4_h_

#include "ExampleApplication.h"

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#include "../res/resource.h"

#endif

#include

#include

#include

#include

//两个场景节点

SceneNode*node1;

 SceneNode* node2;

CEGUI::MouseButton convertOISMouseButtonToCegui(int buttonID)

{

  switch (buttonID)

  { case 0: return CEGUI::LeftButton;

     case 1: return CEGUI::RightButton;

     case 2: return CEGUI::MiddleButton;

     case 3: return CEGUI::X1Button;

     default: return CEGUI::LeftButton;

   }

}

class gkd4FrameListener : public ExampleFrameListener, public OIS::KeyListener, public OIS::MouseListener

{

private: SceneManager* mSceneMgr;

CEGUI::Renderer* mGUIRenderer;

bool mShutdownRequested;

bool mUseBufferedInputKeys, mUseBufferedInputMouse, mInputTypeSwitchingOn;

public: gkd4FrameListener(SceneManager *sceneMgr, RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer) : ExampleFrameListener(win, cam, false, true), mGUIRenderer(renderer), mShutdownRequested(false), mUseBufferedInputKeys(false), mUseBufferedInputMouse(true),
mSceneMgr(sceneMgr)

{

mMouse->setEventCallback(this);

mKeyboard->setEventCallback(this); mInputTypeSwitchingOn = mUseBufferedInputKeys || mUseBufferedInputMouse;

}

bool frameStarted(const FrameEvent& evt)

{ bool ret = ExampleFrameListener::frameStarted(evt);

 if(mUseBufferedInputMouse)     { CEGUI::MouseCursor::getSingleton().show( ); }

else

 { CEGUI::MouseCursor::getSingleton().hide( ); } AxisAlignedBox spbox=node1->_getWorldAABB(); AxisAlignedBox cbbox=node2->_getWorldAABB();

if(spbox.intersects(cbbox))

  { node2->setScale(10,10,10); }

return ret;

}

bool processUnbufferedKeyInput(const FrameEvent& evt)

{

   bool ret =ExampleFrameListener::processUnbufferedKeyInput(evt);

 // see if switching is on, and you want to toggle if (mInputTypeSwitchingOn && mKeyboard->isKeyDown(OIS::KC_M) && mTimeUntilNextToggle <= 0)

  { switchMouseMode();

   mTimeUntilNextToggle = 1;

  }

 

if (mInputTypeSwitchingOn && mKeyboard->isKeyDown(OIS::KC_K) && mTimeUntilNextToggle <= 0)

{

  // must be going from immediate keyboard to buffered         keyboard switchKeyMode();

mTimeUntilNextToggle = 1;

}

 return ret;

}

void requestShutdown(void)

 {

 mShutdownRequested = true;

 }

void switchMouseMode()

{ mUseBufferedInputMouse = !mUseBufferedInputMouse; mMouse->setBuffered(mUseBufferedInputMouse);

}

//----------------------------------------------------------------- void switchKeyMode()

{ mUseBufferedInputKeys = !mUseBufferedInputKeys; mKeyboard->setBuffered(mUseBufferedInputKeys);

}

bool frameEnded(const FrameEvent& evt)

{

if (mShutdownRequested)

return false;

else

return ExampleFrameListener::frameEnded(evt);

}

//----------------------------------------------------------------//

 bool mouseMoved( const OIS::MouseEvent &arg )

{

CEGUI::System::getSingleton().injectMouseMove( arg.state.X.rel, arg.state.Y.rel ); return true;

}

 //----------------------------------------------------------------//

bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )

{

CEGUI::System::getSingleton().injectMouseButtonDown(convertOISMouseButtonToCegui(id));

return true;

}

//----------------------------------------------------------------//

bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )

{

CEGUI::System::getSingleton().injectMouseButtonUp(convertOISMouseButtonToCegui(id));

return true;

}

//----------------------------------------------------------------//

bool keyPressed( const OIS::KeyEvent &arg )

{

if( arg.key == OIS::KC_ESCAPE )

mShutdownRequested = true;

CEGUI::System::getSingleton().injectKeyDown( arg.key ); CEGUI::System::getSingleton().injectChar( arg.text );

return true;

}

//----------------------------------------------------------------//

bool keyReleased( const OIS::KeyEvent &arg )

{

if( arg.key == OIS::KC_M )

mMouse->setBuffered( !mMouse->buffered() );

else if( arg.key == OIS::KC_K )

mKeyboard->setBuffered( !mKeyboard->buffered() ); CEGUI::System::getSingleton().injectKeyUp( arg.key ); return true;

 }

};

class gkd4App : public ExampleApplication

{ private: CEGUI::OgreCEGUIRenderer* mGUIRenderer; CEGUI::System* mGUISystem;

public: gkd4App() : mGUIRenderer(0), mGUISystem(0) {} ~gkd4App()

{

if(mGUISystem)

   {

   delete mGUISystem;

   mGUISystem = 0;

   }

  if(mGUIRenderer)

  {

   delete mGUIRenderer;

   mGUIRenderer = 0;

   }

}

protected: virtual void createCamera(void)

{

// Create the camera

mCamera = mSceneMgr->createCamera("PlayerCam");

// Position it at 500 in Z direction

mCamera->setPosition(Vector3(0,0,80));

// Look back along -Z

mCamera->lookAt(Vector3(0,0,-300));

mCamera->setNearClipDistance(5);

}

virtual bool configure(void)

{

// Show the configuration dialog and initialise the system

// You can skip this and use root.restoreConfig() to load configuration

// settings if you were sure there are valid ones saved in ogre.cfg

if(mRoot->showConfigDialog())

{

// If returned true, user clicked OK so initialise

// Here we choose to let the system create a default rendering window by passing 'true'

mWindow = mRoot->initialise(true);

// Let's add a nice window icon

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 HWND hwnd;

mWindow->getCustomAttribute("WINDOW", (void*)&hwnd); LONG iconID = (LONG)LoadIcon( GetModuleHandle(0), MAKEINTRESOURCE(IDI_APPICON) );

SetClassLong( hwnd, GCL_HICON, iconID );

#endif return true;

}

else

{

return false;

}

}

// Just override the mandatory create scene method

virtual void createScene(void)

{

// setup GUI system

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

mGUISystem = new CEGUI::System(mGUIRenderer); CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);

// load scheme and set up defaults CEGUI::SchemeManager::getSingleton().loadScheme( (CEGUI::utf8*)"TaharezLookSkin.scheme");

mGUISystem->setDefaultMouseCursor( (CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");

mGUISystem->setDefaultFont((CEGUI::utf8*)"BlueHighway-12"); CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow"); CEGUI::MouseCursor::getSingleton().show( ); setupEventHandlers();

Entity* ent1=mSceneMgr->createEntity("robot","robot.mesh");

node1=mSceneMgr->getRootSceneNode()->createChildSceneNode("robotnode");

node1->attachObject(ent1);

Entity *ent2=mSceneMgr->createEntity("ogrehead","ogreHead.mesh");

node2=mSceneMgr->getRootSceneNode()->createChildSceneNode("ogreheadnode",Vector3(2,0,0)); node2->attachObject(ent2);

// Set ambient light mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

// Create a light

Light* l = mSceneMgr->createLight("MainLight"); l->setPosition(20,80,50);

}

// Create new frame listener void createFrameListener(void) { mFrameListener= new gkd4FrameListener(mSceneMgr, mWindow, mCamera, mGUIRenderer);

mRoot->addFrameListener(mFrameListener);

}

void setupEventHandlers(void)

{ }

bool handleQuit(const CEGUI::EventArgs& e)

{

static_cast(mFrameListener)->requestShutdown();

return true;

}

};

#endif

// #ifndef __gkd4_h_

 

在Ogre中,可以创建一个查询器,比如球体查询器,AABB查询器,面查询器等。下面我们以最常用的球形查询器为例来说明:

如下面的代码,我们首先创建一个球形查询器,它需要定义一个球体,我们把它的位置,设置为摄像机的位置,半径为10,掩码为第二个参数,默认为-1,

通过掩码,可以把碰撞的物体分成不同的组,比如我们设置球形场景查询器的掩码为0x1,则它只检测和它掩码相同物体的碰撞。该摄像机检测到entity对象后,就把摄像机位置沿摄像机方向,向后移动一个单位,这样会实现碰撞检测,但是摄像机因为向后有个移动,所以会有抖动。

Code
        //创建球形查询器,第二个参数表示掩码,默认情况下为-1
        SphereSceneQuery * pQuery=mSceneMgr->createSphereQuery(Sphere(mCamera->getPosition(),10));
        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()=="Head")
                    //{
                    //    //m_node->setScale(10,10,10);
                    //    pObject->getParentNode()->scale(0.5,0.5,0.5);
                    //    break;
                    //}
                    Ogre::Vector3 v = mCamera->getPosition();
                    Ogre::Vector3 d = mCamera->getDirection();
                    v = v + d*(-1);
                    mCamera->setPosition(v);

                }
            }
        }
 

              全部的代码:

 

Code
#include "ExampleApplication.h"

RaySceneQuery* raySceneQuery = 0;

// Event handler to add ability to alter curvature
class TerrainFrameListener : public ExampleFrameListener
{
public:
    SceneManager* mSceneMgr;
    TerrainFrameListener(SceneManager *sceneMgr,RenderWindow* win, Camera* cam)
        : ExampleFrameListener(win, cam)
    {
        // Reduce move speed
        mMoveSpeed = 50;
        mSceneMgr = sceneMgr;
    }

    bool frameRenderingQueued(const FrameEvent& evt)
    {
        if( ExampleFrameListener::frameRenderingQueued(evt) == false )
        return false;

        // clamp to terrain
        static Ray updateRay;
        updateRay.setOrigin(mCamera->getPosition());
        updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y);
        raySceneQuery->setRay(updateRay);
        RaySceneQueryResult& qryResult = raySceneQuery->execute();
        RaySceneQueryResult::iterator i = qryResult.begin();
        if (i != qryResult.end() && i->worldFragment)//把摄像机定在地形10个单位高的地方。
        {
            mCamera->setPosition(mCamera->getPosition().x,
                i->worldFragment->singleIntersection.y + 10,
                mCamera->getPosition().z);
        }
        //创建球形查询器,第二个参数表示掩码,默认情况下为-1
        SphereSceneQuery * pQuery=mSceneMgr->createSphereQuery(Sphere(mCamera->getPosition(),10));
        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()=="Head")
                    //{
                    //    //m_node->setScale(10,10,10);
                    //    pObject->getParentNode()->scale(0.5,0.5,0.5);
                    //    break;
                    //}
                    Ogre::Vector3 v = mCamera->getPosition();
                    Ogre::Vector3 d = mCamera->getDirection();
                    v = v + d*(-1);
                    mCamera->setPosition(v);

                }
            }
        }

        return true;

    }

};

 

class TerrainApplication : public ExampleApplication
{
public:
    TerrainApplication() {}

    ~TerrainApplication()
    {
        delete raySceneQuery;
    }

protected:

    virtual void chooseSceneManager(void)
    {
        // Get the SceneManager, in this case a generic one
        mSceneMgr = mRoot->createSceneManager("TerrainSceneManager");
    }

    virtual void createCamera(void)
    {
        // Create the camera
        mCamera = mSceneMgr->createCamera("PlayerCam");

        // Position it at 500 in Z direction
        mCamera->setPosition(Vector3(128,25,128));
        // Look back along -Z
        mCamera->lookAt(Vector3(0,0,-300));
        mCamera->setNearClipDistance( 1 );
        mCamera->setFarClipDistance( 1000 );

    }
  
    // Just override the mandatory create scene method
    void createScene(void)
    {

        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

        // Create a light
        Light* l = mSceneMgr->createLight("MainLight");
        // Accept default settings: point light, white diffuse, just set position
        // NB I could attach the light to a SceneNode if I wanted it to move automatically with
        //  other objects, but I don't
        l->setPosition(20,80,50);

        // Fog
        // NB it's VERY important to set this before calling setWorldGeometry
        // because the vertex program picked will be different
        ColourValue fadeColour(0.93, 0.86, 0.76);
        mSceneMgr->setFog( FOG_LINEAR, fadeColour, .001, 500, 1000);
        mWindow->getViewport(0)->setBackgroundColour(fadeColour);

        std::string terrain_cfg("terrain.cfg");
        mSceneMgr -> setWorldGeometry( terrain_cfg );
        // Infinite far plane?
        if (mRoot->getRenderSystem()->getCapabilities()->hasCapability(RSC_INFINITE_FAR_PLANE))
        {
            mCamera->setFarClipDistance(0);
        }

        // Set a nice viewpoint
        mCamera->setPosition(707,2500,528);
        mCamera->setOrientation(Quaternion(-0.3486, 0.0122, 0.9365, 0.0329));
        //mRoot -> showDebugOverlay( true );

        raySceneQuery = mSceneMgr->createRayQuery(
            Ray(mCamera->getPosition(), Vector3::NEGATIVE_UNIT_Y));//光线的位置和方向,垂直向下

        Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

        //创建ogre head实体,测试通过射线查询movable来实现摄像机碰撞检测
        SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("ogreHead");
        headNode->attachObject(ogreHead);
        headNode->setPosition(500.0, 100.0, 500.0);
        headNode->scale(Vector3(2,2,2));

    }
    // Create new frame listener
    void createFrameListener(void)
    {
        mFrameListener= new TerrainFrameListener(mSceneMgr,mWindow, mCamera);
        mRoot->addFrameListener(mFrameListener);
    }

};

 在3中,我提到检测到碰撞时候,后移一个单位,摄像机会抖动,现在通过记录摄像机上步移动偏移量,如果检测到碰撞则反移回来,可以解决这个问题。

下面是修改后的代码:

 

Code
    bool frameRenderingQueued(const FrameEvent& evt)
    {
        if( ExampleFrameListener::frameRenderingQueued(evt) == false )
        return false;

        // clamp to terrain
        static Ray updateRay;
        updateRay.setOrigin(mCamera->getPosition());
        updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y);
        raySceneQuery->setRay(updateRay);
        RaySceneQueryResult& qryResult = raySceneQuery->execute();
        RaySceneQueryResult::iterator i = qryResult.begin();
        if (i != qryResult.end() && i->worldFragment)//把摄像机定在地形10个单位高的地方。
        {
            mCamera->setPosition(mCamera->getPosition().x,
                i->worldFragment->singleIntersection.y + 10,
                mCamera->getPosition().z);
        }
        //创建球形查询器,第二个参数表示掩码,默认情况下为-1
        Vector3 oldPos = mCamera->getPosition();
        SphereSceneQuery * pQuery=mSceneMgr->createSphereQuery(Sphere(mCamera->getPosition(),10),0x01);
        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()=="Head")
                    //{
                    //    //m_node->setScale(10,10,10);
                    //    pObject->getParentNode()->scale(0.5,0.5,0.5);
                    //    break;
                    //}
                    //Ogre::Vector3 v = mCamera->getPosition();
                    //Ogre::Vector3 d = mCamera->getDirection();
                    //v = v + d*(-1);
                    //mCamera->setPosition(v);
                    mCamera->moveRelative( - mTranslateVector);

                }
            }
        }

        return true;

    }

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wangwang123654/archive/2009/12/10/4980450.aspx

抱歉!评论已关闭.