|
MaNGOS是WoW的模拟服务器端工程,架设在sourceforge上,使用C++,开放源码,代码写的很清楚,架构清晰,质量很高.我看重的,是其对3D游戏服务端编程的通用流程和问题的处理手法和经验.研究消息包结构,一般就能推断WoW采取哪种逻辑方案来解决一些棘手的通用问题,而服务端编程质量在很大程度上取决于程序员的经验. 由于采用大量优雅的模板技法,MaNGOS的编译速度很成问题,调试变得有点困难. C++的编译速度确实是一个很大的问题,编译速度缓慢直接导致了修改代码再调试的这个频繁的开发行为变得难以忍受,开发效率大大降低. C++模板是很优秀的技术,但是因为其难以忍受的编译速度,有时也就不敢大规模应用了,只在些小地方耍耍小聪明.连那个常用的Singleton模板我也是不敢用的,编译的太慢了,还是手写的Instance方法比较快. 本来是希望CPU速度上去后,编译速度的问题自然就会解决,但事实上从VC6->VC2003->VC2005,一个比一个编译地慢,CPU全让M$自己给用光了. 我现在以为使用Lua,Python等脚本技术可以解决C++的编译速度导致的开发问题,把易变的逻辑全转到脚本去,脚本的编译是最快的了. 我的机器比较老,用的1.6G的P4,完全编译MaNGOS要花7-10分钟,game模块特别地慢,一改动关键的头文件就要重新全部编译.我又喜欢在写程序同时看下载的电影,编译地就更慢了.调试时又要开wow客户端,:(. 不知道使用Stdafx预编译后速度会不会快一些.至少每个cpp的Include段要整理一下,把比较稳定的头文件放到最前,有利于预编译,Common.h里的EXPECTED_MANGOS_CLIENT_BUILD经常变动,还是挪出来比较好. 自己最近几天,改了些东西: 0.修改主工程配置,去除zlib和zthread两个dep工程,这样载入工程会快一些. 1.因为很讨厌事先要导出Ddc和Maps数据,所以修改了MaNGOS让其直接去读MPQ文件里的dbc和maps. 2.DOTCONFDocument不支持多字节配置字符串,我用"D:\魔兽世界\Data"这个目录,isspace函数assert报警,改成_istspace解决. 3.整合网上的MaNGOS的2446 TBC版本,做了个_TBC_BUILD_预定义编译分支,意图同时支持当前和TBC版本,TBC升到2046后,CMSG_AUTH_SESSION验证算法失败,然后厌倦了MaNGOS的编译,等我机器升级了再继续.
基于Portal的引擎如何知道当前人物处于哪个sector是个难决的问题.用google搜索了一下论坛,有两种方案: 一种主张跟踪人物位置,给定一个起始sector,而后时刻检查人物是否穿过某个portal平面,一旦穿过则更新当前人物的sector位置变量. 一种主张实时计算,需要附加的对sector进行划分的bsp树数据进行计算.
在程序逻辑上,我比较倾向第2种,因为其逻辑概念比较清晰简单,但其需要额外的数据制作,wmo数据的BSP树是普通的leafy bsp,而非solid leafy bsp,似乎不足以判断一个点是否在BSP整体空间内,而且不能适应2个sector相嵌套的情形,WoW的城内的建筑portal摆放随意性很大,似乎没什么限制.
而判断人物穿过portal,感觉上逻辑比较模糊,脆弱,不稳定.当人物一直在portal边上左右徘徊时,算法是否够强壮?人物初始化时,又通过什么手段得知其初始sector呢?人物从城外进入城内的瞬间,又如何知道进入哪个sector?
WoW使用了哪一种呢?至少穿越portal这方案还可以实现出来,Crystal Space也是这么干的,可惜大部分的资料根本不提这一问题.
1. MPQ resource files reorganized, locale related resource mpqs are moved into locale sub-directory like "enUS", "enGB", "koKR", "zhCN". 2. Some DBC files fields changed and new fields added. 3. M2 format changes a little. a. A int32 is inserted into bone define struct, so the new m2 model can not be used in current wow client. b.Rotation quaternion is packed into 8 bytes as: struct PACK_QUTERNION { int16 x,y,z,w; }; Extract out float quternion, for exsample: PACK_QUTERNION q = pCompressQuats[i]; qRotate.x = float(q.x > 0? q.x - 32767 : q.x + 32767)/32767.0f; qRotate.y = float(q.y > 0? q.y - 32767 : q.y + 32767)/32767.0f; qRotate.z = float(q.z > 0? q.z - 32767 : q.z + 32767)/32767.0f; qRotate.w = float(q.w > 0? q.w - 32767 : q.w + 32767)/32767.0f; Update MyWarCraftStudio to 0.9.5991 for supporting Burning Crusade Closed Beta client, and it should also work for current Wow client and alpha test client. http://www.cnitblog.com/Files/linghuye/MyWarCraftStudio_0.9.5991.rar
记得上回WoW的Beta测试时mdx格式被替换成m2格式,这次暴雪又对资源文件和目录作出了调整,DBC字段有添加和变动.
1.Data下出现有enUS目录,本地化的资源以后将放在Data下对应的本地化子目录中,原speech.mpq变为enUS\speech-enUS.MPQ,Interface.mpq,dbc.mpq并入enUS\locale-enUS.mpq. 想来是暴雪给每个本地化版本做那个2G多的patch做怕了.
2.Common.mpq 达到了3.49G,把之前的各个分类资源mpq全统一了.
3.M2 格式有变动,骨骼结构被修改,旋转四元数数据被压缩成8个字节的int16 * 4,还原4元数失败.
//
To open the new patch.mpq
//
Error if mpq file is bigger than 2G
fseek();
//
or
lseek();
//
or
::SetFilePointer(hFile, dwHashTablePos,
0
, FILE_BEGIN);
//
Correct
_lseeki64();
//
or
LARGE_INTEGER li; li.QuadPart
=
dwHashTablePos; ::SetFilePointer(hFile, li.LowPart,
&
li.HighPart, FILE_BEGIN);
So change the every fseek/SetFilePointer in stormlib or libmpq in WowModelViewer source code and recompile, we can open the new patch.mpq in WoW BC which is 2.7G. The mpq format is NOT changed, I am a fool to decode a new format from the begining.
============================================= http://linghuye.googlepages.com/MyWarCraftStudio_0.9.7z or http://www.cnitblog.com/Files/linghuye/MyWarCraftStudio_0.9.rar 1.Fix the bug when reading Burning Crusade's patch.mpq 2.Fix bugs when displaying model on ATI graphic cards 3.M2Import.dli is a 3D Max import plugin for importing m2 file, but only meshs now, no texture, no skeleton animation. I am learning how to write a 3D Max import plugin, this is a byproduct.
这次真的是以小人之心度君子之腹了,因为一开始就认定玻璃渣修改了资料片patch.mpq的加密算法,所以打开最近刚学的OllyDbg就开始反编译跟踪,兴高采烈地解析出解密后的文件hash表,再兴高采烈地解析出Draenei的模型后就开始跟一个老外炫耀,老外很平静地说已经解出来了,还有C#源码(http://code.google.com/p/mpqtool/),我看了一下,解密算法没有变.再检查了一下才发现是自己程序里SetFilePointer函数使用在大于2G的mpq文件时的调用参数有Bug:
// Error if mpq file is bigger than 2G fseek(...); // or
::SetFilePointer(hFile, dwHashTablePos, 0, FILE_BEGIN);
// Correct _lseeki64(...); // or
LARGE_INTEGER li; li.QuadPart = dwHashTablePos; ::SetFilePointer(hFile, li.LowPart, &li.HighPart, FILE_BEGIN);
// So change the every fseek/SetFilePointer in stormlib or libmpq in WowModelViewer source code and recompile, we can open the new patch.mpq in WoW BC which is 2.7G.
昨天刚从老外朋友那搞到魔兽世界燃烧的远征内部Alpha测试的下载,1.28G. 安装不顺利,出现了错误: The patch "Files\Data\connection-help.html~Patch" could not be applied. (Invalid source file size: expected 8869, actual 6618.) If this problem persists, please contact Blizzard Technical Support. (BNUpdate::PTCApply) 检查发现6618是connection-help.html中文版的大小,8869是英文版的大小,也就是说安装程序根据注册表中游戏的安装信息,读取了原先wow的安装数据.修改注册表中的wow目录到机器上英文版的wow,再次安装后正常.
粗略看了一下,主程序版本2.0.0.5610,多了expansion.MPQ/expansionLoc.MPQ两个数据包,mpq文件格式似乎没有变化,可以看到血精灵的纹理,但里面没有模型文件. Patch.mpq打不开了,mpq格式终于变了,具体是文件和hash列表的加密算法变了,暂时看不到新的场景模型.
2006-09-01, BloodElf&Draenei model data extracted.
Wow的室内环境,奥格瑞玛/血色修道院等,是由wmo文件代表的sector组成的,从解析出来的sector三维景象来看,是很明显的portal sector,比如甬道被格成单独的wmo,每个wmo sector有明显的portal开口.
Portal引擎和BSP是可以一起使用的,不相互排斥,对每个sector的处理将采用BSP算法,这个概念很关键.Portal的另一个重要作用是将室外环境和室内环境在算法逻辑上联系起来.
可以确定,Wow的室内碰撞检测是使用BSP来完成的,其BSP为Leafy BSP. Leafy BSP使用的切分平面可以不与场景中的多边形共面,切分平面目前只看到X,Y,Z三轴,定义等同Quake3源码中的定义. 在碰撞检测中,BSP数据最神奇的地方就在于,它能够快速地给出人物附近的三角面集合,即需要进行碰撞检测的三角形集合.
若将Leaf上的数据全设0,则进入游戏后,室内场景就没有碰撞了. 此BSP除了碰撞检测,还对室内的渲染有影响.
Most engines do not use the BSP-tree to speed up the drawing, such as portal engines, but still almost all of them have built a BSP-tree out of the geometry. This is because of the great advantage when calculating the collision detection, namely that it is very cheap to position the user in the BSP-tree. When that is done, the only polygons needed to be checked; are the polygons in the leaves the object passed through that frame.
References: http://www.flipcode.com/articles/portals_issue01.shtml Core Techniques and Algorithms in Game Programming http://www.devmaster.net/articles/bsp-trees/ http://www.karkza.org/ftp/programming/misc/bsp.htm http://www.devmaster.net/articles/bsp-trees-physics/
在魔兽世界中,在人物奔跑的同时,使用挥手等表情命令,人物会在奔跑的同时作出其他动作,这是骨骼动作混合. 猎人中奔跑时开始拉弓时,身体会过渡到拉弓的动作,而不是瞬间变换为拉弓姿势,这是骨骼动作transitioning.
骨骼动作混合是作动作过渡的技术基础,目前看到有两种方案,一种简单,一种复杂. 简单的方案应该是起源于Quake,魔兽世界亦采用该方案,即,在骨架数据中预先标识某ID骨骼为上半身的根骨骼,依此标识下半身的根骨骼,手臂的根骨骼,腿的根骨骼. 使用骨骼动画架构可以很容易地,让奔跑的动作帧数据只作用于下本身的骨骼层次,即只作用于整个骨骼层次的子骨骼体系.让挥手或开枪的动作只作用上本身骨骼,最终的结果形成了动作混合. 复杂的方案是Weighted animation blending,为每个动作的每根骨骼标识它的权重,即标识每根骨骼在每个动作中的重要性,可以用1.0标识腿部骨骼在奔跑动作中的权重,用0.0标识上半身在奔跑动作中的权重. 与另一动作混合时,两个动作骨骼分别计算自身当前的骨骼帧运动数据,然后依据动作权重进行插值混合两者的运动数据,Jupiter,Nebula使用了该方案.其复杂不在程序上,而在美工制作上,程序代码上更优雅简单.
奔跑时拉弓就要让下半身保持奔跑动作,上本身从奔跑时的姿势过渡到拉弓姿势.Animation transitioning是要解决两个骨架姿势之间的过渡问题,从一个姿势过渡到另一个姿势,即从一个骨架数据渐转到另一个骨架数据,即在过渡间的人物骨架姿势由两个动作的骨架数据混合而成,一个数据渐增0->1,一个数据渐减1->0,这里的权重作用于整个骨架,称Weighted animation blending.这样的过渡是数据计算出来的,而不是美工设计出来的,有时会有变形出现.
这些方案都基于骨架层次体系,适合组合任意个骨骼动作.帧数据插值混合时,若姿势间相差过大,旋转超过180度,slerp计算会出现混乱.
References:
Quake3 source code WowModelViewer's animation blending source code http://www.circlesoft.org/DirectMesh2/DM2Spec/pages/Features.htm Realtime Character Animation Blending Using Weighted Skeleton Hierarchies (该问题最好的论文)
在一般状态下,当把一个纹理应用于一个片断时,只有主颜色与纹理单元的颜色进行了组合.主颜色可能是光照计算的结果,也可能是通过glColor*()函数指定的值.
在纹理处理之后,但在进行雾计算之前,可以对片断应用一种辅助颜色.辅助颜色的应用可以使经过纹理贴图的物体具有更真实的高光效果.
如果当前并没有启用光照,并且启用了颜色求和模式(使用glEnable(GL_COLOR_SUM)),那么当前的辅助颜色(由glSecondaryColor*()所设置)便被加到经过纹理处理后的片断颜色上.
纹理操作是在光照之后进行的,但把镜面亮点和纹理的颜色进行混合通常会减弱光照的效果. 可以为每个顶点计算两种颜色:一种是主颜色,由所有的非镜面成分组成,另一种是辅助颜色,由所有的镜面成分组成的和.如果把镜面颜色分离来,辅助(镜面)颜色就会在纹理计算之后再添加到片断中.
如果当前启用了光照,就会应用辅助镜面颜色,而不管是否为GL_COLOR_SUM模式,并且glSecondaryColor*所设置的任何辅助颜色都会被忽略.
摘自《OpenGL编程指南》第5版,随手翻看时觉得很有体会,摘下以备忘,WoW里的高光应该就是用SecondaryColor实现的.SecondaryColor相当于一层额外的纹理层.
DM引擎转入实现室内环境,有卡马克的Quake3引擎源码在手,应该不会有太陡的阻力,并借此机会深入学习一下大师的编码和思想.
|