朝晖's profileZZH: 这是一个真实的故事PhotosBlogListsMore Tools Help

ZZH: 这是一个真实的故事

Software Developer And Value Investor
11/25/2009

OGRE之000:Paging Scene Manager

Ogre的场景管理体系:

Octree Scene Manager

> Terrain Scene Manager (Small Terrain)

>Paging Scene Manager (Big Terrain)

Portal Connected Zone Scene Manager  (提取source并且编译: https://ogreaddons.svn.sourceforge.net/svnroot/ogreaddons/trunk/paginglandscape/ )

 Paging Scene Manager

PagingLandScapeSceneManager

PagingLandScapeSceneManager实现了场景管理。组织PagingLandScapeRenderables成一个完整的地形。它调用setWorldGeometry从一个.cfg中获取地形信息,并创建地形。

PagingLandScapeTileManager

PagingLandScapeTileManager实现了对所有PagingLandScapeTile的管理。最多可以管理256个PagingLandScapeTile。PagingLandScapeTileManager会把所有生成的PagingLandScapeTile的指针保存到一个PagingLandScapeQueue< PagingLandScapeTile > mQueue中。

PagingLandScapeTile

PagingLandScapeTile表示一个地形Tile,是地形的最小单元。

PagingLandScapeListenerManager

PagingLandScapeListenerManager根据事件的类型,通过维护多种事件侦听器的列表,实现了对场景管理的多种类型的事件侦听,实现了Observer模式。这些类型包括:

  1. 地形就绪:mTerrainReadyListeners - 触发函数为fireTerrainReady
  2. 地形页方面:
    1. 地形页显示:mShowPageListeners - 触发函数为firePageShow
    2. 地形页隐藏:mHidePageListeners- 触发函数为firePageHide
    3. 地形加载前:mPreloadPageListeners- 触发函数为firePagePreloaded
    4. 地形页加载:mLoadPageListeners- 触发函数为firePageLoaded
    5. 地形页卸载:mUnloadPageListeners- 触发函数为firePageUnloaded
    6. 地形页卸载后:mPostunloadPageListeners- 触发函数为firePagePostunloaded
    7. 地形页修改:mModifyPageListeners- 触发函数为firePagePreloaded
  3. 地形Tile方面:
    1. 地形Tile显示:mShowTileListeners- 触发函数为fireTileShow
    2. 地形Tile隐藏:mHideTileListeners- 触发函数为fireTileHide
    3. 地形Tile加载:mLoadTileListeners- 触发函数为fireTileLoaded
    4. 地形Tile卸载:mUnloadTileListeners- 触发函数为fireTileUnloaded
    5. 地形Tile修改:mModifyTileListeners- 触发函数为fireTileDeformed

PagingLandScapeData2DManager

PLSM通过提供Data Source Loading Modes 支持多种数据源,这些都派生自Ogre::PagingLandScapeData2D,这些数据源都通过Ogre::PagingLandScapeData2DManager来管理。Ogre::PagingLandScapeData2DManager采用类似原型化得Abstract Factory模式来管理这些数据源。这些数据源包括:

  1. Ogre::PagingLandScapeData2D_HeightField : Loads data from a 8bits grey image.
  2. Ogre::PagingLandScapeData2D_HeightFieldN : Loads data from a 8bits grey image, and Normals from 32bits image
  3. Ogre::PagingLandScapeData2D_HeightFieldRaw : Loads data from 16bits rawimage
  4. Ogre::PagingLandScapeData2D_HeightFieldRawTC : Loads data from 16bits rawimage and expands heights using TC algo.
  5. Ogre::PagingLandScapeData2D_HeightFieldTC : Loads data from 8bits grey image and expands heights using TC algo.
  6. Ogre::PagingLandScapeData2D_HeightFieldNTC : Loads data from 8bits grey image and expands heights using TC algo, and Normals from 32bits image
  7. Ogre::PagingLandScapeData2D_Spline : create on the fly spline terrain
  8. Ogre::PagingLandScapeData2D_HeightFieldBlendNeighbor: Loads data from a 8bits grey image, and blend it with already loaded neighbors. (dynamically created pages) intensively tested and supported.

PagingLandScapeTextureManager

PagingLandScapeTextureManager支持多种Texture Format,Ogre::PagingLandScapeTextureManager采用类似原型化的Abstract Factory模式来管理这些Texture Format或者说Texture Type。

 

PagingLandScapeIndexBufferManager

PagingLandScapeIndexBufferManager是Index Buffer管理器。

 

PagingLandScapeTextureCoordinatesManager

PagingLandScapeTextureCoordinatesManager是texture coordinate管理器。

 

PagingLandScapeRenderable

PagingLandScapeRenderable是地形渲染单元。

 

PagingLandScapeRenderableManager

PagingLandScapeRenderableManager是渲染管理器。它负责管理所有的PagingLandScapeRenderables,采用PagingLandScapeRenderableSet组织PagingLandScapeRenderables。

 

PagingLandScapeRenderables

PagingLandScapeRenderables (Pool and Loading Queue)

 

PagingLandScapeRenderableSet

PagingLandScapeRenderableSet实现了所有预分配的PagingLandScapeRenderable的管理。

 

Ogre::PagingLandScapePageManager是page管理器

Ogre::PagingLandScapeTile是地形单元,Ogre::PagingLandScapeRenderable是渲染单元。这些渲染单元都通过Ogre::PagingLandScapeRenderableManager进行管理。PagingLandScapePageManager实现了类似操作系统中的调页机制,有空闲页和活动页概念。PagingLandScapePageManager还对frame event进行了侦听。

 

PagingLandScapeOptions

PagingLandScapeOptions对PagingLandscape2.cfg进行了解析,得到Map List。并调用loadMap加载map。对map的加载是解析map对应的.cfg配置文件,如g_canyon_height_4k2k.cfg。

 

渲染:

PagingLandScapeSceneManager::_updateSceneGraph

> mPageManager->updatePaging

>> PagingLandScapePageManager::getPage

> OctreeSceneManager::_updateSceneGraph(cam);

一个地形Page对应一个Scene Node,命名规范为PagingLandScapePage.PageX.PageZ.Node

一个地形Tile对应一个Scene Node,命名规范为PagingLandScapeTile.PageX.PageZ.TileX.TileZ.Node

 

PagingLandScapeRenderable::load

PagingLandScapeTile::load

PagingLandScapeRenderable PagingLandScapeRenderableManager
PagingLandScapeTile PagingLandScapeTileManager
PagingLandScapePage PagingLandScapePageManager
PagingLandScapeTexture PagingLandScapeTextureManager
  PagingLandScapeTextureCoordinatesManager
   
   

 

参考:

  1. evaluated the choices
  2. Paging Scene Manager Option
  3. Ogre的分页大地形场景管理器PLSM2使用手记
  4. Charles Bloom on Texture Splatting
  5. Texture Splatting
  6. Member Function Pointers and the Fastest Possible C++ Delegates
11/10/2009

ZZH:魔兽世界之003:MaNGOS

The Massive Network Game Object Server is a server core project that mainly focuses on general MMORPG development. The project uses WoW as a reference game.

运行Mangos

  1. Compile MaNGOS On Windows
    1. Download (MaNGOS 0.14 release)
    2. Compiling MaNGOS using Visual C++ 2008 (.\win\mangosdVC90.sln)
  2. 按照MaNGOS的MySQL推荐进行安装mysql-5.1.40-win32.msi (配置时候打开端口3306)
  3. Setting up MaNGOS
    1. Binaries and Configuration (There are two .conf files, in src/mangosd/mangosd.conf.dist.in and src/realmd/realmd.conf.dist.in. When you copy this to your MaNGOS folder you need to get rid of the .dist.in on the end of both files.)
    2. Maps, VMAPs and DBCs,采用MaNGOS的工具ad从wow的MPQ中抽取map,得到的所有的map数据文件,文件命名规范为map_id(3位) tileY(2位) tileX(2位).map,如文件名为0002035.map,代表的是Azeroth(地图id为000,tile坐标为(35,20). 注:WOW客户端的Tile对应mangos中的grid,WOW客户端的Chunk对应mangos中的cell。
  4. 安装wow (网易的wow 3.1.3 10146版本)
  5. 修改AuthCodes.h #define EXPECTED_MANGOS_CLIENT_BUILD        {10146,9947, 0}
  6. 按照正常流程把服务器跑起来,使用account create zzh1234567 zzh1234567 创建一个账号
  7. 配置好客户端后,运行WOW,顺利登陆,呵呵

image

初探Mangos代码

Mangos的工程有:

  1. The ADAPTIVE Communication Environment (ACE):作为网络通讯层
  2. zlib:作为压缩库
  3. C++ Sockets Library:完成常规的网络通信任务
  4. g3dlite:游戏逻辑层的底层库
  5. mangosd:游戏逻辑层的核心层 (采用了ACE反应器(Reactor)模式
    1. WorldSocketMgr管理所有的连接。WorldSocketMgr的WorldSocketMgr::StartNetwork对8085(缺省)端口进行侦听。
    2. 类Master实现了mangos server
    3. 类World实现了wow的World
      1. 成员函数SetInitialWorldSettings调用成员函数LoadConfigSettings解析mangosd.conf,解析后内容放入uint32 m_configs[CONFIG_VALUE_COUNT]中
    4. Master在完成了World的初始化后,创建一个单独最高优先级的线程运行World。
    5. Master在跑起了World线程后,启动一个Command Line Interface handling thread处理Command Line ;启动一个RA(Remote Administration)线程。
    6. WorldDatabase:Accessor to the world database
    7. CharacterDatabase:Accessor to the character database
    8. loginDatabase:Accessor to the realm/login database
  6. framework:系统框架
  7. game:wow服务器代码
    1. 类MapManager 
    2. 类Map实现了一个state machine,采用state pattern组织了Gid的4个state object:InvalidState;ActiveState;IdleState;RemovalState。
    3. WorldSocket
    4. World::UpdateSessions会调用所有WorldSession的WorldSession::Update。在WorldSession::Update中
      1. 执行语句OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];得到opHandle 
      2. 根据得到的opHandle,执行(this->*opHandle.handler)(*packet);
    5. void WorldSession::SendPacket(WorldPacket const* packet) 负责发包给客户端。
    6. WorldSession::HandlePlayerLogin处理玩家登陆游戏。
      1. 构建Player
      2. 调用Player::LoadFromDB从数据库中加载玩家数据。在Player::LoadFromDB中会调用SetMap(MapManager::Instance().CreateMap(GetMapId(), this));加载当前player所在的map
    7. Player::SetPosition在Player运动的时候,改变位置,保存处理夸区。
  8. realm:负责登陆和选择游戏服务器,进行负载均衡。用到了C++ Sockets Library进行登录处理,采用select I/O模型。实现了Wow, Mangos登录时的SRP6认证。客户端作为它的client连接到realm server认证和选择了mangos server就断开。 而mangos server和realm server则不进行连接,只是通过数据库交互数据:mangos server把自己的状态和拥有的角色数放入库中。realm server会读取数据库中的这些信息来获知mangos server的状态。
    1. 数据库realm的realmlist表保存了realm的列表
    2. realm通过如下事件处理函数来负责登陆和选择游戏服务器。

      const AuthHandler table[] =
      {
          { AUTH_LOGON_CHALLENGE,     STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge    },
          { AUTH_LOGON_PROOF,         STATUS_CONNECTED, &AuthSocket::_HandleLogonProof        },
          { AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleReconnectChallenge},
          { AUTH_RECONNECT_PROOF,     STATUS_CONNECTED, &AuthSocket::_HandleReconnectProof    },
          { REALM_LIST,               STATUS_AUTHED,    &AuthSocket::_HandleRealmList         },
          { XFER_ACCEPT,              STATUS_CONNECTED, &AuthSocket::_HandleXferAccept        },
          { XFER_RESUME,              STATUS_CONNECTED, &AuthSocket::_HandleXferResume        },
          { XFER_CANCEL,              STATUS_CONNECTED, &AuthSocket::_HandleXferCancel        }
      };

  9. share:提供了通用库,包括了数据库的封装类,实现了对MySql的访问,同样,我们可以编写派生类来支持其他的数据库。

登录过程:

user登录到realm server进行身份认证,并选择登录上哪个mangos server。user登录到mangos server后,将不再和realm server交互。

构想:

Login Server + Map Server +  World Server

参考:

  1. MaNGOS Windows Setup for WoW 2.3.3 (Chinese Edition)
  2. TBB http://www.threadingbuildingblocks.org/
  3. OReilly.Intel.Threading.Building.Blocks.Jul.2007
  4. Improved Error Reporting with DBGHELP 5.1 APIs - (中文翻译)使用dbghelp5.1 改善你的错误报告 
  5. http://forum.byr.edu.cn/wForum/boardcon.php?bid=85&id=27566&ftype=3
  6. Wow, Mangos登录时的SRP6认证
  7. 数据接收中粘包及半包的处理解决TCP网络传输“粘包”问题
  8. Wow 服务器解析
  9. SRP Protocol Design
  10. 魔兽世界服务器端编写参考资料
  11. 源码 mangos/src/realmd/AuthSocket.cpp
10/20/2009

ZZH:魔兽世界之002:如何显示Sky、低精度地形、Chunk、Tile、WMO和World

WoWmapview的World类

WoWmapview的World类处了负责显示和生成minimap外,主要的功能还是生成和显示map(top level map)。

如何显示Sky

WoWmapview中的World::initDisplay会初始化Skies类,Skies会从World\\Maps\\%s(如Azeroth)\\lights.lit中提取信息。LIT files have stored lighting-information until some patch. Today, lightning is stored in the following .DBCs:

  1. Light.dbc
  2. LightFloatBand.dbc
  3. LightIntBand.dbc
  4. LightParams.dbc
  5. LightSkybox.dbc

WoWmapview中的代码还是使用废弃的LIT。

如何显示低精度地形

通过对WoWmapview中的World::initLowresTerrain的代码分析,得到如下内容:

  1. WDL files contain a low-resolution heightmap for a world。解析WDL,其中的WDL包括了MAOF chunk 和 MapAreaLow array,通过解析这些数据得到1map = 64x64maptile,1maptile = 17x17+16x16 heightmap点(16bit)。如:World\Maps\Azeroth\Azeroth.wdl包括了top level map:Azeroth的64x64个tile的所有的heightmap数据。当然,Azeroth有些tile是无效的,假如所有tile都有效的情况下,那么WDL的heightmap数据就有:64x64x(17x17+16x16)*2个字节,那么WDL的heightmap就有((64 x 64 x ((17 x 17) + (16 x 16)) * 2) / 1024) / 1024 = 4.2578125M字节,也就是最大低精度地形数据是4M多。
  2. WoWmapview中对WDL的处理是对每一个tile都建立一个OpenGL的显示列表,这样,就有64x64个显示列表(当然,无效的tile就没有显示列表了),在要显示某个tile的时候,执行该tile对应的显示列表就可以了。

如何显示Tile

通过对WoWmapview中的World::enterTile和World::loadTile的代码分析,得到如下内容:

  1. 活动的是3x3 maptiles(1600x1600),实现了Cache机制,Cache中有16个tile slot,最多保存16个tile的数据。
  2. World::loadTile首先是从Cache中取tile,如果命中(表示需要加载的tile已经在Cache的tile slot中),那么返回,否则构建MapTile去加载指定(xTile,yTile)的Tile,每个tile都对应一个ADT文件。

    如World::loadTile(33, 40)会构建MapTile(33,40,"World\\Maps\\Azeroth\\Azeroth_33_40.adt”)。
  3. A map tile is split up into 16x16 = 256 map chunks. (not the same as file chunks, although each map chunk will have its own file chunk :) ) So there will be a few initial data chunks to specify textures, objects, models, etc. followed by 256 MCNK (mapchunk) chunks :) Each MCNK chunk has a small header of its own, and additional chunks within its data block, following the same id-size-data format.
  4. 一个maptile的size是(1600/3=533.33m)。
  5. MapTile类负责解析ADT中的Tile数据。1个tile 有16x16 map chunks,

    所以ADT中包含16x16=256个chunk数据。一个map chunk的size是(533.33/16=33.33m)。 ADT中的MCIN保存了这256个chunk的索引,真正的chunk数据在MCNK中。每个tile都包含了该tile内使用的
    1. BLP贴图(引用外部)(ADT中的MTEX)(贴图size为256x256)
    2. M2模型(引用外部)(ADT中的MMDX)
    3. WMO(world map objects)(ADT中的MWMO),保存在ADT的MWMO中。 List of filenames for WMOs (world map objects) that appear in this map tile. A contiguous block of zero-terminated strings.
    4. M2模型实例(ADT中的MDDF),所谓M2模型实例就是相同模型在tile内不同摆放位置、大小、角度的说明信息,在wow引擎中的术语是doodad,翻译为小品,即可以随意摆放的小东西,
    5. WMO实例(ADT中的MODF),wmo实例的概念和M2模型实例的概念类似。为了节省文件尺寸,模式实例、wmo实例是通过index模型、wmo的方式保存的,同顶点索引类似。
    6. 256个MCNK。每个chunk都有ADT中的一个MCNK描述。
  6. 下面是Chunk Vertex的分布图
    image(Highres Chunk) image(Lowres Chunk)
    image(Vertex) 
  7. MapChunk原点的计算是:The MCNK header中的/*0x068*/ Vec3f position的X,Z 指定了chunk的左上角坐标,但这个坐标系的原点(0,0)是在World的中央,所以经过如下调整
    1. zbase = zbase*-1.0f + ZEROPOINT;  // #define ZEROPOINT (32.0f * (TILESIZE)) 整个World是64x64tiles
    2. xbase = xbase*-1.0f + ZEROPOINT; // #define ZEROPOINT (32.0f * (TILESIZE))
  8. MapChunk的hole。The MCNK header中的/*0x03C*/ UINT32 holes;holes是32bits的,低16bits作为标示,标示意义如下:
    1. 0x1 MCSH chunk available (控制chunk的shadow map是否开启)
    2. 0x2 Impassable?
    3. 0x4 River
    4. 0x8 Ocean
    5. 0x10 Magma (岩浆)
    6. 0x20 Slime? (泥)
    7. 0x40 MCCV chunk available
    8. 0x8000 Unknown, but heavily used in TBC.
  9. MapChunk中的点的坐标计算:知道了MapChunk原点后,根据MCVT就可以得到每个点的(X,Y,Z)世界坐标。
  10. WotLK中MH2O代替了MCLQ用于水体绘制,所以在WotLK后版本,不能根据MCLQ去渲染水。
  11. WoWmapview中的Tile按照quadtree来组织的。MapNode::setup组织quadtree,quadtree的叶子节点就是一个chunk。MapNode::draw显示quadtree。每个chunk采用2级Index buffer的静态LOD,一个高精度的Index buffer和一个低精度的Index buffer。这种方法称为Interlocking方法,可以参考“Simplified Terrain Using Interlocking Tiles”,在游戏编程精髓,第2册。
  12. WoWmapview通过ModelManager管理M2模型,通过WMOManager管理WMO,通过MapTile类中的std::vector<ModelInstance> modelis管理M2模型实例,通过MapTile类中的std::vector<WMOInstance> wmois管理WMO实例。

如何显示Chunk(Chunk是WOW中的渲染单元)

1个tile 有16x16 map chunks,Each map chunk has 9x9 (2的3次方+1)vertices, and in between them 8x8 (2的3次方)additional vertices, several texture layers, normal vectors, a shadow map, etc.

WoWmapview使用类MapChunk中的MapChunk::init来解析MCNK

每个chunk由一下要素构成:

  1. 9x9+8x8个地形顶点高度和法线
  2. 1到4个texture layer(由MCLY描述),这些texture layer(base texture)有2种类型,一种该层texture单纯是diffuse map,这个时候地形混合需要alpha map;另外一种类型texture是diffuse map+specular map,这个时候不需要使用alpha map,wow通过文件名后缀加_s表示带有specular map, 如某个地形贴图PlaguedEarthcreep01.blp带有specular map的文件对应就是PlaguedEarthcreep01_s.map。image PlaguedEarthcreep01.blp。WOW的地形贴图采用texture splatting技术,base texture重复(X方向上重复8次,Y方向也重复8次)拼缀覆盖整个chunk。
  3. holes
  4. 水面
  5. Alpha map(除去第一个texture layer,每个texture layer都有alpha map,alpha map指定用于控制地表贴图的混合比例,尺寸64x64,由MCAL描述) ,整个64x64的alpha map要覆盖整个拥有9x9+8x8个地形顶点的Chunk。
  6. 一层shadow map(由MCSH描述,Shadow尺寸64x64,是按bit(0或1)而非byte保存,所以需要8x64个字节表示64x64的shadow)组成。整个64x64的shadow map要覆盖整个拥有9x9+8x8个地形顶点的Chunk。

采用PS来渲染地形正好可以是1个pass解决。把3个alpha map和一个shadow map可以组成一个texture。

研究MapChunk::draw的流程:

  1. 对chunk进行frustum剔除,frustum外的chunk不会进行绘制。(注:frustum在每次World::draw的时候都会更新)
  2. 如太远则调用MapChunk::drawNoDetail显示lowres chunk,就完成了绘制。WoWmapview是在Chunk中按距离实现地形的静态LOD,通过静态修改Index Buffer的方法,提供2级Index Buffer(lowres 和 highres)来实现静态LOD。
    1. 每个Chunk的vertex数目是9x9+8x8=145,而Index Buffer却根据如下情况不同:
      1. 在Chunk没包含hole的情况下,lowres 的Chunk地形的Vertex Index是有8*18 + 7*2=158个Index,highres的Chunk地形的Vertex Index是16*18 + 7*2 + 8*2=318。
      2. 在Chunk包含hole的情况下(参考MapChunk::initStrip),Index buffer就只是包含非hole的vertex index。
    2. 距离的计算是相机的世界坐标到Chunk中心点的距离然后减去chunk的边界球的半径,代码参考如下
      1. float mydist = (gWorld->camera - vcenter).length() - r; 
      2. r = (vmax - vmin).length() * 0.5f; (MapChunk::init中)
  3. 如不太远则继续。
    1. 对于固定流水线的渲染,采用mutiple pass渲染。
      1. 调用drawPass渲染第一层texture layer(1 pass)。
      2. 采用Alpha map混合其他的texture layer(1..3pass)。
      3. 最后贴上shadow map(1pass)。

 

image

 

void MapChunk::draw()
{
    if (!gWorld->frustum.intersects(vmin,vmax))
        return;
    float mydist = (gWorld->camera - vcenter).length() - r;
    //if (mydist > gWorld->mapdrawdistance2) return;
    if (mydist > gWorld->culldistance)
    {
        if (gWorld->uselowlod)
            this->drawNoDetail();
        return;
    }
    visible = true;

    if (nTextures==0)
        return;

    if (!hasholes)
    {
        bool highres = gWorld->drawhighres;
        if (highres)
        {
            highres = mydist < gWorld->highresdistance2;
        }
        if (highres)
        {
            strip = gWorld->mapstrip2;
            striplen = stripsize2;
        } else
        {
            strip = gWorld->mapstrip;
            striplen = stripsize;
        }
    }

    // setup vertex buffers
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertices);
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, normals);
    glNormalPointer(GL_FLOAT, 0, 0);
    // ASSUME: texture coordinates set up already

    if (supportShaders && gWorld->useshaders)
    {
        // SHADER-BASED

        // TODO: figure out texture animation for shaders
        // (modifying the texture matrix for only an individual texture layer)

        // setup textures
        /*
        unit 0 - base texture layer
        unit 1 - shadow map and alpha layers
        unit 2 - texture layer 1
        unit 3 - texture layer 2
        unit 4 - texture layer 3
        */
        // base layer
        glActiveTextureARB(GL_TEXTURE0_ARB);
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        // shadow map
        // TODO: handle case when there is no shadowmap?
        glActiveTextureARB(GL_TEXTURE1_ARB);
        glBindTexture(GL_TEXTURE_2D, blend);
        // blended layers
        for (int i=1; i<nTextures; i++)
        {
            int tex = GL_TEXTURE2_ARB + i - 1;
            glActiveTextureARB(tex);
            glBindTexture(GL_TEXTURE_2D, textures[i]);
        }
        glActiveTextureARB( GL_TEXTURE0_ARB );

        terrainShaders[nTextures-1]->bind();
        // setup shadow color as local parameter:
        Vec3D shc = gWorld->skies->colorSet[SHADOW_COLOR] * 0.3f;
        glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, shc.x,shc.y,shc.z,1);

        glDrawElements(GL_TRIANGLE_STRIP, striplen, GL_UNSIGNED_SHORT, strip);
        terrainShaders[nTextures-1]->unbind();

    }
    else
    {
        // FIXED-FUNCTION

        // first pass: base texture
        glActiveTextureARB(GL_TEXTURE0_ARB);  // 选择TEXTURE0为设置目标
        glEnable(GL_TEXTURE_2D); // 激活TEXTURE0单元
        glBindTexture(GL_TEXTURE_2D, textures[0]); 为TEXTURE0单元绑定texture纹理图像

        glActiveTextureARB(GL_TEXTURE1_ARB);// 选择TEXTURE1为设置目标
        glDisable(GL_TEXTURE_2D); // 关闭禁用TEXTURE1单元

        glDisable(GL_BLEND);
        drawPass(animated[0]);
        glEnable(GL_BLEND);

        if (nTextures>1)
        {
            //glDepthFunc(GL_EQUAL); // GL_LEQUAL is fine too...?
            glDepthMask(GL_FALSE);
        }

        // additional passes: if required
        for (int i=0; i<nTextures-1; i++)
        {
            glActiveTextureARB(GL_TEXTURE0_ARB);
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, textures[i+1]);
            // this time, use blending:
            glActiveTextureARB(GL_TEXTURE1_ARB);
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, alphamaps[i]);

            // if we loaded a texture with specular maps, setup the texenv
            // to replace our alpha channel instead of modulating it
            if (supportShaders) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            drawPass(animated[i+1]);
            // back to normal
            if (supportShaders) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        }

        if (nTextures>1)
        {
            //glDepthFunc(GL_LEQUAL);
            glDepthMask(GL_TRUE);
        }

        // shadow map
        glActiveTextureARB(GL_TEXTURE0_ARB);
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_LIGHTING);

        Vec3D shc = gWorld->skies->colorSet[SHADOW_COLOR] * 0.3f;
        //glColor4f(0,0,0,1);
        glColor4f(shc.x,shc.y,shc.z,1);

        glActiveTextureARB(GL_TEXTURE1_ARB);
        glBindTexture(GL_TEXTURE_2D, shadow);
        glEnable(GL_TEXTURE_2D);

        drawPass(0);

        glEnable(GL_LIGHTING);
        glColor4f(1,1,1,1);
    }

    /*
    //////////////////////////////////
    // debugging tile flags:
    GLfloat tcols[8][4] = {    {1,1,1,1},
        {1,0,0,1}, {1, 0.5f, 0, 1}, {1, 1, 0, 1},
        {0,1,0,1}, {0,1,1,1}, {0,0,1,1}, {0.8f, 0, 1,1}
    };
    glPushMatrix();
    glDisable(GL_CULL_FACE);
    glDisable(GL_TEXTURE_2D);
    glTranslatef(xbase, ybase, zbase);
    for (int i=0; i<8; i++) {
        int v = 1 << (7-i);
        for (int j=0; j<4; j++) {
            if (animated[j] & v) {
                glBegin(GL_TRIANGLES);
                glColor4fv(tcols[i]);

                glVertex3f(i*2.0f, 2.0f, j*2.0f);
                glVertex3f(i*2.0f+1.0f, 2.0f, j*2.0f);
                glVertex3f(i*2.0f+0.5f, 4.0f, j*2.0f);

                glEnd();
            }
        }
    }
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_CULL_FACE);
    glColor4f(1,1,1,1);
    glPopMatrix();
    */
}

void MapChunk::drawPass(int anim)
{
    if (anim) {
        glActiveTextureARB(GL_TEXTURE0_ARB); // 选择TEXTURE0为设置目标
        glMatrixMode(GL_TEXTURE); // Setting the matrix mode to GL_TEXTURE means that any subsequent transformation calls will be applied to texture coordinates, rather than vertex coordinates
        glPushMatrix();

        // note: this is ad hoc and probably completely wrong
        int spd = (anim & 0x08) | ((anim & 0x10) >> 2) | ((anim & 0x20) >> 4) | ((anim & 0x40) >> 6);
        int dir = anim & 0x07;
        const float texanimxtab[8] = {0, 1, 1, 1, 0, -1, -1, -1};
        const float texanimytab[8] = {1, 1, 0, -1, -1, -1, 0, 1};
        float fdx = -texanimxtab[dir], fdy = texanimytab[dir];

        int animspd = (int)(200.0f * detail_size);
        float f = ( ((int)(gWorld->animtime*(spd/15.0f))) % animspd) / (float)animspd;
        glTranslatef(f*fdx,f*fdy,0);
    }

    glDrawElements(GL_TRIANGLE_STRIP, striplen, GL_UNSIGNED_SHORT, strip);

    if (anim) {
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glActiveTextureARB(GL_TEXTURE1_ARB);
    }
}

void MapChunk::drawNoDetail()
{
    glActiveTextureARB(GL_TEXTURE1_ARB);  // 选择TEXTURE1为设置目标
    glDisable(GL_TEXTURE_2D);// 禁用TEXTURE1单元
    glActiveTextureARB(GL_TEXTURE0_ARB);// 选择TEXTURE0为设置目标
    glDisable(GL_TEXTURE_2D);// 关闭禁用TEXTURE1单元

    glDisable(GL_LIGHTING);// 禁用光照

    glColor3fv(gWorld->skies->colorSet[FOG_COLOR]);
    //glColor3f(1,0,0);
    //glDisable(GL_FOG);

    // low detail version
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertices);
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glDisableClientState(GL_NORMAL_ARRAY); // 关闭禁用NORMAL Array
    glDrawElements(GL_TRIANGLE_STRIP, stripsize, GL_UNSIGNED_SHORT, gWorld->mapstrip);
    glEnableClientState(GL_NORMAL_ARRAY);

    glColor4f(1,1,1,1);
    //glEnable(GL_FOG);

    glEnable(GL_LIGHTING);
    glActiveTextureARB(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_2D);
    glActiveTextureARB(GL_TEXTURE0_ARB);
    glEnable(GL_TEXTURE_2D);
}

如何绘制 world map objects(WMO) 

每个tile都包含了该tile内使用的WMO,保存在ADT的MWMO中。 MWMO是List of filenames for WMOs (world map objects) that appear in this map tile. A contiguous block of zero-terminated strings. WMO文件的扩展名为wmo,如World\Wmo\Kalimdor\Builders\NightElf2Story\DsNightElf2Story.wmo。

一个.wmo文件,会构建一个WMO对象,并把该WMO对象放入WMOManager进行管理。WMO::WMO会解析.wmo文件。 参考:wmo(wow map object) research

 

如何绘制 World

World::draw的流程:

  1. 重置WMOInstance和M2的动画
  2. 设置相机和frustum
  3. 调用各个chunk的MapChunk::draw

WOW场景管理的总结

  1. 场景管理采用QuadTree+Portal+BSP,实现室内外场景融合。
  2. Terrain按照Tile划分。
  3. Tile按照Chunk划分,Tile按照QuadTree管理,Chunk可重用。
  4. Chunk采用Interlocking方法。Interlocking参考Greg Snook‘s Simplified Terrain Using Interlocking Tiles (在Game Programming GEM 2中的文章)

额外补充Simplified Terrain Using Interlocking Tiles

注:Simplified Terrain Using Interlocking Tiles文章中的的Tile概念不是WOW的Tile的概念,而是类似于WOW的Chunk概念。下面的文字中提及的Tile是指Simplified Terrain Using Interlocking Tiles文章中的的Tile概念。

在文章中:

  1. 每个Tile可能包含独有的一个Vertex Buffer,但Index Buffer可以跨Tile重用。image
  2. 采用联锁处理T型裂缝(body+link)

image

 

增补:

 

参考:

  1. Virtual Terrain Project
  2. http://www.landscapemodeling.org/
  3. http://www.vterrain.org/LOD/Papers/
  4. http://www.verycd.com/topics/63344/
  5. http://www.verycd.com/topics/36450/

参考:

  1. Brute force meshing
  2. Fault Formation fractal terrain generation
  3. Midpoint Displacement fractal terrain generation
  4. Perlin Noise
  5. Simple Texture Mapping
  6. Procedural texture generation (如Height-based texture generation 多张尺寸和Heightmap相同的纹理按照高度分布进行混合产生单张纹理)
  7. Height-based lighting
  8. Applying a lightmap to terrain
  9. Slope-lighting algorithm
  10. Terrain Tutorial
  11. SOAR by Peter Lindstrom and Valerio Pascucci
  12. Boer’s Geometrical MipMapping(geomipmapping )CLOD algorithm (Geomorphing)(Crack-proofing)(按patch进行frustum culling)
  13. Rottger’s Real-Time Generation of Continuous Levels of Detail for Height Fields
  14. Greg Snook‘s Simplified Terrain Using Interlocking Tiles (在Game Programming GEM 2中的文章)
  15. Duchaineau’s ROAM algorithm
  16. Seamus McNally's improvement to the ROAM algorithm (Real-Time Dynamic Level of Detail Terrain Rendering with ROAM)
  17. View-dependent refinement of progressive meshes
  18. Terrain LOD: Runtime Regular-Grid Algorithms
  19. Hierarchical Visibility in Terrains
  20. Chunked LOD: Rendering Massive Terrains using Chunked Level of Detail Control (参考:Real Time 3D Terrain Engines Using C++ and Dx9的170页)
  21. Far Cry and Half Life2 Engine scene techniqueHalf Life2 Displacement Terrain System
  22. Breaking the Walls: Scene Partitioning and Portal Creation
  23. Terrain rendering using GPU-based geometry clipmaps
  24. 新游戏中出现的基于BSP场景分割技术
  25. 3D地形多层纹理混合加阴影渲染方法
  26. Building a 3D Portal Engine
  27. Core Techniques and Algorithms in Game Programming
  28. http://www.devmaster.net/articles/bsp-trees/
  29. http://www.karkza.org/ftp/programming/misc/bsp.htm
  30. http://www.devmaster.net/articles/bsp-trees-physics/
  31. WoW文件结构类
  32. http://www.wowwiki.com/Portal:Main
  33. Automatic Portal Generation 自动Portal 生成
  34. Binary Space Partioning Trees and Polygon Removal in Real Time 3D Rendering
  35. 关于场景管理
  36. http://wowdev.org/
  37. Texture Splatting in Direct3D 理解起来好过于Terrain Texture Compositing by Blending in the Frame-Buffer,但是对Splatting的理解有少许问题,哈哈,所以,还是要好好研究Terrain Texture Compositing by Blending in the Frame-Buffer
  38. Fun with Textures

10/19/2009

ZZH:魔兽世界之001:如何枚举得到WOW的各个地图和如何生成WOW Minimmap

如何枚举得到WOW的各个地图

通过对WoWmapview中的Menu::Menu代码分析,得到如下内容:

  1. DBFilesClient\\Map.dbc :This client database describes the various top level maps (also called continents) the game uses. All dungeon instances have their own map as well.
  2. 通过对DBFilesClient\\Map.dbc的枚举得到每个top level map的
    1. WorldID
    2. World name (used for loading World\Maps\%s\%s.wdt)
    3. World description

如何生成WOW Minimmap

通过对WoWmapview中的World类的成员函数World::initMinimap的分析,得到如下内容

  1. 解析wdl,其中的wdl包括了MAOF chunk 和 MapAreaLow array,通过解析这些数据得到1map = 64x64maptile,1maptile = 17x17+16x16 heightmap点(16bit)
  2. 使用512x512 minimap texture来覆盖64x64maptiles,那么1个tile 就对应 了8x8 pixels
  3. 根据heightmap生成minimap texture,不同的height对应不同的颜色,height < 0的对应blue表示水平面下
 

Heal the world

感谢访问!
Please wait...
Sorry, the comment you entered is too long. Please shorten it.
You didn't enter anything. Please try again.
Sorry, we can't add your comment right now. Please try again later.
To add a comment, you need permission from your parent. Ask for permission
Your parent has turned off comments.
Sorry, we can't delete your comment right now. Please try again later.
You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
Complete the security check below to finish leaving your comment.
The characters you type in the security check must match the characters in the picture or audio.
帆 林wrote:
不告诉俺你成都的号码,导致俺好不容易去趟成都,却没见到你,关于这件事,俺永远都不原谅你!!!
Mar. 2
帆 林wrote:
办公室不错嘛,如果我在的话,应该会布置的更好滴,哈哈^_^
Mar. 2
帆 林wrote:
你侄子好可爱啊!!!!有天赋!!!!最重要的是,他还真有点像你。
Mar. 2