ogre1.8自己写的建立地形源码(注释,可直接使用)
Posted HNFXS985
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ogre1.8自己写的建立地形源码(注释,可直接使用)相关的知识,希望对你有一定的参考价值。
为了便于直接使用,所有代码都放在一个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自己写的建立地形源码(注释,可直接使用)的主要内容,如果未能解决你的问题,请参考以下文章
图像处理基于matlab不同情绪状态下身体感觉的地形图含Matlab源码 2425期
WorldWind源码剖析系列:WorldWind如何确定与视点相关的地形数据的LOD层级与范围