《天龙八部》地形研究

研究方法:我们通过场景加载入口函数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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值