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知道去哪寻找库文件)。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值