zoukankan      html  css  js  c++  java
  • OGRE 选中物体

    原文来源:http://blog.sina.com.cn/s/blog_45209f340100f6ze.html

    选中物体

    这次对于所有的鼠标事件,我创建了封闭函数来处理它们。当用户按下鼠标左键,"onLeftPressed"函数被调用,当按下右键时"onRightReleased"函数被调用,等等。

    一如既往,首先给出框架代码:

       

    #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)
           {
               // Setup default variables
               mCount = 0;
               mCurrentObject = NULL;
               mLMouseDown = false;
               mRMouseDown = false;
               mSceneMgr = sceneManager;
      
               // Reduce move speed
               mMoveSpeed = 50;
               mRotateSpeed /= 500;
      
               // Register this so that we get mouse events.
               mMouse->setEventCallback(this);
      
               // Create RaySceneQuery
               mRaySceneQuery = mSceneMgr->createRayQuery(Ray());
           } // MouseQueryListener
      
           ~MouseQueryListener()
           {
               mSceneMgr->destroyQuery(mRaySceneQuery);
           }
      
           bool frameStarted(const FrameEvent &evt)
           {
               // 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;
      
               // Setup the scene query
               Vector3 camPos = mCamera->getPosition();
               Ray cameraRay(Vector3(camPos.x, 5000.0f, camPos.z), Vector3::NEGATIVE_UNIT_Y);
               mRaySceneQuery->setRay(cameraRay);
      
               // Perform the scene query
               RaySceneQueryResult &result = mRaySceneQuery->execute();
               RaySceneQueryResult::iterator itr = result.begin();
      
               // Get the results, set the camera height
               if (itr != result.end() && itr->worldFragment)
               {
                   Real terrainHeight = itr->worldFragment->singleIntersection.y;
                   if ((terrainHeight + 10.0f) > camPos.y)
                       mCamera->setPosition( camPos.x, terrainHeight + 10.0f, camPos.z );
               }
      
               return true;
           }
      
          
           bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
           {
               // Left mouse button up
               if (id == OIS::MB_Left)
               {
                   onLeftReleased(arg);
                   mLMouseDown = false;
               } // if
      
               // Right mouse button up
               else if (id == OIS::MB_Right)
               {
                   onRightReleased(arg);
                   mRMouseDown = false;
               } // else if
      
               return true;
           }
      
           bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
           {
               // Left mouse button down
               if (id == OIS::MB_Left)
               {
                   onLeftPressed(arg);
                   mLMouseDown = true;
               } // if
      
               // Right mouse button down
               else if (id == OIS::MB_Right)
               {
                   onRightPressed(arg);
                   mRMouseDown = true;
               } // else if
      
               return true;
           }
      
           bool mouseMoved(const OIS::MouseEvent &arg)
           {
               // 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)
               {
                   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
      
               // 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
      
               return true;
           }
      
           // Specific handlers
           void onLeftPressed(const OIS::MouseEvent &arg)
           {
               // 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));
               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)
               {
                   char name[16];
                   sprintf(name, "Robot%d", mCount++);
      
                   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
           }
      
           void onLeftReleased(const OIS::MouseEvent &arg)
           {
           }
      
           void onRightPressed(const OIS::MouseEvent &arg)
           {
               CEGUI::MouseCursor::getSingleton().hide();
           }
      
           virtual void onRightReleased(const OIS::MouseEvent &arg)
           {
               CEGUI::MouseCursor::getSingleton().show();
           }
      
       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)
           {
               // 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 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");
           }
      
           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
               MessageBoxA(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;
       }


        运行程序会发现程序运行的结果和手札19给出的代码,即手札18的程序的最终代码运行结果是一样的,只不过正如前面说的将相应实践放到了封闭的函数中了,你应该花一些时间来消化这些不同之处!

    一、实现选中物体

     

        最终能够做到当你放置物体后,能“拾取”并移动它。我们希望用户知道他目前正在操纵哪一个物体。在游戏里,我们可能以某种特殊的方式来高亮这个物体。而在这里我们用showBoundingBox方法来创建一个围绕该物体的方盒。当鼠标首次按下时,取消旧的选择物体上的包围盒,然后当选择了一新物体时,给新物体加上包围盒。为此,我们

    在onLeftPressed函数的开头添加如下代码:

          // 打开包围盒
          if (mCurrentObject)
              mCurrentObject->showBoundingBox(false);

    并在onLeftPressed末尾添加代码:

          // Show the bounding box to highlight the selected object
          if (mCurrentObject)
              mCurrentObject->showBoundingBox(true);
    运行你的程序发现单击创建的机器人会有一圈的白框

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

    二、实现添加不同种类的物体

     

    实现代码不仅能够添加机器人还能够放置和移动忍者。

    我们需要一个“机器人模式”和一个“忍者模式”,来决定在屏幕上放置的物体。

    我们把空格键设置成切换按钮,并且显示信息提示用户目前处于哪一种模式。

     

    首先,我们把MouseQueryListener设置成机器人模式。我们添加一个变量来保存物体状态

     

    在MouseQueryListener类的protected变量区域添加这个变量:

           bool mRobotMode;   //变量用于标明当前状态

     

     在MouseQueryListener类的构造函数进行初始化:

           // 设置文本、缺省状态
            mRobotMode = true;
            mDebugText = "Robot Mode Enabled - Press Space to Toggle";

    下面有修改单击响应代码,单击时根据mRobotMode的状态来断定应当添加什么模型:

    在onLeftPressed里修改代码:

    将这三句

             

    char name[16];
             sprintf(name, "Robot%d", mCount++);
       
             Entity *ent = mSceneMgr->createEntity(name, "robot.mesh");

    修改为:

              

    Entity *ent;
              char name[16];
    
              if (mRobotMode)
              {
                  sprintf(name, "Robot%d", mCount++);
                  ent = mSceneMgr->createEntity(name, "robot.mesh");
              } // if
              else
              {
                  sprintf(name, "Ninja%d", mCount++);
                  ent = mSceneMgr->createEntity(name, "ninja.mesh");
              } // else这些代码大家一定能够看懂我就不解释了


    下面绑定空格键,来改变状态。

    在frameStarted里(第一个if语句之后)添加:

         

    // 切换模式
            if(mKeyboard->isKeyDown(OIS::KC_SPACE) && mTimeUntilNextToggle <= 0)
           {
                   mRobotMode = !mRobotMode;
                   mTimeUntilNextToggle = 1;
                   mDebugText = (mRobotMode ? String("Robot") : String("Ninja")) + " Mode Enabled - Press Space to Toggle";
           } 

     

    看看效果吧!

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

     

    三、选中物体

     

    首先介绍一下下面这个结构体,你可以通过连接查看的类结构

     

    RaySceneQueryResultEntry

    RaySceneQueryResult返回一个RaySceneQueryResultEntry结构体的iterator。

    这个结构体包含三个变量。

    distance变量告诉你这个物体沿着射线有多远。另外两个变量的其中一个将是null(movable或者worldFramegment)。

    movable变量包含一个MovableObject对象(MovableObject基本上可以是任何你能绑在SceneNode上的对象(像实体、光源,等))你可以看一下后面那个继承关系图

    如果与射线相交的话。如果射线接触到一个地形片段,worldFragment将保存这个worldFragment对象(比如地形)。

    OGRE手札-20 <wbr>选中物体

    大多数RaySceneQueries的应用包括选取和操纵MovableObject对象,以及它们所绑定到的SceneNodes 。调用getName方法获取MovableObject的名称。调用getParentSceneNode(或getParentNode)获取它们所绑定到的SceneNode。如果RaySceneQueryResultEntry的结果不是一个MovableObject,movable变量则为null。

     

     WorldFragment是完全另一种怪物。当RaySceneQueryResult中的worldFragment成员被设置时,就意味着返回结果是SceneManager创建的世界几何(world geometry)的一部分。返回的world fragment的类型是基于SceneManager的。它是这样实现的,WorldFragment结构体包含一个fragmentType变量,以指明world fragment的类型。基于这个fragmentType变量,设置其它成员变量(singleIntersection, planes, geometry, 或者renderOp)。一般来说,RaySceneQueries只返回WFT_SINGLE_INTERSECTION类型的WorldFragments。singleIntersection变量只是一个Vector3,用来报告交点的坐标。

     

    介绍到这里,书归正传

        我们要做的另一件事情是“拾起”并拖拽已经被放置的物体。当前你若点击一个已经放置的物体,程序会忽略它,并在它后面放置另一个机器人。我们现在来修正它。

     

        首先要保证当我们点击鼠标,我们能得到沿射线上的第一个东西。为此,我们需要设置RaySceneQuery按深度排序。找到onLeftPressed函数里的如下代码:

               

    // 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));
               mRaySceneQuery->setRay(mouseRay);
      
               // Execute query
               RaySceneQueryResult &result = mRaySceneQuery->execute();
               RaySceneQueryResult::iterator itr = result.begin( );


    修改成这样(两处变化):

    // Setup the ray scene query
          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);
          mRaySceneQuery->setSortByDistance(true);//按顺序返回结果
          // Execute query
          RaySceneQueryResult &result = mRaySceneQuery->execute();
          RaySceneQueryResult::iterator itr;



    以下这段代码是我们要重写的,因为我们不能在单击时在创建实体   

            

    // Get results, create a node/entity on the position
               if (itr != result.end() && itr->worldFragment)
               {
              Entity *ent;
              char name[16];
              if (mRobotMode)
              {
                  sprintf(name, "Robot%d", mCount++);
                  ent = mSceneMgr->createEntity(name, "robot.mesh");
              } // if
              else
              {
                  sprintf(name, "Ninja%d", mCount++);
                  ent = mSceneMgr->createEntity(name, "ninja.mesh");
              } // else这些代码大家一定能够看懂我就不解释了
    
                   mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(String(name) + "Node", itr->worldFragment->singleIntersection);
                   mCurrentObject->attachObject(ent);
                   mCurrentObject->setScale(0.1f, 0.1f, 0.1f);
               } // if
              }

    实现能够选取已经放置在屏幕上的物体分为两步。

     

    首先,如果用户点击一个物体,则使mCurrentObject等于它的父节点。

    如果用户没有点击在物体上(而是点在地型上)时,就像以前一样放置一个新的机器人。

     

    第一个要做的修改就是,使用一个for循环来代替if语句:

           

    for ( itr = result.begin(); itr != result.end(); itr++ )
          {
       if (itr->movable && itr->movable->getName().substr(0, 5) != "tile[")
              {
                  mCurrentObject = itr->movable->getParentSceneNode();
                  break;
        // 首先我们要检查第一个交点的是不是一个MovableObject
         //如果是,我们把它的父节点赋给mCurrentObject。还要做另一个判断
         //TerrainSceneManager会为地型本身创建MovableObject
         //所以我们可能实际上会与他们相交。为了修正这个问题,我通过检查对象的名称来保证
         //它们的名称不类似于地型名称。一个典型的地形名称比如"tile[0][0,2]"。
         //最后,注意这个break语句。我们只需要在第一个物体上做操作,一旦我们找到一个合法的,
         //我们就应该跳出循环。
              } // if
        else if (itr->worldFragment)
              {
                  Entity *ent;
                  char name[16];
                  if (mRobotMode)
                  {
                      sprintf(name, "Robot%d", mCount++);
                      ent = mSceneMgr->createEntity(name, "robot.mesh");
                  } // if
                  else
                  {
                      sprintf(name, "Ninja%d", mCount++);
                      ent = mSceneMgr->createEntity(name, "ninja.mesh");
                  } // else
                  mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(String(name) + "Node", itr->worldFragment->singleIntersection);
                  mCurrentObject->attachObject(ent);
                  mCurrentObject->setScale(0.1f, 0.1f, 0.1f);
                  break;
              } // 

  • 相关阅读:
    递归和分类问题之间的区别
    基于TF-IDF的推荐
    mnist
    服饰分类的神经网络模型
    基于内容的推荐
    深度学习-术语
    FiBiNET-学习
    30,新起点,再出发。
    md5加盐加密
    批处理for命令中的/r和/d参数
  • 原文地址:https://www.cnblogs.com/SunkingYang/p/11049190.html
Copyright © 2011-2022 走看看