您的位置:首页 > 其它

《天龙八部》地形研究

2011-06-13 20:29 309 查看
研究方法:我们通过场景加载入口函数EnterScene一步一步推导出《天龙八部》地形系统的具体结构。

1、一个地形场景所需的文件有下面这些:(以苏州为例)
suzhou.GridInfo
suzhou.Heightmap
suzhou.lightmap.png
suzhou.nav (服务端用的)
suzhou.region (这个是不可行走区域信息)
suzhou.Scene (场景的模型都在这里)
suzhou.Terrain (地形基本信息,大小,纹理,纹理层次等)
suzhou.WCollosion (地形上的建筑物行走面,通常是地形上的桥面信息等)

天龙的地形也是分title渲染的,比如苏州的大小是320x320(实际是这么多的方格,比例是100),title大小是32,那么这个地形就分为10*10个地形mesh,
所以title也是个继承自Ogre::Renderable的可渲染对象,
Ogre中由void queueRenderables(Ogre::RenderQueue* queue, const RenderableList& renderables);
总共100个,剪裁方式还是由摄像机负责的,由 OctreeSceneManager 管理(见下面的代码),由OctreeSceneManager::_findVisibleObjects在每帧里将可见的title加载到地形渲染队列的(可以参考Ogre渲染队列的文章)。







2、场景加载入口点。
每个场景加载入口由
BOOL CWorldManager::EnterScene(INT nSceneID, INT nCityLevel)
函数管理,当然这里的每个场景都是一个服务场景,即和服务器端的一一对应。
如果你要使用不和服务器有联系的场景,就得使用 EnterScene 里的相关场景加载自己加载场景了,比如一个选人台的场景。

3.场景加载的步骤。

//创建新的场景
m_pActiveScene = new CScene(pSceneDef, bUserCity);

//加载新的场景, 加载静态物体定义
m_pActiveScene->Initial();

//进入场景
m_pActiveScene->EnterScene();

可见场景类对应CScene类,下面我们来分析CScene类的加载过程。

4. CScene类

CScene由 ZONE 组成,
CZone保存的数据
/*
|
| o 将整个游戏场景分割成固定大小的网格,每个网格单位为一个Zone,
| Zone的大小跟一个屏幕的大小类似
|
| o 继承于MapObject的物体都会注册到自己所在的Zone,每个Zone都有
| 一个链表,保存注册到该网格的物体
|
| o 在Zone中注册的数据还有"不可行走区域" Region,这样从
| Zone可以快速找到附近的Region
|
|
*/

5、地形数据的加载

【5.1】
先加载场景文件的
再加载地形数据的加载 .Terrail

【5.2】
void
System::loadSceneFromResource(XMLParser* parser, const String& name, const String& groupName)
{
_preprocessScene();

mSceneInfo->load(parser, name, groupName, isEditable());

_postprocessScene(); //这里加载地形的数据
}

//加载场景 Scene文件,把.Scene文件里的对象保存到 mObjects 列表里。
void SceneInfo::load(XMLParser* parser, const String& filename, const String& groupName, bool completely)
{
SceneSerializer serializer;
serializer.load(this, parser, filename, groupName); //这一步主要把.Scene文件里的对象保存到 mObjects 列表里。

if (!mTerrainFilename.empty())
{
getTerrainData()->load(parser, mTerrainFilename, groupName, completely);
}
}

地形的加载函数
void
TerrainData::load(XMLParser* parser, constString& filename, constString& groupName, boolloadLightmap)
{
// Clear old data
reset();

mResourceGroupName= groupName;

// Parse the terrain file
_loadBaseInfo(parser, filename, groupName);

_fixupMissingInfo();
_fixupSupportedMaterials();

// Setup derived fields
_updateDerivedFields();

// Validate the terrain file ,验证地形的纹理和图片(每个网格对应的纹理)
size_t numTextures= mTextures.size();
for(PixmapArray::const_iterator it= mPixmaps.begin(); it!= mPixmaps.end(); ++it)
{
constPixmap& pixmap= *it;
if(pixmap.textureId>= numTextures)
{
OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS,
"Invalid pixmap textureId in terrain '"+ filename+ "'.",
"TerrainData::load");
}
}

// Load heightmap
_loadHeightmap(mHeightmapFilename, mHeightmapType, groupName);

// Load grid info
_loadGridInfo(mGridInfoFilename, groupName);

if(loadLightmap)
{
// Load lightmap
_loadLightmap(mLightmapFilename, mLightmapType, groupName);
}
}

System::_postprocessScene(void)
{
mBaseScale = Ogre::Math::Sqrt(getTerrainData()->mScale.x * getTerrainData()->mScale.z);

// Adjust the camera to fit to current scene
_adjustCamera();

clearExceptionInfo();

//bakeStaticGeometries(0);

// 生?成?terrain type info
mTerrainTypeInfos->setTerrainData(mTerrainData);
mTerrainTypeInfos->updateTerrainTypeInfos();//*****

// Create render instances
mSceneInfo->initialise(this);//*****

// 告?诉?特?效?系?统?当?前?场?景?的?灯?光?信?息?
EffectManager::getSingleton().getMainSceneLight()->updateLightInfo();
}这个开始创建网格mesh,//-----------------------------------------------------------------------
void SceneInfo::initialise(WX::System* system)
{
assert(system);
system->getSceneManager()->setAmbientLight(Ogre::ColourValue::Black);
// *****
getTerrain()->buildGeometry(system->getBaseSceneNode(), system->isEditable());

if (system->getDisableIncrementalBuildScene() || system->isEditable())
{
if (mIncrementalSceneBuilder)
{
mIncrementalSceneBuilder->reset();
}

for (Objects::const_iterator it = mObjects.begin(); it != mObjects.end(); ++it)
{
try
{
const ObjectPtr& object = *it;

if (object->hasProperty("create level"))
{
if ( false == system->_determineCreateLevel(
VariantCast<Real>( object->getProperty("create level") ) ) )
continue;
}

object->createRenderInstance(system);
}
catch ( Ogre::Exception &e )
{
system->addExceptionInfo( e.getDescription(), System::ExceptionInfo( (*it)->getName() ) );
}
}
}
else
{
if (!mIncrementalSceneBuilder)
{
mIncrementalSceneBuilder = new IncrementalSceneBuilder(system);
}

mIncrementalSceneBuilder->reset();

for (Objects::const_iterator it = mObjects.begin(); it != mObjects.end(); ++it)
{
const ObjectPtr& object = *it;
if (!mIncrementalSceneBuilder->addObject(object))
{
object->createRenderInstance(system);
}
}
}
}
下面是调用到createGeometry时的堆栈,

WXRender.dll!WX::TerrainTileOptimized::createGeometry(WX::TerrainData * data=0x02525388, int xbase=96, int zbase=96, int xsize=32, int zsize=32) 行73 C++
WXRender.dll!WX::TerrainTileOptimized::_updateRenderQueue(Ogre::RenderQueue * queue=0x02412910) 行57 C++
Main.dll!Ogre::OctreeNode::_addToRenderQueue(Ogre::Camera * cam=0x024133e0, Ogre::RenderQueue * queue=0x02412910, bool onlyShadowCasters=false) 行162 C++
Main.dll!Ogre::OctreeSceneManager::walkOctree(Ogre::OctreeCamera * camera=0x024133e0, Ogre::RenderQueue * queue=0x02412910, Ogre::Octree * octant=0x0b1a83d0, bool foundvisible=false, bool onlyShadowCasters=false) 行658 C++
Main.dll!Ogre::OctreeSceneManager::walkOctree(Ogre::OctreeCamera * camera=0x024133e0, Ogre::RenderQueue * queue=0x02412910, Ogre::Octree * octant=0x0b1a8300, bool foundvisible=false, bool onlyShadowCasters=false) 行680 C++
Main.dll!Ogre::OctreeSceneManager::walkOctree(Ogre::OctreeCamera * camera=0x024133e0, Ogre::RenderQueue * queue=0x02412910, Ogre::Octree * octant=0x0b229030, bool foundvisible=false, bool onlyShadowCasters=false) 行697 C++
Main.dll!Ogre::OctreeSceneManager::walkOctree(Ogre::OctreeCamera * camera=0x024133e0, Ogre::RenderQueue * queue=0x02412910, Ogre::Octree * octant=0x0b228f60, bool foundvisible=false, bool onlyShadowCasters=false) 行697 C++
Main.dll!Ogre::OctreeSceneManager::walkOctree(Ogre::OctreeCamera * camera=0x024133e0, Ogre::RenderQueue * queue=0x02412910, Ogre::Octree * octant=0x0a8b8950, bool foundvisible=false, bool onlyShadowCasters=false) 行674 C++
Main.dll!Ogre::OctreeSceneManager::_findVisibleObjects(Ogre::Camera * cam=0x024133e0, bool onlyShadowCasters=false) 行571 C++
Main.dll!Ogre::SceneManager::_renderScene(Ogre::Camera * camera=0x024133e0, Ogre::Viewport * vp=0x02413b58, bool includeOverlays=true) 行1129 C++
Main.dll!Ogre::Camera::_renderScene(Ogre::Viewport * vp=0x02413b58, bool includeOverlays=true) 行394 C++
Main.dll!Ogre::Viewport::update() 行192 C++
Main.dll!Ogre::RenderTarget::update() 行108 C++
Main.dll!Ogre::RenderWindow::update(bool swap=true) 行72 C++
Main.dll!Ogre::D3D9RenderWindow::update(bool swap=true) 行978 C++
Main.dll!Ogre::RenderWindow::update() 行64 C++
Main.dll!Ogre::RenderSystem::_updateAllRenderTargets() 行102 C++
Main.dll!Ogre::Root::_updateAllRenderTargets() 行1101 C++
Main.dll!Ogre::Root::renderOneFrame() 行761 C++
WXRender.dll!CRenderSystem::RenderFrame() 行656 C++

void
TerrainTileOptimized::createGeometry(TerrainData* data, int xbase, int zbase, int xsize, int zsize)
{
destoryGeometry(); 这里对材质地形的材质进行了分类,主要是分为一层的纹理的和两层的纹理的两组,因为此地形最多支持两层纹理+一层lightmap。
// build the material backet map
MaterialBucketMap materialBucketMap;
buildMaterialBucketMap(materialBucketMap);

// statistic number grids for each layer
size_t numGridsOfLayer[2] = { 0 };//记录两层和一层的个数
for (MaterialBucketMap::const_iterator im = materialBucketMap.begin(); im != materialBucketMap.end(); ++im)
{
numGridsOfLayer[im->second.layerIndex] += im->second.grids.size();
}

bool includeLightmap = mOwner->_isLightmapUsed(); 所以定点声明也分为两种 ,即一层纹理和两层纹理的两种。
// create vertex buffer and lock it
Ogre::VertexData vertexDatas[2];
Ogre::HardwareVertexBufferSharedPtr buffers[2];
float* pBuffers[2] = { NULL };
for (size_t layerIndex = 0; layerIndex < 2; ++layerIndex)
{
if (!numGridsOfLayer[layerIndex])
continue;

enum
{
MAIN_BINDING,
};

Ogre::VertexDeclaration* decl = vertexDatas[layerIndex].vertexDeclaration;
Ogre::VertexBufferBinding* bind = vertexDatas[layerIndex].vertexBufferBinding;
vertexDatas[layerIndex].vertexStart = 0;
vertexDatas[layerIndex].vertexCount = numGridsOfLayer[layerIndex] * 4;

size_t offset = 0;
size_t texCoordSet = 0;
// positions
decl->addElement(MAIN_BINDING, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += 3 * sizeof(float);
// normals
decl->addElement(MAIN_BINDING, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
offset += 3 * sizeof(float);
// texture layer 0
decl->addElement(MAIN_BINDING, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, texCoordSet++);
offset += 2 * sizeof(float);
// texture layer 1
if (layerIndex == 1)
{
decl->addElement(MAIN_BINDING, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, texCoordSet++);
offset += 2 * sizeof(float);
}
// light-map layer
if (includeLightmap)
{
decl->addElement(MAIN_BINDING, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, texCoordSet++);
offset += 2 * sizeof(float);
}

buffers[layerIndex] =
Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(
decl->getVertexSize(MAIN_BINDING),
vertexDatas[layerIndex].vertexCount,
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
bind->setBinding(MAIN_BINDING, buffers[layerIndex]);

pBuffers[layerIndex] = static_cast<float*>(buffers[layerIndex]->lock(Ogre::HardwareBuffer::HBL_DISCARD));
}

Real xscale = 1.0 / xsize;
Real zscale = 1.0 / zsize;

// build renderables, group by material
size_t vertexStarts[2] = { 0 };
for (MaterialBucketMap::const_iterator im = materialBucketMap.begin(); im != materialBucketMap.end(); ++im)
{
TerrainTileOptimizedRenderable* renderable = new TerrainTileOptimizedRenderable(this);
mRenderables.push_back(renderable);

const MaterialBucket* mb = &im->second;
size_t layerIndex = mb->layerIndex;
size_t numQuads = mb->grids.size();
size_t vertexCount = numQuads * 4;

renderable->mMaterial = mb->material;

// Clone vertex data but shared vertex buffers
Ogre::VertexData* vertexData = vertexDatas[layerIndex].clone(false);
vertexData->vertexStart = vertexStarts[layerIndex];
vertexData->vertexCount = vertexCount;

renderable->mRenderOp.vertexData = vertexData;
renderable->mRenderOp.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;
renderable->mRenderOp.useIndexes = true;
renderable->mRenderOp.indexData = mOwner->_getIndexData(numQuads);

float* pFloat = pBuffers[layerIndex];
for (GridIdList::const_iterator igrid = mb->grids.begin(); igrid != mb->grids.end(); ++igrid)
{
size_t grid = *igrid;
const TerrainData::GridInfo& gridInfo = data->mGridInfos[grid];
const TerrainData::Corner* corners = gridInfo.getCorners();
int x = grid % data->mXSize;
int z = grid / data->mXSize;

// NB: Store the quad vertices in clockwise order, index data will
// take care with this.
for (size_t i = 0; i < 4; ++i)
{
Ogre::Vector3 v;
std::pair<Real, Real> t;
TerrainData::Corner corner = corners[i];
// position
v = data->_getPosition((x+(corner&1)), (z+(corner>>1)));
*pFloat++ = v.x; *pFloat++ = v.y; *pFloat++ = v.z;
// normal
v = data->_getNormal((x+(corner&1)), (z+(corner>>1)));
*pFloat++ = v.x; *pFloat++ = v.y; *pFloat++ = v.z;
// layer 0
t = mOwner->_getPixmapCorner(gridInfo.layers[0], corner, gridInfo.flags);
*pFloat++ = t.first; *pFloat++ = t.second;
// layer 1
if (gridInfo.layers[1].pixmapId)
{
t = mOwner->_getPixmapCorner(gridInfo.layers[1], corner, gridInfo.flags);
*pFloat++ = t.first; *pFloat++ = t.second;
}
// light-map
if (includeLightmap)
{
*pFloat++ = xscale * (x - xbase + (corner&1));
*pFloat++ = zscale * (z - zbase + (corner>>1));
}
}
}
pBuffers[layerIndex] = pFloat;
vertexStarts[layerIndex] += vertexCount;
}

// unlock vertex buffer
for (size_t layerIndex = 0; layerIndex < 2; ++layerIndex)
{
if (!buffers[layerIndex].isNull())
buffers[layerIndex]->unlock();
}

mGeometryOutOfDate = false;
}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/butwang/archive/2011/02/13/6182899.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: