《天龙八部》地形研究-1

    技术2025-06-20  8

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

    最新回复(0)