FBXを解読したい①

#文脈

<font color="Red">修正2017.7.31</font>
~~AssetBundlesを使って.fbxモデルをネット上から回収~~ めっちゃ文脈書き間違えてました。

fbxバイナリファイルを読み込み、Unity上で再現
http://qiita.com/gshirato/items/b58dd8545dc215e41b34
より

#先に続きの紹介
http://qiita.com/gshirato/items/3d3041b8abbca13b769c
この先はかなり粗い翻訳と例になっているので先にこっちを読むことをおすすめします。(執筆中)
#FBXファイルを解読したい
FBX binary file format specification
https://code.blender.org/2013/08/fbx-binary-file-format-specification/
を超簡訳。

#FBX binary file format

Originally developed by Kaydata for MotionBuilder, 2006年にAutodeskに買収される
多くの３Dツールとして使われている主な３Dコンテナの一つである(exchange formats)

free SDK for FBXは存在するものの、ライセンスやソフトウェア自体は完全に閉じられている。

##テキストベース(ascii)とバイナリバージョンがある。


FBXバイナリのフォーマットにアクセスできるドキュメントがないため、BlenderはAlexanderに現在まで思いついたことを書き上げるように頼んだ。
これが一般的な３Dアプリのより良い互換性につながることを願う。Blenderの次のリリース(2.69)はFBXファイル読み込みもサポート予定。(2013年8月10日の記事)


これはFBXファイルフォーマットの仕様書であるが完璧ではない。
2011年のファイルでテストを行ったが、以前のものでも動作するはずである。

このドキュメントではFBXファイルのエンコードのみに触れる。エンコード済みデータの解釈ではない。



##Text-based file structure

テキストベースフォーマットの知識はこのドキュメントにおいて重要であるのでここに記しておく。
核となるFBXテキストファイルのヒエラルキーは下の通り

```
NodeType: SomeProperty0a, SomeProperty0b, … , {

NestedNodeType1 : SomeProperty1a, …
NestedNodeType1 : SomeProperty1a, … , {
… Sub-scope
}

…
}
```

つまり、ファイルは本質的にネストされた(入れ子状になった)ノードのリストなのである。各ノードは
- ノードタイプ識別子(class name)
- 識別子に関連づいたプロパティの組。型はプリミティブデータ型(float, integer, stringなど)を用いる
- 同じフォーマットで書かれたノードのリスト(再帰的)

グローバルには幾つかのノードを定義するための潜在的なリストが存在する(上の階層に実際にはもう一つリストが存在する)。つまり中括弧{}、プロパティのリストと名前は省かれている。
それらの標準ノードはネストされたリストのみから成り立っている、(実際には顕在化しないが書くとするなら)リストはこのように見えるはず。

```
FBXHeaderExtension: {…}
GlobalSettings: {…}
Documents: {…}
Definitions: {…}
Connections: {…}
```

アプリはFBXの構造にアクセスするためにはこれらのまずパースしなければいけない。


##Binary File Structure

最初の27バイトはヘッダーを含んでいる表す。

Wikipedia: ASCII
https://wiki.rogiken.org/specifications/file-format/fbx/7400/syntax/binary/

0-20バイトまで(21バイト分): « Kaydata FBX binary  \x00 » (« binary »の後2つ半角スペース、最後にNull文字)
21-22バイトまで(2バイト分): 0x1a, 0x00 (使いみちは不明だが全ての確認済みファイルでこの並び)
23-26バイトまで(4バイト分): 符号なし整数、バージョンを表す



適当なfbxファイルを見ると16進数で

```
4b61 7964 6172 6120 4642 5820 4269 6e61
7279 2020 001a 00e8 1c00 000d 0900 0000...
```

となっている。

```:マジックナンバー
4b61 7964 6172 6120 4642 5820 4269 6e61
7279 2020 00
```

1byte分 = 16進数の2桁分ということで2桁ずつ読んでいくと
https://ja.wikipedia.org/wiki/ASCII より
K = 0x4B
a = 0x61
y = 0x79 
…
SP= 0x20

`1a00`省略

```:バージョン
e8 1c00 00
```

リトルエンディアン(下の位から読んでいく)為、(0000)1CE8が16進数表記であり、十進数で表すと7400、つまりversion 7.4。

このデータの後すぐに、トップレベルオブジェクトレコード(top-level object record)が始まる。テキストファイルフォーマットと違い、これは省かれていない。(全ノードレコード[空の名前・空のプロパティであっても]が書かれている)

このファイルはファイルの全情報を再帰的に含んでおり、最後には定義不能な内容が入ったフッターが存在する。



##Node Record Format
ノードレコードと名付けられたものは次のメモリーの構造を持つ。


|Size(Bytes)|Data Type|Name|
|:---:|:----:|:---:|
|4|Unit32|EndOffset|
|4|Unit32|NumProperties|
|4|Unit32|PropertyListLen|
|1|Unit8t|NameLen|
|NameLen|char|Name|
|?|?|Property[n]( nは0からNameLenまでありえる)|
|OptionalOptional|Optional|Optional|
|?|?|NestedList|
|13|uint8[]|NULL-record|


###説明

- EndOffsetはファイルの始まりからノードレコードの終わりを表す。(つまり次にくる何かの最初のバイト数)Unknownなレコードや必要のないレコードをスキップするのに役立つ
- NumPropertiesはノードに関連付けれたプロパティの数。最後の要素としてネストされたリストはプロパティとしてはカウントされないので注意。
- PropertyListLenはプロパティリストの大きさ。NumPropertiesを入れるために必要であり、その大きさはプロパティのデータタイプに依存する。
- NameLenはオブジェクトの名前の大きさをcharacterである。0がある部分だけがトップレベルのリストであるようである。
- Nameはオブジェクトとの名前。ヌル終端文字はない。

####〜〜オプショナル〜〜
- Property[n] はn番目のプロパティ。フォーマットについてはProperty Record Formatを参照。プロパティは連続しておりパッディングがない。(文字で空白を埋められたりしていない。)
- NestedListはネストされたリストでありその存在は一番最後のNull-recordで示される。


ノードレコードを読み上げてプロパティをインクルードすることは正直でわかりやすい。ネストされたリストのエントリーが存在するかどうかを確認するには、EndoOffsetに到達するときに読み込んでいないバイトがあるかをチェックする。もし残っているのなら、最後のプロパティから直接・再帰的にオブジェクトレコードを読む。その後ろに、13バイト分の0があり、それはEndOffsetと一致しているはずである。(NULLエントリーが必要な理由は明らかではない。これはFBXの細部の情報だったり、フォーマットがあることを暗示しているが著者にはわからない。)(私にもわからない)


##Property Record Format
プロパティレコードは次のようなメモリの構造を持つ

|Size|Data Type|Name|
|:--:|:-------:|:--:|
|1|char|TypeCode|
|?|?|Data|

(Dataはもちろんファイルによって異なる)

TypeCodeは次の文字コードのうちのどれかとなる。文字コードはグループ化されており、それぞれ似た扱いが必要。

1. プリミティブ型
    - Y: 2 byte 符号付き整数型
    - C: 1 bit ブーレアン　1Byteの最下位ビットとしてエンコード
    - I: 4 byte 符号付き整数型(Integer)
    - F: 4 byte 単精度浮動小数点数(float)
    - D: 8 byte 倍精度浮動小数点数(double)
    - L: 8 byte 符号付き整数型(Long)
プリミティブ型(数値)ではレコード内のDataはバイナリ形式の数値の表し方と全く同じだが並びはリドルエンディアン。

2. 配列型
- f: Fの配列
- d: Dの配列
- l: Lの配列
- i: Iの配列
- b: Cの配列(ブーレアン) 

配列型では、Dataはより多くの要素を持ち、

|Size(Bytes)|Data Type|Name|
|:---------:|:-------:|:---:|
|4|Unit32|ArrayLength|
|4|Unit32|Encoding|
|4|Unit32|CompressedLength|
|?|?|Contents|

Encodingが0の場合、ContentsはArrayLengthに配列のデータ型をかけたものと成る。1の場合、Contentsは長さCompressedLengthバイトの圧縮されたバッファである。これは例えばzlibなどを用いてデコーディングされる。
0,1 以外のEncodingの存在は確認されていない。


3. 特殊型
- S: string
- R: raw binary data (生データ) 

2つとも同じ構造を持ち

|Size(Bytes)|Data Type|Name|
|:---------:|:-------:|:---:|
|4|Unit32|Length|
|Length|byte/char|Data|

Stringはヌル終端文字列ではないが \0文字を含むことができる。

#疑問
このドキュメント4年前のだけど大丈夫なんだろか

#実践

To be continued…
http://qiita.com/gshirato/items/3d3041b8abbca13b769c

#HoloLensアプリ開発記事一覧
http://qiita.com/gshirato/items/6c026f2c67dc332f829b から