研究方法:我们通过场景加载入口函数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; }