詳細講解3DMAX匯出外掛-tiamo(轉)
詳細講解3DMAX匯出外掛-tiamo(轉)[@more@] 3dmax的匯出外掛是用來把做好的3d模型匯出成自己引擎需要的格式的一個dll,它由3dmax載入呼叫.具體怎樣去寫一個外掛,小T不多說,在3dmax的sdk裡面有比較詳細的介紹,在google上面也能搜尋到不少的原始碼,這裡說的只是3dmax的資料組織方式,以及怎麼獲取轉換3dmax的資料. 3dmax裡面一個比較重要的概念就是INode,3dmax的場景模型都是由一個個的INode組成,這些INode構成一棵體系樹,而各個真實的模型都是附著到一個INode上面的,3dmax的sdk提供了怎樣獲取INode指標,怎樣獲取INode的幾個Matrix的方法,這個能在max的sdk裡面找到,也不是小T這次主要談的東西.獲取了相應的Matrix以後,用INode的EvalWorldState等等函式就能獲取到附著在這個INode上面的geom object,然後能獲取到vertex資訊,face資訊,material資訊,這些都相對容易,隨便的一個匯出外掛的例子都會有提到這些方法,小T也不多少.說了半天,小T究竟想說什麼呢?嘿嘿.一個是skin mesh的weight資料獲取,一個是keyframe的control資料獲取以及3dmax的幾種不同的control的keyframe的插值方法. 先說skin mesh的weight table資料.X檔案的匯出外掛裡面使用的skin工具屬於charactor studio(cs)的一個部分,小T沒有找到合適的cs安裝,所以小T自己的外掛不準備支援cs,小T推薦的也是唯一支援的工具是3dmax5自帶的skin工具.下面說的就是skin工具的資料獲取.skin這個工作在3dmax裡面被稱為了modifier,3dmax對於每一個object都維護一個modifier stack(關於這個方面的詳細資訊可以檢視3dmax的sdk,或者使用google),現在首先要作的就是獲取到skin這個modifier的介面指標ISkin.--->使用GetModifier函式一一遍歷每個modifier,檢查它的class id是不是SKIN_CLASSID,然後呼叫GetInterface獲得ISkin的指標,透過這個指標呼叫GetContextInterface獲取ISkinContextData指標,這個指標裡面就維護了weight table.首先呼叫ISkinContextData指標的GetNumAssignedBones,傳人vertex的id(從face的資料裡面獲得這個id),得到了影響這個vertex的bone的數目,然後從0到bone數目減1,一一呼叫GetAssignedBone,傳人vertex的id和bone index,得到bone id,然後使用ISkin的GetBone傳人bone id獲得bone的INode指標,然後呼叫ISkinContextData的GetBoneWeight傳人vertex的id和bone的index,就能獲得weight資料.有點亂,貼程式碼上來. // get weights ,CFace is a class that hold face info,i0,i1,i2 is face's vertexes id void CExporter::GetWeights(CFace* pFace,INode *pNode,Mesh *pMesh,int i0,int i1,int i2) { // find skin modifier Object *pObject = pNode->GetObjectRef(); if (pObject->SuperClassID() == GEN_DERIVOB_CLASS_ID) { IDerivedObject *pDerivedObject = (IDerivedObject *)pObject; int nMod = pDerivedObject->NumModifiers(); for(int i = 0; i < nMod; i++) { Modifier *pModifier = pDerivedObject->GetModifier(i); if (pModifier->ClassID() == SKIN_CLASSID) { ISkin *pSkin = (ISkin*)pModifier->GetInterface(I_SKIN); // get ISkin interface if(pSkin) { ISkinContextData* pSkinContext = pSkin->GetContextInterface(pNode); // get context interface int nBones,j; // bones nBones = pSkinContext->GetNumAssignedBones(i0);// param is vertex id,use pmaxMesh->faces[i].v[0] for(j = 0; j < nBones; j ++) { int nBoneIndex = pSkinContext->GetAssignedBone(i0,j); // FindNode is function that take a INode pointer reture a index id. pFace->m_vertex[0].m_ltWeights.push_back(std::make_pair(FindNode(pSkin->GetBone(nBoneIndex)), pSkinContext->GetBoneWeight(i0,j))); } nBones = pSkinContext->GetNumAssignedBones(i1); // ........same for i1 and i2 } } } } skin mesh 的weight資料就算是獲取完成了.接下來的是3dmax的control資料獲取.這個部分是整個3dmax裡面最為隱諱的一個部分,它的格式只有在3dmax的debug sdk裡面才有,而這個debug sdk是要錢的,小T現在可沒有那個能力支付多少多少的美圓..嘿嘿.下來的這些資料來自小T從網上收集到的各個open source的3d引擎的原始碼,有一小部分是小T自己研究的結果.先列出資料的來源.首先的一個是魔獸的mdl匯出外掛'DeX.然後的一個是fairy-project,還有一個就是 3dmax裡面的control有很多很多,小T只是打算支援主要的3種,linear,bezier和tcb control.下面一個一個的講. linear是最簡單的,幾乎不需要講,他使用線性插值演算法.對於旋轉資料使用quat的slerp演算法就ok. void CExporter::GetLinearPosition(CNode *pOurNode,INode *pMaxNode,Control *pControl,IKeyControl *pKeyControl) { ILinPoint3Key maxKey; CAnimationPositionLinearKey ourKey; for(int i = 0; i < pKeyControl->GetNumKeys(); i ++) { // abs position,local system pKeyControl->GetKey(i,&maxKey); ourKey.m_fPosition[0] = maxKey.val.x; ourKey.m_fPosition[1] = maxKey.val.z; ourKey.m_fPosition[2] = maxKey.val.y; ourKey.m_nTime = maxKey.time * 1000 / TIME_TICKSPERSEC; AddAnimationKey(pOurNode,LinearPositionKey,&ourKey); } // when do interpolation,key1 is prev key,key2 is next key,t is time,then the position at t is // pos = key1.pos + (key2.pos - key1.pos)*(t - key1.time)/(key2.time - key1.time) } // linear rotation void CExporter::GetLinearRotation(CNode *pOurNode,INode *pMaxNode,Control *pControl,IKeyControl *pKeyControl) { Matrix3 maxMatrix; ILinRotKey maxKey; CAnimationRotationLinearKey ourKey; for(int i = 0; i < pKeyControl->GetNumKeys(); i ++) { pKeyControl->GetKey(i,&maxKey); // this key's quat is an abs value,not a rel value...error in max sdk // convert to matrix maxKey.val.MakeMatrix(maxMatrix); ConvertMaxMat2OurMat(maxMatrix,ourKey.m_matNode); ourKey.m_nTime = maxKey.time * 1000 / TIME_TICKSPERSEC; AddAnimationKey(pOurNode,LinearRotationKey,&ourKey); } // when do interpolation // rotation is Quat::Slerp(key1.qRot,key2.qRot,(t - key1.time)/(key2.time - key1.time)) } 接下來說tcb control 這個要比linear複雜一點,tcb control使用的是hermite(埃爾米特)插值,hermite插值是指給定有限個點的值和這些點的一階導數,構造一個多項式,在那些給定的點的值和一階導數都和已知值相同.這個在數值分析裡面有講到,給個連結.很明顯,一個物體的位置,旋轉角度是一個關於時間的函式,給定一個時間,就有一個唯一的位置,一個唯一的旋轉,而現在我們不可能記錄任何時間的位置和旋轉資訊,我們只是知道在某些特定的時間點(這些點叫keyframe)的位置和旋轉資訊,還有這些點的導數資訊,現在就要利用這些已知資訊計算出任何時間點的值來.這個就叫插值.(呃,這個解釋不算是完備,但是我個人覺得還是容易理解的).而利用值和導數,我們已經能用hermite插值方法計算出任何時間點的值來了,但是,實際上,獲得單個點的導數資訊卻並不是已經很容易的事情,所以tcb就應運而生了,他並沒有記錄單個點的導數,而是記錄了3個額外的資料,而單個點的導數資訊可以透過這些已知道資訊計算出來(具體的方式可以看上面的連結裡面的文章),特殊的點是第一個和最後一個點,第一個點只需要計算TD的值, float tm = 0.5f * (1.0f - firstKey->Tension); firstKey->TD = tm * ((secondKey->Value - firstKey->Value) * 3.0f - secondKey->TS); 最後一個點計算TS的值 float tm = 0.5f * (1.0f - lastKey->Tension); lastKey->TS = tm * ((lastKey->Value - previousLastKey->Value) * 3.0f - previousLastKey->TD); 然後,上面那個連結裡面給出來的方法裡面必須的資料就都差不多了,唯一例外的是那個s.表面上看s就是(t - key1.time)/(key2.time - key1.time),其實不是,在3dmax裡面還有一個easeIn和easeOut資料,剛剛得到的結果還得經過一系列的計算才能作為插值引數s.方法列出來: ease : first calc float e0 = Keys[i].m_fEaseOut; float e1 = Keys[i+1].m_fEaseIn; float s = e0 + e1; if (s > 1.0) { e0 /= s; e1 /= s; } Keys[i].m_fEase0 = e0; Keys[i].m_fEase1 = e1; Keys[i].m_fEaseK = 1.0f / (2.0f - e0 - e1); if ( e0 != 0.0f ) { Keys[i].m_fEaseKOverEase0 = Keys[i].m_fEaseK / e0; } if ( e1 != 0.0f ) { Keys[i].m_fEaseKOverEase1 = Keys[i].m_fEaseK / e1; } // for the last key m_fEaseK = 0.5f when do ease if(key->m_fEaseK == 0.5f) { // keep the same s = t; } else if(t < key->m_fEase0) { s = key->m_fEaseKOverEase0 * t * t; } el
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-951948/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- npm 釋出外掛NPM
- 指標的詳細講解指標
- dart類詳細講解Dart
- 詳細講解DirectDraw程式設計基礎(轉)程式設計
- Go Struct超詳細講解GoStruct
- Java EL 詳細用法講解Java
- 核心交換機的TRUNK配置功能詳細講解(轉)
- 詳細講解SQL中CONVERT轉化函式用法SQL函式
- react的詳細知識講解!React
- 詳細講解函式呼叫原理函式
- Java中的static詳細講解Java
- MyBatis-Plus詳細講解(一)MyBatis
- Spark開發-WordCount詳細講解Spark
- Struts配置檔案詳細講解
- 詳細講解矩陣求逆的快速演算法(轉)矩陣演算法
- 機器學習之決策樹詳細講解及程式碼講解機器學習
- 詳細講解23種設計模式設計模式
- 最詳細的JVM&GC講解JVMGC
- Spark開發-WordCount流程詳細講解Spark
- HTML 超級連結詳細講解HTML
- oracle 9i statspack詳細講解Oracle
- mysqldump匯出引數詳細解釋MySql
- 詳細講解遊戲開發中的DirectX-tasy(II)(轉)遊戲開發
- 演算法--揹包九講(詳細講解+程式碼)演算法
- 網路安全Bypass網路卡詳細講解
- DeFi和CeFi的區別詳細講解
- MVC 三層架構案例詳細講解MVC架構
- 詳細講解!RabbitMQ防止資料丟失MQ
- Spark開發-WordCount詳細講解Java版本SparkJava
- Spring @Conditional註解 詳細講解及示例Spring
- mybatis log sql日誌輸出外掛MyBatisSQL
- EventBus 3.0+ 原始碼詳解(史上最詳細圖文講解)原始碼
- vue-cli 目錄結構詳細講解Vue
- ES6中rest引數詳細講解REST
- webpack4.x最詳細入門講解Web
- 超詳細講解頁面載入過程
- ios GCD 死鎖幾個案例 詳細講解iOSGC
- 全網最!詳!細!Tarjan演算法講解。演算法