重剑无锋,大巧不工
常有人询问,编程需要天赋吗?啊,任何事情走往极致,都需要天赋。任何一个软件产品的极致成功,都需要创意天赋、编程天赋、管理天赋、行销天赋……。然而,只需用心模仿,再加一点匠心独具,任何人都能够把编程路走得稳当顺遂。能读千赋则善赋,能观千剑则晓剑,巧者不过习者之门也。你把名家源码融为己用,别人也会赞叹一声“你有编程天赋”。子曾经曰过:编程无他,唯手熟尔!
C++博客
首页
新随笔
联系
聚合
管理
随笔 - 505 文章 - 1034 trackbacks - 0
<
2007年4月
>
日
一
二
三
四
五
六
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
子曾经曰过:编程无他,唯手熟尔!
常用链接
我的随笔
我的评论
我参与的随笔
留言簿
(94)
给我留言
查看公开留言
查看私人留言
随笔分类
(649)
C++ lib -- ACE(1)
C++ lib -- Boost(2)
C++ lib -- CEGUI(10)
C++ lib -- Loki(2)
C++ lib -- MFC (2)
C++ lib -- Qt(39)
C++ lib -- STL(1)
C++ lib -- wxWidgets
Debug(2)
Encrypt & Decrypt(3)
Enjoy Hardware(16)
Enjoy Software(3)
Everyday Life(10)
Flash Scaleform GFx(13)
Game Engine(18)
Game Graphics(160)
Game Music & Sound(18)
GameDevStory(12)
GameResManagement
HLSL&ShaderMonkey(27)
IDE -- c++ builder(6)
IDE -- visual c++(37)
Kungfu(4)
Movie & TV(2)
Multi Threads(1)
Network Programming(2)
Nokia S60(1)
OGRE(14)
OS -- Vista(5)
P2P(4)
PL--c/c++(63)
PL--Lua(15)
PL--Python(4)
PL--WindowsShell(3)
Secret(13)
Text Editor(2)
Unreal(2)
大话IT(4)
乱七八糟(118)
面试笔试(7)
自动更新(3)
随笔档案
(505)
2018年6月 (1)
2016年10月 (1)
2015年1月 (1)
2012年6月 (1)
2012年4月 (1)
2012年3月 (1)
2011年8月 (1)
2011年4月 (1)
2011年2月 (2)
2010年8月 (1)
2009年9月 (8)
2009年8月 (5)
2009年7月 (9)
2009年6月 (2)
2009年5月 (3)
2009年4月 (5)
2009年3月 (6)
2009年1月 (6)
2008年12月 (23)
2008年11月 (25)
2008年10月 (16)
2008年9月 (5)
2008年8月 (5)
2008年7月 (6)
2008年6月 (10)
2008年5月 (10)
2008年4月 (52)
2008年3月 (42)
2008年2月 (16)
2008年1月 (24)
2007年12月 (31)
2007年11月 (27)
2007年10月 (38)
2007年9月 (24)
2007年8月 (25)
2007年7月 (15)
2007年6月 (3)
2007年5月 (3)
2007年4月 (6)
2007年3月 (1)
2007年2月 (10)
2006年12月 (3)
2006年11月 (16)
2006年10月 (4)
2006年9月 (1)
2006年7月 (9)
相册
剑
BCB
C++ Builder研究
Crytek
crymod
Crytek's Offical Modding Portal
Game Industry
AMD Developer Central
Welcome to AMD Developer Central
BeautifulPixels
A guy who works on the game engine Gamebryo at Emergent Game Technologies.
CGJOY
Develop
Breaking news and the lastest jobs for European games developers
Emergent Game Technologies in Asia
Emergent sales type guy
Gamasutra
Gamasutra
GameIndustry
GameIndustry
NVIDIA Developer Zone
Programming Vertex, Geometry, and Pixel Shaders
OGRE
Ogre3d API
Ogre官网
Ogre中文Wiki
Ogre中文社区
other
Google代码搜索
搜索代码,搜索Loki::试试
Windows7之家
季庄新闻
a guy in America whose old hometown is in ShanDong Province
维基百科
我的豆瓣
read books
阳光牛牛 的个人空间
中国青年
Programmers
Qt
Qt Center
Qt的信息和资源,很全
Qt Forum
Qtopia.org.cn
中国人讨论Qt的地方
Qt简体中文
有“Qt简体中文文档“
Qt中文论坛
貌似人气很高啊
Qwt
好多QT控件,开源项目,哈哈,爽
QwtPlot3D
用Qt和OpenGL实现的Qt控件库,3D的,震撼死了
Trolltech
Qt老家
Trolltech Labs
齐亮博客
一个正在挪威奥斯陆工作的中国软件工程师
WOW Stuff
WoW Wiki
搜索
积分与排名
积分 - 903576
排名 - 14
最新随笔
1. 2018年再更新下
2. 2016年更新下
3. 离开天朝,跑到新加坡了
4. 一个中国穷人在美国的生活
5. 用Windows批处理启动需要更改系统时间破解的程序
6. 程序中添加某个目录的路径
7. 网游客户端弹出个“Runtime Error”不产生dump文件的解决办法
8. 招聘UI帝,客户端逻辑,熟悉OGRE/CEGUI/Lua优先,薪水给力
9. reinterpret_cast相关
10. const相关
11. 滔滔备份
12. 搬家到博客园去了
13. 理解D3D--(0)批次batch
14. 工程师和科学家
15. 文章分类
16. [转]寻找遗失的同步类视图
17. 《天骄3》不错
18. Vertex Formats
19. 3D地形多层纹理混合加阴影渲染方法
20. 山寨Dota之路--(0)起始
最新评论
1. re: 2016年更新下
之前考察下来,ios职位薪水budget太低了,后劲不足,这个技术方向已经放弃了
--七星重剑
2. re: 2016年更新下
2020年了,再看当初这篇blog,欣然一笑,原来当时我是这么想的,哈哈。现在坡县绿卡已到手,是不是该有新的规划和选择了?:)
--七星重剑
3. re: 每天花30分钟看OGRE--(5) Demo_TextureFX(超强4个ViewPort测试)
11年多过去了! How time flies!
--七星重剑
4. re: 在for循环里对std::map进行元素移除
过了11年多,再回来复习下,哈哈
--七星重剑
5. re: [metaprogramming] 求一个数Num的N次方
还能这样,多年后的今天都不记得了
--七星重剑
6. re: [zt]一步一步学习Vim 全图解释 (强烈推荐)
这个要捡起来重新熟悉下
--七星重剑
7. re: 用p2p优化网络游戏客户端自动更新工具
我擦,我居然10几年前还研究过这个
--七星重剑
8. re: 2016年更新下
@炮灰九段
新加坡啊,先在这待几年再说了
--七星重剑
9. re: 2016年更新下
现在在哪个国家呢?没想过回国再发展发展啊
--炮灰九段
10. re: 2016年更新下
@思月行云
一直待在天朝的话,就坚持游戏领域了,毕竟搞了十年游戏开发了。但是现在跑到国外了,游戏职位实在太少了。
一起加油吧!:)
--七星重剑
阅读排行榜
1. 每天30分钟看Shader--(1)HLSL固有函数 【Intrinsic Functions (DirectX HLSL)】(25328)
2. 3D MAX 插件的基本知识和安装方法(20735)
3. AlienBrain初体验(12216)
4. python网络编程学习初步(11874)
5. 图形学扫盲--(4)3D图形学的学习策略(11511)
评论排行榜
1. 【下载】Qt 4.3 商业版(41)
2. 华为某牛人的超强言论.年轻的可以看看(27)
3. Qwt 终于可以调试它的examples了(猛图,重剑开发环境大曝光)(18)
4. 求Gamebryo 2.3(18)
5. PerfHud Scaleform GFx(17)
WOW m2模型与WowModelViewer
好长时间没学shader了,拿这个
物件的边缘高亮(Entity edge highlight)
练习了下,用render monkey,参照逍遥剑客的blog,很快就看到效果了,但是在现在的项目中实现的话有点麻烦,主要是对目前项目的Model还不熟悉,shader是怎么用的也不清楚,看了好几天,依然是迷迷糊糊,真他妈的菜啊!
现在的项目兼容m2模型,因为这引擎朝哥写的时候用的就是wow的资源,呵呵,山寨版wow。所以搞清楚了m2模型也就搞清楚了目前项目的model.开始看WowModelViewer.
MPQ Archives The
StormLib
library
魔兽世界m2模型文件分析及wowModelView代码阅读心得
Title
1、由于是浏览器,所以读取数据的函数再ModelViewer::OnTreeSelect方法中。选择分为角色模型和非角色模型,如下:
if (isChar) {
modelAtt = canvas->LoadCharModel(rootfn.fn_str());
canvas->modelType = MT_CHAR;
} else {
modelAtt = canvas->LoadModel(rootfn.fn_str());
canvas->modelType = MT_NORMAL;
}
其中的canvas是ModelCanvas *canvas,是ModelViewer类的一个属性;
然后ModelCanvas::LoadCharModel再调用ModelCanvas::LoadModel方法读取模型数据;
ModelCanvas::LoadModel新建了一个Modal,把传入的文件地址const char *fn传给了Modal的构造函数。
Modal的构造函数通过新建一个MPQFile类: MPQFile f(tempname);来读取模型文件数据;
MPQFile构造函数中用file.Read(buffer, size)实际读取了模型文件;
然后再Model::isAnimated中读取了骨骼、顶点等模型数据(通过从上一条中的 buffer=后来的header 中拷贝);
2、这个浏览器是用opgl来开发图像部分的,浏览器有一些坐标转换函数,浏览器中的转换函数在:
model.cpp中的Vec3D fixCoordSystem(Vec3D v)函数。
调用这个函数的函数是:Model::initCommon,initAnimated函数调用的它。
而initAnimated函数就在Model::isAnimated之后不久调用,被Model的构造函数调用。
if (animated)
initAnimated(f);
3、在initCommon中,依次读取vertices,normals,bounds(包括boundTris),textures
这些都经过了上一条说的fixCoordSystem坐标转换。
4、纹理是和顶点绑定的,一个顶点就有一个纹理坐标。读取顶点数据同时就要读取纹理坐标。
然后再实际渲染之前,设置相应的纹理对象。
这个是dx的纹理处理方案,opgl也应该差不多。
5、在initCommon接下来的继续读取中,我发现了一件事。那就是在读取attachments、colors和transparency的时候,initCommon先把数据读入ModelAttachmentDef、ModelColorDef和ModelTransDef等结构中,然后再读入到ModelAttachment、ModelColor和ModelTransparency这些具体的、直接可以使用的数据结构中。我推测带Def后缀的数据结构大部分都是一种过渡用的数据结构。
6、在initCommon之后,initAnimaited继续读取了bone、初始化了bones、texcoords、animTextures、particle systems(粒子系统)、ribbon、Camera和初始化了light。
在读取这些数据的时候,都用到了第5条所说的读取方式。
7、很多数据结构再读入数据后,里面都包含一种数据结构:Animated类。由于这写数据结构都与动画有关,所以每当读取玩自己的数据后都要调用Animated::init方法进行对自己相关的动画部分进行初始化。
8、推测AnimManager类是真正管理动画(播放、选择等)。推测ModelAnimation类读取得动画数据就是按照关键帧来排列的。
9、在ModelViewer::OnTreeSelect中load模型的所有数据之后,通过animControl->UpdateModel(canvas->model)来设置(不是渲染!)选择的非玩家角色模型;而通过charControl->UpdateModel(modelAtt)来设置(不是渲染!)玩家角色模型。
在第1条中(关于模型读取的部分),读取模型后,数据存放在Attachment*modelAtt和canvas->model中,然后如果设置(不是渲染!)非玩家角色模型就用nimControl->UpdateModel(canvas->model);设置(不是渲染!)玩家角色模型就用charControl->UpdateModel(modelAtt)。
10、真正的渲染模型工作是在ModelCanvas::OnPaint中调用ModelCanvas::Render。
然后ModelCanvas::Render中调用Attachment::draw;Attachment::draw中通过判定ModelCanvas的drawModel(bool变量,事先在ModelViewer::OnToggleCommand中给出,这个部分就是消息函数);然后Attachment::draw调用model::draw;model::draw调用Model::drawModel
最后的实际渲染在Model::drawModel中。
11、实际渲染模型的Model::drawModel中关键的参数passes,是在Model::initCommon的结尾处赋给的值,通过一个pass的值。
12、在人物自定义上,以DBCFile类为基类,CharHairGeosetsDB、CharRacesDB、CharFacialHairDB、CharClassesDB、HelmGeosetDB类为子类,利用DBCFile类的浏览器Iterator类,来操作人物的自定义。在CharControl::RefreshModel中。
for (CharHairGeosetsDB::Iterator it = hairdb.begin(); it != hairdb.end(); ++it)
这个选择人物的各种发型、面部特征的原理是,把这些特殊的发型、特征等做成一个数组,然后选好后显示其中的一个,其它的不现实,这样就达到了自定义的效果。
for (size_t j=0; j<model->geosets.size(); j++) {
if (model->geosets[j].id == id) {
model->showGeosets[j] = (cd.hairStyle==section) && showHair;
}
}
在每个子类中的// Fields 部分,都是指的dbc.mpq中每个子项内部的字段。
DBCFile::begin把data中的数据读入Iterator浏览器类中
13、CharControl::UpdateModel被ModelViewer::OnTreeSelect调用,负责自定义角色的工作,第12条所说的。
14、 在animated.h文件中的inline T interpolate(const float r, const T &v1, const T &v2)函数将v1和v2做了线性插值。整个Animated::getValue就是为了做特定的类的插值有关键帧的信息。比如做平移的变换矩阵,在2个关键帧之间用这个getValve做差值,返回去就是做成了当前的平移变换矩阵
if (trans.used) {
Vec3D tr = trans.getValue(anim, time);
m *= Matrix::newTranslation(tr);
}
15、动画中的顶点变换是在Model::animate中(Model::draw调用),先根据数据产生bone的3中变换矩阵(这里可以确定wow用的是关键帧骨骼动画技术。同时,这个工作每帧都要做),然后用这3个矩阵乘以顶点,还有法线。定点变换代码如下:
// transform vertices
ModelVertex *ov = origVertices;
for (size_t i=0,k=0; i<header.nVertices; ++i,++ov) {
Vec3D v(0,0,0), n(0,0,0);
for (size_t b=0; b<4; b++) {
if (ov->weights[b]>0) {
Vec3D tv = bones[ov->bones[b]].mat * ov->pos;
Vec3D tn = bones[ov->bones[b]].mrot * ov->normal;
v += tv * ((float)ov->weights[b] / 255.0f);
n += tn * ((float)ov->weights[b] / 255.0f);
}
}
vertices[i] = v;
if (supportVBO)
vertices[header.nVertices + i] = n.normalize(); // shouldn't these be normal by default?
else
normals[i] = n;
}
16、gl中设置环境光强度的函数是:glLightModelfv(GL_LIGHT_MODEL_AMBIENT, la)。其中la是一个Vec4D变量,是事先设定好的值,作为环境光的强度。这个函数在ModelCanvas::Render中被调用。
glEnable(GL_COLOR_MATERIAL)容许程序将材质颜色加入到当前颜色中,然后调用glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)来实际把材质颜色放入当前颜色中。接着,程序调用glColor来实际设置和操作当前颜色。
glLightf(light, GL_CONSTANT_ATTENUATION, 0.0f);
glLightf(light, GL_LINEAR_ATTENUATION, 0.7f);
glLightf(light, GL_QUADRATIC_ATTENUATION, 0.03f);
这3个语句设置了点光源和聚光灯的3个衰减因子(常数、一次系数、二次系数)
17、已经证实AnimManager::Frame就是一个动画帧序列中的某一帧,可以说是当前帧把。
更新这个Frame就可以实现动画,这里骨骼起了至关重要的作用。
18、TextureManager::add函数实际读取了纹理图片(BLP格式),在Model::initCommon中被调用
19、opgl的纹理绘制似乎是这样的:首先读取纹理文件,然后glGenTextures生成纹理对象,在要渲染之前调用glBindTexture绑定纹理,用glTexParameteri设置一些参数
20、Iterator类重载了->,重载函数返回的是(return &record)Iterator的一个属性,就是一个Record对象的指针;同时又重载了*,返回return record,所以能够让*i返回为Record对象
21、在CharControl::Init中初始化了hairdb、chardb等这些要读取dbc.mpq文件包的数据结构,通过DBCFile::open方法读取。而CharControl类的那些hairdb、chardb等对象都是在其构造函数中直接写明了自己所要读取的文件数据的路径,由于这些对象是DBCFile类的字类,所以直接在自己构造函数后调用父类的构造函数来把自己的读取文件数据的路径告诉父类(CharSectionsDB(): DBCFile("DBFilesClient\\CharSections.dbc") {}),以便让父类在其open方法中直接使用这个路径为他们准确的读取数据。
Character\Scourge\Male\ScourgeMale.m2
Record类是最终程序直接使用的dbc.mpq中数据的数据结构。
CharRacesDB::Record CharRacesDB::getByName(wxString name)
{
for(Iterator i=begin(); i!=end(); ++i)
{
if (name.IsSameAs(i->getString(Name),false) == true)
return (*i);
}
throw NotFound();
}
类似这样的程序段都是把当前的DBCFile放到i中,然后返回。这样Record类就能发挥储存器的作用。
22、自定义角色的外貌是通过cd这个变量,事先再UpdateModel里设定的,然后再通过RefreshModel读取实际的blp纹理文件。先通过CharSectionsDB::Record rec = chardb.getByParams这样的函数来从dbc.mpq的脚本文件里读取相应的纹理脚本数据;然后通过rec.getString翻译这些脚本语句,接着通过furtex = texturemanager.add来实际读取纹理数据
我只要只改写实际读取纹理的函数就可以了,就是在texturemanager.add中的LoadBlp函数,在这里应该改写成d3d的api:CreateTextureFromFileEx
23、Model::animate中的顶点变换(15中提到的),每个顶点被4个骨骼所影响,所以在变换的时候分别把影响每个顶点的骨骼矩阵都乘以顶点向量,然后在加合,这样就把4个骨骼的影响合在一起了。
for (size_t b=0; b<4; b++)
{
if (ov->weights[b]>0)
{
Vec3D tv = bones[ov->bones[b]].mat * ov->pos;
Vec3D tn = bones[ov->bones[b]].mrot * ov->normal;
v += tv * ((float)ov->weights[b] / 255.0f);
n += tn * ((float)ov->weights[b] / 255.0f);
}
}
以上代码中的v和n,就是用来整合影响每个顶点的4个骨骼的影响的。
24、ModelGeoset结构就是设置人物角色自定义的数据结构。CCharModel::InitCommon函数的最后创建了一系列的ModelRenderPass结构,我推测每一个此结构的对象都是一种角色自定义中的一种具体的实例,每一个都是占据了顶点数据中的一段(pass.indexstart)
25、原来渲染角色的方式是通过依次渲染模型的sub部分来实现的,24中所说的ModelRenderPass结构就是用来储存每个sub部分的数据结构的。然后在实际渲染的时候(Model::drawModel函数负责实际渲染工作),通过其一个方法init(Model*m)来选择是否渲染当前的sub部分,在init方法的最后:
return m->showGeosets[geoset] && ( (ocol.w > 0) && (color==-1 || (ecol.w > 0)) );
其中的m->showGeosets[geoset]就是Model类中的选择人物模型的sub部分的标志结构,这个结构在CharControl的UpdateModel方法和RefreshModel中被设置,这2个方法自定义了模型的实际外貌
26、类Attachment就是装备(手上的武器和副手,或者披风等)的数据结构(里面包含一个model类指针),渲染的时候每个Attachment对象实例就包含一个Model对象实例,就是说单独依次渲染每个Attachment里的Model对象,然后根据主Model(人物角色Model)的坐标来给这些附属Model对象来进行世界坐标变换,这样就能解释Attachment::draw中的setup的作用(包含了ModelAttachment::setup),再ModelAttachment::setup中包含了一个世界坐标变换,就很有可能是根据人物角色的坐标来变换这些附属模型(手上的武器和副手,或者披风等)的坐标
27、在CCharModel::DrawModel中的
m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
0,
0,
// number of vertices
p.vertexEnd - p.vertexStart,
p.indexStart,
// number of primitives
p.indexCount/3);
函数,最后一个参数,这里需要将p.indexCount除以3,因为这个indexCount是索引的数量,而不是要渲染的图元(三角形)的数量(3个顶点一个三角形)
28、CharTexture::compose函数,就是在CharControl::RefreshModel后部调用的、用来混合纹理的函数,有这样一段代码:
TextureID temptex = texturemanager.add(comp.name);
Texture &tex = *((Texture*)texturemanager.items[temptex]);
第一行用来从事先读取号、创建好的纹理储存器中(TextureManager类)通过从std::vector<CharTextureComponent>容器(事先add纹理层的容器类)通过纹理名字(comp.name)来读取id(temptex),然后通过这个id调用真正存储这纹理数据的TextureManager类中的纹理数据。
29、CharControl::RefreshModel()中的代码:
// select hairstyle geoset(s)
for (CharHairGeosetsDB::Iterator it = hairdb.begin(); it != hairdb.end(); ++it) {
if (it->getUInt(CharHairGeosetsDB::Race)==cd.race && it->getUInt(CharHairGeosetsDB::Gender)==cd.gender) {
unsigned int id = it->getUInt(CharHairGeosetsDB::Geoset);
unsigned int section = it->getUInt(CharHairGeosetsDB::Section);
if (id!=0) {
for (size_t j=0; j<model->geosets.size(); j++) {
if (model->geosets[j].id == id) {
//std::cout << "Hair:\t" << id << "\t" << section << "\t" << ((cd.hairStyle==section) && cd.showHair) << "\n";
model->showGeosets[j] = (cd.hairStyle==section) && showHair;
}
}
} else if (cd.hairStyle==section)
bald = true;
}
}
CharHairGeosetsDB::Geoset这个field值的意思是:当前record属于角色模型那个sub类(比如头发?胡须?等等);
CharHairGeosetsDB::Section这个field值的意思是:当前record所储存的某类sub中是哪种,比如头发sub类中的第几种头型;
30、CharControl::RefreshItem()是用来处理头部、肩部、双手处的模型的——需要用另外的模型文件——相对于手套、鞋子和衣服等直接从人物模型本身就能获取的;
而CharControl::AddEquipment()则处理的是手套、鞋子和衣服等直接从人物模型本身就能获取的模型装备的添加、删除修改等
31、当用户选择了装备时候,调用CharControl::OnUpdateItem(),然后事件设为UPDATE_ITEM,执行如下代码:
switch (type) {
case UPDATE_ITEM:
cd.equipment[choosingSlot] = numbers[id];
if (slotHasModel(choosingSlot))
RefreshItem(choosingSlot);
choosingSlot就是角色的装备槽代号,比如肩膀、头部、双手等等;cd.equipment[choosingSlot]就是用装备槽代号代表的装备序列号(每个装备单独的id号,是唯一的)
WoWModelViewer分析
终于完成魔兽世界的换装系统
2009-7-12 Sunday
从google code上svn最新的wowmodelviewer,用vs2008生成直接通过,不需要任何改动!我靠!0.48e,0.5.08,……唉!折腾啊!
就是装个VS2008费劲啊!幸亏以前大林同志给了个VS2008的安装文件(现在找不到了),装在家里笔记本上体验了下然后一直没用,这次派上用场了!公司的外网机懒得装了!
posted on 2009-07-03 01:12
七星重剑
阅读(4836)
评论(0)
编辑
收藏
引用
所属分类:
Game Graphics
只有注册用户
登录
后才能发表评论。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
相关文章:
理解D3D--(0)批次batch
《天骄3》不错
Vertex Formats
3D地形多层纹理混合加阴影渲染方法
在游戏全屏模式下调试的解决办法
《Advanced Animation with DirectX》源码编译不过的解决办法
WildMagic4p7
野猪写的《游戏程序中的骨骼插件》
WOW m2模型与WowModelViewer
图形学扫盲--(6)凹凸贴图Bump mapping
网站导航:
博客园
IT新闻
BlogJava
知识库
博问
管理