1. 程式人生 > >Maya教程 :用MEL匯出3D模型

Maya教程 :用MEL匯出3D模型

前面已經寫過一個Hello EGL的練習程式,手工設計和推算3D座標是非常累,非常沒有效率的,實際上我們應該使用專業的三維建模工具來製作模型。

Maya是我最喜歡的三維製作軟體,除了操作介面非常符合我的直覺外,它強大而開放的MEL及SDK也是非常重要的因素。前些日子偶遇一個在好萊塢混過三維製作的朋友說,最強悍而又符合程式設計師思維的工具是Houdini,不過還沒學習過,目前Maya是我會用的最好的3D製作工具了。可惜曾經3D高階的代名詞SGI把它賣給Autodesk了,這讓我心裡多少有些鬱悶。

Maya內部對3D場景有一個相當優美的描述,由結點連線而成的網路,這個網路可以MEL指令碼來訪問和控制,當然更可以通過SDK來利用和增強。

由於OpenGL|ES 1.x不支援QUADE,所以在為這類應用轉換模型之前,注意先將模型表面三角化:
maya triangulate model

在MEL中轉換模型的要點如下:

  1. 獲取當前選中的物體清單,對每個物體分別進行轉換和匯出
    1. string $objs[] = `ls -sl`
    2. for( $obj in $objs ) { ... }
  2. 讀取一個物體的所有的面
    1. string $f2v[]=`polyInfo -faceToVertex`
      1. 注意:這條命令在PSE版本里是不支援的,如果你是在使用這個版本學習建模的話,可能需要找一個有正式版本的朋友幫你匯出模型。
      2. 這條命令的結果包含很多個字串,每個字串描述一個面,其內容類似於"FACE 0: 88 64 85",如果是沒有三角化的模型,還會多出一個數字來(預設使用四邊形)。
      3. FACE id:後面的數字是形成該面的頂點的索引
    2. int $faceCount = `size $f2v`
    3. 對於每一個面的字串進行分析,讀取構成面的頂點索引
      1. for ( int $i = 0; $i < $faceCount; $i++ )
      2. string oneFace[]
      3. tokenize($f2v[$i], $oneFace)
      4. (int) $oneFace[2,3,4] 就得到了頂點索引 (a,b,c)
  3. 讀取物體所有的頂點
    1. int vc[] = `polyEvaluate -vertex`
    2. [0]是vertexCount
    3. 對於每一個頂點,讀取其座標
      1. for ( int $i = 0; $i < $faceCount; $i++ )
      2. string $point = $obj.vtx[$i]
      3. float v[] = `$pointPosition -l $point`
        1. ([0], [1], [2])就是(x, y, z)
  4. 讀取法向座標
    1. 對於每一個面 (參考2.2)
    2. 對於面的每一個頂點
      1. string $v = $obj.vtxFace[$f2v[$i*3 + 0,1,2]][$i]
        1. $i 是面的索引
        2. $f2v[$i*3 + 0,1,2] 分別取出第$i個面的三個頂點索引
        3. $obj.vtxFace[vertex][face]是一個關聯陣列或者說偽陣列
        4. 比較有意思的是我在Maya聯機文件裡並沒有找到這個vtxFace的說明,在網上倒有些討論,感興趣的可以之為關鍵字google之。
    3. 選中該頂點
      1. select -r $v
    4. 計算其法向
      1. float n[] = `polyNormalPerVertex -q -xyz`
      2. 取得的n[0],[1],[2]就是該頂點法向的(x,y,z)
  5. 恢復最初的選中狀態
    1. select -r $objs;
  6. 讀取UV座標
    1. 對於每一個面 (參考2.2)
    2. 對於面上的每一個頂點 (參考4.2)
    3. 選中該頂點
    4. 分析UV座標
      1. string u[] = `polyListComponentConversion -fromVertexFace -toUV`
      2. 如果返回陣列長度為0,則未指定UV座標
      3. 否則可獲取UV座標
        1. float uv[] = `polyEditUV -q u[0]`
        2. [0],[1]就是(u,v)
  7. 將讀取的資料寫入檔案
    1. 開啟檔案 int $fid = `fopen $fname "w"`
    2. 寫入資料 fwrite $fid $data
      1. 可寫入MEL能識別的各種資料型別
      2. string寫入後尾部會跟一個0(BYTE)
      3. int寫入成32位
      4. float寫入成64位,對應C語言裡的double
      5. vector將寫入三個float
    3. 關閉檔案 fclose $fid

有經驗的程式設計師都知道合理的程式碼重用是提高效率的好辦法,自己重頭寫一個對於掌握以上要點是有好處的,但直接找一個現成的指令碼來用顯然可以節省大量的時間,要知道,指令碼程式的除錯可是非常麻煩的。

可以直接修改MEL以輸出自己想要的格式,也可以修改程式適應匯出格式,不過我覺得兩者都太麻煩,還是再寫一個轉換程式將MEL輸出格式轉成自己認為使用較為檔案格式比較爽。