• [Ogre][地形][原创]基于OgreTerrain的地形实现


     需要用到的外部图片资源:

    在ogre调用时需要多用到的几个外部dll:

    OgreTerrain_d.dll

    需要添加头文件

    #include "OgreOgre.h"
    #include "OgreOgreFileSystemLayer.h"
    #include "OgreOgreTerrain.h"
    #include "OgreOgreTerrainGroup.h"
    #include "OgreOgreTerrainQuadTreeNode.h"
    #include "OgreOgreTerrainMaterialGeneratorA.h"
    #include "OgreOgreTerrainPaging.h"

    定义类初始化需要的结构体

    struct CrisTerrainInitStruct
    {
    CrisTerrainInitStruct():m_scenemanager(NULL),fMaxHoriz(1000),fMinHoriz(0),sMapFilename(""),sElevationFilename(""),fMapSize(TERRAIN_WORLD_SIZE)
    {
    fInputScale = fMaxHoriz - fMinHoriz;
    }
    SceneManager* m_scenemanager;
    String sMapFilename; //地图卫星图片
    String sElevationFilename;//高程图
    Real fMinHoriz;//最低点
    Real fMaxHoriz;//最高点
    Real fMapSize;//地形边长
    Real fInputScale; //地形高低差 对比像素的0~1 .可以不需要设置
    //位置放在000点,
    };

    类的外部接口

    void Init(CrisTerrainInitStruct* sInitData);

    实现的一些重要步骤:

    第一步  创建地形全局配置 TerrainGlobalOptions

    mTerrainGlobals = OGRE_NEW TerrainGlobalOptions();
    //TerrainGlobalOptions是一个类,定义了地形块的一些全局变量和默认值,需要的话我们可以改变他的变量参数,我们后面再做改变。

     第二步  创建地形分组Ogre::TerrainGroup

    //实例化一个TerrainGroup对象

    mTerrainGroup = OGRE_NEW TerrainGroup(mSceneMgr, Terrain::ALIGN_X_Z, TERRAIN_SIZE, m_sInitData->fMapSize);

    第三步  配置地图块参数 configureTerrainDefaults

    configureTerrainDefaults();

    第四步  创建地形分块

    //defineTerrain方法首先要指定该块地形在地形分组中的索引位置,然后第三个参数必须指定高度数据,用灰度图创建山地地形
    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, blankTerrain);

    下面是我的代码:

    头文件:

    #pragma once
    #define DebugTerrain
    #include "OgreOgre.h"
    #include "OgreOgreFileSystemLayer.h"
    #include "CrisConfigCrisConfig.h"
    #include "OgreOgreTerrain.h"
    #include "OgreOgreTerrainGroup.h"
    #include "OgreOgreTerrainQuadTreeNode.h"
    #include "OgreOgreTerrainMaterialGeneratorA.h"
    #include "OgreOgreTerrainPaging.h"
    
    //分页多少
    #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("testTerrain")
    #define TERRAIN_FILE_SUFFIX String("dat")
    #define TERRAIN_WORLD_SIZE 1200.0f
    #define TERRAIN_SIZE 513
    
    
    using namespace Ogre;
    
    struct CrisTerrainInitStruct
    {
    	CrisTerrainInitStruct():m_scenemanager(NULL),fMaxHoriz(1000),fMinHoriz(0),sMapFilename(""),sElevationFilename(""),fMapSize(TERRAIN_WORLD_SIZE)
    	{
    		fInputScale = fMaxHoriz - fMinHoriz;
    	}
    	SceneManager*	m_scenemanager;
    	String			sMapFilename;	//地图卫星图片
    	String			sElevationFilename;//高程图
    	Real			fMinHoriz;//最低点
    	Real			fMaxHoriz;//最高点
    	Real			fMapSize;//地形边长
    	Real			fInputScale;	//地形高低差 对比像素的0~1 .可以不需要设置
    //位置放在000点,
    };
    
    class CrisTerrain
    {
    public:
    	CrisTerrain();
    	~CrisTerrain();
    	void Init(CrisTerrainInitStruct*	sInitData);
    
    	//void setTerrainImage(const Ogre::String& szCfgFilename = "terrain_texture.jpg");
    
    protected:
    	TerrainGlobalOptions* mTerrainGlobals;
    	TerrainGroup* mTerrainGroup;
    	TerrainPaging* mTerrainPaging;
    	PageManager* mPageManager;
    
    	void testOption();//为了小样例测试加载的,在大工程中可以直接不用
    private:
    	CrisTerrainInitStruct*	m_sInitData;
    	void loadConfigFile(const Ogre::String& szCfgFilename = "");
    
    	Ogre::String						m_szConfigFileName;
    	CCrisConfigManager                  m_hConfigMgr;
    
    	bool mFly;
    	Real mFallVelocity;
    	Real mBrushSizeTerrainSpace;
    		
    	Real mHeightUpdateCountDown;
    	Real mHeightUpdateRate;
    	Vector3 mTerrainPos;
    	bool mTerrainsImported;
    
    	SceneManager*			mSceneMgr;
    
    	/** @brief 实例化地形 */
    	void setupContent();
    	/** @brief 实例化地形参数 */
    	void configureTerrainDefaults();
    	void initBlendMaps(Terrain* terrain);
    	void defineTerrain(long x, long y, bool flat = false);
    	void getTerrainImage(bool flipX, bool flipY, Image& img);
    
    
    };
    

      实现文件:

    #include "Terrain.h"
    
    CrisTerrain::CrisTerrain(): mTerrainGroup(0)
    	, mTerrainPaging(0)
    	, mPageManager(0)
    	, mFallVelocity(0)
    	, mBrushSizeTerrainSpace(0.02)
    	, mHeightUpdateCountDown(0)
    	, mTerrainPos(0,0,0)
    	, mTerrainsImported(false)
    {
    
    }
    
    CrisTerrain::~CrisTerrain()
    {
    
    }
    
    
    void CrisTerrain::Init(CrisTerrainInitStruct*	sInitData)
    {
    	mSceneMgr = sInitData->m_scenemanager;
    	m_sInitData = sInitData;
    	setupContent();
    	//loadConfigFile("terrain.cfg");
    }
    
    void CrisTerrain::loadConfigFile(const String& szFilename)
    {
    	if(!szFilename.empty())
    		m_szConfigFileName = szFilename;
    	else
    		return;
    	
    	//m_hConfigMgr.LoadFromResourceSystem(m_szConfigFileName);
    	//String worldTexture = m_hConfigMgr.GetValueString("Ter", "WorldTexture", "testter.jpg");
    	//OutputDebugString(worldTexture.c_str());
    	
    }
    
    //void CrisTerrain::setTerrainImage(const Ogre::String& szCfgFilename)
    //{
    //	Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
    //	defaultimp.terrainSize = TERRAIN_SIZE;//不太了解,调试中,这个值越小,地图边缘锯齿现象越严重,太小的话,运行起来程序会跑死、出错
    //	defaultimp.worldSize = m_sInitData->fMapSize;//假设为a,那么地图大小为 a x a
    //	defaultimp.inputScale = 60;//决定地图最大落差(高度),即位图中白色和黑色部分的高度差
    //	defaultimp.minBatchSize = 33;
    //	defaultimp.maxBatchSize = 65;
    //	// textures
    //	defaultimp.layerList.resize(3);//这里设置了3层纹理,DDS为一种高级的纹理模式,DirectDrawSurface,觉得难以理解的话
    ////可以理解为一种特殊的.jpg图片模式,但是用DDS质材的话可以接收并显示地形阴影,用JPG就显示不出来,而且据我调试观
    ////察发现,第一个.dds质材是用来显示纹理图形,第二个.dds才是用来接收和显示阴影的
    //
    //	defaultimp.layerList[0].worldSize = 1;//这个值关系到此贴图的细致程度,太大的话图片被拉伸得很大,看起来模糊
    //	defaultimp.layerList[0].textureNames.push_back(szCfgFilename);
    //	defaultimp.layerList[0].textureNames.push_back(szCfgFilename);
    //	defaultimp.layerList[1].worldSize = 1;
    //	defaultimp.layerList[1].textureNames.push_back(szCfgFilename);
    //	defaultimp.layerList[1].textureNames.push_back(szCfgFilename);
    //	defaultimp.layerList[2].worldSize = 2;
    //	defaultimp.layerList[2].textureNames.push_back(szCfgFilename);
    //	defaultimp.layerList[2].textureNames.push_back(szCfgFilename);
    //
    //	mTerrainGroup->loadAllTerrains(true);
    //
    //	if (mTerrainsImported)
    //	{
    //		TerrainGroup::TerrainIterator ti = mTerrainGroup->getTerrainIterator();
    //		while(ti.hasMoreElements())
    //		{
    //			Terrain* t = ti.getNext()->instance;
    //			initBlendMaps(t);
    //		}
    //	}
    //
    //	mTerrainGroup->freeTemporaryResources();
    //}
    
    void CrisTerrain::testOption()
    {
    	//mEditMarker = mSceneMgr->createEntity("editMarker", "sphere.mesh");
    	//mEditNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    	//mEditNode->attachObject(mEditMarker);
    	//mEditNode->setScale(0.05, 0.05, 0.05);
    
    	
    	Ogre::FileSystemLayer* mFSLayer = OGRE_NEW_T(Ogre::FileSystemLayer, Ogre::MEMCATEGORY_GENERAL)(OGRE_VERSION_NAME);
    	ResourceGroupManager::getSingleton().createResourceGroup("Terrain");
    	ResourceGroupManager::getSingleton().addResourceLocation(mFSLayer->getWritablePath(""), "FileSystem", "Terrain", false, false);
    	
    	MaterialManager::getSingleton().setDefaultTextureFiltering(TFO_ANISOTROPIC);
    	MaterialManager::getSingleton().setDefaultAnisotropy(7);
    	
    
    	//mSceneMgr->setFog(FOG_LINEAR, ColourValue(0.7, 0.7, 0.8), 0, 1000, 2500);
    
    	LogManager::getSingleton().setLogDetail(LL_BOREME);
    		
    	mSceneMgr->setAmbientLight(ColourValue(0.6, 0.6, 0.6));
    
    	
    	//// create a few entities on the terrain
    	//Entity* e = mSceneMgr->createEntity("tudorhouse.mesh");
    	//Vector3 entPos(mTerrainPos.x + 2043, 0, mTerrainPos.z + 1715);
    	//Quaternion rot;
    	//entPos.y = mTerrainGroup->getHeightAtWorldPosition(entPos) + 65.5 + mTerrainPos.y;
    	//rot.FromAngleAxis(Degree(Math::RangeRandom(-180, 180)), Vector3::UNIT_Y);
    	//SceneNode* sn = mSceneMgr->getRootSceneNode()->createChildSceneNode(entPos, rot);
    	//sn->setScale(Vector3(0.12, 0.12, 0.12));
    	//sn->attachObject(e);
    
    	//mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");
    }
    
    void CrisTerrain::setupContent()
    {
    	bool blankTerrain = false;
    	//blankTerrain = true;
    
    	mTerrainGlobals = OGRE_NEW TerrainGlobalOptions();
    	//TerrainGlobalOptions是一个类,定义了地形块的一些全局变量和默认值,需要的话我们可以改变他的变量参数,我们后面再做改变。
    
    #ifdef DebugTerrain
    	testOption();
    #endif
    
    	//创建地形分组Ogre::TerrainGroup	
    	/*实例化一个TerrainGroup对象
    	参数1:为他指定场管理器、
    	参数2:地形的平铺方向,平铺方向一般采用ALIGN_X_Z,也就是采用Y作为高度
    	参数3:unit16 TERRAINSIZE=2~n+1,比如512+1=513,不符合公式的可能会导致地图显示异常,表示“The
    	size of each terrain down one edge in vertices (2^n+1)”楼主只能理解意思,不能完全正确
    	表达其中的专有名词,实际调试中,这个参数影响地图边缘的锯齿度,越小锯齿越明显,取值太小
    	的话,运行会错误;
    	参数4:Real TERRAINWORLDSIZE,地图大小,表示地图正方形的边长)
    	*/
    	mTerrainGroup = OGRE_NEW TerrainGroup(mSceneMgr, Terrain::ALIGN_X_Z, TERRAIN_SIZE, m_sInitData->fMapSize);
    	//定义命名前缀
    	mTerrainGroup->setFilenameConvention(TERRAIN_FILE_PREFIX, TERRAIN_FILE_SUFFIX);
    	//设置了该地形组的起始位置,在以后创建的地形块中均采用此位置作为相对位置
    	mTerrainGroup->setOrigin(mTerrainPos);
    	mTerrainGroup->setResourceGroup("Terrain");
    
    	//配置地图块参数 configureTerrainDefaults
    	configureTerrainDefaults();
    
    	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, blankTerrain);
    
    	// 开始前加载好
    	mTerrainGroup->loadAllTerrains(true);
    
    	if (mTerrainsImported)
    	{
    		TerrainGroup::TerrainIterator ti = mTerrainGroup->getTerrainIterator();
    		while(ti.hasMoreElements())
    		{
    			Terrain* t = ti.getNext()->instance;
    			initBlendMaps(t);
    		}
    	}
    
    	mTerrainGroup->freeTemporaryResources();
    }
    
    void CrisTerrain::getTerrainImage(bool flipX, bool flipY, Image& img)
    {
    	img.load(m_sInitData->sElevationFilename, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
    	if (flipX)
    		img.flipAroundY();
    	if (flipY)
    		img.flipAroundX();
    
    }
    
    void CrisTerrain::defineTerrain(long x, long y, bool flat)
    {
    	// if a file is available, use it
    	// if not, generate file from import
    
    	// Usually in a real project you'll know whether the compact terrain data is
    	// available or not; I'm doing it this way to save distribution size
    
    	if (flat)
    	{
    		mTerrainGroup->defineTerrain(x, y, 0.0f);
    	}
    	else
    	{
    		String filename = mTerrainGroup->generateFilename(x, y);
    		if (ResourceGroupManager::getSingleton().resourceExists(mTerrainGroup->getResourceGroup(), filename))
    		{
    			mTerrainGroup->defineTerrain(x, y);
    		}
    		else
    		{
    			Image img;
    			getTerrainImage(x % 2 != 0, y % 2 != 0, img);
    			mTerrainGroup->defineTerrain(x, y, &img);
    			mTerrainsImported = true;
    		}
    
    	}
    }
    
    void CrisTerrain::configureTerrainDefaults()
    {
    	// Configure global
    	mTerrainGlobals->setMaxPixelError(8);
    	// testing composite map
    	mTerrainGlobals->setCompositeMapDistance(3000);//距离镜头超过3000部分使用地图合成(CompositeMap)模式表现
    	//mTerrainGlobals->setUseRayBoxDistanceCalculation(true);
    	//mTerrainGlobals->getDefaultMaterialGenerator()->setDebugLevel(1);
    	//mTerrainGlobals->setLightMapSize(256);
    
    	// Configure default import settings for if we use imported image
    	Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
    	defaultimp.terrainSize = TERRAIN_SIZE;//不太了解,调试中,这个值越小,地图边缘锯齿现象越严重,太小的话,运行起来程序会跑死、出错
    	defaultimp.worldSize = m_sInitData->fMapSize;//假设为a,那么地图大小为 a x a
    	defaultimp.inputScale = m_sInitData->fInputScale;//决定地图最大落差(高度),即位图中白色和黑色部分的高度差
    	defaultimp.minBatchSize = 33;
    	defaultimp.maxBatchSize = 65;
    	// textures
    	defaultimp.layerList.resize(3);//这里设置了3层纹理,DDS为一种高级的纹理模式,DirectDrawSurface,觉得难以理解的话
    //可以理解为一种特殊的.jpg图片模式,但是用DDS质材的话可以接收并显示地形阴影,用JPG就显示不出来,而且据我调试观
    //察发现,第一个.dds质材是用来显示纹理图形,第二个.dds才是用来接收和显示阴影的
    
    	defaultimp.layerList[0].worldSize = m_sInitData->fMapSize;//这个值关系到此贴图的细致程度,太大的话图片被拉伸得很大,看起来模糊
    	defaultimp.layerList[0].textureNames.push_back(m_sInitData->sMapFilename);
    	defaultimp.layerList[0].textureNames.push_back(m_sInitData->sMapFilename);
    	defaultimp.layerList[1].worldSize = m_sInitData->fMapSize;
    	defaultimp.layerList[1].textureNames.push_back("white2.dds");
    	defaultimp.layerList[1].textureNames.push_back("white2.dds");
    	defaultimp.layerList[2].worldSize = m_sInitData->fMapSize;
    	defaultimp.layerList[2].textureNames.push_back("white2.dds");
    	defaultimp.layerList[2].textureNames.push_back("white2.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 = 100;
    	//defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_diffusespecular.dds");
    	//defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_normalheight.dds");
    }
    
    
    void CrisTerrain::initBlendMaps(Terrain* terrain)
    {
    	TerrainLayerBlendMap* blendMap0 = terrain->getLayerBlendMap(1);
    	TerrainLayerBlendMap* blendMap1 = terrain->getLayerBlendMap(2);
    	Real minHeight0 = 70;
    	Real fadeDist0 = 40;
    	Real minHeight1 = 70;
    	Real fadeDist1 = 15;
    	float* pBlend1 = blendMap1->getBlendPointer();
    	for (Ogre::uint16 y = 0; y < terrain->getLayerBlendMapSize(); ++y)
    	{
    		for (Ogre::uint16 x = 0; x < terrain->getLayerBlendMapSize(); ++x)
    		{
    			Real tx, ty;
    
    			blendMap0->convertImageToTerrainSpace(x, y, &tx, &ty);
    			Real height = terrain->getHeightAtTerrainPosition(tx, ty);
    			Real val = (height - minHeight0) / fadeDist0;
    			Math::Clamp(val, (Real)0, (Real)1);
    
    			val = (height - minHeight1) / fadeDist1;
    			val = Math::Clamp(val, (Real)0, (Real)1);
    			*pBlend1++ = val;
    
    
    		}
    	}
    	blendMap0->dirty();
    	blendMap1->dirty();
    	//blendMap0->loadImage("blendmap1.png", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
    	blendMap0->update();
    	blendMap1->update();
    
    	// set up a colour map
    	/*
    	if (!terrain->getGlobalColourMapEnabled())
    	{
    	terrain->setGlobalColourMapEnabled(true);
    	Image colourMap;
    	colourMap.load("testcolourmap.jpg", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
    	terrain->getGlobalColourMap()->loadImage(colourMap);
    	}
    	*/
    
    }
    

      

  • 相关阅读:
    asp.net后台获取html控件的值
    asp.net自定义错误页面
    关于asp.net网站中web.config的配置
    在asp.net中如何使用Session
    Ubuntu 14.10 进入单用户模式
    原码,反码和补码
    利用位运算进行权限管理
    php redis扩展安装
    不同浏览器Cookie大小
    include和require的区别
  • 原文地址:https://www.cnblogs.com/lyggqm/p/5507689.html
Copyright © 2020-2023  润新知