转自:http://www.cnblogs.com/WindyMax/
研究Ogre的程序笔记
编译环境 WIN7 32 VS2008 Ogre的版本 1.8
Ogre的地形算法是采用Geometry MIPMap的算法,当然贴图也是采用MIPMap的算法,这里有Ogre LOD算法的论文解释:http://www.flipcode.com/articles/article_geomipmaps.pdf
看到Ogre的Terrain,突然也想自己实现一个地形的打算,不过估计要过一段时间了,现在来说,ClipMap算法应该更适应现在的游戏,具体的内容可以查看http://www.docin.com/p-118698727.html,对于了解现在LOD的地形算法,这是一篇相当不错的论文
关于Ogre地形的代码解释,网上很多高手都已经给出很全面的解释了,我也就不多说了
#include <Ogre.h> #include <OIS/OIS.h> #include <iostream> #include <OgreTerrain.h> #include <OgreTerrainLayerBlendMap.h> #include <OgreTerrainGroup.h> 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 { private: Ogre::Root *m_pRoot; Ogre::SceneManager *m_pSceneManage; MyFrameListener *m_pFrameListener; bool m_bKeepRunning; Ogre::TerrainGlobalOptions *m_pTerrGloOp; Ogre::TerrainGroup *m_pTerrGroup; bool m_bLoadNewMap; public: MyApplication() : m_pRoot( NULL ), m_pSceneManage( NULL ), m_pFrameListener( NULL ), m_bKeepRunning( true ), m_pTerrGloOp( NULL ), m_bLoadNewMap( false ) { } ~MyApplication() { if( m_pTerrGloOp ) { delete m_pTerrGloOp; } if( m_pTerrGroup ) { delete m_pTerrGroup; } 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 ); m_pSceneManage = m_pRoot->createSceneManager( Ogre::ST_GENERIC ); Ogre::Camera *pCamera = m_pSceneManage->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 ConfigureTerrain( Ogre::Light *pLight ) { m_pTerrGloOp = new Ogre::TerrainGlobalOptions(); m_pTerrGloOp->setMaxPixelError( 8 ); m_pTerrGloOp->setCompositeMapDistance( 3000 ); m_pTerrGloOp->setCompositeMapAmbient( m_pSceneManage->getAmbientLight() ); m_pTerrGloOp->setLightMapDirection( pLight->getDerivedDirection() ); m_pTerrGloOp->setCompositeMapDiffuse( pLight->getDiffuseColour() ); m_pTerrGroup = new Ogre::TerrainGroup( m_pSceneManage, Ogre::Terrain::ALIGN_X_Z, 513, 12000 ); m_pTerrGroup->setFilenameConvention( Ogre::String( "SaveTerrain" ), Ogre::String( "dat" ) ); m_pTerrGroup->setOrigin( Ogre::Vector3::ZERO ); Ogre::Terrain::ImportData &Imp = m_pTerrGroup->getDefaultImportSettings(); Imp.maxBatchSize = 65; //一个tile中最多包含的顶点数 Imp.minBatchSize = 33; //一个tile中最少包含的顶点数 Imp.inputScale = 600; Imp.layerList.resize( 3 ); Imp.layerList[0].worldSize = 100; Imp.layerList[0].textureNames.push_back( "dirt_grayrocky_diffusespecular.dds" ); Imp.layerList[0].textureNames.push_back( "dirt_grayrocky_normalheight.dds" ); Imp.layerList[1].worldSize = 30; Imp.layerList[1].textureNames.push_back( "grass_green-01_diffusespecular.dds" ); Imp.layerList[1].textureNames.push_back( "grass_green-01_normalheight.dds" ); Imp.layerList[2].worldSize = 200; Imp.layerList[2].textureNames.push_back( "growth_weirdfungus-03_diffusespecular.dds" ); Imp.layerList[2].textureNames.push_back( "growth_weirdfungus-03_normalheight.dds" ); } void InitBlend( Ogre::Terrain *pTerrain ) { Ogre::TerrainLayerBlendMap *pBlend1 = pTerrain->getLayerBlendMap( 1 ); Ogre::TerrainLayerBlendMap *pBlend2 = pTerrain->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 < pTerrain->getLayerBlendMapSize(); ++y ) { for( Ogre::uint16 x = 0; x <pTerrain->getLayerBlendMapSize(); ++x ) { Ogre::Real tx, ty; pBlend1->convertImageToTerrainSpace( x, y, &tx, &ty ); Ogre::Real height = pTerrain->getHeightAtTerrainPosition( tx, ty ); Ogre::Real val = ( height - MinHeight1 ) / FadeDist1; val = Ogre::Math::Clamp( val, static_cast<Ogre::Real>( 0 ), static_cast<Ogre::Real>( 1 ) ); *pBlend1Point++ = val; val = ( height - MinHeight2 ) / FadeDist2; val = Ogre::Math::Clamp( val, static_cast<Ogre::Real>( 0 ), static_cast<Ogre::Real>( 1 ) ); *pBlend2Point++ = val; } } pBlend1->dirty(); pBlend2->dirty(); pBlend1->update(); pBlend2->update(); } void GetTerrainImage( bool bFlipx, bool bFlipy, Ogre::Image &img ) { img.load( "terrain.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); if( bFlipx ) { img.flipAroundY(); } if( bFlipy ) { img.flipAroundX(); } } void DefineTerrain( long x, long y ) { Ogre::String FileName = m_pTerrGroup->generateFilename( x, y ); if( Ogre::ResourceGroupManager::getSingleton().resourceExists( m_pTerrGroup->getResourceGroup(), FileName ) ) { m_pTerrGroup->defineTerrain( x, y ); } else { Ogre::Image img; GetTerrainImage( x % 2, y % 2, img ); m_pTerrGroup->defineTerrain( x, y, &img ); m_bLoadNewMap = true; } } void CreateScene() { Ogre::Light *pLight = m_pSceneManage->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 ); m_pSceneManage->setAmbientLight( Ogre::ColourValue( 0.2f, 0.2f, 0.2f ) ); ConfigureTerrain( pLight ); DefineTerrain( 0, 0 ); m_pTerrGroup->loadAllTerrains( true ); Ogre::TerrainGroup::TerrainIterator iter = m_pTerrGroup->getTerrainIterator(); while( iter.hasMoreElements() ) { Ogre::Terrain *t = iter.getNext()->instance; InitBlend( t ); } if( m_bLoadNewMap ) { //如果要反复修改TerrainGroup的数据,就不必保存地形了 m_pTerrGroup->saveAllTerrains( true ); //注意,保存的是地形顶点和TerrainGroup的数据,TerrainGlobalOption的数据不会被保存 m_bLoadNewMap =false; } m_pTerrGroup->freeTemporaryResources(); Ogre::ColourValue FadeColour( 0.9, 0.9, 0.9 ); m_pSceneManage->setFog( Ogre::FOG_LINEAR, FadeColour, 0.0f, 15000.0f, 28000.0f ); Ogre::Plane plane; plane.d = 1000; plane.normal = Ogre::Vector3::NEGATIVE_UNIT_Y; m_pSceneManage->_setSkyPlane( true, plane, "Examples/CloudySky", 500, 20, true, 0.5f, 150, 150 ); } }; int main() { MyApplication app; app.StartUp(); while( app.KeepRunning() ) { app.RenderOneFrame(); } return 0; }
不过在这里主要讲下Terrain::getLayerBlendMapSize()函数,这个函数返回的是Terrain的mLayerBlendMapSize。一开始没有设置的话,mLayerBlendMapSize默认是1024。也就是说将Blend Layer平铺完这个地形,然后分成1024个“区域”,然后计算高度平铺适合的纹理,混合等。可以通过Ogre::TerrainGlobalOptions::setLayerBlendMapSize( value )函数设置...= =~~一开始困扰了我很久。
总得来说,Ogre的源码很适合想了解游戏引擎的新手研究,里面的东西不会很高深,各种设计模式也很值得去学习,无论是不是专业的游戏开发人员。待续...