现在的位置: 首页 > 综合 > 正文

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

2013年11月04日 ⁄ 综合 ⁄ 共 11147字 ⁄ 字号 评论关闭

研究方法:我们通过场景加载入口函数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渲染队列的文章)。

classOgre_1_1Renderable__inherit__graph

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;
}

抱歉!评论已关闭.