ogre1.8自己写的建立地形源码(注释,可直接使用)

来源:互联网 发布:python 定义字典 编辑:程序博客网 时间:2024/05/18 07:18

为了便于直接使用,所有代码都放在一个cpp文件中了,如下:

/*这个cpp文件包括监听器类,Application类和main函数*/#define TERRAIN_PAGE_MIN_X 0#define TERRAIN_PAGE_MIN_Y 0#define TERRAIN_PAGE_MAX_X 0#define TERRAIN_PAGE_MAX_Y 0#define TERRAIN_FILE_PREFIX String("ygTerrain") //保存的地形文件名#define TERRAIN_FILE_SUFFIX String("dat")  //保存的地形文件的扩展名#define TERRAIN_WORLD_SIZE 12000.0f#define TERRAIN_SIZE 513#include <Ogre.h>#include <OIS/OIS.h>#include <iostream>#include <OgreTerrain.h>#include <OgreTerrainLayerBlendMap.h>#include <OgreTerrainGroup.h>using namespace Ogre;class MyFrameListener : public Ogre::FrameListener  //监听器类{private:OIS::InputManager *m_pInputManage;OIS::Keyboard *m_pKeyBoard;OIS::Mouse *m_pMouse;Ogre::Camera *m_pCamera;Ogre::Viewport *m_pViewport;Ogre::Timer m_Time;bool m_bWirmline;float m_fMovementSpeed;public:MyFrameListener( Ogre::RenderWindow *pWin, Ogre::Camera *pCamera, Ogre::Viewport *pViewport ){m_pCamera = pCamera;m_fMovementSpeed = 10;m_Time.reset();m_pViewport = pViewport;m_bWirmline = true;OIS::ParamList Params;size_t WindowHandle = 0;std::ostringstream WinHandleString;pWin->getCustomAttribute( "WINDOW", &WindowHandle );WinHandleString<<WindowHandle;Params.insert( std::make_pair( "WINDOW", WinHandleString.str() ) );m_pInputManage = OIS::InputManager::createInputSystem( Params );m_pKeyBoard = static_cast<OIS::Keyboard*>( m_pInputManage->createInputObject( OIS::OISKeyboard, false ) );m_pMouse = static_cast<OIS::Mouse*>( m_pInputManage->createInputObject( OIS::OISMouse, false ) );}~MyFrameListener(){m_pInputManage->destroyInputObject( m_pKeyBoard );m_pInputManage->destroyInputObject( m_pMouse );OIS::InputManager::destroyInputSystem( m_pInputManage );}bool frameStarted( const Ogre::FrameEvent& evt ){m_pKeyBoard->capture();bool bWalk = false;if( m_pKeyBoard->isKeyDown( OIS::KC_ESCAPE ) ){return false;}if( m_pKeyBoard->isKeyDown( OIS::KC_R ) && m_Time.getMilliseconds() > 250 ){m_Time.reset();if( m_bWirmline ){m_pCamera->setPolygonMode( Ogre::PolygonMode::PM_WIREFRAME );m_bWirmline = false;}else{m_pCamera->setPolygonMode( Ogre::PolygonMode::PM_SOLID );m_bWirmline = true;}}Ogre::Vector3 translate( 0, 0, 0 );if( m_pKeyBoard->isKeyDown( OIS::KC_W ) ){translate += Ogre::Vector3( 0, 0, -1 );}if( m_pKeyBoard->isKeyDown( OIS::KC_S ) ){translate += Ogre::Vector3( 0, 0, 1 );}if( m_pKeyBoard->isKeyDown( OIS::KC_D ) ){translate += Ogre::Vector3( 1, 0, 0 );}if( m_pKeyBoard->isKeyDown( OIS::KC_A ) ){translate += Ogre::Vector3( -1, 0, 0 );}m_pCamera->moveRelative( translate * m_fMovementSpeed * evt.timeSinceLastFrame * m_fMovementSpeed * 8 );m_pMouse->capture();float fDotX = m_pMouse->getMouseState().X.rel * evt.timeSinceLastFrame * -1;float fDotY = m_pMouse->getMouseState().Y.rel * evt.timeSinceLastFrame * -1;m_pCamera->yaw( Ogre::Radian( fDotX ) );m_pCamera->pitch( Ogre::Radian( fDotY ) );return true;}bool frameRenderingQueued( const Ogre::FrameEvent& evt ){return true;}bool frameEnded( const Ogre::FrameEvent& evt){return true;}};class MyApplication  //Application类{private:Ogre::Root *m_pRoot;Ogre::SceneManager *mSceneMgr;MyFrameListener *m_pFrameListener;bool m_bKeepRunning;Ogre::TerrainGlobalOptions *mTerrainGlobals;Ogre::TerrainGroup *mTerrainGroup;bool mTerrainsImported;public:MyApplication(): m_pRoot( NULL ),mSceneMgr( NULL ),m_pFrameListener( NULL ),m_bKeepRunning( true ),mTerrainGlobals( NULL ),mTerrainsImported( false ){}~MyApplication(){if( mTerrainGlobals ){delete mTerrainGlobals;}if( mTerrainGroup ){delete mTerrainGroup;}if( m_pRoot ){delete m_pRoot;m_pRoot = NULL;}}void RenderOneFrame(){Ogre::WindowEventUtilities::messagePump();m_bKeepRunning = m_pRoot->renderOneFrame();}bool KeepRunning(){return m_bKeepRunning;}void LoadResources(){Ogre::ConfigFile cf;cf.load( "resources_d.cfg" );Ogre::ConfigFile::SectionIterator SecIter = cf.getSectionIterator();Ogre::String SectionName, DataName, TypeName;while( SecIter.hasMoreElements() ){SectionName = SecIter.peekNextKey();Ogre::ConfigFile::SettingsMultiMap *SetMap = SecIter.getNext();Ogre::ConfigFile::SettingsMultiMap::iterator SetIter;for( SetIter = SetMap->begin(); SetIter != SetMap->end(); ++SetIter ){TypeName = SetIter->first;DataName = SetIter->second;Ogre::ResourceGroupManager::getSingleton().addResourceLocation( DataName, TypeName, SectionName );}}Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();}int StartUp(){if( !m_pRoot ){m_pRoot = new Ogre::Root( "plugins_d.cfg" );if( !m_pRoot->showConfigDialog() ){return -1;}}Ogre::RenderWindow *pWindow = m_pRoot->initialise( true );mSceneMgr = m_pRoot->createSceneManager( Ogre::ST_GENERIC );Ogre::Camera *pCamera = mSceneMgr->createCamera( "Camera1" );pCamera->setPosition( 0, 1400, 100 );pCamera->lookAt( 0, 0, 0 );pCamera->setNearClipDistance( 5 );Ogre::Viewport *pViewport = pWindow->addViewport( pCamera );pViewport->setBackgroundColour( Ogre::ColourValue( 0, 0, 0, 1 ) );pCamera->setAspectRatio( Ogre::Real( pViewport->getActualWidth() ) / Ogre::Real( pViewport->getActualHeight() ) );LoadResources();CreateScene();m_pFrameListener = new MyFrameListener( pWindow, pCamera, pViewport );m_pRoot->addFrameListener( m_pFrameListener );return 0;}void CreateScene(){//建立光源Ogre::Light *pLight = mSceneMgr->createLight( "Light1" );pLight->setType( Ogre::Light::LT_DIRECTIONAL );pLight->setDirection( Ogre::Vector3(0.55, -0.3, 0.75) );pLight->setSpecularColour( Ogre::ColourValue( 0.4f, 0.4f, 0.4f ) );pLight->setDiffuseColour( Ogre::ColourValue::White );mSceneMgr->setAmbientLight( Ogre::ColourValue( 0.2f, 0.2f, 0.2f ) );//设置场景背景色和建立天空盒Ogre::ColourValue FadeColour( 0.9, 0.9, 0.9 );mSceneMgr->setFog( Ogre::FOG_LINEAR, FadeColour, 0.0f, 15000.0f, 28000.0f );Ogre::Plane plane;plane.d = 1000;plane.normal = Ogre::Vector3::NEGATIVE_UNIT_Y;mSceneMgr->_setSkyPlane( true, plane, "Examples/CloudySky", 500, 20, true, 0.5f, 150, 150 );//--------------------------以下内容是建立地形过程-----------------------------//第一步  创建地形全局配置 TerrainGlobalOptions//在Ogre中,地形是由一块一块的地形组成的,他们每快地形都有共同的属性,所以在创建地形之前我们必须指定地形块的全局配置。mTerrainGlobals = OGRE_NEW TerrainGlobalOptions();//第二步  创建地形分组Ogre::TerrainGroupmTerrainGroup = OGRE_NEW TerrainGroup(mSceneMgr, Terrain::ALIGN_X_Z, TERRAIN_SIZE, TERRAIN_WORLD_SIZE);//调用saveAllTerrain函数时保存的地形文件的名字为TERRAIN_FILE_PREFIX.TERRAIN_FILE_SUFFIXmTerrainGroup->setFilenameConvention(TERRAIN_FILE_PREFIX, TERRAIN_FILE_SUFFIX); mTerrainGroup->setOrigin(/*mTerrainPos*/Ogre::Vector3::ZERO);/*上面这段代码比较简单首先是实例化一个TerrainGroup对象并为他指定场管理器、地形的平铺方向、地形的大小平铺方向一般采用ALIGN_X_Z,也就是采用Y作为高度然后第二句设置了该地形组的起始位置,在以后创建的地形块中均采用此位置作为相对位置*///第三步  配置地图块参数 configureTerrainDefaults(pLight);//包括设置地形全局选项和地形分组的属性//第四步  创建地形分块for (long x = TERRAIN_PAGE_MIN_X; x <= TERRAIN_PAGE_MAX_X; ++x)for (long y = TERRAIN_PAGE_MIN_Y; y <= TERRAIN_PAGE_MAX_Y; ++y)defineTerrain(x, y,false); //为true则为一块平地mTerrainGroup->loadAllTerrains(true);//第五步  地形纹理图层混合合成Ogre::TerrainGroup::TerrainIterator iter = mTerrainGroup->getTerrainIterator();while( iter.hasMoreElements() ) //迭代每个插槽中的地形块{Ogre::Terrain *t = iter.getNext()->instance;initBlendMaps( t );}//--------------------------以上内容是建立地形过程-----------------------------//下面是保存地形,下次直接加载就行了if( mTerrainsImported ) {//如果要反复修改TerrainGroup的数据,就不必保存地形了mTerrainGroup->saveAllTerrains( true );  //注意,保存的是地形顶点和TerrainGroup的数据,TerrainGlobalOption的数据不会被保存mTerrainsImported =false;}}//配置地图块参数函数void configureTerrainDefaults(Light* l){mTerrainGlobals->setMaxPixelError(8);mTerrainGlobals->setCompositeMapDistance(3000);//距离镜头超过3000部分使用地图合成(CompositeMap)模式表现mTerrainGlobals->setLightMapDirection(l->getDerivedDirection());//地图光照方向(和实时阴影生成相关)mTerrainGlobals->setCompositeMapAmbient(mSceneMgr->getAmbientLight());mTerrainGlobals->setCompositeMapDiffuse(l->getDiffuseColour());/*如果有了地形分组之后,我们就可以通过地形分组创建地形块了,但是每一个地形块都有很多属性,我们可以在创建地形块的同时设置那些属性,但是这样极为不方便。所以,我们可以先设置默认的地形块属性,那么创建地形块的时候就可以一个方法搞定了*///设置地形默认属性  Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();defaultimp.terrainSize = TERRAIN_SIZE;//不太了解,调试中,这个值越小,地图边缘锯齿现象越严重,太小的话,运行起来程序会跑死、出错defaultimp.worldSize = TERRAIN_WORLD_SIZE;//假设为a,那么地图大小为 a x adefaultimp.inputScale = 600;//决定地图最大落差(高度),即位图中白色和黑色部分的高度差defaultimp.minBatchSize = 33;defaultimp.maxBatchSize = 65;defaultimp.layerList.resize(3);//这里设置了3层纹理,DDS为一种高级的纹理模式,DirectDrawSurface,觉得难以理解的话//可以理解为一种特殊的.jpg图片模式,但是用DDS质材的话可以接收并显示地形阴影,用JPG就显示不出来,//而且据我调试观察发现,第一个.dds质材是用来显示纹理图形,第二个.dds才是用来接收和显示阴影的。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");////下面内容用于测试,换一种地形//defaultimp.layerList.resize(3);  //defaultimp.layerList[0].worldSize = 100; //这个值关系到贴图的细致程度,太大的话图片被拉伸得很大,看起来模糊//defaultimp.layerList[0].textureNames.push_back("BeachStones.jpg");//defaultimp.layerList[0].textureNames.push_back("");//defaultimp.layerList[1].worldSize = 30;//defaultimp.layerList[1].textureNames.push_back("grass_1024.jpg");//defaultimp.layerList[1].textureNames.push_back("");//defaultimp.layerList[2].worldSize = 200;//defaultimp.layerList[2].textureNames.push_back("terr_rock6.jpg");//defaultimp.layerList[2].textureNames.push_back("");}//defineTerrain(x, y, blankTerrain)定义为:void defineTerrain(long x, long y, bool flat = false){/*如果想创建平坦的地形(例子中是山地地形,这里只是延伸说明,源码没有下面内容),但可以作为参考,帮助理解地形的建立过程,如下:*/if (flat) //表示平坦地图,高度为0{mTerrainGroup->defineTerrain(x, y, 0.0f);}else //若是想读取位图来创建地形,而不是平坦的地面,需要进行如下操作*/{String filename = mTerrainGroup->generateFilename(x, y);//经调试,发现filename=ygTerrain_00000000.dat//如果有存档的,或者说以前运行过这个例子,直接加载.dat文件if (ResourceGroupManager::getSingleton().resourceExists(mTerrainGroup->getResourceGroup(),filename)){/*仔细研究了下resourceExists函数,由于调试中filename=ygTerrain_00000000.dat,那么这个dat文件是怎么加载入资源组管理器中的呢?发现resources_d.cfg文件中这么一句话[General]:FileSystem=D:/MySoft/ogre_src_v1-8-1/Samples/Media,所以资源加载阶段,把这个文件夹中的所有文件包括.bat文件都加载进入资源组了,而我的ygTerrain_00000000.dat文件就放在这个文件夹下。*///下面函数功能是定义一个在地形网格中的插槽,在这个阶段,地形实例实际上并没有出现在网格中,//只有做只是为它准备一个位置。我们像这样做的原因是为了支持这种地形实例的后台准备。//参数的x,y坐标表示地形槽相对中心插槽的位置//只有xy坐标参数的情况下,这个函数从一个现成的如terrain.bat文件中加载地形插槽mTerrainGroup->defineTerrain(x, y);}//否则读取位图,创建加载地形else{Image img;getTerrainImage(x % 2 != 0, y % 2 != 0, img);//从PNG格式的灰度图图片读取灰度值,作为高度的换算基值//调用重载函数defineTerrain创建地形,注意此时img代表了已经加载好高度图的数据集mTerrainGroup->defineTerrain(x, y, &img); mTerrainsImported = true; //作为下文地形纹理渲染标志,失败则表示没有创建好地形}}}//getTerrainImage()定义为:void getTerrainImage(bool flipX, bool flipY, Image& img){//高度图在这里给出img.load("terrain.png", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);if (flipX)img.flipAroundY(); //这些函数封装在.dll里了,没能看个究竟~if (flipY)img.flipAroundX(); //这些函数封装在.dll里了,没能看个究竟~}//地形纹理图层混合合成函数void initBlendMaps(Terrain* terrain){/*纹理混合,Ogre1.7的地形纹理默认支持8层纹理混合(1.8的还不清楚),如果想更改,可通过地形的全局配置设置,混合越多越慢。刚才我们定义了三层纹理。默认情况下,引擎只显示第一层纹理纹理,所以我们得为每一层指定混合系数,0表示不显示,1表示显示,0~1表示混合混合显示。*///取得第2层纹理(绿色地皮grass_green)Ogre::TerrainLayerBlendMap *pBlend1 = terrain->getLayerBlendMap( 1 );//取得第3层纹理(长了植物growth_weirdfungus)Ogre::TerrainLayerBlendMap *pBlend2 = terrain->getLayerBlendMap( 2 );Ogre::Real MinHeight1 = 70;Ogre::Real FadeDist1 = 40;Ogre::Real MinHeight2 = 70;Ogre::Real FadeDist2 = 15;float *pBlend1Point = pBlend1->getBlendPointer();//取得纹理地图起始位置float *pBlend2Point = pBlend2->getBlendPointer();for( Ogre::uint16 y = 0; y < terrain->getLayerBlendMapSize(); ++y ){for( Ogre::uint16 x = 0; x <terrain->getLayerBlendMapSize(); ++x ){Ogre::Real tx, ty;//将纹理坐标转换为地形坐标(类似蒙皮)pBlend1->convertImageToTerrainSpace( x, y, &tx, &ty );//取得在(x,y)点坐标的高度(2D坐标系的xy)Ogre::Real height = terrain->getHeightAtTerrainPosition( tx, ty );//在不同的高度使用不同的纹理,//当高度<70,Val=0不显示纹理1,高度>110,val=1,只显示纹理2 ,70~110之间混合显示//当高度<70,Val=0不显示纹理1,高度>85,val=1,只显示纹理3 ,70~85之间混合显示Ogre::Real val = ( height - MinHeight1 ) / FadeDist1; //70-110之间混合显示val = Ogre::Math::Clamp( val, static_cast<Ogre::Real>( 0 ), static_cast<Ogre::Real>( 1 ) );*pBlend1Point++ = val;val = ( height - MinHeight2 ) / FadeDist2; //70-85之间混合显示val = Ogre::Math::Clamp( val, static_cast<Ogre::Real>( 0 ), static_cast<Ogre::Real>( 1 ) );//执行混合设置,0的话不显示,1只显示它,0~1混合显示,值设置完毕之后,指针++指向下一个*pBlend2Point++ = val;}}pBlend1->dirty();pBlend2->dirty();pBlend1->update();pBlend2->update();/*总之,第一层范围最大,全部都100%显示;第二层范围稍大,70-110之间按比例混合显示,第三层范围最小,70-85之间按比例混合显示。即:当小于70时,则第二层和第三层都不显示,只显示第一层。当大于70且小于85时,第一层100%显示,第二层和第三层按比例混合显示。当大于85且小于110时,第一层和第二层100%显示(只显示第二层),第三层按比例混合显示。当大于110时,三层都100%显示,由于第三层最后铺上,实际上只显示第三层。总之,可以把70-85看成第二层纹理的过渡带,70-110看成第三层纹理的过渡带,分析每一层纹理时独立开来,不要管其他纹理的影响。*/}};int main() //入口,main函数{MyApplication app;app.StartUp();while( app.KeepRunning() ){app.RenderOneFrame();}return 0;}


使用时,把上面所有代码拷贝到一个cpp文件中,设置好头文件包含目录、库文件目录(附加依赖项)以及工作目录,就可以直接运行了。下面是截图:


附:

我的vs2010附加包含目录:

D:/MySoft/ogre_src_v1-8-1/OgreMain/include;
D:/MySoft/ogre_src_v1-8-1_OgreBuild/include;
D:/MySoft/ogre_src_v1-8-1/Dependencies/include;
D:/MySoft/ogre_src_v1-8-1/Dependencies/include/OIS;
D:/MySoft/ogre_src_v1-8-1/Dependencies/include/Cg;
D:/MySoft/ogre_src_v1-8-1;
D:/MySoft/ogre_src_v1-8-1/OgreMain/include/Threading;
D:/MySoft/ogre_src_v1-8-1/Components/Terrain/../Paging/include;
D:/MySoft/ogre_src_v1-8-1/Components/Terrain/include;

我的附加依赖项:

D:\MySoft\ogre_src_v1-8-1_OgreBuild\lib\Debug\OgreMain_d.lib
D:\MySoft\ogre_src_v1-8-1\Dependencies\lib\Debug\OIS_d.lib
D:\MySoft\ogre_src_v1-8-1_OgreBuild\lib\Debug\OgreTerrain_d.lib
D:\MySoft\ogre_src_v1-8-1_OgreBuild\lib\Debug\OgrePaging_d.lib
D:\MySoft\ogre_src_v1-8-1\Dependencies\lib\debug\freetype2311_d.lib
D:\MySoft\ogre_src_v1-8-1\Dependencies\lib\debug\FreeImaged.lib
D:\MySoft\ogre_src_v1-8-1\Dependencies\lib\debug\zziplibd.lib
D:\MySoft\ogre_src_v1-8-1\Dependencies\lib\debug\zlibd.lib

注意,上面的D:\MySoft\ogre_src_v1-8-1_OgreBuild是我用cmake构建代码时使用的目录,根据不同情况更改,或者使用OGRE_HOME更方便。另外,由于附加依赖项直接使用了路径+库文件名,所以附加库目录就可以不用设置了。(如果附加依赖项不含路径,只有库文件名的话,则需要设置附加库目录,以便vs知道去哪寻找库文件)。