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 |
6 | 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:
- // 基本塊(Primary Chunk),位於檔案的開始
- #define PRIMARY 0x4D4D
- // 主塊(Main Chunks)
- #define OBJECTINFO 0x3D3D // 網格物件的版本號
- #define VERSION 0x0002 // .3ds檔案的版本
- #define EDITKEYFRAME 0xB000 // 所有關鍵幀資訊的頭部
- // 物件的次級定義(包括物件的材質和物件)
- #define MATERIAL 0xAFFF // 儲存紋理資訊
- #define OBJECT 0x4000 // 儲存物件的面、頂點等資訊
- // 材質的次級定義
- #define MATNAME 0xA000 // 儲存材質名稱
- #define MATDIFFUSE 0xA020 // 物件/材質的顏色
- #define MATMAP 0xA200 // 新材質的頭部
- #define MATMAPFILE 0xA300 // 儲存紋理的檔名
- #define OBJECT_MESH 0x4100 // 新的網格物件
- // OBJECT_MESH的次級定義
- #define OBJECT_VERTICES 0x4110 // 物件頂點
- #define OBJECT_FACES 0x4120 // 物件的面
- #define OBJECT_MATERIAL 0x4130 // 物件的材質
- #define OBJECT_UV 0x4140 // 物件的UV紋理座標
明顯這個類只用到了3DS檔案中的一小部分(chunk):頂點資訊,面資訊,紋理資訊,材質資訊,和一些標誌資訊。事實上一個複雜模型對應的3DS檔案中有更多的chunks。可以參看以下文件: