青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 505  文章 - 1034  trackbacks - 0
<2007年10月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910


子曾經曰過:編程無他,唯手熟爾!

常用鏈接

留言簿(94)

隨筆分類(649)

隨筆檔案(505)

相冊

BCB

Crytek

  • crymod
  • Crytek's Offical Modding Portal

Game Industry

OGRE

other

Programmers

Qt

WOW Stuff

搜索

  •  

積分與排名

  • 積分 - 918858
  • 排名 - 14

最新隨筆

最新評論

閱讀排行榜

評論排行榜

好長時間沒學shader了,拿這個物件的邊緣高亮(Entity edge highlight) 練習了下,用render monkey,參照逍遙劍客的blog,很快就看到效果了,但是在現在的項目中實現的話有點麻煩,主要是對目前項目的Model還不熟悉,shader是怎么用的也不清楚,看了好幾天,依然是迷迷糊糊,真他媽的菜啊!

現在的項目兼容m2模型,因為這引擎朝哥寫的時候用的就是wow的資源,呵呵,山寨版wow。所以搞清楚了m2模型也就搞清楚了目前項目的model.開始看WowModelViewer.

MPQ Archives  The StormLib library




魔獸世界m2模型文件分析及wowModelView代碼閱讀心得

 

Title1、由于是瀏覽器,所以讀取數據的函數再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 七星重劍 閱讀(4943) 評論(0)  編輯 收藏 引用 所屬分類: Game Graphics
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            夜夜嗨av色一区二区不卡| 欧美成人中文字幕| 欧美一区二区在线播放| 亚洲午夜国产一区99re久久| 狠狠爱综合网| 在线成人中文字幕| 亚洲国产视频一区| 夜夜嗨av一区二区三区| 99视频精品在线| 亚洲欧美在线免费观看| 久久深夜福利免费观看| 久久―日本道色综合久久| 久久久av水蜜桃| 欧美激情欧美狂野欧美精品| 亚洲国产二区| 亚洲国产高潮在线观看| 宅男精品视频| 欧美大尺度在线| 国产精品婷婷| 亚洲乱码国产乱码精品精98午夜| 99re66热这里只有精品4| 欧美自拍丝袜亚洲| 国产精品成人国产乱一区| 国产综合婷婷| 欧美中文字幕视频| 一区二区日韩伦理片| 另类天堂av| 亚洲成人在线| 久久国产精品久久久| 亚洲精品九九| 欧美日韩日本国产亚洲在线| 尤物精品在线| 蜜桃av噜噜一区二区三区| 亚洲在线视频一区| 国产精品卡一卡二| 午夜亚洲伦理| 99国产成+人+综合+亚洲欧美| 欧美成人蜜桃| 一区二区免费看| 日韩视频亚洲视频| 久久天天狠狠| 亚洲三级视频在线观看| 日韩写真在线| 国产欧美精品国产国产专区| 欧美一级久久久| 久久99在线观看| 亚洲第一精品夜夜躁人人躁| 亚洲激情校园春色| 国产精品久久久久久av福利软件 | 99国产一区| 一区二区三区四区五区精品| 亚洲经典在线| 国产精品久久午夜| 久久亚洲国产成人| 欧美日韩dvd在线观看| 午夜精品视频在线| 欧美不卡在线视频| 欧美一区二区三区四区在线观看地址| 亚洲视频在线二区| 樱花yy私人影院亚洲| 亚洲少妇最新在线视频| 黄色成人免费观看| 亚洲最新色图| 亚洲日本电影在线| 久久久久久久999| 亚洲免费影院| 欧美日韩在线播放三区四区| 免费国产自线拍一欧美视频| 国产精品hd| 亚洲视频福利| 亚洲一区二区三区视频| 欧美1区3d| 亚洲欧洲精品一区| 亚洲国产精品综合| 久久久国产精品一区二区三区| 久久精品亚洲| 久久久久国产一区二区三区四区| 国产精品视频精品| 亚洲欧美日韩天堂一区二区| 亚洲一区二区不卡免费| 欧美午夜在线| 性色一区二区| 久久综合九色综合欧美就去吻| 国产日韩精品综合网站| 亚洲欧美精品中文字幕在线| 久久精品国语| 亚洲国内欧美| 亚洲精品在线观看免费| 欧美jizz19hd性欧美| 日韩亚洲欧美成人一区| 亚洲欧美精品在线观看| 国产日韩在线视频| 免费在线看一区| 亚洲一区二区综合| 可以免费看不卡的av网站| 一本久道久久综合婷婷鲸鱼| 国产欧美亚洲一区| 日韩天堂av| 久久综合亚洲社区| 亚洲一区二区三区在线视频| 红桃视频国产精品| 国产精品红桃| 欧美福利一区二区三区| 欧美伊人久久大香线蕉综合69| 欧美激情在线狂野欧美精品| 亚洲欧美国产日韩中文字幕| 亚洲黄色在线看| 精品成人在线视频| 国产亚洲精品一区二555| 欧美午夜精品久久久久久人妖| 久久精品一区二区国产| 一本大道久久a久久精二百| 亚洲黄色有码视频| 亚洲国产视频一区二区| 麻豆精品传媒视频| 久久偷看各类wc女厕嘘嘘偷窃| 午夜精品久久久久久99热软件| 一本久道久久综合婷婷鲸鱼 | 欧美专区亚洲专区| 久久精品日产第一区二区| 亚洲欧美在线一区二区| 亚洲欧美日韩国产一区二区| 亚洲综合激情| 欧美综合二区| 欧美国产精品久久| 欧美日韩一区三区| 亚洲激情一区| 亚洲在线视频| 久久美女艺术照精彩视频福利播放| 久久久久久噜噜噜久久久精品| 久久综合久久久久88| 欧美女同在线视频| 黄色成人在线网站| 亚洲四色影视在线观看| 欧美一区二区免费视频| 久久噜噜噜精品国产亚洲综合 | 亚洲成人在线观看视频| 亚洲黄色精品| 欧美在线日韩在线| 亚洲日本乱码在线观看| 欧美一级淫片aaaaaaa视频| 欧美专区日韩视频| 欧美亚州在线观看| 亚洲国产精品高清久久久| 午夜精品视频在线观看| 亚洲国产你懂的| 久久一区二区精品| 国产亚洲综合在线| 午夜视频在线观看一区二区| 亚洲人成网站精品片在线观看| 久久gogo国模裸体人体| 欧美日韩另类字幕中文| 亚洲国产裸拍裸体视频在线观看乱了| 欧美影院一区| 午夜欧美理论片| 国产自产v一区二区三区c| 久久精品免费播放| 欧美在线欧美在线| 国产真实久久| 亚洲精品国产视频| 欧美视频精品在线| 久久精品国产96久久久香蕉| 欧美一区二区三区在线观看视频 | 韩日成人av| 免费不卡中文字幕视频| 久久综合久久综合这里只有精品 | 久久国内精品视频| 亚洲欧美激情视频| 国产精品久久久久av| 亚洲精选中文字幕| 亚洲国产岛国毛片在线| 欧美a级理论片| 亚洲高清不卡一区| 亚洲国产欧美一区二区三区同亚洲| 久久裸体艺术| 91久久久在线| 亚洲精品视频免费在线观看| 午夜激情综合网| 在线观看日韩av电影| 亚洲人成在线观看| 国产精品久久中文| 麻豆精品网站| 欧美日本国产在线| 久久九九精品99国产精品| 久久免费高清| 午夜精品久久久久99热蜜桃导演| 亚洲色图自拍| 日韩午夜在线播放| 欧美一级淫片aaaaaaa视频| 亚洲另类一区二区| 久久久五月婷婷| 欧美亚洲系列| 欧美区二区三区| 久久最新视频| 国产欧美欧洲在线观看| 亚洲伦理在线观看| 亚洲黄色免费网站| 欧美在线亚洲一区| 久热精品视频在线观看一区| 国产精品区二区三区日本|