OGRE中级教程二 RaySceneQueries and Basic Mouse Usage

英语水平有限,欢迎大家批评指正微笑

本文并没有将原文全部翻译,只是将其中的一些知识点翻译总结了一下,想要查看详细讲解的话,可以到原文处看一下,附上英文原文地址:http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Intermediate+Tutorial+2&structure=Tutorials

Setting up the Scene

   添加如下代码到ITutorial02::createScene函数中:

   // Set ambient light

        mSceneMgr->setAmbientLight(Ogre::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(Ogre::Degree(-30));

        mCamera->yaw(Ogre::Degree(-45));

   现在我们已经建立起了一个基本的世界几何体(world geometry),还需要显示鼠标光标,为此我们调用CEGUI函数来实现。在此之前我们要用bootstrapSystem()函数启动CEGUI,注意它还让CEGUI使用了OGRE的资源管理系统:

   // CEGUI setup

        mGUIRenderer = &CEGUI::OgreRenderer::bootstrapSystem();

   现在显示鼠标光标:

   // Mouse                        CEGUI::SchemeManager::getSingleton().create((CEGUI::utf8*)"TaharezLook.scheme");

CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");

编译运行程序,你将在屏幕中间看到一个光标,但它不会移动。你还需要告诉OGRE的资源管理器CEGUI的资源在哪里,如果你得到一个运行时异常,那么试一下把下面添加到resources.cfg中:

[CEGUI]

FileSystem=/usr/share/CEGUI/schemes

FileSystem=/usr/share/CEGUI/fonts

FileSystem=/usr/share/CEGUI/imagesets

FileSystem=/usr/share/CEGUI/layouts

FileSystem=/usr/share/CEGUI/looknfeel

FileSystem=/usr/share/CEGUI/lua_scripts

FileSystem=/usr/share/CEGUI/schemes

FileSystem=/usr/share/CEGUI/xml_schemas

Introducing the FrameListener

   FrameListener是比较复杂的一段代码,下面我们概括一下我们要做什么,好让你有一个概念:

第一,我们要给鼠标右键绑定一个"鼠标查看"模式。如果不能使用鼠标来四处查看会很难受,所以我们的第一件事就是添加鼠标控制(只有当我们按下鼠标右键时)。

第二,让camera不能穿过地面。

第三,当我们点击鼠标左键时可以再地面上的任何位置添加实体。

最后,我们想要可以拖拽实体。按下鼠标左键不放,然后移动选择的实体到我们想要放置他的位置。松开鼠标,就会把实体放置该处。

为此我们要用几个protected变量:

Ogre::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

     Ogre::SceneNode *mCurrentObject;         // The newly created object

     CEGUI::Renderer *mGUIRenderer;     // cegui renderer

     float mRotateSpeed;

mRaySceneQuery变量保存了我们要用来寻找地面上的坐标的RaySceneQuery的复制。mLMouseDownmRMouseDown变量用来判断鼠标是否被按下。mCount用来计数我们在屏幕上创建的实体的数量。mCurrentObject保存一个指向我们最近创建的场景节点的指针(我们要用他来拖拽实体)。mGUIRenderer保存一个指向用来更新CEGUICEGUI渲染器的指针。

Setting up the FrameListener

  添加如下代码到createFrameListener方法的BaseApplication::createFrameListener()之后:

// Setup default variables

         mCount = 0;

         mCurrentObject = NULL;

         mLMouseDown = false;

         mRMouseDown = false;

         // Reduce rotate speed

         mRotateSpeed =.1;

最后,我们需要创建RaySceneQuery对象:

// Create RaySceneQuery

         mRaySceneQuery = mSceneMgr->createRayQuery(Ogre::Ray());

这就是我们的createFrameListener所需要的,但如果我们创建一个RaySceneQuery,我们之后必须销毁它。添加如下代码到ITutorial02的析构函数:

// We created the query, and we are also responsible for deleting it.

         mSceneMgr->destroyQuery(mRaySceneQuery);

Adding Mouse Look

我们要把鼠标查看模式绑定到鼠标右键上,为此我们要:

1.当鼠标移动时更新CEGUI

2.当鼠标被按下时设mRMouseDowntrue

3.当鼠标白松开时设mRMouseDown false

4.当鼠标拖拽时改变view(视野)。

5.当鼠标拖拽时隐藏鼠标光标。

   添加如下代码到ITutorial02::mousePressed

   // Left mouse button down

        if (id == OIS::MB_Left)

        {

           mLMouseDown = true;

        } // if

        // Right mouse button down

        else if (id == OIS::MB_Right)

        {

            CEGUI::MouseCursor::getSingleton().hide();

            mRMouseDown = true;

        } // else if

   当鼠标右键被按下时光标隐藏,并设mRMouseDowntrue。当鼠标右键松开时光标可见,并设mRMouseDownfalse。添加如下代码到mouseReleased函数:

   // Left mouse button up

        if (id == OIS::MB_Left)

        {

            mLMouseDown = false;

        } // if

        // Right mouse button up

        else if (id == OIS::MB_Right)

        {

            CEGUI::MouseCursor::getSingleton().show();

            mRMouseDown = false;

        } // else if

   现在我们已经写好了所有的基础代码,我们想要当鼠标被按下右键不放而进行移动时改变view(视野)。我们要做的就是读取自上一次该方法被调用开始到现在,他被移动的距离。添加如下代码到ITutorial::mouseMoved函数中:

   // If we are dragging the left mouse button.

        if (mLMouseDown)

        {

        } // if

        // If we are dragging the right mouse button.

        else if (mRMouseDown)

        {

            mCamera->yaw(Ogre::Degree(-arg.state.X.rel * mRotateSpeed));

            mCamera->pitch(Ogre::Degree(-arg.state.Y.rel * mRotateSpeed));

        } // else if

Terrain Collision Detection

   现在我们要使对象不能穿过地面,BaseApplication::createFrameListener函数已经处理了移动摄像机的操作,我们不动这些代码。当BaseApplication::createFrameListener函数移动摄像机后,我们要确保摄像机在地面之上10个单位以上。如果不是,我们就把它移到地面之上10个单位以上的位置。当本教程结束时,我们会用RaySceneQuery来做几件事情。

   删除ITutorial02::frameRenderingQueued函数中的所有代码,添加如下代码:

   // Process the base frame listener code.  Since we are going to be

         // manipulating the translate vector, we need this to happen first.

         if (!BaseApplication::frameRenderingQueued(evt))

             return false;

   我们把这些代码添加到最上面,因为BaseApplication::frameRenderingQueued函数处理了TrayManager窗口的更新,且这之后我们在该函数中需要执行剩余的动作。我们的目的是找到摄像机当前的位置,并发射一个垂直向下穿过地面的射线(Ray)。这叫做射线场景查询(RaySceneQuery),它可以告诉我们距离地面的高度。找到摄像机当前的位置后,我们需要创建一个射线(Ray)。射线获取原点和方向,本例中方向为NEGATIVE_UNIT_Y,因为我们要射线垂直向下穿过地面。创建射线后,我们要告诉RaySceneQuery对象来使用:

   // Setup the scene query

        Ogre::Vector3 camPos = mCamera->getPosition();

        Ogre::Ray cameraRay(Ogre::Vector3(camPos.x, 5000.0f, camPos.z),           Ogre::Vector3::NEGATIVE_UNIT_Y);

        mRaySceneQuery->setRay(cameraRay);

   注意我们使用的射线高度为5000.0f,而不是摄像机的实际位置。如果我们使用摄像机的Y轴位置而不是这个高度,当摄像机是在地面下面时,我们可能会根本就找不到(miss entirely)地面。现在我们要执行查询并获取结果,结果以std::iterator的形式获得。

   // Perform the scene query

         Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute();

         Ogre::RaySceneQueryResult::iterator itr = result.begin();

   查询结果基本上就是一个worldFragments(本例中就是地面Terrain)链表和一个movables链表。如果你对STL迭代器不是很熟悉,那么你只需要知道调用begin方法来获取迭代器的第一个元素。如果result.begin() == result.end(),那么就没有结果返回。下面的代码确保查询至少会返回一个结果(itr != result.end()),并且结果是地面(itr->worldFragment)。

   // Get the results, set the camera height

         if (itr != result.end() && itr->worldFragment)

         {

   worldFragments结构体包含着射线碰到地面的位置,这个位置保持在Vector3变量singleIntersection中。我们要通过把这个向量的Y值赋给一个本地向量来获取地面的高度。当我们得到了高度值,我们还要看一下摄像机是否在该高度之下,如果是我们要把摄像机移到这个高度。注意我们实际要把摄像机移动到地面之上10个单位,这确保当我们离地面太近时不至于让我们可以穿透地面。

   Ogre::Real terrainHeight = itr->worldFragment->singleIntersection.y;

             if ((terrainHeight + 10.0f) > camPos.y)

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

         }

         return true;

   最后返回true来继续渲染。

Terrain Selection

  这部分中,每次你点击鼠标左键,我们都会在屏幕上创建并添加一个对象。每次你点击并按住鼠标左键,一个对象被创建并"held" on your cursor。在你松开按键前,你都可以移动该对象。为此我们需要修改mousePressed函数,当你点击鼠标左键时做一些不同的事情。找到ITutorial02::mousePressed,我们要在这个if语句中添加代码。

   // Left mouse button down

        if (id == OIS::MB_Left)

        {

            mLMouseDown = true;

        } // if

   第一段代码看起来很熟悉,我们将创建一条射线(Ray)来让mRaySceneQuery 对象使用,并设置改射线。OGRE为我们提供了一个可以将屏幕上的点击变换为一条射线的Camera::getCameraToViewportRay函数,该射线可以让mRaySceneQuery对象来使用它。

   // Left mouse button down

            if (id == OIS::MB_Left)

            {

                // Setup the ray scene query, use CEGUI's mouse position

                CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();

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

                mRaySceneQuery->setRay(mouseRay);

   下面我们要执行查询并确保它返回一个结果:

   // Execute query

                Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute();

                Ogre::RaySceneQueryResult::iterator itr = result.begin( );

                // Get results, create a node/entity on the position

                if (itr != result.end() && itr->worldFragment)

                {

   现在我们有了worldFragment,我们要创建对象并把他放到该位置。我们的第一个难题是OGRE中的每个实体和场景节点都需要一个唯一的名字。为此我们要给每个实体、场景节点命名"Robot1", "Robot2", "Robot3""Robot1Node", "Robot2Node","Robot3Node", 首先我们创建名字:

      char name[16];

                sprintf( name, "Robot%d", mCount++ );

   然后我们创建实体和场景节点。注意我们使用itr->worldFragment->singleIntersection 作为机器人的默认位置。我们还把他缩小到原来的1/10大小,因为地面很小。注意我们要把新创建的对象赋给成员变量mCurrentObject。我们在下一部分会用到。

   Ogre::Entity *ent = mSceneMgr->createEntity(name, "robot.mesh");

                    mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(std::string(name) + "Node", itr->worldFragment->singleIntersection);

                    mCurrentObject->attachObject(ent);

                    mCurrentObject->setScale(0.1f, 0.1f, 0.1f);

                } // if

                mLMouseDown = true;

            } // if

   现在编译运行例子。你可以通过点击地面任意位置在屏幕上摆放机器人。我们基本上一届完成了程序,但我们还要完成对象拖拽的任务。添加代码到如下if语句中:

   // If we are dragging the left mouse button.

     if (mLMouseDown)

     {

     } // if

   根据鼠标当前位置创建一条射线,然后执行一个射线场景查询(RaySceneQuery)并把对象移动到新的位置。注意我们不必检查mCurrentObject是否有效,因为如果mCurrentObject没有被mousePressed设置过mLMouseDown就不会为true

   if (mLMouseDown)

        {

            CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();

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

            mRaySceneQuery->setRay(mouseRay);

            Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute();

            Ogre::RaySceneQueryResult::iterator itr = result.begin();

            if (itr != result.end() && itr->worldFragment)

                mCurrentObject->setPosition(itr->worldFragment->singleIntersection);

        } // if

编译运行你的程序,我们已经完成了!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值