1. 程式人生 > >3DS檔案結構的初步認識

3DS檔案結構的初步認識

3DS檔案的結構比想象中複雜,也可以說我之前想得太簡單了.它跟可以直接檢視的obj檔案的複雜度完全不同。儲存的資訊很多很多(若這些資訊在模型中存在)。雖然是一種老字號的通用格式,但是檔案結構從來沒被髮布過,只是網路上很多高手不吝麻煩,一一嘗試測試,找出其紛亂二進位制下的含義,並公開讓建模者和程式設計師得以應用3DS模型檔案。這裡主要記錄一下我的認識(比較膚淺呵呵)。——ZwqXin.com

其實按我最近的理解,3DS檔案格式,對資料的組織跟記憶體很相像。它分成很多的“chunk”(塊),按順序排列。每個chunk都包含著一些資訊,比如頂點啊材質啊燈光啊等等,相應地,就被稱為頂點chunk材質chunk燈光chunk等等,每個chunk都有其功用。每個chunk有其ID也有其長度和資料。如果把ID所在位置作為一個chunk的地址的話,把該“chunk地址+chunk長度”就可以找到下一個chunk的地址(ID所在位置)了。具體描述如下:

Offset  Length Name
0 2 Chunk-ID
2 4 Chunk-length = 6+n+m
n Data
6+n m Sub-chunks

因此只要知道入口地址(ID為0x4D4D,稱為基本塊,標識3DS檔案)和所需要的chunk對於它的偏移量就能找到你想要的資料了。Chunk-length就是一個chunk的容量。Data是主資料,Sub-chunks是子塊。當然,與記憶體的最大區別是3DS檔案不依賴於硬體。記憶體的話總容量是固定的,地址為0長度為8的記憶體分配來儲存某資料,如果該資料僅僅佔用了開頭的4個記憶體格子,其餘4個格子什麼也沒有(或者說無意義)等於浪費了,但它們還是“被使用”了,沒法用來幹別的事。而3DS檔案,哪怕一個chunk的長度多麼長而被填充的資料多麼少,或者根本沒有相應資料,多餘的那些“虛擬的資料地址”不會佔用整個檔案的大小。這是符合實際的:一個模型的資料量可大可小,因此對應3DS檔案也跟著可大可小。至於資料量超過了長度所允許怎麼辦呢?據說某些chunk,如描述頂點數的chunk的“長度”也是varying,也就是說長度也跟隨資料量變化,望有心人指教。另一個區別在於ID非一定按順序的,譬如基本塊ID為0x4D4D,設其地址為0,有一個chunk的ID是0x0001,明顯小於0x4D4D,但其地址可能很大。所以說ID是名字,ID所在3DS檔案結構種的“位置”才是地址。

順帶一提的是,3DS檔案中塊的內部組織更像是一種樹結構(The chunk tree),因此就有了父塊和子塊這種概念,父塊包含子塊。其中ID號0x4D4D的塊就是樹幹,其長度是“0 + sub-chunks”,而ID為0x3D3D的3D editor chunk(描述物件資訊)和EDITKEYFRAME(關鍵幀資訊)等等就是樹幹上的大樹枝,Object block (描述物件的點與面總的資訊)等等就是大樹枝上的小樹枝,然後還有小小樹枝,小小小樹枝……較小的樹枝都是比它大一點的樹枝的“子塊sub-chunk”,且較大樹枝的“長度length ”上標示的是其屬下所有小樹枝的“長度”總和(想起你電腦上的檔案夾了嗎呵呵)。資料上的樹關係(其中一部分如下,左邊空格的多少突顯出父子關係):
0x4D4D            Main chunk
  0x3D3D          3D editor chunk
    0x4000        Object block (with name of your object)
     0x4100       Triangular mesh
       0x4110     Your vertices
       0x4120     Your faces

在實際應用中,因為某種chunk的長度固定,故偏移確定,也就能直接計算得某個塊的ID所在位置了。眾多高手們已經幫我們算好了(雖然還沒有全部解析),給出一個chunk的ID和它儲存的資料的作用和格式,也就是說我們應用的可以通過其ID訪問該chunk,按照資料儲存格式把資料讀出來,為我們所用。例如ID號為0x4110的chunk就是用來描述物件頂點的,按照其儲存資料的方式可以遍歷之,儲存到一種容器(實際記憶體)中,使用的時候就能直接從此容器中把模型頂點拿出來了。

譬如我用到的一個3DS檔案讀取類就用到了以下ID的chunk:

  1. // 基本塊(Primary Chunk),位於檔案的開始
  2. #define PRIMARY            0x4D4D
  3. // 主塊(Main Chunks)
  4. #define OBJECTINFO         0x3D3D        // 網格物件的版本號
  5. #define VERSION            0x0002        // .3ds檔案的版本
  6. #define EDITKEYFRAME       0xB000        // 所有關鍵幀資訊的頭部
  7. // 物件的次級定義(包括物件的材質和物件)
  8. #define MATERIAL           0xAFFF        // 儲存紋理資訊
  9. #define OBJECT             0x4000        // 儲存物件的面、頂點等資訊
  10. // 材質的次級定義
  11. #define MATNAME            0xA000        // 儲存材質名稱
  12. #define MATDIFFUSE         0xA020        // 物件/材質的顏色
  13. #define MATMAP             0xA200        // 新材質的頭部
  14. #define MATMAPFILE         0xA300        // 儲存紋理的檔名
  15. #define OBJECT_MESH        0x4100        // 新的網格物件
  16. // OBJECT_MESH的次級定義
  17. #define OBJECT_VERTICES    0x4110      // 物件頂點
  18. #define OBJECT_FACES       0x4120      // 物件的面
  19. #define OBJECT_MATERIAL    0x4130      // 物件的材質
  20. #define OBJECT_UV          0x4140      // 物件的UV紋理座標

明顯這個類只用到了3DS檔案中的一小部分(chunk):頂點資訊,面資訊,紋理資訊,材質資訊,和一些標誌資訊。事實上一個複雜模型對應的3DS檔案中有更多的chunks。可以參看以下文件: