麒麟子

~~

导航

<2013年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

常用链接

留言簿(12)

随笔分类

随笔档案

Friends

WebSites

积分与排名

最新随笔

最新评论

阅读排行榜

评论排行榜

魔兽世界客户端数据研究(三)

终于决定,还是通过wow model viewer起手,研究一下WOW的数据类型,从另一个角度,体验一把这个唯一让我充过值的游戏。

这将是一系列随笔,即在读代码的时候,顺便记录,以理清思路和加深映象。 其中会有很多让人费解的地方,如果有幸被某位兄弟看见

,请勿见笑。

 

今天来说一下M2中的LOD的数据

WOW中,为了降低远处模型的渲染开销,为模型做了LOD,即远处的模型,使用更少的顶点,更粗略的材质。 比如远处的模型在渲染的时

候,面片数量减少,关闭光照,不渲染挂接的特效等等。

因此,不用证明也知道,M2中,材质是存在每一个LOD信息中的。
哎,也就写这几句的时候顺手些,其实不用分析,也是这个结果。因为我们自己的引擎就是这样做的,何况是WOW这种大师级的作品呢。

从WMV的解析代码下手,看看它是如何解析的吧。
首先,它使用了这样一行代码
int16 *transLookup = (int16*)(f.getBuffer() + header.ofsTransparencyLookup);
读取了一串用于透明值的查找数组。 不过暂时没有使用,后面材质构建的地方才会用到。
接下来,就是读取相关数据了。 在WLK以后,所有的这些数据,被分离到了.skin文件里面,不知道是咱想的,以后再来作讨论。 但是在

WLK之前,这个数据还是被放在了一起的。

通过模型的名字我们组合上.skin,就是当前所要的渲染数据了。
这个组合是这样的。
假如我们一个模型是 humanmale.m2
那么它的四个LOD数据分别就是 humanmale01.skin humanmale02.skin humanmale03.skin humanmale04.skin

当我们得到了这个数据后,就可以通过MPQFile加载想要的数据了。

OK,假设上面的过程,我们已经完全搞定了,此时,我们就得到了一个skin的数据。有了这个数据,我们就可以为所欲为了,嘿嘿。有点

夸张了。 在这个数据的最前面,肯定是数据头了。 数据头在WMV中本来一直是以xxxxHeader来定义的,不过在这里,它一改风格,定义

了一个叫ModelView的东西。

我们来看看这货的定义
struct ModelView
{
#ifdef WotLK
    char id[4]; //巫妖王版本新增的一个标记位,必须是 'S' 'K' 'I' 'N'
#endif
    uint32 nIndex; //这个表示此LOD有多少个INDEX
    uint32 ofsIndex; //这个表示此LOD的INDEX从模型的哪里开始数
    uint32 nTris; //这个表示此LOD有多少个构建成三角形的索引
    uint32 ofsTris;     //三角形个数
    uint32 nProps; //额外的顶点属性
    uint32 ofsProps; //顶点属性读取
    uint32 nSub; //有多少个子部件 后面定义的ModelGeoset表示一个子部件,其包括了MESH数据,材质,渲染状态等内容
    uint32 ofsSub;     //
    uint32 nTex;  //纹理
    uint32 ofsTex;     // ModelTexUnit, material properties/textures
    int32 lod;                 // LOD bias?  WMV作者也打了问号。
};

有了这个数据头以后,我们就可以无脑的先读取上面的数据,然后再进行构建。

索引数据
uint16 *indexLookup = (uint16*)(g.getBuffer() + view->ofsIndex);
构成三角形的顶点索引序列
uint16 *triangles = (uint16*)(g.getBuffer() + view->ofsTris);

当前模型在渲染时候的索引数目
nIndices = view->nTris;
重新分配索引
wxDELETEA(indices);
indices = new uint16[nIndices];

将本地索引转换成全局索引
for (size_t i = 0; i<nIndices; i++)
{
        indices[i] = indexLookup[triangles[i]];
}

索引数据总算是完了,下面就得准备子模型和材质相关的事情。
大家都知道,在渲染管线中,一次渲染提交只能提交具有相同渲染状态和纹理的模型。 于是,我们的模型如果具有不同的材质,就需要

先做分割处理。 这是所有WOW这样的3D MMORPG引擎都需要处理的问题。

在WMV中,模型渲染状态相关的数据,使用了ModelGeoset来表示,纹理相关的,使用了ModelTexUnit来表示
先看看ModelGeoset的定义
/// Lod part, One material + render operation
struct ModelGeoset
{
    uint32 id;        // mesh part id?
    uint16 vstart;    // first vertex, Starting vertex number.
    uint16 vcount;    // num vertices, Number of vertices.
    uint16 istart;    // first index, Starting triangle index (that's 3* the number of triangles drawn so far).
    uint16 icount;    // num indices, Number of triangle indices.
    uint16 nSkinnedBones;    // number of bone indices, Number of elements in the bone lookup table.
    uint16 StartBones;        // ? always 1 to 4, Starting index in the bone lookup table.
    uint16 rootBone;        // root bone?
    uint16 nBones;        //
    Vec3D BoundingBox[2];
    float radius;
};

由上可知,它定义了渲染相关的顶点,以及骨骼,和包围盒信息,最后一个是作为构建包围球用的。

/// Lod part, A texture unit (sub of material)
struct ModelTexUnit
{
    // probably the texture units
    // size always >=number of materials it seems
    uint16 flags;        // Usually 16 for static textures, and 0 for animated textures.
    uint16 shading;        // If set to 0x8000: shaders. Used in skyboxes to ditch the need for depth buffering.

See below.
    uint16 op;            // Material this texture is part of (index into mat)
    uint16 op2;            // Always same as above?
    int16 colorIndex;    // A Color out of the Colors-Block or -1 if none.
    uint16 flagsIndex;    // RenderFlags (index into render flags, TexFlags)
    uint16 texunit;        // Index into the texture unit lookup table.
    uint16 mode;        // See below.
    uint16 textureid;    // Index into Texture lookup table
    uint16 texunit2;    // copy of texture unit value?
    uint16 transid;        // Index into transparency lookup table.
    uint16 texanimid;    // Index into uvanimation lookup table.
};
而上面这个结构,是纹理相关的信息。

上面的信息,都是一些索引和ID值,真正的数据是放在全局信息中的。

读取完上面的数据后,LOD信息基本上就大功造成了。 而这些索引是如何使用的,只有下一次再研究了。今天又很晚了。

由此可知,WOW中的数据组织和一般的引擎没有太多区别。 即HEADER信息用于分割数据区域。
整个模型要使用的数据,放在了最上层,然后,不同的LOD和子MESH要使用数据的时候,只需要保存一些索引值,再到全局数据里去查询就可以了。

暂时到此吧,下次继续。。。。

posted on 2013-04-26 01:11 麒麟子 阅读(2012) 评论(2)  编辑 收藏 引用 所属分类: BLIZZARD

评论

# re: 魔兽世界客户端数据研究(三) 2013-04-26 08:03 rosan

LZ加油,呵呵,我会继续关注的。希望最后能够写一些代码来说明你的文章  回复  更多评论   

# re: 魔兽世界客户端数据研究(三) 2013-05-04 03:57 monn

谢谢楼主,,期待下一篇~  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理