为了便于直接使用,所有代码都放在一个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::TerrainGroup
- mTerrainGroup = OGRE_NEW TerrainGroup(mSceneMgr, Terrain::ALIGN_X_Z, TERRAIN_SIZE, TERRAIN_WORLD_SIZE);
- //调用saveAllTerrain函数时保存的地形文件的名字为TERRAIN_FILE_PREFIX.TERRAIN_FILE_SUFFIX
- mTerrainGroup->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 a
- defaultimp.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知道去哪寻找库文件)。
提供了一段使用Ogre1.8引擎实现地形构建的完整源码,包括监听器类、应用类和main函数,详细解释了如何创建地形全局配置、地形分组、配置地图块参数、创建地形分块以及地形纹理图层混合合成等关键步骤。此外,还介绍了如何加载资源、启动应用以及保存和导入地形数据。代码经过优化,易于直接使用。
1864

被折叠的 条评论
为什么被折叠?



