もう誰も使ってない！？ Python FBX SDK 完全利用ガイド

# 序論

　私は、序論が長い技術記事は好みませんが、この記事は技術的なメリットはほとんどないので例外とします。

　さて、3DCG を扱うにあたって FBX ファイルとの付き合いは欠かせません。特に、VRChat ではアバターの改変等でお世話になる方が多いでしょう。今となっては、Unity 内での編集ツールが充実し、SDK はおろか Blender 内での手作業すら必要ないですが、それでも FBX のデータに直接アクセスしたいという方はいると思います。

　そのためのツールとして、Autodesk が出している FBX SDK そしてその Python のラッパーである FBX Python Bindings（本家は C++ なので、以下、便宜上これの Python バインディングを SDK と呼ぶことにします）があります。FBX の仕様を定めた Autodesk が配布しているものなので、もはや公式ツールといってもよいでしょう。

　しかしこの SDK には大きな問題があって、Python2.7 と Python3.3 にしか対応していないということです。ちなみに、現在（2023 年 5 月）の Python の最新バージョンは **Python3.11** です。3.3 は古すぎるので、アクティブリリースから DL することも、Anaconda で仮想環境を作ることすらもできません（2.7 はできる）。

　要は、**Autodesk はもうこれをまともにメンテナンスする気はない**ということです。

　以下の記事では 2.7 に準拠します。

# 環境構築：手作業でする場合

　さて、Autodesk のサイトから SDK をダウンロードした人は手元に以下のファイルがあると思います。

```
fbx.pyd
FbxCommon.py
fbxsip.pyd
```

　ローカルに 2.7 環境がある人（ない人は Anaconda で環境を作ってもいいです）は、作業フォルダにこのファイルをぶちこめば、同じディレクトリにある `.py` からは `from fbx import *` ができるはずです。

# 環境構築：Docker でする場合

　現代には Docker があるので、わざわざローカルフォルダに手を加えたり仮想環境を増やすことはないです。2023 年においてはこちらの方がモダンで良い感じかと思われます。幸い、Python で FBX SDK を動かす用のイメージを公開してくれている方（https://hub.docker.com/r/lerignoux/python-fbx ）がいるので、これを使わせてもらいましょう。

```
docker pull lerignoux/python-fbx
```

　このままだとイメージ名が `lerignoux/python-fbx` になってしまうので、`python-fbx` に改名させたい場合は以下のようにします。

```
docker tag lerignoux/python-fbx python-fbx
```

　後は実行ファイルとして `main.py` を作り、以下のようにすれば Docker 上で Python ファイルを実行させることができます。

```
docker run -v ${PWD}:/app -w /app python-fbx python main.py
```

　なお、`${PWD}` の部分は、私の環境（Windows の VSCode）だと動きましたが、別の OS だとまた違うコマンドになるかもしれません。要は、カレントディレクトリのパスを示せればそれでいいので、もし動かない人がいたらそこを中心に検索してみてください。

# 素材を用意

　解析対象となる FBX ファイルを用意します。Blender の初期シーンをそのままエクスポートします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/60e95341-a879-5138-b16e-daa88da1a49b.png)

　GitHub にも上げておいたので、解析したい人がいたらお好きにどうぞ。

https://github.com/hibit-at/python-fbx

# コーディング

　FBX に含まれる全部のオブジェクトと、もしそれがメッシュならば頂点も全部表示するプログラムを書きます。

　以下のようになります。

```python
# -*- coding: utf-8 -*-

import FbxCommon

def print_node(node, depth=0):
    indent = " " * depth * 2
    print("{}Node: {}".format(indent, node.GetName()))
    
    # トランスフォームを取得
    translation = node.LclTranslation.Get()
    rotation = node.LclRotation.Get()
    scale = node.LclScaling.Get()

    print("{}Translation: {}, {}, {}".format(indent, translation[0], translation[1], translation[2]))
    print("{}Rotation: {}, {}, {}".format(indent, rotation[0], rotation[1], rotation[2]))
    print("{}Scale: {}, {}, {}".format(indent, scale[0], scale[1], scale[2]))

    # ノードからメッシュを取得
    mesh = node.GetNodeAttribute()
    if mesh and mesh.GetAttributeType() == FbxCommon.FbxNodeAttribute.eMesh:
        print("{}Mesh found".format(indent))
        control_points = mesh.GetControlPoints()
        for i in range(mesh.GetControlPointsCount()):
            print("{}Vertex: {}, {}, {}".format(indent, control_points[i][0], control_points[i][1], control_points[i][2]))

    # 子ノードを再帰的に処理
    for i in range(node.GetChildCount()):
        print_node(node.GetChild(i), depth + 1)

# ファイルを読み込むためのマネージャーとシーンを作成します
manager, scene = FbxCommon.InitializeSdkObjects()

# FBXファイルをロードします
result = FbxCommon.LoadScene(manager, scene, 'default.fbx')
if not result:
    print('An error occurred while loading the scene...')

# シーンのルートノードを取得します
root_node = scene.GetRootNode()

# ノードの構造を表示します
print_node(root_node)
```

　ちなみに ChatGPT4 君にほとんど書いてもらいました。

# 実行

　以下のような結果が得られます。

```python
Node: RootNode
Translation: 0.0, 0.0, 0.0
Rotation: 0.0, 0.0, 0.0
Scale: 1.0, 1.0, 1.0
  Node: Cube
  Translation: 0.0, 0.0, 0.0
  Rotation: -90.0000093347, 0.0, 0.0
  Scale: 100.0, 100.0, 100.0
  Mesh found
  Vertex: 1.0, 1.0, 1.0
  Vertex: 1.0, 1.0, -1.0
  Vertex: 1.0, -1.0, 1.0
  Vertex: 1.0, -1.0, -1.0
  Vertex: -1.0, 1.0, 1.0
  Vertex: -1.0, 1.0, -1.0
  Vertex: -1.0, -1.0, 1.0
  Vertex: -1.0, -1.0, -1.0
  Node: Light
  Translation: 407.624542236, 590.386230469, -100.545394897
  Rotation: 47.5335643022, 107.220767214, 10.7436613144
  Scale: 100.000007629, 100.0, 100.0
  Node: Camera
  Translation: 735.889160156, 495.830932617, 692.579101562
  Rotation: 22.7688044285, 142.108328475, 34.3484988265
  Scale: 100.0, 100.000007629, 99.9999923706
```

# 実際この SDK はどうなの

　メンテが止まっていることからもわかる通り、これを使う実利的な意味はほとんどないかと思われます。

　データの編集をしたいなら Blender で直接触ればよいし、その中でスクリプト化したい処理があれば、Blender 内の Python で処理を行う方が 100 倍楽です。環境構築の必要性がないので。あと API を読み解くのも楽なので。対象となるデータが `fbx` でしか存在しなく、いちいち Blender でロードするのもめんどくさいぐらい大量にある、というならばメリットはあるかもしれません……。

　じゃあ、なぜこのような記事を書いたかというと、3DCG の勉強をしはじめた時に、これを使おう！　と思って四苦八苦した記憶があるので、それに対する供養のようなものです。結局、その後 `libigl` のような代替ライブラリを見つけたり、そもそも Blender のスクリプトで十分な効率化ができたりで、この SDK をいじることはなくなったのですが。

　今では知見が溜まったり、そもそも他の技術（主に ChatGPT4 の登場がデカすぎる）が進歩したりで、あんな苦労したことも一瞬で出来るようになったということで、技術の進歩を感じるとともに、あの苦しみは何だったのかという哀愁に浸るための記事でした。

　また、あまりこれを学ぶ意味はないと申した後ですが、日本語で Python FBX SDK の解説をした記事はほとんどなく、特に実際の FBX ファイルを対象に操作を行うものはかなり稀だと思うので、もしそういった文脈で誰かの役に立つことがあれば幸いです。

# 参考にさせていただいた記事

[Python版FBX SDKをWindowsにインストールする](https://qiita.com/segur/items/5d0dff790e784760a547)