[OGRE]基础教程来七发:来谈一谈缓冲绑定

上一章我们处理监听的方案是,每一帧只处理一次。

这一次,当鼠标键盘的事件发生时,我们会立即处理它。

这里只是对缓冲输入的一个简单介绍,而不是完整的如何使用OIS的教程。

若想了解更多内容,请查阅相关的OIS使用教程。


初始的项目源码如下:

 

#include "ExampleApplication.h"

 

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

{

public:

    TutorialFrameListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr)

        : ExampleFrameListener(win, cam, true, true)

    {

    }

 

    bool frameStarted(const FrameEvent &evt)

    { 

        if(mMouse)

            mMouse->capture();

        if(mKeyboard) 

            mKeyboard->capture();

        return mContinue;

    }

 

    // MouseListener

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

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

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

 

    // KeyListener

    bool keyPressed(const OIS::KeyEvent &e) { return true; }

    bool keyReleased(const OIS::KeyEvent &e) { return true; }

protected:

    Real mRotate;          // The rotate constant

    Real mMove;            // The movement constant

   

    SceneManager *mSceneMgr;   // The current SceneManager

    SceneNode *mCamNode;   // The SceneNode the camera is currently attached to

 

    bool mContinue;        // Whether to continue rendering or not

    Vector3 mDirection;     // Value to move in the correct direction

};

     

class TutorialApplication : public ExampleApplication

{

public:

    void createCamera(void)

    { 

        // create camera, but leave at default position

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

        mCamera->setNearClipDistance(5);

    }

 

    void createScene(void)

    {

        mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));

 

        // add the ninja

        Entity *ent = mSceneMgr->createEntity("Ninja", "ninja.mesh");

        SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode");

        node->attachObject(ent);  

 

        // create the light

        Light *light = mSceneMgr->createLight("Light1");

        light->setType(Light::LT_POINT);

        light->setPosition(Vector3(250, 150, 250));

        light->setDiffuseColour(ColourValue::White);

        light->setSpecularColour(ColourValue::White); 

 

        // Create the scene node

        node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1", Vector3(-400, 200, 400));

 

        // Make it look towards the ninja

        node->yaw(Degree(-45));

 

        // Create the pitch node

        node = node->createChildSceneNode("PitchNode1");

        node->attachObject(mCamera); 

 

        // create the second camera node/pitch node

        node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2", Vector3(0, 200, 400));

        node = node->createChildSceneNode("PitchNode2");

    }

 

    void createFrameListener(void)

    {

        // Create the FrameListener

        mFrameListener = new TutorialFrameListener(mWindow, mCamera, mSceneMgr);

        mRoot->addFrameListener(mFrameListener); 

 

        // Show the frame stats overlay

        mFrameListener->showDebugOverlay(true);

    }

};

 

#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

    TutorialApplication app;

 

    try {

        app.go();

    } catch(Exception& e) {

#if OGRE_PLATFORM == PLATFORM_WIN32 || 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
",

            e.getFullDescription().c_str());

#endif

    }

 

    return 0;

}

上一次我们使用的是无缓冲的输入,也就是说,在每一帧里我们查询OIS::KeyboardOIS::Mouse实例的状态,以判断它们是否被按下。

而缓冲输入使用了一个listener接口,以便在事件发生时通知你的程序。

比如,当一个键被按下时,会触发一个 KeyListener::keyPressed 事件,

而当这个键被释放(不再按下)时,KeyListener::keyReleased 事件被触发给所有已注册的KeyListener类。

这些能用在追踪按键的时间,或判断按键在上一帧中是否没有被按下。


 

关于OIS的监听系统有一点要注意的是,对于每一个KeyboardMouseJoystick对象只能有一个监听器,这样是为了简单(也为了速度)。

多次调用setEventCallback函数的结果是只有最后一次注册的监听器才得到事件消息。

如果你有多个对象需要获得KeyMouse事件,你只有自己写一个消息分发。


还有,千万记得在frameStarted方法里调用Keyboard::captureMouse::capture

OIS不会使用线程来确定键盘鼠标的状态,所以你必须指明什么时候去获取输入。


OISKeyListener接口提供了两个纯虚函数。

第一个是keyPressed函数,每次按下某个键时调用它,

还一个是keyReleased,每次离开某个键时调用它,

传入这些函数的参数是一个KeyEvent,它包含被按下/释放的按键的键码。


 

鼠标监听界面MouseListener接口比KeyListener接口要稍微复杂一些。

它包含查看何时鼠标键被按下/释放的函数: MouseListener::mousePressed MouseListener::mouseReleased。

它还包含一个mouseMoved函数,当鼠标移动时调用它。

这些函数都接收一个MouseEvent对象,在state变量里保存着当前鼠标的状态。

 

需要注意的是,MouseState对象即包含了鼠标移动的相对XY坐标(即,从上一次调用MouseListener::mouseMoved开始,它所移动的距离),

还包含了绝对XY坐标(即,屏幕上的准确位置)。

 

在我们开始修改TutorialFrameListener之前,请注意先对TutorialFrameListener类做两处大的改变:

 

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



我们继承了OISMouseListenerKeyListener类,这样我们才能从它们那里接收事件。


 

同样,调用ExampleFrameListener构造器也有变化:

 TutorialFrameListener( RenderWindow* win, Camera* cam, SceneManager *sceneMgr ):ExampleFrameListener(win, cam, true, true)


将后面两个参数改为true 指明了我们将要使用带缓冲的键盘鼠标输入。

再对类中的变量稍作修改:

 

protected:
	Real mRotate;          // 旋转常量
	Real mMove;            // 运动常量
	SceneManager *mSceneMgr;   // 当前的场景管理器
	SceneNode *mCamNode;   // 当前摄像机附着的场景节点
	bool mContinue;        // 是否要继续渲染
	Vector3 mDirection;     // 指向正确的移动方向


mContinue变量是frameStarted方法的返回值。

mContinuefalse的时候,程序退出。

mDirection变量指定了在每一个帧里我们如何移动摄像机节点。

在构造器里,我们像在上次那样初始化一些变量,并把mContinue设成true。添加如下代码到TutorialFrameListener的构造器里:

 

	TutorialFrameListener( RenderWindow* win, Camera* cam, SceneManager *sceneMgr )
		:ExampleFrameListener(win, cam, true, true)
	{
		// Populate the camera and scene manager containers
		mCamNode = cam->getParentSceneNode();
		mSceneMgr = sceneMgr;
		// 设置旋转和移动速度
		mRotate = 0.13;
		mMove = 250;
		// 继续渲染
		mContinue = true;
	}

ExampleFrameListener的构造器里已经取得了OISmMousemKeyboard对象。

我们调用这些输入对象的setEventCallback方法,把TutorialFrameListener注册成一个监听器。

		//注册监听器
		mMouse->setEventCallback(this);
		mKeyboard->setEventCallback(this);


 

最后,我们还要把mDirection初始化成零向量(因为我们最开始不需要它动):

	TutorialFrameListener( RenderWindow* win, Camera* cam, SceneManager *sceneMgr )
		:ExampleFrameListener(win, cam, true, true)
	{
		// Populate the camera and scene manager containers
		mCamNode = cam->getParentSceneNode();
		mSceneMgr = sceneMgr;
		// 设置旋转和移动速度
		mRotate = 0.13;
		mMove = 250;
		// 继续渲染
		mContinue = true;
		//注册监听器
		mMouse->setEventCallback(this);
		mKeyboard->setEventCallback(this);
		//零向量
		mDirection = Vector3::ZERO;
	}


在我们深入之前,我们应该设置 Escape 键用来退出程序。

找到TutorialFrameListener::keyPressed方法,每当键盘上一个键被按下时,都会调用这个方法并传入一个KeyEvent对象。

我们能够通过这个对象的key变量来获取按键的键码(KC_*)。基于这个值,我们构造一个switch,为绑定所有程序里用到的按钮。


 

我们需要在switch语句里为其它按钮做绑定。首先我们要让用户按12键进行视口的切换。

代码基本上与上次相同:

 

      case OIS::KC_1:

          mCamera->getParentSceneNode()->detachObject(mCamera);

          mCamNode = mSceneMgr->getSceneNode("CamNode1");

          mCamNode->attachObject(mCamera);

          break;

 

      case OIS::KC_2:

          mCamera->getParentSceneNode()->detachObject(mCamera);

          mCamNode = mSceneMgr->getSceneNode("CamNode2");

          mCamNode->attachObject(mCamera);

          break;

接下来我们要添加键盘移动。每次用户按下移动按键,我们都要朝正确的方向加上或者减去mMove

      case OIS::KC_UP:

      case OIS::KC_W:

          mDirection.z -= mMove;

          break;

 

      case OIS::KC_DOWN:

      case OIS::KC_S:

          mDirection.z += mMove;

          break;

 

      case OIS::KC_LEFT:

      case OIS::KC_A:

          mDirection.x -= mMove;

          break;

 

      case OIS::KC_RIGHT:

      case OIS::KC_D:

          mDirection.x += mMove;

          break;

 

      case OIS::KC_PGDOWN:

      case OIS::KC_E:

          mDirection.y -= mMove;

          break;

 

      case OIS::KC_PGUP:

      case OIS::KC_Q:

          mDirection.y += mMove;

          break;

当按键被释放时,我们要立即取消mDirection向量上的移动。找到keyReleased方法,添加如下代码:

      switch (e.key)

      {

      case OIS::KC_UP:

      case OIS::KC_W:

          mDirection.z += mMove;

          break;

 

      case OIS::KC_DOWN:

      case OIS::KC_S:

          mDirection.z -= mMove;

          break;

 

      case OIS::KC_LEFT:

      case OIS::KC_A:

          mDirection.x += mMove;

          break;

 

      case OIS::KC_RIGHT:

      case OIS::KC_D:

          mDirection.x -= mMove;

          break;

 

      case OIS::KC_PGDOWN:

      case OIS::KC_E:

          mDirection.y += mMove;

          break;

 

      case OIS::KC_PGUP:

      case OIS::KC_Q:

          mDirection.y -= mMove;

          break;

      } // switch

      return true;

好了,我们能根据按键输入对mDirection进行更新了。下面的代码与上次是一样的,添加到frameStarted函数里:

       mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);


接下来轮到鼠标了。我们从点击鼠标左键来控制灯的开关开始。

找到mousePressed函数并看看它的参数。用OIS,我们可以访问MouseEventMouseButtonID

我们用MouseButtonID作为switch条件,来确定按下的是哪个按钮。用下面的代码替换掉mousePressed函数里的:

 

Light *light = mSceneMgr->getLight("Light1");

      switch (id)

      {

      case OIS::MB_Left:

          light->setVisible(! light->isVisible());

          break;

      }

      return true;


剩下来的事情就是绑定鼠标右键来进入鼠标观察模式。

每当鼠标移动时我们都检查右键是否按下。如果是,我们基于相对运动来转动摄像机。

通过传入函数的MouseEvent对象,我们能获取相对运动。它包含一个switch变量,里面有鼠标的状态(是关于鼠标的详细信息)。

MouseState::buttonDown告诉我们是否一个特定的按钮被按下,而“X”“Y”变量告诉我们鼠标的相对运动。找到mouseMoved方法,用以下代码替换掉原来的:

 

      if (e.state.buttonDown(OIS::MB_Right))

      {

          mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD);

          mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL);

      }

      return true;


至此,完整的代码如下:

 

#include "ExampleApplication.h"

class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
{
public:
    TutorialFrameListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr)
        : ExampleFrameListener(win, cam, true, true)
    {
        // Populate the camera and scene manager containers
        mCamNode = cam->getParentSceneNode();
        mSceneMgr = sceneMgr;

        // set the rotation and move speed
        mRotate = 0.13;
        mMove = 250;

        // continue rendering
        mContinue = true;

        mMouse->setEventCallback(this);
        mKeyboard->setEventCallback(this);

        mDirection = Vector3::ZERO;
    }

    bool frameStarted(const FrameEvent &evt)
    {
        if(mMouse)
            mMouse->capture();
        if(mKeyboard) 
            mKeyboard->capture();

        mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);
        return mContinue;
    }

    // MouseListener
    bool mouseMoved(const OIS::MouseEvent &e)
    {
        if (e.state.buttonDown(OIS::MB_Right))
        {
            mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD);
            mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL);
        }
        return true;
    }

    bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id)
    {
        Light *light = mSceneMgr->getLight("Light1");
        switch (id)
        {
        case OIS::MB_Left:
            light->setVisible(! light->isVisible());
            break;
        }
        return true;
    }

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

    // KeyListener
    bool keyPressed(const OIS::KeyEvent &e)
    {
        switch (e.key)
        {
        case OIS::KC_ESCAPE: 
            mContinue = false;
            break;

        case OIS::KC_1:
            mCamera->getParentSceneNode()->detachObject(mCamera);
            mCamNode = mSceneMgr->getSceneNode("CamNode1");
            mCamNode->attachObject(mCamera);
            break;

        case OIS::KC_2:
            mCamera->getParentSceneNode()->detachObject(mCamera);
            mCamNode = mSceneMgr->getSceneNode("CamNode2");
            mCamNode->attachObject(mCamera);
            break;

        case OIS::KC_UP:
        case OIS::KC_W:
            mDirection.z -= mMove;
            break;

        case OIS::KC_DOWN:
        case OIS::KC_S:
            mDirection.z += mMove;
            break;

        case OIS::KC_LEFT:
        case OIS::KC_A:
            mDirection.x -= mMove;
            break;

        case OIS::KC_RIGHT:
        case OIS::KC_D:
            mDirection.x += mMove;
            break;

        case OIS::KC_PGDOWN:
        case OIS::KC_E:
            mDirection.y -= mMove;
            break;

        case OIS::KC_PGUP:
        case OIS::KC_Q:
            mDirection.y += mMove;
            break;
        }
        return true;
    }

    bool keyReleased(const OIS::KeyEvent &e)
    {
        switch (e.key)
        {
        case OIS::KC_UP:
        case OIS::KC_W:
            mDirection.z += mMove;
            break;

        case OIS::KC_DOWN:
        case OIS::KC_S:
            mDirection.z -= mMove;
            break;

        case OIS::KC_LEFT:
        case OIS::KC_A:
            mDirection.x += mMove;
            break;

        case OIS::KC_RIGHT:
        case OIS::KC_D:
            mDirection.x -= mMove;
            break;

        case OIS::KC_PGDOWN:
        case OIS::KC_E:
            mDirection.y += mMove;
            break;

        case OIS::KC_PGUP:
        case OIS::KC_Q:
            mDirection.y -= mMove;
            break;
        } // switch
        return true;
    }
protected:
    Real mRotate;          // The rotate constant
    Real mMove;            // The movement constant

    SceneManager *mSceneMgr;   // The current SceneManager
    SceneNode *mCamNode;   // The SceneNode the camera is currently attached to

    bool mContinue;        // Whether to continue rendering or not
    Vector3 mDirection;     // Value to move in the correct direction
};

class TutorialApplication : public ExampleApplication
{
public:
    void createCamera(void)
    {
        // create camera, but leave at default position
        mCamera = mSceneMgr->createCamera("PlayerCam"); 
        mCamera->setNearClipDistance(5);
    }

    void createScene(void)
    {
        mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));

        // add the ninja
        Entity *ent = mSceneMgr->createEntity("Ninja", "ninja.mesh");
        SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode");
        node->attachObject(ent);

        // create the light
        Light *light = mSceneMgr->createLight("Light1");
        light->setType(Light::LT_POINT);
        light->setPosition(Vector3(250, 150, 250));
        light->setDiffuseColour(ColourValue::White);
        light->setSpecularColour(ColourValue::White);

        // Create the scene node
        node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1", Vector3(-400, 200, 400));
        node->yaw(Degree(-45));
        node->attachObject(mCamera);

        // create the second camera node/pitch node
        node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2", Vector3(0, 200, 400));
    }

    void createFrameListener(void)
    {
        // Create the FrameListener
        mFrameListener = new TutorialFrameListener(mWindow, mCamera, mSceneMgr);
        mRoot->addFrameListener(mFrameListener);

        // Show the frame stats overlay
        mFrameListener->showDebugOverlay(true);
    }
};

#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
    TutorialApplication app;

    try {
        app.go();
    } catch(Exception& e) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || 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
",
            e.getFullDescription().c_str());
#endif
    }

    return 0;
}


运行效果如图:






原文地址:https://www.cnblogs.com/pangblog/p/3364647.html