1
FBX解説 (1: 構文編) (1)らりお
美少女の3Dモデルを表示したい
23 . 1
概要FBXの構文レベルの仕様の解説
自作FBXライブラリ fbx_direct の紹介
(このスライドはasciidocで書いてヴァーっと生成しました)
FBX #とはAutodesk社が仕様を管理している、3Dコンテンツのファイル形式。
メッシュだけでなく、アニメーションなど様々なデータを突っ込める
プロプライエタリ
祝 バージョンアップ(7.5)
3 . 24
第1部: 構文
5 . 1
FBX形式実はFBX形式は、2つの概念に分けて考えられる。
構文 (syntax)
3Dデータ構造 (schema)
構文 (SYNTAX)データがどのようにファイル上に配置されるか
バイナリやテキストをどのような構造として捉えるか
→SVGやCOLLADAで言うところのXMLフォーマット、 動画で言うところのコンテナ(AVIとかMPEG-2 TSとかMP4
とか)
5 . 2
3Dデータ構造 (SCHEMA)構文通りに読み取った汎用的なデータ構造を、どのように3Dデータとして解釈するか
頂点と法線とUVはメッシュの持つ情報で、テクスチャはマテリアルが保持していて、等々
→SVGやCOLLADAで言うところのschema、 動画で言うところのCODEC(DivXとかXvidとかH.264とか)
5 . 3
論文に喩えるなら「日本語」「英語」が構文
汎用的なデータを表現する形式を決めるだけで、中身までは(一般には)決定しない
この段階では、あらゆる文章が表現できる
が、たまーに「この言語でないと表現できない情報」のようなものも存在することがある
5 . 4
論文に喩えるなら「中身の構成」が構造(スキーマ)
小説は論文としては解釈できない
最初に「はじめに」みたいなのがあって、最後にまとめとかがあって、参考文献もある。謝辞は任意?
セクションの名前、内容、順番や個数、ネストの構造等が重要
論文書いたことないのでわかりませ〜んwww
ここでは、構文(この喩えでは言語)がどのようなものかは問われない
5 . 56
今回は今回は、この構文について解説する。
(3Dデータ構造の方は間に合わなかった、今実装中)
2種類の構文一般の木構造を表現できる。
(つまり、XMLやjsonと、情報を失わず相互に変換できるはず)
FBX binary
バイナリ形式
たぶんこっちの方が一般的
FBX ascii
テキスト(ASCII)形式
人間でも読める感じ
7
FBXファイル全体の(構文的な)構造FBXのデータは、以下の要素で構成される
ノード
node property (正式名称不明)
その他メタデータ(ファイル自体のヘッダやフッタ、コメントなど)
ファイルは複数のトップレベルノードを持つ
もちろん、暗黙のルートノードを想定することはできる
8
FBXが表現できる一般的な木構造
ノード (XMLならelement) は以下のものを持つ
0個以上のnode property (XMLでの属性(attribute))
0個以上の子ノード
9 . 1
実例見ようぜ (1)Definitions (トップレベルノードのひとつ)
; Document Description ;---------------------------------------- Definitions: { Version: 100 Count: 1 ObjectType: "GlobalSettings" { Count: 1 }}
References (トップレベルノードのひとつ); Document References ;---------------------------------------- References: { }
9 . 2
実例見ようぜ (2)Documents (トップレベルノードのひとつ)
; Object definitions ;---------------------------------------- Documents: { Count: 1 Document: 22452928, "Test Scene", "Scene" { Properties70: { P: "SourceObject", "object", "", "" P: "ActiveAnimStackName", "KString", "", "", "" } RootNode: 0 }}
9 . 3
これがFBX ASCII形式だ! (ババーン)普通のノード
(ノード名): (プロパティ0個以上、カンマ区切り) { (子ノード0個以上) }
node propertyあり、子ノードなしの場合の略記法(ノード名): (プロパティ1個以上)
コメント; comments poyo
実際、手書き可能あと、デバッグに便利
10
対して、FBX BINARYはこんな感じマジックバイナリ
謎の2バイト (内容固定)
FBXバージョン
トップレベルノード
NULL-record
ファイルのフッタ
なんだかんだ読める皆様にも読めるようになっていただきます
11 . 1
マジックバイナリと謎の2バイトマジックバイナリ
FBXファイルであると確証を得るための、内容固定のバイト列
詳細不明な2バイト ([0x1a, 0x00])
マジックの一部と見做すべきかな?
ここでFBX asciiかFBX binaryか判断がつくので、結構重要
4b 61 79 64 61 72 61 20 46 42 58 20 42 69 6e 61 Kaydara FBX Bina 72 79 20 20 00 1a 00 ry ...
11 . 2
FBXバージョンFBXバージョン (構文とスキーマで共通)
7.4.0なら 7400 、7.5.0なら 7500 といった感じ
FBX SDK 2015では7.4、FBX SDK 2016では7.5
リトルエンディアン、4バイト
00000017: 4c 1d 00 00
0x00001d4c ⇒ 750000000017: e8 1c 00 00
0x00001ce8 ⇒ 7400
11 . 3
トップレベルノードトップレベルノード
先述のノードをひたすら並べる
ファイルのコンテンツ
マイナーバージョンアップにより、7.5から仕様に変更がありました!!!
詳しくは後で
11 . 4
NULL-RECORD
NULL-record
暗黙のルートノードの終了を示すマーカー
FBX binaryでのノードの表現と深く関係があるので後述
正式な用語ではない
11 . 5
ファイルのフッタ以下の要素からなる
乱数っぽい謎の16バイト
パディング
謎の4バイト(内容固定)
FBXバージョン
0 (120バイト)
謎の16バイト(内容固定)
12 . 1
乱数っぽい謎の16バイト一例 (FBX 7.5):
fa bc a8 02 d8 c8 d7 6a bf 74 fb 8f 11 ff 29 72
FBX 7.3〜7.5で確認した限り、どのファイルも
fx bx ax 0x dx cx dx 6x bx 7x fx 8x 1x fx 2x 7x
のような16バイトである
→完全な乱数でもない、UUIDでもない…
情報求む
12 . 2
パディング、謎の4バイトパディング
16バイト境界まで揃える
blender-2.72bのFBXエクスポータは、これをやっていない
謎の4バイト
常に0 (0x00000000)
存在意義不明。謎。
00005980: 00 00 00 00 00 00 00 00 00 00 00 00 fa bc a8 02 00005990: d8 c8 d7 6a bf 74 fb 8f 11 ff 29 72 00 00 00 00
12 . 3
FBXバージョン、120バイトの0、謎の16バイト
FBXバージョン
これヘッダにもあったんだけど…
例のごとくリトルエンディアン、4バイト
120バイトの 0
謎の16バイト (内容固定)
000059a0: 00 00 00 00 4c 1d 00 00
f8 5a 8c 6a de f5 d9 7e ec e9 0c e3 75 8f 29 0b
12 . 4
フッタの全体像$ xxd -g1 -s -176 binary_props.fbx 00005980: 00 00 00 00 00 00 00 00 00 00 00 00 fa bc a8 02 ................00005990: d8 c8 d7 6a bf 74 fb 8f 11 ff 29 72 00 00 00 00 ...j.t....)r....000059a0: 00 00 00 00 4c 1d 00 00 00 00 00 00 00 00 00 00 ....L...........000059b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000059c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000059d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000059e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000059f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................00005a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................00005a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................00005a20: f8 5a 8c 6a de f5 d9 7e ec e9 0c e3 75 8f 29 0b .Z.j...~....u.).
視えますね
12 . 5
FBX BINARYでのノードの表現ひとつのノード(子孫含む)全体の、ファイル上での表現を、ノードレコードと呼ぶことにする(公式の名称ではな
い)。 以下のものが並んでいる:
Node record header (正式名称ではない)
ノード名
プロパティリスト (node propertyの羅列)
子ノード(の羅列)
NULL-record (条件を満たす場合のみ省略可能)
13 . 1
NODE RECORD HEADER全てのノードレコードが持つ固定長のヘッダ。
フィールド <=FBX7.4 >=FBX7.5
終端オフセット 4 bytes 8 bytes
node propertyの数 4 bytes 8 bytes
プロパティリストのバイト長 4 bytes 8 bytes
ノード名のバイト長 1 byte 1 byte
13 . 2
NODE RECORD HEADERのフィールド終端オフセット
ノードの終了場所の(正確には、そのノードの次の情報の最初の)バイトオフセット。
node propertyの数
自明にて説明不要。
プロパティリストのバイト長
各node propertyは可変長なので、合計のバイトサイズ。
13 . 3
ノード名ノード名のバイト長は1バイト。
ただし、ノード名はNUL終端ではない。
FBX ascii (最初のトップレベルノード)FBXHeaderExtension: { FBXHeaderVersion: 1003
FBX binary (先頭アドレスは 0x00000010)72 79 20 20 00 1a 00 4c 1d 00 00 9c 09 00 00 00 | ry ...L........ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 12 46 42 58 48 65 61 64 65 72 45 78 74 | ....FBXHeaderExt 65 6e 73 69 6f 6e 74 00 00 00 00 00 00 00 01 00 | ensiont.........
13 . 4
NODE PROPERTY以下からなる。
型コード (1バイト、ASCII文字)
以下のいずれか (型コードによる)
プリミティブ型 (型に応じて固定長)
配列型 (可変長)
特殊型 (可変長)
14 . 1
プリミティブ型型コード C Y I L F D
型 bool i16 i32 i64 f32 f64
全てリトルエンディアン。
booleanは1バイト。
booleanは true が Y 、false が T であることに注意。
blender-2.72bのFBXエクスポータは true で 0x01 、false で 0x00 を吐くため、 FBX SDK等で読むと情報が欠落する。
14 . 2
BLENDERのFBXエクスポータのBOOL
6b 58 00 00 01 00 00 00 02 00 00 | kX......... 00 07 53 68 61 64 69 6e 67 43 01 | ..ShadingC.
終端オフセット: 0x0000586b
プロパティ数: 0x00000001
プロパティのバイト長: 0x00000002
ノード名(0x07 バイト): Shading
最初のプロパティ(型コード C): 0x01
14 . 3
UNITYCHAN.FBX のBOOL先頭アドレスは 0x00092090
00 00 00 00 00 ab 20 09 00 01 00 00 00 02 00 00 | ...... ......... 00 07 53 68 61 64 69 6e 67 43 59 ce 20 09 00 01 | ..ShadingCY.....
終端オフセット: 0x000920ab
プロパティ数: 0x00000001
プロパティのバイト長: 0x00000002
ノード名(0x07 バイト): Shading
最初のプロパティ(型コード C): 0x59 (Y)
14 . 4
配列型型コード b i l f d
型 [bool] [i32] [i64] [f32] [f64]
配列型のプロパティは、型コード後に配列用の追加ヘッダ、 その後配列データ。
配列の要素数: 4 bytes
エンコーディング(後述): 4 bytes
ファイル中でのバイトサイズ: 4 bytes
15 . 1
配列型の値の圧縮配列データは圧縮されている場合がある。
(unitychan.fbxなどでは、小さな配列も根刮ぎ圧縮されてるっぽかった)
エンコーディングが 0 の場合
全く処理されない生のデータ。
エンコーディングが 1 の場合
zlibヘッダありのzlib形式で圧縮されたデータ。
(目視で脳内inflateできる人はFBXバイナリ完全に理解できるよ!)
15 . 2
配列の例unitychan.fbx (先頭アドレス 0x5e70)
44 69 72 65 63 74 4f 66 00 00 01 00 00 00 c5 07 | DirectOf........ 00 00 07 4e 6f 72 6d 61 6c 73 64 fe 04 00 00 01 | ...Normalsd..... 00 00 00 b8 07 00 00 78 01 95 99 6b 6c 15 55 10 | .......x...kl.U.
NakanoSisters_1_2_FBX/naka.fbx (先頭アドレス 0xd430)74 58 d4 00 00 01 00 00 00 11 00 00 00 09 4d 61 | tX............Ma 74 65 72 69 61 6c 73 69 01 00 00 00 00 00 00 00 | terialsi........ 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
15 . 3
特殊型型コード S R
型 String [u8]
特殊型のプロパティは、型コード後にバイト長(4バイト)、その後データ。
文字列・生バイナリともに圧縮等は無し。
文字列はNUL文字等を含む場合がある。
16 . 116 . 2
バイナリの例NakanoSisters_1_2_FBX/naka.fbx (先頭アドレス 0x0790)00 00 00 00 00 bd 07 00 00 01 00 00 00 15 00 00 | ................ 00 06 46 69 6c 65 49 64 52 10 00 00 00 2b b6 2f | ..FileIdR....+./ e6 b8 2a c2 ce b6 c7 bc 2f aa 2d ff f4 f2 07 00 | .....ζǼ./Ǽ./Ǽ./
文字列の(特殊な)例NakanoSisters_1_2_FBX/naka.fbx (先頭アドレス 0x01c0)02 00 00 00 27 00 00 00 09 53 63 65 6e 65 49 6e | ....'....SceneIn 66 6f 53 15 00 00 00 47 6c 6f 62 61 6c 49 6e 66 | foS....GlobalInf 6f 00 01 53 63 65 6e 65 49 6e 66 6f 53 08 00 00 | o..SceneInfoS...
ascii版 SceneInfo: "SceneInfo::GlobalInfo", "UserData" {
バイナリで "Hoge\x00\x01Piyo" のとき、 ASCIIで"Piyo::Hoge" のようになる。
(私の知る限りでは、NUL文字が出てくるのは このような「::」が使われる場合のみ)
16 . 3
NULL RECORD
node record headerはノード開始の印
全てのフィールド(<=FBX 7.4であれば13バイト、>=FBX7.5であれば25バイト)すべてが0のとき、これをNULLrecordと呼ぶ (公式な名称ではない) 。
一般のヘッダでないことはすぐにわかる (終端オフセットが 0 はありえない)
これによって、ファイル先頭からのオフセットを数えることなくノード終端がわかる
ストリームからの読み込みに便利だったりとかするのかな?
17 . 1
NULL RECORDの出現条件出現条件は以下の条件の1つ以上を満たすこと。
子ノードが存在する
プロパティが存在しない
FBX ASCIIで括弧 {} が必要になる条件と同じ。
17 . 2
NULL RECORDの例 ObjectType: "GlobalSettings" { Count: 1 }} Objects: { }
FBX 7.5(先頭アドレス 0x1280)00 00 13 00 00 00 00 00 00 00 0a 4f 62 6a 65 63 | ...........Objec 74 54 79 70 65 53 0e 00 00 00 47 6c 6f 62 61 6c | tTypeS....Global 53 65 74 74 69 6e 67 73 cb 12 00 00 00 00 00 00 | Settings........ 01 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 | ................ 05 43 6f 75 6e 74 49 01 00 00 00 00 00 00 00 00 | .CountI......... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 36 13 00 | .............6.. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 07 4f 62 6a 65 63 74 73 00 00 00 | ......Objects... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 73 13 00 00 00 00 00 00 00 00 | ......s.........
17 . 3
抜粋から読んでみよう65 73 74 20 53 63 65 6e 65 53 05 00 00 00 53 63 | est SceneS....Sc 65 6e 65 6e 11 00 00 00 00 00 00 00 00 00 00 00 | enen............ 00 00 00 00 00 00 00 00 00 00 00 0c 50 72 6f 70 | ............Prop 65 72 74 69 65 73 37 30 08 11 00 00 00 00 00 00 | erties70........ 04 00 00 00 00 00 00 00 26 00 00 00 00 00 00 00 | ........&....... 01 50 53 0c 00 00 00 53 6f 75 72 63 65 4f 62 6a | .PS....SourceObj 65 63 74 53 06 00 00 00 6f 62 6a 65 63 74 53 00 | ectS....objectS. 00 00 00 53 00 00 00 00 55 11 00 00 00 00 00 00 | ...S....U....... 05 00 00 00 00 00 00 00 33 00 00 00 00 00 00 00 | ........3....... 01 50 53 13 00 00 00 41 63 74 69 76 65 41 6e 69 | .PS....ActiveAni 6d 53 74 61 63 6b 4e 61 6d 65 53 07 00 00 00 4b | mStackNameS....K
53 74 72 69 6e 67 53 00 00 00 00 53 00 00 00 00 | StringS....S.... 53 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 98 11 | ................ 00 00 00 00 00 00 01 00 00 00 00 00 00 00 09 00 | ................ 00 00 00 00 00 00 08 52 6f 6f 74 4e 6f 64 65 4c | .......RootNodeL 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 06 12 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 0a 52 65 66 65 72 65 6e 63 65 73 00 00 00 | ...References...
18 . 1
ヒント特徴的なデータを探そう
S で終わる文字列は高確率で「ノード名+文字列プロパティ」
S で終わらなければ、文字列プロパティの可能性高し
0がたくさん続いているところ(NULL record)の次はnoderecord header
4・8バイトの小さな数(0が3・7個続く)は、個数や整数プロパティの可能性を考慮
18 . 2
こたえあわせ(1) Properties70: { P: "SourceObject", "object", "", "" P: "ActiveAnimStackName", "KString", "", "", "" } RootNode: 0 }} References: { }
65 73 74 20 53 63 65 6e 65 53 05 00 00 00 53 63 | est SceneS....Sc 65 6e 65 6e 11 00 00 00 00 00 00 00 00 00 00 00 | enen............ 00 00 00 00 00 00 00 00 00 00 00 0c 50 72 6f 70 | ............Prop 65 72 74 69 65 73 37 30 08 11 00 00 00 00 00 00 | erties70........ 04 00 00 00 00 00 00 00 26 00 00 00 00 00 00 00 | ........&....... 01 50 53 0c 00 00 00 53 6f 75 72 63 65 4f 62 6a | .PS....SourceObj 65 63 74 53 06 00 00 00 6f 62 6a 65 63 74 53 00 | ectS....objectS. 00 00 00 53 00 00 00 00 55 11 00 00 00 00 00 00 | ...S....U....... 05 00 00 00 00 00 00 00 33 00 00 00 00 00 00 00 | ........3....... 01 50 53 13 00 00 00 41 63 74 69 76 65 41 6e 69 | .PS....ActiveAni 6d 53 74 61 63 6b 4e 61 6d 65 53 07 00 00 00 4b | mStackNameS....K
18 . 3
こたえあわせ(2) Properties70: { P: "SourceObject", "object", "", "" P: "ActiveAnimStackName", "KString", "", "", "" } RootNode: 0 }} References: { }
53 74 72 69 6e 67 53 00 00 00 00 53 00 00 00 00 | StringS....S.... 53 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 98 11 | ................ 00 00 00 00 00 00 01 00 00 00 00 00 00 00 09 00 | ................ 00 00 00 00 00 00 08 52 6f 6f 74 4e 6f 64 65 4c | .......RootNodeL 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 06 12 00 00 00 00 | ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00 00 0a 52 65 66 65 72 65 6e 63 65 73 00 00 00 | ...References...
18 . 4
ね?もうあなたはFBXバイナリを読めるようになりました!
読める!読めるぞ!!
19 . 1
補足1-1: FBX ASCIIでのバイナリプロパティの表現
node propertyのバイナリ型の値は、一般的にFBX asciiには出てこない。
FileId ノードはFBX asciiでは存在しない
例外的に、 Properties70 直下の P ノードには出現しうるが、その場合Base64エンコードされる。
P: "MyBlob", "Blob", "", "U",4 { BinaryData: "cG95bw==", }
19 . 2
補足1-2: FBX ASCIIでのバイナリプロパティの表現
あまりに長いと、例外的に(4の倍数で?)分割が許される。
P: "MyBlob", "Blob", "", "U",4 { BinaryData: "cG95", "b3Bv", "eW8=", }
↑先頭には一文字空白あり
19 . 3
補足2-1: FBX ASCIIでの文字列のエスケープ
特殊な文字はエスケープされる
ダブルクォート(") → 「 " 」
LF(0x0a 、または \n) → 「 &lf; 」
CR(0x0d 、または \r) → 「 &cr; 」
何故か「 & 」がエスケープされない○○仕様
P: "MyString3", "KString", "", "U", "esc"esc, raw"raw"
↑本来は「 esc"esc, raw"raw」だった
19 . 4
補足2-2: FBX ASCIIでの文字列のエスケープ
前述のもの以外はエスケープされない
よって、日本語等もそのまま含められる
文字コードは不明(utf-8か、変換なしか)
19 . 5
補足3: その他FBX ASCIIについてtrueとfalseは、FBX binaryと同じで Y と T 。
FBX asciiでは、プロパティの型が明示的でないため、区別が困難な場合がある。
「 0 」が整数か小数か、スキーマを知らないとわからない
配列型は以下のような文法 Edges: *12 { a: 0,1,2,3,4,5,6,7,8,10,14,18 }
19 . 6
まとめFBX binaryは結構読める
明確でわかりやすい
FBX 7.5になって、バイナリの構文に僅かに変更があった
FBX asciiは欠陥が目立つので使うべきでない
でもコメントはちょっと便利
今までに述べたことは全て非公式・独自見解の情報
プロプライエタリなので、仕様は公開されていない!
2021
次やることは… パーサ書くしかないでしょ!
へ続く第2部
参考画像:
私が書いているページ(未完成):
blenderの中の人の解説:
天空の城ラピュタ
FBX 7.4 フォーマット解説 - cardina1.red
FBX binary file formatspecification | Blender Code