精華區beta GameDesign 關於我們 聯絡資訊
作者 DickG (龍龍) 站內 GameSchool 標題 Re: 推薦台大資工的原因 時間 Sun Oct 10 02:25:29 2004 ─────────────────────────────────────── Brilliance Studio: Shuen-Huei Guan (Drake) 前言: 一口氣問了一堆問題,還是三年前的事了,有多少印象寫 多少好了。 先寫 Maya exporter 的心路歷程好了。程式實作與問題 解決的過程已經有點不大記得了,所以內容在技術層面上 的著墨不多。不過這也都是三年前的情況,看的人不要太 嚴肅看待一些數字或細節。 前置作業: 我們的團隊(Brilliance Studio )在最後的一位台藝大 同學的加入後,才算正式進入有 3D 資料的這個階段,當 時我們考慮的有 3D Max 和 Maya 。他建議使用 Maya , 原因好像是「他只會用 Maya 」。因為美術總監都這麼說 了,就沒啥好異議的了。 首當其衝的問題:如何把美術辛苦製作出來的 3D 模型和 程式整合。我經過了約一個禮拜的研究後,覺得非得自定 一個自己用的格式不可,加上在這之前有自行寫過 3D Max 的 plugin ,那時寫得很愉快,於是乎覺得 Maya 應該也 ok,那次會議我的一句「Maya exporter 交給我就行了」 可把我害慘了。 要寫的 Maya plugin (exporter) 主要的功能如下: 1.要處理的 maya 檔有兩種 static model: 場景、物品、武器… animated character: 有使用了 rigid-bind 的角色 2.正確取得 vertex set, face set, normal set, uv set 其中 uv 只取第一組,不考慮 multi-texture。 3.取出 skeleton 的資訊,這方面主要就是 joints 以及 之間的 hierarchical relationship 4.角色的動作方面,原打算只取出每個設 key 的時間點 ,再自行仿照 maya 作內插的方式,來得出任意一個時 間點的 skeleton(亦即是每個 joint 相對於 parent 的 transformation)。後來發覺到 maya 的內插預設是 非線性的,雖然有請美術把動作方面的內插都改為線性 內插,但為了降低寫 exporter 的困難度,於是改成每 個 frame 都取出它的 skeleton 來。 5.美術有使用 paint skin weights tool,所以每個 vertex 的 binding weight 得正確取出來。 6.我們經過一段時間的 survey 後,決定使用 Cal3D (一個 open source 的 character animation library) 作為處理 animation 的底層。Cal3D 有幾個特點: (1) 它把資料拆成三部分:mesh, skeleton, animation transformation(簡稱 anim)。 (2) 每個 anim 獨立一個檔,是以一個角色如果擁有 5 個動作的話(ex, stand1, stand2, run, die, fight) ,就得有五個相對應的 anim 檔。 (3) Cal3D 提供 anim 之間的 blending ,有點像連續 播放的前後兩首音樂之間的 fade-in/fade-out 。 舉個例子,我們每個角色都只有上述提到的 5 個 動作,但我們可以透過 Cal3D 來做到「邊跑邊打 人」(walk + fight)這個動作。而這也是我們使 用 Cal3D 的主要原因之一。 (4) Cal3D 有提供 level-of-detail,這是我們選它的 另一個主要原因。 (5) 很遺憾地,Cal3D 提供的 exporter 不包括 maya 在內。 於是乎在寫 maya exporter 之前,我們完成了以下幾件 事: 1.我們確定下來把 static model 和 animated character 分開來處理,這避免掉一些不必要的問題。雖然後來我 們還是把 exporter 寫成一個有 UI 的 plugin ,你可 以在裏頭選要 export 的是 static 還是 animated 的。 2.不論是 static 還是 animated ,我們都已經定好了要 { 輸出的格式了,且確定不會作大變更。 3.由於寫 maya exporter 和把 Cal3D 整合進我們的 3D Engine 兩件事都不容易,但其間的耦合性又不高( 除了需要一個 animation player 來驗證 export 出來 的資料是不是對的以外),所以兩邊可以同時進行。 一切準備就緒後,就開始動工了。 實作概述: 首先參考 Maya 附的一個範例 exporter: lepTranslator ,很快地就把 static 的部分搞定了。比較欠缺的是 texture 方面的資訊。後來我們只抓出每個 vertex 的 uv 資訊, 有關 material 和 multi-texture 的部分先不理會,而 且強制要求每個 vertex 只能有一組 uv (這裏指的是, 同一個 vertex 在不同的 face 裏時,它的 uv 都要一樣 。因為 Maya 的彈性很大,是可以讓一個 vertex 在每一 個相連的 face 上有不同的 uv 的。) vertex 的 binding weight 就沒那麼簡單了,由於它被 儲存在 property 裏頭(Maya 裏叫 Plug ),所以得先 挖出相對應的 plug ,再由裏頭翻出要的資料來。這也是 為什麼我們使用 rigid-bind 而不是使用 smooth-bind 的主要原因之一:smooth-bind 的儲存方式更複雜 @@ 我只記得那時 binding weight 是被存在 cluster 一類 的 function class (or function set) 裏。 附上一段當時寫的,用來取出 binding weight 的 code: m_oMeshFS << "Selection list length " << clusterMeshSetList.length() << endl; for (size_t kk = 0; kk < clusterMeshSetList.length(); ++kk) { MDagPath mypath; MObject components; MFloatArray weights; clusterMeshSetList.getDagPath(kk,mypath,components); m_oMeshFS << "\t" << kk << ": " << mypath.partialPathName().asChar() << endl; MFnWeightGeometryFilter cluster(m_oObj); cluster.getWeights(mypath,components,weights); m_oMeshFS << "\t\tWeight: "; for (size_t ww = 0 ; ww < weights.length(); ++ww) { m_oMeshFS << weights[ww] << " "; } m_oMeshFS << endl; } 後記: * 寫的過程中受挫很大,應該說從來都沒有使用一套系統 會使用得這麼不順手,即便是看過後就會忘了的 MFC 都沒那麼累人。我想主要是那時相關文件實在太少了吧 ,而且 Maya 內部使用了 wrapping class (or function class ),與一般的 OO 概念很不一樣,加上每個 node 存的資料又是以 property 的方式,是以存取方式變得 很複雜。下頭舉個大致的例子: 假設 mesh 這個 node 有個 property 叫 uv_set 。 in traditional OO: MMesh* oMesh = (MMesh*)oObject; const UV_SET* cpUV = oMesh->getUVSet (); in Maya functional class: if( MS::kFailure == prepairDagNode( m_oDagNode, m_oObj ) ) { return (MS::kFailure); } if( MS::kFailure == prepairDependencyNode( m_oDependNode, m_oObj ) ) { return (MS::kFailure); } if( MS::kFailure == prepairDagPath( m_oDagPath, m_oDagNode ) ) { return (MS::kFailure); } // 使用 functional class 來存取 MFnMesh mesh(m_oDagPath, &status); int iPolyNum = mesh.numPolygons (&status); int iVertexNum = mesh.numVertices (&status); MIntArray aUVIndex(iVertexNum, -1); //&&&& MString str; int iUVNum = mesh.numUVs (str, &status); status = mesh.getUVs (aU, aV); // 有時無法直接有類似 getUVs 這類的 method 可用時 // 得先取出 mesh 的 property members 出來,再由裏 // 頭找出你所要的那個 property 再轉換成你要的型態 * 為了寫它,我的桌面除了 Maya 和 Visual Studio ,還 開了數十頁的 Maya document pages ,最高紀錄是同時 間有二十多個視窗處於使用中。 * 當初在 schedule 上頭,我畫上的時間是兩週完成 maya exporter,但最後卻花了快兩個月。雖然這期間同時進 行一些其它方面的 coding ,但過程常常想叫「媽呀」。 * 一兩年後,出了本 Complete Maya Programming: An Extensive Guide to MEL and the C++ API 是本非常入門,且也是唯一一本的 maya programming 好書。 * 寫的過程中,當發覺一直取不出要的資料時,就會把檔 案存成 .ma 檔 (maya scene file in ascii) ,然後 我會和我們的美術總監兩個人一行一行猜它的 format ,猜出關鍵的地方後,我再反推回去它可能位在哪個 class/object/function class/property 裏的哪個 member data 裏。 * 現在想想,那時候的過程真是「遠古且克難得很」 ^^' * 那時寫好後 demo 給台大資工多媒體實驗室的教授看, 他說是全台灣前十位寫出來的吧,就覺得很爽 XD * 目前這方面的資料很多,已經有不少 lib 有提供 Maya exporter 了,像是 Ogre (3D Engine), DirectX, WildMagic (3D Engine), ... 後序: * Maya 的架構實在很複雜,雖然已經有些商業軟體方便 你做資料的轉換與使用(ex, PolyTrans) ,但這些軟 體支援的程式還有待商議(至少我未來工作的地方就發 覺 PolyTrans 不符合需求),但我覺得 Maya 比其它 3D Package 來得有「玩賞」的價值在。不論是使用它 的 API 或是 MEL (Maya Embedded Language) 來開發 相關輔助程式,最好的情況是你擁有兩顆不同的腦袋, 一顆裝 programming/computer graphics/OO ,另一顆 裝 Maya experience ,這樣會讓你寫的過程中好過一 些,再不然,有位很會操作 Maya 的美術人員在你身體 可以讓你不用一直自言自語。 * 出國參加了兩次 SIGGRAPH 、聽了幾場台灣數位內容ooxx 辦的講座,與幾位在國外工作十年有餘的人聊過後,發 覺懂得 Maya 的操作與曉得怎麼寫 Maya plugin 的人 現在是比較有價值的,理由也很簡單:它實在很難。這 種人在 hollywood 裏叫 technical software 之類的 ,是 film/game company 很搶手的人,鼓勵有志朝這 方面發展的人,好好加強你的「美術工匠與程式工匠這 兩邊的整合」。 當時的參考資料: * http://www.highend3d.com/ * http://cal3d.sourceforge.net/ * http://www.ewertb.com/maya/ -- ※ Origin: 巴哈姆特<bbs.gamer.com.tw> ◆ From: 219-84-11-184-adsl-tpe.dynamic. ※ 修改: 2004/10/10 2:26:25 [219-84-11-184-adsl-tpe.dynamic.]