• <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>
            隨筆 - 505  文章 - 1034  trackbacks - 0
            <2008年1月>
            303112345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789


            子曾經(jīng)曰過:編程無他,唯手熟爾!

            常用鏈接

            留言簿(94)

            隨筆分類(649)

            隨筆檔案(505)

            相冊(cè)

            BCB

            Crytek

            • crymod
            • Crytek's Offical Modding Portal

            Game Industry

            OGRE

            other

            Programmers

            Qt

            WOW Stuff

            搜索

            •  

            積分與排名

            • 積分 - 917347
            • 排名 - 14

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

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

            現(xiàn)在的項(xiàng)目兼容m2模型,因?yàn)檫@引擎朝哥寫的時(shí)候用的就是wow的資源,呵呵,山寨版wow。所以搞清楚了m2模型也就搞清楚了目前項(xiàng)目的model.開始看WowModelViewer.

            MPQ Archives  The StormLib library




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

             

            Title1、由于是瀏覽器,所以讀取數(shù)據(jù)的函數(shù)再M(fèi)odelViewer::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類的一個(gè)屬性;
            然后ModelCanvas::LoadCharModel再調(diào)用ModelCanvas::LoadModel方法讀取模型數(shù)據(jù);
            ModelCanvas::LoadModel新建了一個(gè)Modal,把傳入的文件地址const char *fn傳給了Modal的構(gòu)造函數(shù)。
            Modal的構(gòu)造函數(shù)通過新建一個(gè)MPQFile類: MPQFile f(tempname);來讀取模型文件數(shù)據(jù);
            MPQFile構(gòu)造函數(shù)中用file.Read(buffer, size)實(shí)際讀取了模型文件;
            然后再M(fèi)odel::isAnimated中讀取了骨骼、頂點(diǎn)等模型數(shù)據(jù)(通過從上一條中的 buffer=后來的header 中拷貝);

            2、這個(gè)瀏覽器是用opgl來開發(fā)圖像部分的,瀏覽器有一些坐標(biāo)轉(zhuǎn)換函數(shù),瀏覽器中的轉(zhuǎn)換函數(shù)在:
            model.cpp中的Vec3D fixCoordSystem(Vec3D v)函數(shù)。
            調(diào)用這個(gè)函數(shù)的函數(shù)是:Model::initCommon,initAnimated函數(shù)調(diào)用的它。
            而initAnimated函數(shù)就在Model::isAnimated之后不久調(diào)用,被Model的構(gòu)造函數(shù)調(diào)用。
              if (animated)
                  initAnimated(f);

            3、在initCommon中,依次讀取vertices,normals,bounds(包括boundTris),textures
            這些都經(jīng)過了上一條說的fixCoordSystem坐標(biāo)轉(zhuǎn)換。

            4、紋理是和頂點(diǎn)綁定的,一個(gè)頂點(diǎn)就有一個(gè)紋理坐標(biāo)。讀取頂點(diǎn)數(shù)據(jù)同時(shí)就要讀取紋理坐標(biāo)。
            然后再實(shí)際渲染之前,設(shè)置相應(yīng)的紋理對(duì)象。
            這個(gè)是dx的紋理處理方案,opgl也應(yīng)該差不多。

            5、在initCommon接下來的繼續(xù)讀取中,我發(fā)現(xiàn)了一件事。那就是在讀取attachments、colors和transparency的時(shí)候,initCommon先把數(shù)據(jù)讀入ModelAttachmentDef、ModelColorDef和ModelTransDef等結(jié)構(gòu)中,然后再讀入到ModelAttachment、ModelColor和ModelTransparency這些具體的、直接可以使用的數(shù)據(jù)結(jié)構(gòu)中。我推測(cè)帶Def后綴的數(shù)據(jù)結(jié)構(gòu)大部分都是一種過渡用的數(shù)據(jù)結(jié)構(gòu)。

            6、在initCommon之后,initAnimaited繼續(xù)讀取了bone、初始化了bones、texcoords、animTextures、particle systems(粒子系統(tǒng))、ribbon、Camera和初始化了light。
            在讀取這些數(shù)據(jù)的時(shí)候,都用到了第5條所說的讀取方式。

            7、很多數(shù)據(jù)結(jié)構(gòu)再讀入數(shù)據(jù)后,里面都包含一種數(shù)據(jù)結(jié)構(gòu):Animated類。由于這寫數(shù)據(jù)結(jié)構(gòu)都與動(dòng)畫有關(guān),所以每當(dāng)讀取玩自己的數(shù)據(jù)后都要調(diào)用Animated::init方法進(jìn)行對(duì)自己相關(guān)的動(dòng)畫部分進(jìn)行初始化。

            8、推測(cè)AnimManager類是真正管理動(dòng)畫(播放、選擇等)。推測(cè)ModelAnimation類讀取得動(dòng)畫數(shù)據(jù)就是按照關(guān)鍵幀來排列的。

            9、在ModelViewer::OnTreeSelect中l(wèi)oad模型的所有數(shù)據(jù)之后,通過animControl->UpdateModel(canvas->model)來設(shè)置(不是渲染!)選擇的非玩家角色模型;而通過charControl->UpdateModel(modelAtt)來設(shè)置(不是渲染!)玩家角色模型。
            在第1條中(關(guān)于模型讀取的部分),讀取模型后,數(shù)據(jù)存放在Attachment*modelAtt和canvas->model中,然后如果設(shè)置(不是渲染!)非玩家角色模型就用nimControl->UpdateModel(canvas->model);設(shè)置(不是渲染!)玩家角色模型就用charControl->UpdateModel(modelAtt)。

            10、真正的渲染模型工作是在ModelCanvas::OnPaint中調(diào)用ModelCanvas::Render。
            然后ModelCanvas::Render中調(diào)用Attachment::draw;Attachment::draw中通過判定ModelCanvas的drawModel(bool變量,事先在ModelViewer::OnToggleCommand中給出,這個(gè)部分就是消息函數(shù));然后Attachment::draw調(diào)用model::draw;model::draw調(diào)用Model::drawModel
            最后的實(shí)際渲染在Model::drawModel中。

            11、實(shí)際渲染模型的Model::drawModel中關(guān)鍵的參數(shù)passes,是在Model::initCommon的結(jié)尾處賦給的值,通過一個(gè)pass的值。

            12、在人物自定義上,以DBCFile類為基類,CharHairGeosetsDB、CharRacesDB、CharFacialHairDB、CharClassesDB、HelmGeosetDB類為子類,利用DBCFile類的瀏覽器Iterator類,來操作人物的自定義。在CharControl::RefreshModel中。
            for (CharHairGeosetsDB::Iterator it = hairdb.begin(); it != hairdb.end(); ++it)
            這個(gè)選擇人物的各種發(fā)型、面部特征的原理是,把這些特殊的發(fā)型、特征等做成一個(gè)數(shù)組,然后選好后顯示其中的一個(gè),其它的不現(xiàn)實(shí),這樣就達(dá)到了自定義的效果。
            for (size_t j=0; j<model->geosets.size(); j++) {
                if (model->geosets[j].id == id) {
             model->showGeosets[j] = (cd.hairStyle==section) && showHair;
                }
            }
            在每個(gè)子類中的// Fields 部分,都是指的dbc.mpq中每個(gè)子項(xiàng)內(nèi)部的字段。
            DBCFile::begin把data中的數(shù)據(jù)讀入Iterator瀏覽器類中

            13、CharControl::UpdateModel被ModelViewer::OnTreeSelect調(diào)用,負(fù)責(zé)自定義角色的工作,第12條所說的。

            14、 在animated.h文件中的inline T interpolate(const float r, const T &v1, const T &v2)函數(shù)將v1和v2做了線性插值。整個(gè)Animated::getValue就是為了做特定的類的插值有關(guān)鍵幀的信息。比如做平移的變換矩陣,在2個(gè)關(guān)鍵幀之間用這個(gè)getValve做差值,返回去就是做成了當(dāng)前的平移變換矩陣
            if (trans.used) {
                Vec3D tr = trans.getValue(anim, time);
                m *= Matrix::newTranslation(tr);
            }

            15、動(dòng)畫中的頂點(diǎn)變換是在Model::animate中(Model::draw調(diào)用),先根據(jù)數(shù)據(jù)產(chǎn)生bone的3中變換矩陣(這里可以確定wow用的是關(guān)鍵幀骨骼動(dòng)畫技術(shù)。同時(shí),這個(gè)工作每幀都要做),然后用這3個(gè)矩陣乘以頂點(diǎn),還有法線。定點(diǎn)變換代碼如下:
            // 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中設(shè)置環(huán)境光強(qiáng)度的函數(shù)是:glLightModelfv(GL_LIGHT_MODEL_AMBIENT, la)。其中l(wèi)a是一個(gè)Vec4D變量,是事先設(shè)定好的值,作為環(huán)境光的強(qiáng)度。這個(gè)函數(shù)在ModelCanvas::Render中被調(diào)用。
            glEnable(GL_COLOR_MATERIAL)容許程序?qū)⒉馁|(zhì)顏色加入到當(dāng)前顏色中,然后調(diào)用glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)來實(shí)際把材質(zhì)顏色放入當(dāng)前顏色中。接著,程序調(diào)用glColor來實(shí)際設(shè)置和操作當(dāng)前顏色。
            glLightf(light, GL_CONSTANT_ATTENUATION, 0.0f);
            glLightf(light, GL_LINEAR_ATTENUATION, 0.7f);
            glLightf(light, GL_QUADRATIC_ATTENUATION, 0.03f);
            這3個(gè)語句設(shè)置了點(diǎn)光源和聚光燈的3個(gè)衰減因子(常數(shù)、一次系數(shù)、二次系數(shù))

            17、已經(jīng)證實(shí)AnimManager::Frame就是一個(gè)動(dòng)畫幀序列中的某一幀,可以說是當(dāng)前幀把。
            更新這個(gè)Frame就可以實(shí)現(xiàn)動(dòng)畫,這里骨骼起了至關(guān)重要的作用。

            18、TextureManager::add函數(shù)實(shí)際讀取了紋理圖片(BLP格式),在Model::initCommon中被調(diào)用

            19、opgl的紋理繪制似乎是這樣的:首先讀取紋理文件,然后glGenTextures生成紋理對(duì)象,在要渲染之前調(diào)用glBindTexture綁定紋理,用glTexParameteri設(shè)置一些參數(shù)

            20、Iterator類重載了->,重載函數(shù)返回的是(return &record)Iterator的一個(gè)屬性,就是一個(gè)Record對(duì)象的指針;同時(shí)又重載了*,返回return record,所以能夠讓*i返回為Record對(duì)象

            21、在CharControl::Init中初始化了hairdb、chardb等這些要讀取dbc.mpq文件包的數(shù)據(jù)結(jié)構(gòu),通過DBCFile::open方法讀取。而CharControl類的那些hairdb、chardb等對(duì)象都是在其構(gòu)造函數(shù)中直接寫明了自己所要讀取的文件數(shù)據(jù)的路徑,由于這些對(duì)象是DBCFile類的字類,所以直接在自己構(gòu)造函數(shù)后調(diào)用父類的構(gòu)造函數(shù)來把自己的讀取文件數(shù)據(jù)的路徑告訴父類(CharSectionsDB(): DBCFile("DBFilesClient\\CharSections.dbc") {}),以便讓父類在其open方法中直接使用這個(gè)路徑為他們準(zhǔn)確的讀取數(shù)據(jù)。
            Character\Scourge\Male\ScourgeMale.m2
            Record類是最終程序直接使用的dbc.mpq中數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。
            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();
            }
            類似這樣的程序段都是把當(dāng)前的DBCFile放到i中,然后返回。這樣Record類就能發(fā)揮儲(chǔ)存器的作用。

            22、自定義角色的外貌是通過cd這個(gè)變量,事先再UpdateModel里設(shè)定的,然后再通過RefreshModel讀取實(shí)際的blp紋理文件。先通過CharSectionsDB::Record rec = chardb.getByParams這樣的函數(shù)來從dbc.mpq的腳本文件里讀取相應(yīng)的紋理腳本數(shù)據(jù);然后通過rec.getString翻譯這些腳本語句,接著通過furtex = texturemanager.add來實(shí)際讀取紋理數(shù)據(jù)
            我只要只改寫實(shí)際讀取紋理的函數(shù)就可以了,就是在texturemanager.add中的LoadBlp函數(shù),在這里應(yīng)該改寫成d3d的api:CreateTextureFromFileEx

            23、Model::animate中的頂點(diǎn)變換(15中提到的),每個(gè)頂點(diǎn)被4個(gè)骨骼所影響,所以在變換的時(shí)候分別把影響每個(gè)頂點(diǎn)的骨骼矩陣都乘以頂點(diǎn)向量,然后在加合,這樣就把4個(gè)骨骼的影響合在一起了。
            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,就是用來整合影響每個(gè)頂點(diǎn)的4個(gè)骨骼的影響的。

            24、ModelGeoset結(jié)構(gòu)就是設(shè)置人物角色自定義的數(shù)據(jù)結(jié)構(gòu)。CCharModel::InitCommon函數(shù)的最后創(chuàng)建了一系列的ModelRenderPass結(jié)構(gòu),我推測(cè)每一個(gè)此結(jié)構(gòu)的對(duì)象都是一種角色自定義中的一種具體的實(shí)例,每一個(gè)都是占據(jù)了頂點(diǎn)數(shù)據(jù)中的一段(pass.indexstart)

            25、原來渲染角色的方式是通過依次渲染模型的sub部分來實(shí)現(xiàn)的,24中所說的ModelRenderPass結(jié)構(gòu)就是用來儲(chǔ)存每個(gè)sub部分的數(shù)據(jù)結(jié)構(gòu)的。然后在實(shí)際渲染的時(shí)候(Model::drawModel函數(shù)負(fù)責(zé)實(shí)際渲染工作),通過其一個(gè)方法init(Model*m)來選擇是否渲染當(dāng)前的sub部分,在init方法的最后:
            return m->showGeosets[geoset] && ( (ocol.w > 0) && (color==-1 || (ecol.w > 0)) );
            其中的m->showGeosets[geoset]就是Model類中的選擇人物模型的sub部分的標(biāo)志結(jié)構(gòu),這個(gè)結(jié)構(gòu)在CharControl的UpdateModel方法和RefreshModel中被設(shè)置,這2個(gè)方法自定義了模型的實(shí)際外貌

            26、類Attachment就是裝備(手上的武器和副手,或者披風(fēng)等)的數(shù)據(jù)結(jié)構(gòu)(里面包含一個(gè)model類指針),渲染的時(shí)候每個(gè)Attachment對(duì)象實(shí)例就包含一個(gè)Model對(duì)象實(shí)例,就是說單獨(dú)依次渲染每個(gè)Attachment里的Model對(duì)象,然后根據(jù)主Model(人物角色Model)的坐標(biāo)來給這些附屬M(fèi)odel對(duì)象來進(jìn)行世界坐標(biāo)變換,這樣就能解釋Attachment::draw中的setup的作用(包含了ModelAttachment::setup),再M(fèi)odelAttachment::setup中包含了一個(gè)世界坐標(biāo)變換,就很有可能是根據(jù)人物角色的坐標(biāo)來變換這些附屬模型(手上的武器和副手,或者披風(fēng)等)的坐標(biāo)

            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);
            函數(shù),最后一個(gè)參數(shù),這里需要將p.indexCount除以3,因?yàn)檫@個(gè)indexCount是索引的數(shù)量,而不是要渲染的圖元(三角形)的數(shù)量(3個(gè)頂點(diǎn)一個(gè)三角形)

            28、CharTexture::compose函數(shù),就是在CharControl::RefreshModel后部調(diào)用的、用來混合紋理的函數(shù),有這樣一段代碼:
            TextureID temptex = texturemanager.add(comp.name);
            Texture &tex = *((Texture*)texturemanager.items[temptex]);
            第一行用來從事先讀取號(hào)、創(chuàng)建好的紋理儲(chǔ)存器中(TextureManager類)通過從std::vector<CharTextureComponent>容器(事先add紋理層的容器類)通過紋理名字(comp.name)來讀取id(temptex),然后通過這個(gè)id調(diào)用真正存儲(chǔ)這紋理數(shù)據(jù)的TextureManager類中的紋理數(shù)據(jù)。

            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這個(gè)field值的意思是:當(dāng)前record屬于角色模型那個(gè)sub類(比如頭發(fā)?胡須?等等);
            CharHairGeosetsDB::Section這個(gè)field值的意思是:當(dāng)前record所儲(chǔ)存的某類sub中是哪種,比如頭發(fā)sub類中的第幾種頭型;

            30、CharControl::RefreshItem()是用來處理頭部、肩部、雙手處的模型的——需要用另外的模型文件——相對(duì)于手套、鞋子和衣服等直接從人物模型本身就能獲取的;
            而CharControl::AddEquipment()則處理的是手套、鞋子和衣服等直接從人物模型本身就能獲取的模型裝備的添加、刪除修改等

            31、當(dāng)用戶選擇了裝備時(shí)候,調(diào)用CharControl::OnUpdateItem(),然后事件設(shè)為UPDATE_ITEM,執(zhí)行如下代碼:
            switch (type) {
            case UPDATE_ITEM:
             cd.equipment[choosingSlot] = numbers[id];
             if (slotHasModel(choosingSlot))
              RefreshItem(choosingSlot);
            choosingSlot就是角色的裝備槽代號(hào),比如肩膀、頭部、雙手等等;cd.equipment[choosingSlot]就是用裝備槽代號(hào)代表的裝備序列號(hào)(每個(gè)裝備單獨(dú)的id號(hào),是唯一的)


            原創(chuàng) WoWModelViewer分析

            終于完成魔獸世界的換裝系統(tǒng)

            2009-7-12 Sunday

            從google code上svn最新的wowmodelviewer,用vs2008生成直接通過,不需要任何改動(dòng)!我靠!0.48e,0.5.08,……唉!折騰啊!
            就是裝個(gè)VS2008費(fèi)勁啊!幸虧以前大林同志給了個(gè)VS2008的安裝文件(現(xiàn)在找不到了),裝在家里筆記本上體驗(yàn)了下然后一直沒用,這次派上用場了!公司的外網(wǎng)機(jī)懶得裝了!

             

            posted on 2009-07-03 01:12 七星重劍 閱讀(4939) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Game Graphics
            欧美一区二区三区久久综合 | 精品久久久无码21p发布| 污污内射久久一区二区欧美日韩| 久久久久九九精品影院| 久久婷婷五月综合色奶水99啪| 18禁黄久久久AAA片| 久久精品免费观看| 亚洲精品乱码久久久久久久久久久久| 久久影院综合精品| 亚洲性久久久影院| 亚洲国产精久久久久久久| 久久国内免费视频| 岛国搬运www久久| 久久精品国产亚洲av水果派| 日韩久久久久中文字幕人妻| 亚洲乱亚洲乱淫久久| 色偷偷偷久久伊人大杳蕉| 日韩欧美亚洲国产精品字幕久久久| 亚洲va中文字幕无码久久| 久久无码人妻精品一区二区三区 | 国产69精品久久久久久人妻精品| 国产免费福利体检区久久| 中文字幕乱码久久午夜| 国产69精品久久久久APP下载| 女人香蕉久久**毛片精品| 久久久久久亚洲AV无码专区| 精品伊人久久久| 久久久久久午夜精品| 久久久久国产精品三级网| 亚洲国产天堂久久综合网站| 99久久人妻无码精品系列| 香蕉久久av一区二区三区| 久久狠狠爱亚洲综合影院| 久久精品免费全国观看国产| 国产精品99久久久精品无码| 久久久精品国产| 精产国品久久一二三产区区别 | 久久精品a亚洲国产v高清不卡| 国产精品中文久久久久久久| 97精品依人久久久大香线蕉97 | 99精品国产99久久久久久97|