登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: "OGRE手札-20 选中物体"     [回主站]     [分站链接]
标题
"OGRE手札-20 选中物体"
clq
浏览(0) + 2010-02-24 15:22:12 发表 编辑

关键字:

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



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

OGRE手札-20 选中物体

(2009-07-23 13:19:28)
标签:

杂谈

分类:OGRE学习

选中物体

这次对于所有的鼠标事件,我创建了封闭函数来处理它们。当用户按下鼠标左键,"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\n",
               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 选中物体

大多数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;
          } // else if
      } // for

 

运行你的程序可以实现选中了


clq
2010-2-24 15:40:55 发表 编辑

原创  OGRE的2D坐标、CEGUI坐标、鼠标坐标 收藏

屏幕坐标系:左上角为(0, 0)右下角为(1, 1)

OGRE的2D坐标系:左上角为(-1, 1)右下角为(1, -1)

CEGUI坐标系:左上角为(0, 0),单位像素

 

转换公式(鼠标坐标=>OGRE的2D坐标)

void setCorners(float left, float top, float right, float bottom)

{

    left = left * 2 - 1;
    right = right * 2 - 1;
    top = 1 - top * 2;
    bottom = 1 - bottom * 2;
}

 

对于根据鼠标位置来产生射线:

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

{

...

    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));

...
}

其中函数

Ray getCameraToViewportRay(Real x, Real y) const;

// x and y are in “normalized” (0.0 to 1.0) screen coordinates

其中两个参数是对屏幕坐标系来说的,

所以

x = mousePos.d_x / float(arg.state.width)

y = mousePos.d_y / float(arg.state.height)

 

arg.state.width是渲染窗口的宽单位为像素

arg.state.height是渲染窗口的高单位为像素

mousePos.d_x是鼠标所在位置到渲染窗口左边界的距离单位为像素

mousePos.d_y是鼠标所在位置到渲染窗口上边界的距离单位为像素


来自 http://blog.csdn.net/geeeeeeee/archive/2008/12/03/3440560.aspx


clq
2010-2-24 16:14:35 发表 编辑

射线还是可以在 tree.. 场景起作用的(奇怪,和我前几天做的测试有点不同)。

不过这里用到了 CEGUI ,这个是要另外加入 include 和 lib 文件的。另外还要在创建场景时初始化一个管理器(?)

见下文. 来自 http://bbs.vrchina.net/viewthread.php?tid=3364

OGRE基础教程六--CEGUI和OGRE [Basic Tutorials 6]

  1. 必备知识
  2. 本教程是在假设你已经拥有c++编程基础并能够成功建立和编译Ogre程序(如果你设置程序方面还存在问题,请参阅SettingUpAnApplication来获取详细信息)。本教程建立在之前的初学者教程的基础上,并且假设你已经学习了它们。

  3. 工程设置
  4. 下面的适用于下载源代码的用户:

  5.     添加include文件夹: $(OGRE_HOME)\Dependencies\include,
  6.                       $(OGRE_HOME)\Dependencies\include\CEGUI
  7.     添加lib库路径:     $(OGRE_HOME)\OgreMain\Dependencies\Lib\Debug
  8. 确信已经链接 'CEGUIBase' 和 'OgreGUIRender' 库,也就是说将下面一行添加进你的Makefile文件或g++命令行:

  9.     -L/usr/local/lib -lCEGUIBase -lCEGUIOgreRenderer
  10. 下面的适用于SDK的用户:

  11.    添加include文件夹:$(OGRE_HOME)\include\CEGUI
  12. 确信已经在debug配置的中添加 'CEGUIBase_d.lib' 和 'OgreGUIRender_d.lib' 库( 'CEGUIBase.lib' 和 'OgreGUIRender.lib' 在release配置中)。在Visual C++中添加依赖,依次点击:项目 -> 属性 -> 配置属性 -> 链接。

  13. CEGUIRender源程序现在是从Ogre CVS下载代码中的一部分,一个示例工程,因此你必须将包含OgreGUIRenderer头文件和lib文件的文件夹路径添加到属性配置中。

  14. 另外,下面两个目录是必需的。尽管你在你的安装路径中的文件夹找不到。将其作为约定它就会起作用:

  15.     添加Include文件夹: $(OGRE_HOME)\Samples\Common\CEGUIRenderer\include
  16.     添加 Lib 路径:      $(OGRE_HOME)\Samples\Common\CEGUIRenderer\lib
  17. 介绍
  18. Crazy Eddies GUI系统是一个为不具备或缺乏用户界面制作功能的图形API或引擎提供免费用户界面支持的开源的库。这个使用c++编写的库是针对那些想制作优秀的游戏却又没有GUI(图形用户界面)子系统的专业游戏开发者。

  19. 开始
  20. 首先,你需要架构(skeleton)代码来创建具有CEGUI组件的Ogre程序。注意:如果你使用<Windows.h>,你必须在<Windows.h>之前添加#define NOMINMAX。

  21.    //mem probs without this next one
  22.   #include <OgreNoMemoryMacros.h>
  23.   #include <CEGUI/CEGUIImageset.h>
  24.   #include <CEGUI/CEGUISystem.h>
  25.   #include <CEGUI/CEGUILogger.h>
  26.   #include <CEGUI/CEGUISchemeManager.h>
  27.   #include <CEGUI/CEGUIWindowManager.h>
  28.   #include <CEGUI/CEGUIWindow.h>
  29.   #include "OgreCEGUIRenderer.h"
  30.   #include "OgreCEGUIResourceProvider.h"
  31.   //regular mem handler
  32.   #include <OgreMemoryMacros.h>
  33.   #include "ExampleApplication.h"
  34.   class GuiFrameListener : public ExampleFrameListener
  35.   {
  36.   private:
  37.     CEGUI::Renderer* mGUIRenderer;
  38.   public:
  39.     GuiFrameListener(RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer)
  40.       : ExampleFrameListener(win, cam, false, false),
  41.         mGUIRenderer(renderer)
  42.     {
  43.     }
  44.   };
  45. 仅仅是一个不做任何动作的空帧监听器,但在你按下“Esc”之前会一直循环。

  46.   class TutorialApplication : public ExampleApplication
  47.   {
  48.   private:
  49.      CEGUI::OgreCEGUIRenderer* mGUIRenderer;
  50.      CEGUI::System* mGUISystem;
  51.      CEGUI::Window* mEditorGuiSheet;
  52. 这些是包含所有CEGUI数据的数据成员。我喜欢显示的调用CEGUI成员,一但你开始对Ogre成员添加调用,这将会明确的说明它们是来自CEGUI。

  53.   public:
  54.       TutorialApplication()
  55.         : mGUIRenderer(0),
  56.           mGUISystem(0),
  57.           mEditorGuiSheet(0)
  58.       {
  59.       }
  60.       ~TutorialApplication()
  61.       {
  62.          if(mEditorGuiSheet)
  63.          {
  64.              CEGUI::WindowManager::getSingleton().destroyWindow(mEditorGuiSheet);
  65.          }
  66.          if(mGUISystem)
  67.          {
  68.              delete mGUISystem;
  69.              mGUISystem = 0;
  70.          }
  71.          if(mGUIRenderer)
  72.          {
  73.              delete mGUIRenderer;
  74.              mGUIRenderer = 0;
  75.          }
  76.       }
  77. 下面是你可以设置任意Ogre场景的地方,使用你在前五章教程学到的方法。在这个Ogre场景中,你仍要为其添加一个独立的相机(camera)和视窗(viewport)。

  78.   protected:
  79.       void createScene(void)
  80.       {
  81.          // Set ambient light
  82.          mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
  83. 下面是创建CEGUI日志的地方,一般都设置为Informative模式的。其具有四种模式:Standard, Errors, Informative 和 Insane。

  84.           // Set up GUI system
  85.          mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow,  
  86.                  Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr);
  87.          mGUISystem = new CEGUI::System(mGUIRenderer);   
  88.          CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);
  89. 创建一个新的CEGUI系统,使用“TaharezLook”来设置图(sheme)与鼠标指针,使用“BlueHighway-12”来设置字体。

  90.            CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");
  91.           mGUISystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook",(CEGUI::utf8*)"MouseArrow");
  92.           CEGUI::MouseCursor::getSingleton().setImage("TaharezLook","MouseMoveCursor");
  93.           mGUISystem->setDefaultFont((CEGUI::utf8*)"BlueHighway-12");
  94.           mEditorGuiSheet=CEGUI::WindowManager::getSingleton().createWindow((CEGUI::utf8*)"DefaultWindow",
  95.                                                                       (CEGUI::utf8*)"Sheet");  
  96.           mGUISystem->setGUISheet(mEditorGuiSheet);
  97.       }
  98. 调用自定义的帧监听器,这样我们可以在需要时访问“mGUIRender”。

  99.   void createFrameListener(void)
  100.      {
  101.          mFrameListener = new GuiFrameListener(mWindow, mCamera, mGUIRenderer);
  102.          mRoot->addFrameListener(mFrameListener);
  103.      }
  104.   };
  105. 下面是主函数也是程序的主循环,在本教程并不需要你修改这段代码。

  106.    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
  107.   #define WIN32_LEAN_AND_MEAN
  108.   #include "windows.h"
  109.   INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
  110.   #else
  111.   int main(int argc, char **argv)
  112.   #endif
  113.   {
  114.       // Create application object
  115.       TutorialApplication app;
  116.       try {
  117.           app.go();
  118.       } catch( Exception& e ) {
  119.   #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
  120.           MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
  121.   #else
  122.           fprintf(stderr, "An exception has occured: %s\n",e.getFullDescription().c_str());
  123.   #endif
  124.       }
  125.       return 0;
  126.   }
  127. 完成后,编译程序能得到一个空的窗口。请关掉程序,继续我们的学习。注意:如果实际操作中出现问题,你可以在应用程序所在文件夹中找到“CEGUI.log”文件分析查找错误

  128. CEGUI是如何工作的
  129. 本质上CEGUI是通过向窗口添加第二个场景,这个场景是在Ogre的基本渲染队列完成后才渲染的。这个场景仅仅是由一系列3D矩形对 象组成的。(也就是两个多边形沿着其边压制到一起)。渲染矩阵是为消除矩形的突兀与歪斜而根据他们的位置建立的。使用这些矩形,添加材质和响应就构成了用 户界面(GUI)。一般情况下这是很不错的,因为一个3D的用户界面将会自动的缩放其元素来适应屏幕,并且使用硬件材质过滤。其将会比C++标准的2D用 户界面更加快速和漂亮。

  130.   “So in one sentence: CEGUI renders a 2D gui using 3D methods and hardware so you don't have to.”——zeroskill
  131. 添加退出按钮
  132. 首先,我们需要为应用程序添加下面的头文件。本例中是“Push Button”

  133.    #include <CEGUI/elements/CEGUIPushButton.h>
  134. 我们要在场景底部添加退出按钮。

  135.    CEGUI::PushButton* quitButton = (CEGUI::PushButton*)CEGUI::WindowManager::getSingleton().createWindow
  136.                                              ("TaharezLook/Button", (CEGUI::utf8*)"Quit");
  137.    mEditorGuiSheet->addChildWindow(quitButton);
  138.    quitButton->setPosition(CEGUI::Point(0.35f, 0.45f));
  139.    quitButton->setSize(CEGUI::Size(0.3f, 0.1f));
  140.    quitButton->setText("Quit");
  141. 完成后执行程序,一个漂亮的按钮将会出现在屏幕中。但请注意,程序此时仍然不做任何事情,因为我们并没有为其添加响应时间。

  142. 如果你编译时遇到了setPosition()和setSize()的调用错误: 'CEGUI::Window::setPosition' : cannot convert parameter 1 from 'CEGUI::Vector2' to 'const CEGUI::UVector2 &'

  143. 将setPosition()和setSize()所在行分别用下面的代码替换:

  144. quitButton->setPosition(CEGUI::UVector2(cegui_reldim(0.35f), cegui_reldim( 0.45f)) );
  145. quitButton->setSize(CEGUI::UVector2(cegui_reldim(0.35f), cegui_reldim( 0.1f)) );
  146. 响应事件
  147. 将下面函数添加到TutorialApplication的public:中

  148.   void setupEventHandlers(void)
  149.   {
  150.      CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
  151.      wmgr.getWindow((CEGUI::utf8*)"Quit")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber
  152.                                        (&TutorialApplication::handleQuit, this));
  153.   }
  154.   bool handleQuit(const CEGUI::EventArgs& e)
  155.   {
  156.      static_cast<GuiFrameListener*>(mFrameListener)->requestShutdown();
  157.      return true;
  158.   }
  159. 重写 GuiFrameListener 类来响应键盘和鼠标输入

  160.   class GuiFrameListener : public ExampleFrameListener, public MouseMotionListener, public MouseListener
  161.   {
  162.   private:
  163.      CEGUI::Renderer* mGUIRenderer;
  164.      bool mShutdownRequested;
  165.   public:
  166.      // NB using buffered input
  167.      GuiFrameListener(RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer)
  168.          : ExampleFrameListener(win, cam, true, true),
  169.            mGUIRenderer(renderer),
  170.            mShutdownRequested(false)
  171.      {
  172.          mEventProcessor->addMouseMotionListener(this);
  173.          mEventProcessor->addMouseListener(this);
  174.          mEventProcessor->addKeyListener(this);
  175.      }
  176.      // Tell the frame listener to exit at the end of the next frame
  177.      void requestShutdown(void)
  178.      {
  179.          mShutdownRequested = true;
  180.      }
  181.      bool frameEnded(const FrameEvent& evt)
  182.      {
  183.          if (mShutdownRequested)
  184.              return false;
  185.          else
  186.              return ExampleFrameListener::frameEnded(evt);
  187.      }
  188.      void mouseMoved (MouseEvent *e)
  189.      {
  190.          CEGUI::System::getSingleton().injectMouseMove(
  191.                  e->getRelX() * mGUIRenderer->getWidth(),
  192.                  e->getRelY() * mGUIRenderer->getHeight());
  193.          e->consume();
  194.      }
  195.      void mouseDragged (MouseEvent *e)
  196.      {
  197.          mouseMoved(e);
  198.      }
  199.      void mousePressed (MouseEvent *e)
  200.      {
  201.          CEGUI::System::getSingleton().injectMouseButtonDown(
  202.                 convertOgreButtonToCegui(e->getButtonID()));
  203.          e->consume();
  204.      }
  205.      void mouseReleased (MouseEvent *e)
  206.      {
  207.          CEGUI::System::getSingleton().injectMouseButtonUp(
  208.            convertOgreButtonToCegui(e->getButtonID()));
  209.          e->consume();
  210.      }
  211.      void mouseClicked(MouseEvent* e) {}
  212.      void mouseEntered(MouseEvent* e) {}
  213.      void mouseExited(MouseEvent* e) {}
  214.      void keyPressed(KeyEvent* e)
  215.      {
  216.          if(e->getKey() == KC_ESCAPE)
  217.          {
  218.              mShutdownRequested = true;
  219.              e->consume();
  220.              return;
  221.          }
  222.          CEGUI::System::getSingleton().injectKeyDown(e->getKey());
  223.          CEGUI::System::getSingleton().injectChar(e->getKeyChar());
  224.          e->consume();
  225.      }
  226.      void keyReleased(KeyEvent* e)
  227.      {
  228.          CEGUI::System::getSingleton().injectKeyUp(e->getKey());
  229.          e->consume();
  230.      }
  231.      void keyClicked(KeyEvent* e)
  232.      {
  233.          // Do nothing
  234.          e->consume();
  235.      }
  236.   };
  237. Ogre 1.4.0

  238. 如果你使用的是Ogre 1.4.0 你将会要使用OIS。在Ogre3d中使用OIS的更多细节,请参阅使用OIS并且再看一看基础教程5:

  239.   class GuiFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
  240.   {
  241.   private:
  242.     CEGUI::Renderer* mGUIRenderer;
  243.     bool mShutdownRequested;
  244.   public:
  245.     // NB using buffered input
  246.     GuiFrameListener(RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer)
  247.         : ExampleFrameListener(win, cam, true, true),
  248.           mGUIRenderer(renderer),
  249.           mShutdownRequested(false)
  250.     {
  251.          mMouse->setEventCallback( this );
  252.          mKeyboard->setEventCallback( this );
  253.     }
  254.     // Tell the frame listener to exit at the end of the next frame
  255.     void requestShutdown(void)
  256.     {
  257.         mShutdownRequested = true;
  258.     }
  259.     bool frameEnded(const FrameEvent& evt)
  260.     {
  261.         if (mShutdownRequested)
  262.             return false;
  263.        else
  264.             return ExampleFrameListener::frameEnded(evt);
  265.     }
  266.      bool mouseMoved( const OIS::MouseEvent &e )
  267.     {
  268.         using namespace OIS;
  269.         CEGUI::System::getSingleton().injectMouseMove(e.state.X.rel,e.state.Y.rel);
  270.         return true;
  271.    }
  272.     bool mousePressed (const OIS::MouseEvent &e, OIS::MouseButtonID id)
  273.     {
  274.         CEGUI::System::getSingleton().injectMouseButtonDown(convertOgreButtonToCegui(id));
  275.         return true;
  276.     }
  277.   bool mouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id )
  278.     {
  279.         CEGUI::System::getSingleton().injectMouseButtonUp(convertOgreButtonToCegui(id));
  280.         return true;
  281.     }
  282.     bool keyPressed( const OIS::KeyEvent &e )
  283.     {
  284.         if(e.key == OIS::KC_ESCAPE)
  285.         {
  286.             mShutdownRequested = true;
  287.             return true;
  288.         }
  289.         CEGUI::System::getSingleton().injectKeyDown(e.key);
  290.         CEGUI::System::getSingleton().injectChar(e.text);
  291.         return true;
  292.     }
  293.     bool keyReleased( const OIS::KeyEvent &e )
  294.     {
  295.         CEGUI::System::getSingleton().injectKeyUp(e.key);
  296.         return true;
  297.     }
  298.   };

  299. 在include语句后GuiFrameListener声明前添加下面代码

  300.   CEGUI::MouseButton convertOgreButtonToCegui(int buttonID)
  301.   {
  302.      switch (buttonID)
  303.      {
  304.      case MouseEvent::BUTTON0_MASK:
  305.          return CEGUI::LeftButton;
  306.      case MouseEvent::BUTTON1_MASK:
  307.          return CEGUI::RightButton;
  308.      case MouseEvent::BUTTON2_MASK:
  309.          return CEGUI::MiddleButton;
  310.      case MouseEvent::BUTTON3_MASK:
  311.          return CEGUI::X1Button;
  312.      default:
  313.          return CEGUI::LeftButton;
  314.      }
  315.   }
  316. Ogre 1.4.0

  317.   CEGUI::MouseButton convertOgreButtonToCegui(int buttonID)
  318.   {
  319.       using namespace OIS;
  320.       switch (buttonID)
  321.       {
  322.       case OIS::MB_Left:
  323.           return CEGUI::LeftButton;
  324.       case OIS::MB_Right:
  325.           return CEGUI::RightButton;
  326.       case OIS::MB_Middle:
  327.           return CEGUI::MiddleButton;
  328.       default:
  329.           return CEGUI::LeftButton;
  330.       }
  331.   }
  332. 将下面语句添加到创建场景方法(createscene)的末尾。

  333.   setupEventHandlers();
  334. 现在你可以编译并执行程序了。实现效果是点击按钮后退出。

  335. 加载设置(Layout)
  336. CEGUI使用XML格式来加载图形用户界面样式设置。复制下面xml代码到记事本,并将其以“Tutoral Gui.xml”命名另存在“\media\gui”文件夹下。

  337.   <?xml version="1.0" ?>
  338.   <GUILayout>
  339.       <Window Type="DefaultWindow" Name="Tutorial Gui">
  340.           <Window Type="TaharezLook/Button" Name="Quit">
  341.                <Property Name="AbsoluteRect" Value="l:224.000000 t:216.000000 r:416.000000 b:264.000000" />
  342.                <Property Name="RelativeRect" Value="l:0.350000 t:0.450000 r:0.650000 b:0.550000" />
  343.                <Property Name="Text" Value="Quit" />
  344.                </Window>
  345.           </Window>
  346.   </GUILayout>
  347. Ogre 1.4.0

  348.   <?xml version="1.0" ?>
  349.   <GUILayout>
  350.       <Window Type="DefaultWindow" Name="Tutorial Gui">
  351.           <Window Type="TaharezLook/Button" Name="Quit">
  352.                <Property Name="UnifiedPosition" Value="{{0.35,0},{0.45,0}}" />
  353.                <Property Name="UnifiedSize" Value="{{0.3,0},{0.1,0}}" />
  354.                <Property Name="Text" Value="Quit" />
  355.                </Window>
  356.           </Window>
  357.   </GUILayout>
  358. 现在将程序中下列代码段注释掉

  359.   mEditorGuiSheet= CEGUI::WindowManager::getSingleton().createWindow((CEGUI::utf8*)"DefaultWindow", (CEGUI::utf8*)"Sheet");
  360.   mGUISystem->setGUISheet(mEditorGuiSheet);
  361.   CEGUI::PushButton* quitButton = (CEGUI::PushButton*)CEGUI::WindowManager::getSingleton().createWindow
  362.                                           ("TaharezLook/Button",  (CEGUI::utf8*)"Quit");
  363.   mEditorGuiSheet->addChildWindow(quitButton);
  364.   quitButton->setPosition(CEGUI::Point(0.35f, 0.45f));
  365.   quitButton->setSize(CEGUI::Size(0.3f, 0.1f));
  366.   quitButton->setText("Quit");
  367. 在同样位置添加下列代码

  368.   mEditorGuiSheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"Tutorial Gui.xml");
  369.   mGUISystem->setGUISheet(mEditorGuiSheet);
  370.   CEGUI::PushButton* quitButton=(CEGUI::PushButton*)CEGUI::
  371.           WindowManager::getSingleton().getWindow((CEGUI::utf8*)"Quit");
  372. 最后一行多余的,因为我们没有在之后使用指针,但是其说明了如何通过加载文件来进行访问。注意:我们在创建xml文件时要根据实际窗口进行设计。

  373. 完成后编译并执行,程序在外观上并没有变化。

  374. 尝试
  375. •视线相交和选取Ogre mesh—当鼠标没有从GUI元素上移过。 •定义一个在GUI根菜单上的鼠标点击动作。当你鼠标点击一个不在根窗口中的GUI元素时,它将会响应你的鼠 标点击。如果你的鼠标并没有从一个 GUI元素上滑过(也就是说当你的鼠标指针还在我们的3D场景中)则根窗口响应鼠标点击。 •将鼠标点击转换到世界坐 标系和视线相交((Camera::getCamera)到ViewportRay(mouseX, mouseY))

  376.   // Start a new ray query
  377.    Ogre::Ray cameraRay = root::getSingleton( ).
  378.      getCamera( )->getCameraToViewportRay( mouseX, mouseY );
  379.    Ogre::RaySceneQuery *raySceneQuery = root::getSingleton( ).
  380.      getSceneManager( )->createRayQuery( cameraRay );
  381.    raySceneQuery->execute( );
  382.    Ogre::RaySceneQueryResult result = raySceneQuery->getLastResults( );
  383.    Ogre::MovableObject *closestObject = NULL;
  384.    real closestDistance = LONG_MAX;
  385.    std::list< Ogre::RaySceneQueryResultEntry >::iterator rayIterator;
  386.    for ( rayIterator = result.begin( );
  387.    rayIterator != result.end( );
  388.    rayIterator++ ) {
  389.    if ( ( *rayIterator ).movable->getUserObject( ) != NULL )
  390.       {
  391.         if ( ( *rayIterator ).distance < closestDistance )
  392.            {
  393.               closestObject = ( *rayIterator ).movable;
  394.               closestDistance = ( *rayIterator ).distance;
  395.            }
  396.       }
  397.    }
  398.    // No object clicked
  399.    if ( closestObject == NULL ) {   
  400.      clickedObject = NULL;                                  ---- clickedObject is a class scoped variable
  401.    } else {
  402.      clickedObject = static_cast< object* >( closestObject->getUserObject( ) );
  403.    }
  404.    raySceneQuery->clearResults( );
  405.    root::getSingleton( ).getSceneManager( )->destroyQuery( raySceneQuery )
  406. 如何在两个GUI(用户界面)之间转换(使用透明度)
  407. 例如:如果你有一个登陆界面,在成功登陆后,进入了你的用户主界面。你将会想在这两个界面间切换。 •第一步,加载登陆用户界面。

  408.     //First loading with this
  409.    mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000,mSceneMgr);
  410.    mGUISystem = new CEGUI::System(mGUIRenderer);
  411.    CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);
  412.    CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"WindowsLook.scheme");
  413.    mGUISystem->setDefaultMouseCursor((CEGUI::utf8*)"WindowsLook", (CEGUI::utf8*)"MouseArrow");
  414.    CEGUI::Font *f = CEGUI::FontManager::getSingleton().createFont("Commonwealth-10.font");
  415.    mGUISystem->setDefaultFont(f);
  416.    //End "first loading with this"
  417.    //Load a XML file
  418.    mEditorGuiSheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"Presentation.xml");
  419.    mGUISystem->setGUISheet(mEditorGuiSheet);
  420.  •第二步,如果你想删除并重建一个GUI,你需要做到以下:

  421.    if(mEditorGuiSheet)
  422.       CEGUI::WindowManager::getSingleton().destroyWindow(mEditorGuiSheet);
  423.  •最后一步,加载其他的GUI

  424.    mEditorGuiSheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"Futura.xml");
  425.    mGUISystem->setGUISheet(mEditorGuiSheet);
  426.  重做第二步和最后一步来加载其他GUI。

  427. 结论
  428. 这个教程为你展示了在Ogre3D下使用CEGUI的一些基本方法,你可以感受下使用CUEGUI编程的乐趣:)
复制代码

[ 本帖最后由 obuil 于 2007-8-17 09:30 PM 编辑 ]

clq
2010-2-24 16:15:45 发表 编辑

我用的是 frameStarted 事件,没有 arg 参数,所以我的代码有点区别,如下


bool Input::frameStarted(const FrameEvent& evt)
{
    //return true;
    //clq del//mInputDevice->capture();
    //clq del//if(mInputDevice->isKeyDown(Ogre::KC_ESCAPE))
    //clq del//    return false;

    /*
    //鼠标左键 ok
    mMouse->capture();
    if (mMouse->getMouseState().buttonDown(OIS::MB_Left))
        return false;
    */

    /*
    //键盘 回车 ok
    mKeyboard->capture();
    if (mKeyboard->isKeyDown(OIS::KC_RETURN))
        return false;
    */

    mKeyboard->capture();
    if (mKeyboard->isKeyDown(OIS::KC_0))
    {
        mCamera_i1--;
        //
        mCamera->yaw(Degree(1));//沿 y 轴旋转
    }

    if (mKeyboard->isKeyDown(OIS::KC_1))
    {
        mCamera_i1--;
        //
        mCamera->pitch(Degree(1));//向上仰一个角度//z 轴的上仰角度
    }


    if (mKeyboard->isKeyDown(OIS::KC_2))
    {   
        mSceneMgr->setAmbientLight(ColourValue(0.1, 0.1, 0.1));//光线变暗
        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.1, 0.1));//光线变暗


    }

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


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

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

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

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

 

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

       std::cout << "x:" << camPos.x << " y:" << camPos.y << "aaa3\r\n";//clq add

/*
       //一旦我们创建了射线,我们就告诉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 );

            std::cout << "aaa\r\n";//clq add
        }
        std::cout << "aaa2\r\n";//clq add

        */

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

   //创建一个球体查询,这里的100是m_SphereNode挂着的那个球体的半径
   //SphereSceneQuery * pQuery=mSceneMgr->createSphereQuery(Sphere(m_SphereNode->getPosition(),100));
   SphereSceneQuery * pQuery=mSceneMgr->createSphereQuery(Sphere(camPos,10));

   //执行这个查询
   SceneQueryResult QResult=pQuery->execute();
   //遍历查询列表找出范围内的物体
   for (std::list<MovableObject*>::iterator iter = QResult.movables.begin(); iter != QResult.movables.end();++iter)
   {
        std::cout << "aaa22\r\n";//clq add

        //mCamera->setPosition( camPos.x, terrainHeight + 10.0f, camPos.z);


    MovableObject* pObject=static_cast<MovableObject*>(*iter);
    if(pObject)
    {
     if(pObject->getMovableType()=="Entity")
     {
      Entity* ent = static_cast<Entity*>(pObject);
      //这里简化了操作,由于只有一个"球体"和一个"正方体",
      //所以只判断了球体和正方体的相交

      //pObject->getWorldBoundingBox().getSize().z;

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

      std::cout <<pObject->getWorldBoundingBox().getSize().y << ent->getName() << "aaa22\r\n";//clq add

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

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

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

   //不在创建场景时初始化 CEGUI 会出错的
    //CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();//应该是鼠标位置
   CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();//应该是鼠标位置

   mMouse->capture();
   //mMouse->getMouseState().X;

   //Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width),mousePos.d_y/float(arg.state.height));
   Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(mMouse->getMouseState().width),mousePos.d_y/float(mMouse->getMouseState().height));

   mRaySceneQuery->setRay(mouseRay);

   RaySceneQueryResult &result = mRaySceneQuery->execute();
   RaySceneQueryResult::iterator itr = result.begin();
 
   //if (itr != result.end() && itr->worldFragment)
   if (itr != result.end())
   {
   //    mCurrentObject->setPosition(itr->worldFragment->singleIntersection);
       std::cout << "obj 与一个物体相交了!\r\n";//clq add
   }


    return true;
}

clq
2010-2-24 16:17:14 发表 编辑

mRaySceneQuery 的建立听说是很消耗资源的,不过我们是示例嘛,所以放在函数体里了,正式要放在构造函数中。

clq
2010-2-24 16:21:00 发表 编辑

错了,应该这个。来自 http://bbs.vrchina.net/viewthread.php?tid=3364

OGRE中级教程二 射线场景查询及基础鼠标用法[Intermediate_Tutorial_2]

  1. 介绍
  2. 本教程中,我们会初步创建一个基础场景编辑器。在过程之中,我们会涉及到:

  3. 如何使用RaySceneQueries阻止镜头穿透地面
  4. 如何使用MouseListener和MouseMotionListener接口
  5. 使用鼠标选取地面上的x和y坐标
  6. 你可以在这里找到完整代码。跟随着教程,你会慢慢地向你自己的工程项目中增加代码,并且随着编译看到结果。


  7. 前期准备
  8. 本教程假设你已经知道了如何创建Ogre工程,并且可以成功编译。假设你已经了解了基本的Ogre对象(场景节点,实体,等等)。你也 应该熟悉STL迭代器基本的使用方法,因为本教程会用到。(Ogre也大量用到STL,如果你还不熟悉STL,那么你需要花些时间学习一下。)


  9. 开始
  10. 首先,你需要为此演示程序创建一个新工程。在创建工程时,选空工程、自己的框架,以及初始化进度条和CEGUI支持,不选编译后拷贝。向工程中,增加一个名叫“MouseQuery.cpp”的文件,并向其中添加如下代码:

  11. #include <CEGUI/CEGUISystem.h>
  12. #include <CEGUI/CEGUISchemeManager.h>
  13. #include <OgreCEGUIRenderer.h>

  14. #include "ExampleApplication.h"

  15. class MouseQueryListener : public ExampleFrameListener, public OIS::MouseListener
  16. {
  17. public:

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

  22.         ~MouseQueryListener()
  23.         {
  24.         }

  25.         bool frameStarted(const FrameEvent &evt)
  26.         {
  27.                 return ExampleFrameListener::frameStarted(evt);
  28.         }

  29.         /* MouseListener callbacks. */
  30.         bool mouseMoved(const OIS::MouseEvent &arg)
  31.         {
  32.                 return true;
  33.         }

  34.         bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
  35.         {
  36.                 return true;
  37.         }

  38.         bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
  39.         {
  40.                 return true;
  41.         }


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

  50. class MouseQueryApplication : public ExampleApplication
  51. {
  52. protected:
  53.         CEGUI::OgreCEGUIRenderer *mGUIRenderer;
  54.         CEGUI::System *mGUISystem;         // cegui system
  55. public:
  56.         MouseQueryApplication()
  57.         {
  58.         }

  59.         ~MouseQueryApplication()
  60.         {
  61.         }
  62. protected:
  63.         void chooseSceneManager(void)
  64.         {
  65.                 // Use the terrain scene manager.
  66.                 mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);
  67.         }

  68.         void createScene(void)
  69.         {
  70.         }

  71.         void createFrameListener(void)
  72.         {
  73.                 mFrameListener = new MouseQueryListener(mWindow, mCamera, mSceneMgr, mGUIRenderer);
  74.                 mFrameListener->showDebugOverlay(true);
  75.                 mRoot->addFrameListener(mFrameListener);
  76.         }
  77. };


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

  81. INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
  82. #else
  83. int main(int argc, char **argv)
  84. #endif
  85. {
  86.         // Create application object
  87.         MouseQueryApplication app;

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

  98.         return 0;
  99. }

  100. 在继续下面教程以前,先确保上面代码可以正常编译。


  101. 创建场景
  102. 找到MouseQueryApplication::createScene方法。下面的代码应该都很熟悉了。如果你不知道其中某些是做什么用的,请在继续本教程前,参考Ogre API。向createScene中,增加如下代码:

  103.         // Set ambient light
  104.        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
  105.        mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

  106.        // World geometry
  107.        mSceneMgr->setWorldGeometry("terrain.cfg");

  108.        // Set camera look point
  109.        mCamera->setPosition(40, 100, 580);
  110.        mCamera->pitch(Degree(-30));
  111.        mCamera->yaw(Degree(-45));
  112. 既然我们建立了基本的世界空间,那么就要打开光标。打开光标,要使用CEGUI函数调用。不过在此之前,我们需要启用CEGUI。我们 首先创建一个OgreCEGUIRenderer,然后创建系统对象并将刚创建的Renderer传给它。创建CEGUI我们会专门留待后续教程介绍,现 在只要知道创建mGUIRenderer时必须以最后一个参数告诉CEGUI你要用那个场景管理器。

  113.        // CEGUI setup
  114.        mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr);
  115.        mGUISystem = new CEGUI::System(mGUIRenderer);
  116. 现在我们需要实际显示光标了。同样地,我不打算过多解释这些代码。我们会在后面的教程中详细介绍。(其实也没什么,就是设置了一下CEGUI的窗口和鼠标的样式。——Aaron注释)

  117.        // Mouse
  118.        CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");
  119.        CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");
  120. 如果你编译并运行这个程序,你会发现一个光标出现在屏幕中央,但它还动不了。


  121. 帧监听器介绍
  122. 这是程序要做的全部事情。FrameListener是代码中复杂的部分,所以我会花一些时间强调我们要完成的东西,以便在我们开始实现它之前,是你有一个大体的印象。

  123. 首先,我们想要将鼠标右键绑定到“鼠标观察”模式。不能使用鼠标四下看看是相当郁闷的,所以我们首先对程序增加鼠标控制(尽管只是在我们保持鼠标右键按下时)。
  124. 第二,我们想要让镜头不会穿过地表。这会使它更接近我们期望的样子。
  125. 第三,我们想要在地表上用鼠标左键点击一下,就在那里增加一个实体。
  126. 最后,我们想要能“拖拽”实体。即选中我们想要看到的实体,按住鼠标左键不放,将它移动到我们想要放置的地方。松开鼠标左键,就又会将它锁定在原地。
  127. 要做到这几点,我们要使用几个受保护的变量(这些已经加到类中了):

  128.     RaySceneQuery *mRaySceneQuery;     // The ray scene query pointer
  129.     bool mLMouseDown, mRMouseDown;     // True if the mouse buttons are down
  130.     int mCount;                        // The number of robots on the screen
  131.     SceneManager *mSceneMgr;           // A pointer to the scene manager
  132.     SceneNode *mCurrentObject;         // The newly created object
  133.     CEGUI::Renderer *mGUIRenderer;     // cegui renderer
  134. 变量mRaySceneQuery握有RaySceneQuery的一个拷贝,我们会它来寻找地面上的坐标。变量 mLMouseDown和mRMouseDon会追踪我们是否按下鼠标键(例如:如果按下鼠标左键,则mLMouseDown为true;否则,为 false)。mCount计数屏幕上有的实体数。mCurrentObject握有指向最近创建的场景节点的指针(我们将用这个“拖拽”实体)。最 后,mGUIRenderer握有指向CEGUI Renderer的指针,我们将用它更新CEGUI。

  135. 还要注意的是,有序多和鼠标监听器相关的函数。在本演示程序中,我们不会全部用到,但是它们必须全部在那儿,否则编译会报错说你没定义它们。


  136. 创建帧监听器
  137. 找到MouseQueryListener构造函数,增加如下初始化代码。注意,由于地形相当小,所以我们也要减少移动和旋转速度。

  138.         // Setup default variables
  139.         mCount = 0;
  140.         mCurrentObject = NULL;
  141.         mLMouseDown = false;
  142.         mRMouseDown = false;
  143.         mSceneMgr = sceneManager;

  144.         // Reduce move speed
  145.         mMoveSpeed = 50;
  146.         mRotateSpeed /= 500;
  147. 为了MouseQueryListener能收到鼠标事件,我们必须把它注册为一个鼠标监听器。如果对此不太熟悉,请参考基础教程5。

  148.         // Register this so that we get mouse events.
  149.         mMouse->setEventCallback(this);
  150. 最后,在构造函数中我们需要创建一个RaySceneQuery对象。用场景管理器的一个调用创建:

  151.         // Create RaySceneQuery
  152.         mRaySceneQuery = mSceneMgr->createRayQuery(Ray());
  153. 这是我们需要的全部构造函数了,但是如果我们创建一个RaySceneQuery,以后我们就必须销毁它。找到MouseQueryListener析构函数(~MouseQueryListener),增加如下代码:

  154.         // We created the query, and we are also responsible for deleting it.
  155.         mSceneMgr->destroyQuery(mRaySceneQuery);
  156. 在进入下一阶段前,请确保你的代码可以正常编译。

  157. 增加鼠标查看
  158. 我们要将鼠标查看模式绑定到鼠标右键上,需要:

  159. 当鼠标被移动时,更新CEGUI(以便光标也移动)
  160. 当鼠标右键被按下时,设置mRMouseButton为true
  161. 当鼠标右键被松开时,设置mRMouseButton为false
  162. 当鼠标被“拖拽”时,改变视图
  163. 当鼠标被“拖拽”时,隐藏鼠标光标
  164. 找到MouseQueryListener::mouseMoved方法。我们将要增加代码使每次鼠标移动时移动鼠标光标。向函数中增加代码:

  165.        // Update CEGUI with the mouse motion
  166.        CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
  167. 现在找到MouseQueryListener::mousePressed方法。这段代码当鼠标右键按下时,隐藏光标,并设置变量mRMouseDown为true。

  168.        // Left mouse button down
  169.        if (id == OIS::MB_Left)
  170.        {
  171.            mLMouseDown = true;
  172.        } // if

  173.        // Right mouse button down
  174.        else if (id == OIS::MB_Right)
  175.        {
  176.            CEGUI::MouseCursor::getSingleton().hide();
  177.            mRMouseDown = true;
  178.        } // else if
  179. 接下来,当鼠标右键抬起时,我们需要再次显示光标,并将mRMouseDown设置为false。找到mouseReleased函数,增加如下代码:

  180.        // Left mouse button up
  181.        if (id == OIS::MB_Left)
  182.        {
  183.            mLMouseDown = false;
  184.        } // if

  185.        // Right mouse button up
  186.        else if (id == OIS::MB_Right)
  187.        {
  188.            CEGUI::MouseCursor::getSingleton().show();
  189.            mRMouseDown = false;
  190.        } // else if
  191. 现在,我们有了全部准备好的代码,我们想要在按住鼠标右键移动鼠标时改变视图。我们要做的就是,读取他自上次调用方法后移动的距离。这 可以用与基础教程5中旋转摄像机镜头一样的方法实现。找到TutorialFrameListener::mouseMoved函数,就在返回状态前,增 加如下代码:

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

  196.        // If we are dragging the right mouse button.
  197.        else if (mRMouseDown)
  198.        {
  199.            mCamera->yaw(Degree(-arg.state.X.rel * mRotateSpeed));
  200.            mCamera->pitch(Degree(-arg.state.Y.rel * mRotateSpeed));
  201.        } // else if
  202. 现在如果你编译并运行这些代码,你将能够通过按住鼠标右键控制摄像机往哪里看。

  203. 地形碰撞检测
  204. 我们现在要实现它,以便当我们向着地面移动时,能够不穿过地面。因为BaseFrameListener已经处理了摄像机移动,所以我 们就不用碰那些代码了。替代地,在BaseFrameListener移动了摄像机后,我们要确保摄像机在地面以上10个单位处。如果它不在,我们要把它 移到那儿。请跟紧这段代码。我们将在本教程结束前使用RaySceneQuery做几件别的事,而且在这段结束后,我不会再做如此详细的介绍。

  205. 找到MouseQueryListener::frameStarted方法,移除该方法的全部代码。我们首先要做的事是调用ExampleFrameListener::frameStarted方法。如果它返回false,则我们也会返回false。

  206.         // Process the base frame listener code.  Since we are going to be
  207.         // manipulating the translate vector, we need this to happen first.
  208.         if (!ExampleFrameListener::frameStarted(evt))
  209.             return false;
  210. 我们在frameStarted函数的最开始处做这些,是因为ExampleFrameListener的frameStarted成 员函数移动摄像机,并且在此发生后我们需要在函数中安排我们的剩余行动。我们的目标及时找到摄像机的当前位置,并沿着它向地面发射一条射线。这被称为射线 场景查询,它会告诉我们我们下面的地面的高度。得到了摄像机的当前位置后,我们需要创建一条射线。这条射线有一个起点(射线开始的地方),和一个方向。在 本教程的情况下,我们的方向是Y轴负向,因为我们指定射线一直向下。一旦我们创建了射线,我们就告诉RaySceneQuery对象使用它。

  211.        // Setup the scene query
  212.        Vector3 camPos = mCamera->getPosition();
  213.        Ray cameraRay(Vector3(camPos.x, 5000.0f, camPos.z), Vector3::NEGATIVE_UNIT_Y);
  214.        mRaySceneQuery->setRay(cameraRay);
  215. 注意,我们已经使用了5000.0f高度代替了摄像机的实际位置。如果我们使用摄像机的Y坐标代替这个高度,如果摄像机在地面以下,我们会错过整个地面。现在我们需要执行查询,得到结果。查询结果是std::iterator类型的。

  216.         // Perform the scene query
  217.         RaySceneQueryResult &result = mRaySceneQuery->execute();
  218.         RaySceneQueryResult::iterator itr = result.begin();
  219. 在本教程中的这个地形条件下,查询结果基本上是一个worldFragment的列表和一个可移动物体(稍后的教程会介绍到)的列表。 如果你对STL迭代器不太熟悉,只要知道调用begin方法获得迭代器的第一个元素。如果result.begin() == result.end(),那么无返回结果。在下一个演示程序里,我们将处理SceneQuery的多个返回值。目前,我们只要挥挥手,在其间移动。下面 的这行代码保证了至少返回一个查询结果(itr != result.end()),那个结果是地面(itr->worldFragment)。

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

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

  228.         return true;
  229. 最后,我们返回true,继续渲染。此时,你应该编译测试你的程序了。

  230. 地形选择
  231. 在这部分中,每次点击鼠标左键,我们将向屏幕上创建和添加对象。每次你点击、按住鼠标左键,就会创建一个对象并跟随你的光标。你可以移 动对象,直到你松开鼠标左键,同时对象也锁定在那一点上。要做到这些,我们需要改变mousePressed函数。在 MouseQueryLlistener::mousePressed函数中,找到如下代码。我们将要在这个if语句中增加一些代码。

  232.        // Left mouse button down
  233.        if (id == OIS::MB_Left)
  234.        {
  235.            mLMouseDown = true;
  236.        } // if
  237. 第一段代码看起来会很熟悉。我们会创建一条射线以供mRaySceneQuery对象使用,设置射线。Ogre给我们提供了 Camera::getCameraToViewpointRay;一个将屏幕上的点击(X和Y坐标)转换成一条可供RaySceneQuery对象使用 的射线的好用函数。

  238.            // Left mouse button down
  239.            if (id == OIS::MB_Left)
  240.            {
  241.                // Setup the ray scene query, use CEGUI's mouse position
  242.                CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
  243.                Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width), mousePos.d_y/float(arg.state.height));
  244.                mRaySceneQuery->setRay(mouseRay);
  245. 接下来,我们将执行查询,并确保它返回一个结果。

  246.                // Execute query
  247.                RaySceneQueryResult &result = mRaySceneQuery->execute();
  248.                RaySceneQueryResult::iterator itr = result.begin( );

  249.                // Get results, create a node/entity on the position
  250.                if (itr != result.end() && itr->worldFragment)
  251.                {
  252. 既然我们有了worldFragment(也就是点击的位置),我们就要创建对象并把它放到位。我们的第一个难题是,Ogre中每个实 体和场景节点都需要一个唯一的名字。要完成这一点,我们要给每个实体命名为“Robot1”,“Robot2”,“Robot3”……同样将每个场景节点 命名为“Robot1Node”,“Robot2Node”,“Robot3Node”……等等。首先,我们创建名字(更多关于sprintf的信息,请 参考C语言)。

  253.                char name[16];
  254.                sprintf( name, "Robot%d", mCount++ );
  255. 接下来,我们创建实体和场景节点。注意,我们使用 itr->worldFragment->singleIntersection作为我们的机器人的默认位置。由于地形太小所以我们也把他缩 小为原来的十分之一。注意我们要将这个新建的对象赋值给成员变量mCurrentObject。我们将在下一段要用到它。

  256.                    Entity *ent = mSceneMgr->createEntity(name, "robot.mesh");
  257.                    mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(String(name) + "Node", itr->worldFragment->singleIntersection);
  258.                    mCurrentObject->attachObject(ent);
  259.                    mCurrentObject->setScale(0.1f, 0.1f, 0.1f);
  260.                } // if

  261.                mLMouseDown = true;
  262.            } // if
  263. 现在编译运行程序。你可以在场景中点击地形上任意地点放置机器人。我们几乎完全控制了我们的程序,但是在结束前,我们需要实现对象拖拽。我们要在这个if语句段中添加代码:

  264.        // If we are dragging the left mouse button.
  265.         if (mLMouseDown)
  266.         {
  267.         } // if
  268. 接下来的代码段现在应该是不言而喻的。我们创建了一条基于鼠标当前位置的射线,然后我们执行了射线场景查询且将对象移动到新位置。注意 我们不必检查mCurrentObject看看它是不是有效的,因为如果mCurrentObject未被mousePressed设置,那么 mLMouseDown不会是true。

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

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

  276.            if (itr != result.end() && itr->worldFragment)
  277.                mCurrentObject->setPosition(itr->worldFragment->singleIntersection);
  278.        } // if
  279. 编译运行程序。现在全部都完成了。点击几次后,你得到的结果应该看起来如下图所示。 [image:Intermediate_Tutorial_2.jpg]

  280. 进阶练习
  281. 简单练习
  282. 要阻止摄像机镜头看穿地形,我们选择地形上10个单位。这一选择是任意的。我们可以改进这一数值使之更接近地表而不穿过吗?如果可以,设定此变量为静态类成员并给它赋值。
  283. 有时我们确实想要穿越地形,特别是在场景编辑器中。创建一个标记控制碰撞检测开关,并绑定到一个键盘上的按键上。确保碰撞检测被关闭时,你不会在frameStarted中进行SceneQuery场景查询。
  284. 中级练习
  285. 当前我们每帧都要做场景查询,无论摄像机是否实际移动过。修补这个问题,如果摄像机移动了,只做一次场景查询。(提示:找到ExampleFrameListener中的移动向量,调用函数后,测试它是否为Vector3::ZERO。)
  286. 高级练习
  287. 注意到,每次我们执行一个场景查询调用时,有许多代码副本。将所有场景查询相关功能打包到一个受保护的函数中。确保处理地形一点不交叉的情况。
  288. 进阶练习
  289. 在这个教程中,我们使用了RaySceneQueries来放置地形上的对象。我们也许可以用它做些别的事情。拿来中级教程1的代码,完成困难问题1和专家问题1。然后将那个代码融合到这个代码中,使机器人行走在地面上,而不是虚空中。
  290. 增加代码,使每次你点击场景中的一点时,机器人移动到那个位置。
复制代码

[ 本帖最后由 obuil 于 2007-8-17 09:40 PM 编辑 ]


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


所在合集/目录



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


附件:



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

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