Ogre学习记录(二)-RaySceneQuery

来源:互联网 发布:瑞安司法淘宝网拍卖 编辑:程序博客网 时间:2024/04/27 23:01

这篇主要记录关于Ogre Wiki上的IntermediateTutorial教程2,3的一些改动:

        这两篇的内容涉及到了 TerrainSystem和CEGUI,而由于Ogre和CEGUI的快速更新,这两篇教程已经不能在Ogre1.8和cegui0.8正确的运行了,但是这两个教程在开始的时候也都声明了1.8的变化,但是没有给出实现的代码,自己在实现这两个例子的时候也花费了不少时间,现在直接把最新的实现记录一下,仅供参考。

先贴出来:

Tutorial2:

Changes needed for Ogre 1.8 and above:- The Terrain Scene Manager is obsolete and has been replaced by the terrain component system. Please go through Basic Tutorial 3 to learn how to use it. - For now, use the terrain generated in Basic Tutorial 3 for this tutorial. There will be glitches (such as the camera being able to go through parts of the terrain if you follow this tutorial's code). - This tutorial will ask you to use RaySceneQueries to find objects of type worldFragment. General-purpose queries are no longer used for this. Instead, call the rayIntersects() function in your TerrainGroup object. It takes an Ogre::Ray as an argument (and a distance limit as an optional second argument, of type Real) and returns an Ogre::TerrainGroup::RayResult object (ex. Ogre::TerrainGroup::RayResult result = terrain_group->rayIntersects(mouse_ray)). RayResult has three public members: bool hit, true if the given ray hit the TerrainGroup object, Ogre::Vector3 position, the x, y and z coordinates of the point of intersection and Ogre::Terrain terrain, the terrain object that was hit.

Tutorial3:

Changes needed for Ogre 1.8 and above:- Don't test for worldFragments when you are checking if we clicked on the terrain or a movable. Instead: - Make your ray query as normal - Add a boolean that tracks if we found a movable object or not (set it to true by default). Place the code that adds an entity to the scene inside an if statement that checks if this bool is true - In the for loop where you iterate through your query results, in the if statement, check if iter is a movable, its name is not the name of your camera entity and its name is not "" - If true, set the current entity to the entity you found as normal, but set the boolean to false (so it doesn't run the code that adds a new entity to the scene), then break

        这两篇教程主要是告诉大家如何利用Ogre的Ray和RaySceneQuery来实现场景内的一些基本的碰撞检测.

        Ray可以理解为从场景中一个点来发射一条射线,然后RaySceneQuery可以返回场景中所有与这条射线有交点的物体,及其交点的具体坐标,Ray的source可以是很多东西,可以是你的Camera,可以是你的Mouse,可以是你的Scene中的任何一个Entity等等;返回的交点坐标集被存储在一个RaySceneQueryResult的vector中,供用户使用。


        例如教程2就利用Camera发出的Ray来检测Camera与Terrain之间的距离,防止我们的Camera发生穿越地形的现象,然后利用mouse发出的Ray在mouse与Terrain的交点处创建物体,这就是告诉大家如何实现星际和War3中在指定的位置建建筑的方法。

        然后教程3告诉大家如何利用RaySceneQueryResult来检测movable object,也就是在地形之上的东西,这里用到了RaySceneQuery的排序功能,将交点按照远近距离排序,这就可以实现用鼠标获取地形上的某个entity,例如你要选择某一个英雄,或者你要攻击某一个敌人,都需要用鼠标点击这个你要操作的对象,最后教程3介绍了RaySceneQuery的一些Mask,设置这些Mask可以用来忽略一些特殊的交点:例如,你希望让你的Ray忽略与地面的交点,只关注movable object,例如你想忽略粒子系统和一些GUI的框框等等。

        Ogre1.8下由于引入了TerrainSceneManager,之前用于建立地形的代码(如下)已经不能使用了。

 // World geometrymSceneMgr->setWorldGeometry("terrain.cfg");
        用于建立地形的新玩意儿叫做TerrainManager,具体的用法在BasicTuroial3中有很详细的代码,我这里不全列出来,只是将其中关于地形的方面列出来(所有代码都是基于wiki的BaseApplication框架的)

        首先,类的声明中你需要使用到的类成员变量与成员方法。

/*added for Terrain System*/Ogre::TerrainGlobalOptions* mTerrainGlobals;Ogre::TerrainGroup* mTerrainGroup;bool mTerrainsImported;void defineTerrain(long x, long y);void initBlendMaps(Ogre::Terrain* terrain);void configureTerrainDefaults(Ogre::Light* light);
        类的实现中具体创建的方法:

void IntermediateTutorial3::createScene(void){<span style="white-space:pre"></span>/*其它你需要建立的东西*//*开始建立地形*/Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(Ogre::TFO_ANISOTROPIC);Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(7);Ogre::Vector3 lightdir(0.55, -0.3, 0.75);lightdir.normalise();Ogre::Light* light = mSceneMgr->createLight("tstLight");light->setType(Ogre::Light::LT_DIRECTIONAL);light->setDirection(lightdir);light->setDiffuseColour(Ogre::ColourValue::White);light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mSceneMgr, Ogre::Terrain::ALIGN_X_Z, 513, 12000.0f);mTerrainGroup->setFilenameConvention(Ogre::String("IntermediateTutorail"), Ogre::String("dat"));mTerrainGroup->setOrigin(Ogre::Vector3::ZERO);configureTerrainDefaults(light);for (long x = 0; x <= 0; ++x)for (long y = 0; y <= 0; ++y)defineTerrain(x, y);mTerrainGroup->loadAllTerrains(true);if(mTerrainsImported){Ogre::TerrainGroup::TerrainIterator ti = mTerrainGroup->getTerrainIterator();while(ti.hasMoreElements()){Ogre::Terrain* t = ti.getNext()->instance;initBlendMaps(t);}}mTerrainGroup->freeTemporaryResources();/*地形建立完毕*/<span style="white-space:pre"></span>/*其它你需要建立的东西*/}

configureTerrainDefaults:

void IntermediateTutorial3::configureTerrainDefaults(Ogre::Light* light){mTerrainGlobals->setMaxPixelError(8);mTerrainGlobals->setCompositeMapDistance(3000);mTerrainGlobals->setLightMapDirection(light->getDerivedDirection());mTerrainGlobals->setCompositeMapAmbient(mSceneMgr->getAmbientLight());mTerrainGlobals->setCompositeMapDiffuse(light->getDiffuseColour());Ogre::Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();defaultimp.terrainSize = 513;defaultimp.worldSize = 12000.0f;defaultimp.inputScale = 600; // due terrain.png is 8 bppdefaultimp.minBatchSize = 33;defaultimp.maxBatchSize = 65;defaultimp.layerList.resize(3);defaultimp.layerList[0].worldSize = 100;defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_diffusespecular.dds");defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_normalheight.dds");defaultimp.layerList[1].worldSize = 30;defaultimp.layerList[1].textureNames.push_back("grass_green-01_diffusespecular.dds");defaultimp.layerList[1].textureNames.push_back("grass_green-01_normalheight.dds");defaultimp.layerList[2].worldSize = 200;defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_diffusespecular.dds");defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_normalheight.dds");}

defineTerrain:

void IntermediateTutorial3::defineTerrain(long x, long y){Ogre::String filename = mTerrainGroup->generateFilename(x, y);if (Ogre::ResourceGroupManager::getSingleton().resourceExists(mTerrainGroup->getResourceGroup(), filename)){mTerrainGroup->defineTerrain(x, y);}else{Ogre::Image img;getTerrainImage( x%2 != 0, y%2 != 0, img);mTerrainGroup->defineTerrain(x, y, &img);mTerrainsImported = true;}}
void getTerrainImage(bool flipX, bool flipY, Ogre::Image& img){    img.load("terrain.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);    if (flipX)        img.flipAroundY();    if (flipY)        img.flipAroundX();}
initBlendMaps:

void IntermediateTutorial3::initBlendMaps(Ogre::Terrain* terrain){Ogre::TerrainLayerBlendMap* blendMap0 = terrain->getLayerBlendMap(1);Ogre::TerrainLayerBlendMap* blendMap1 = terrain->getLayerBlendMap(2);Ogre::Real minHeight0 = 70;Ogre::Real fadeDist0 = 40;Ogre::Real minHeight1 = 70;Ogre::Real fadeDist1 = 15;float* pBlend0 = blendMap0->getBlendPointer();float* pBlend1 = blendMap1->getBlendPointer();for (Ogre::uint16 y = 0; y < terrain->getLayerBlendMapSize(); ++y){for (Ogre::uint16 x = 0; x < terrain->getLayerBlendMapSize(); ++x){Ogre::Real tx, ty;blendMap0->convertImageToTerrainSpace(x, y, &tx, &ty);Ogre::Real height = terrain->getHeightAtTerrainPosition(tx, ty);Ogre::Real val = (height - minHeight0) / fadeDist0;val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);*pBlend0++ = val;val = (height - minHeight1) / fadeDist1;val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);*pBlend1++ = val;}}blendMap0->dirty();blendMap1->dirty();blendMap0->update();blendMap1->update();}
        是不是感觉很繁杂。。。。我也这么觉得,突然觉得1.7的一行代码搞定真是太简单的,但是据说TerrainManager可以大大增加Ogre的地形处理的能力,具体的我也还没有深入了解,但是搞清楚上面例子你也可以学到很多东西,理解地形建立过程中到底做了那些事情,可以有哪些全局的配置,比如地形是由heighMap和贴图组成的,比如你可以将img文件生成的地图信息保存起来,下次进入时就可以直接读取而不需要再从img重建地形了。

        CEGUI的部分我就不详细说了,BasicTutorial7有详细的应用,下面要开始介绍这两个例子在Ogre1.8中TerrainManager下RaySceneQuery的一些变化:

  • RaySceneQuery不能再用于检测与Terrain之间的交点。
  • Ogre::Ray与Terrain的交点直接由TerrianGroup::RayResult来管理。
  • RaySceneQuery仍可以用于检测Movable Object。
Ogre1.8中求Ray与Terrain交点的方法:
CEGUI::Vector2f  mousePos = CEGUI::System::getSingleton().getDefaultGUIContext().getMouseCursor().getPosition();Ogre::Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width), mousePos.d_y/float(arg.state.height));mRaySceneQuery->setRay(mouseRay);Ogre::TerrainGroup::RayResult result = mTerrainGroup->rayIntersects(mouseRay);// Get results, create a node/entity on the positionif (result.hit)/*如果有交点,result.hit为true,并且result.position中含有交点具体坐标,根据此坐标创建一个robot*/{Ogre::Entity *ent;char name[16];sprintf( name, "Robot%d", mCount++ );ent = mSceneMgr->createEntity(name, "robot.mesh");<span style="white-space:pre"></span>mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(std::string(name)+ "Node", result.position);mCurrentObject->attachObject(ent);mCurrentObject->setScale(1.0f, 1.0f, 1.0f);} // if
Ogre1.8中求与movable object交点的方法,不变,但是由于与Terrain交点的方法改变,因此教程3中的mousePressed代码应该更改如下:
bool IntermediateTutorial3::mousePressed(const OIS::MouseEvent& arg, OIS::MouseButtonID id){//show that the current object has been deselected by removing the bounding box visualif(mCurrentObject){mCurrentObject->showBoundingBox(false);}if(id == OIS::MB_Left){// Setup the ray scene query, use CEGUI's mouse positionCEGUI::Vector2f  mousePos = CEGUI::System::getSingleton().getDefaultGUIContext().getMouseCursor().getPosition();Ogre::Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width), mousePos.d_y/float(arg.state.height));mRaySceneQuery->setRay(mouseRay);mRaySceneQuery->setSortByDistance(true);Ogre::RaySceneQueryResult &queryResult = mRaySceneQuery->execute();Ogre::RaySceneQueryResult::iterator itr = queryResult.begin();bSelectedMovable = false;for (; itr != queryResult.end(); itr++){if(itr->movable && itr->movable->getName() != ""&& itr->movable->getName() != mCamera->getName()){bSelectedMovable = true;std::cout << "find movable object!" <<   itr->movable->getName() << std::endl;mCurrentObject = itr->movable->getParentSceneNode();break;}}if(bSelectedMovable == false){Ogre::TerrainGroup::RayResult result = mTerrainGroup->rayIntersects(mouseRay);// Get results, create a node/entity on the positionif (result.hit){Ogre::Entity *ent;char name[16];if (bRobotMode){sprintf( name, "Robot%d", mCount++ );ent = mSceneMgr->createEntity(name, "robot.mesh");}else{sprintf(name, "Ninja%d", mCount++);ent = mSceneMgr->createEntity(name, "ninja.mesh");} // elsemCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(std::string(name)+ "Node", result.position);mCurrentObject->attachObject(ent);mCurrentObject->setScale(1.0f, 1.0f, 1.0f);} // if}mLMouseDown = true;}else if(id == OIS::MB_Right){CEGUI::System::getSingleton().getDefaultGUIContext().getMouseCursor().hide();mRMouseDown = true;}//now we show the bounding box so the user can see that this object is selectedif(mCurrentObject){        mCurrentObject->showBoundingBox(true);}return true;}
        提供一个bSelectedMovable变量来表示鼠标是否与某个已经存在的对象有交点,如果是,获取此对象,如果不是,再测试鼠标与地形的交点,并在交点处创建一个robot或者ninja。
        以下是测试结果:




0 0
原创粉丝点击