• [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;
    }
    


    运行效果如图:






  • 相关阅读:
    马拉车算法【Manachar】
    AcWing算法进阶课 基础算法 启发式合并
    iframe嵌套跨域子页面变化高度自适应
    js数组中的每一项异步请求
    利用geo3d地图数据画地图上面的柱子
    地图上面加柱状图组
    antd-select选项位置偏移问题解决
    git
    React
    前端面试题
  • 原文地址:https://www.cnblogs.com/pangblog/p/3364647.html
Copyright © 2020-2023  润新知