Help us understand the problem. What is going on with this article?

PythonでFBXファイルを ASCII <--> BINARY 変換

任意ディレクトリ内にある、
全てのfbxファイルを
ASCIIフォーマット、又はBINARYフォーマットに
一括で変換したい時があったので、
python fbx sdk でやってみました。

環境

  • macOS Catalina
  • python 3.7.0
  • FBX Python SDK (2020.0.1)

FBX Python Bindings

公式ドキュメントを参考に、FBX Python Bindings を環境に入れます。
Installing Python FBX (2020.0.1)

ちなみに、その時の最新版をみたい場合は、
下記のようにいくと取りにいけるようです。
https://www.autodesk.com/products/fbx/overview -> GET FBX SDK -> FBX Python Bindings

変換する

FBXファイルのフォーマット変換は、
こんな感じでできるようです。

こちらを参考にさせてもらいました

任意fbxファイルのフォーマットを変換する
from fbx import FbxManager
from fbx import FbxScene
from fbx import FbxImporter
from fbx import FbxExporter

FBX_MANAGER = FbxManager.Create()

def _get_file_fomrat(format_name):
    io_plugin_registry = FBX_MANAGER.GetIOPluginRegistry()
    for format_id in range(io_plugin_registry.GetWriterFormatCount()):
        if io_plugin_registry.WriterIsFBX(format_id):
            desc = io_plugin_registry.GetWriterFormatDescription(format_id)
            if format_name in desc:
                return format_id
    # Default format is auto
    return -1

def _convert(path, file_format_id):
    scene = FbxScene.Create(FBX_MANAGER, "")
    importer = FbxImporter.Create(FBX_MANAGER, "")
    importer.Initialize(path, -1)
    importer.Import(scene)
    importer.Destroy()
    exporter = FbxExporter.Create(FBX_MANAGER, "")
    exporter.Initialize(path, file_format_id)
    exporter.Export(scene)
    exporter.Destroy()
    scene.Destroy()

# 対象のfbxファイルを、バイナリに変換する
_convert('target_fbx_file_path', _get_file_fomrat('binary'))
# 対象のfbxファイルを、アスキーに変換する
_convert('target_fbx_file_path', _get_file_fomrat('ascii'))

変換前に、対象がバイナリフォーマットか確認する

同じフォーマットに変換するのは無駄なので、
目的と同じフォーマットへの変換を除外するようにします。

こちらに、非公式のようですがバイナリフォーマットの
ヘッダー情報が載っているので参考にしてみます。
こちらにもある
※公式の情報が見当たらない。。。

任意fbxファイルがバイナリフォーマットであるか確認する
FBX_BINARY_SIGNATURE = b"Kaydara FBX Binary  \x00\x1A\x00"
FBX_BINARY_SIGNATURE_LENGTH = len(FBX_BINARY_SIGNATURE)

def _is_binary_fbx(path):
    with open(path, 'rb') as file:
        return file.read(FBX_BINARY_SIGNATURE_LENGTH) == FBX_BINARY_SIGNATURE
    return False

先頭23bytesは固定のバイナリデータなので、
必要な分だけファイル読み込みをして比較しています。

変換メソッドの最初に、この判定を仕込むと、
目的のフォーマットと違うファイルだけ変換されるようになります。

def _convert(path, file_format_id, required_binary):
    if _is_binary_fbx(path) != required_binary:
        scene = FbxScene.Create(FBX_MANAGER, "")
        importer = FbxImporter.Create(FBX_MANAGER, "")
        importer.Initialize(path, -1)
        importer.Import(scene)
        importer.Destroy()
        exporter = FbxExporter.Create(FBX_MANAGER, "")
        exporter.Initialize(path, file_format_id)
        exporter.Export(scene)
        exporter.Destroy()
        scene.Destroy()

マルチプロセスで高速化する

1fbxの変換を1jobとして、
ファイル単位での変換を並列化して
高速化をねらってみます。

concurrent.futures.ProcessPoolExecutorを使います。
python 3.x系が必要になります。

並列化して高速化
import concurrent.futures

with concurrent.futures.ProcessPoolExecutor() as executor:
    executor.submit(_convert, 'target_fbx_file_path', _get_file_fomrat('ascii'), false)

Walkするー

任意ディレクトリ内を全て対象にしたいので、
os.walk します。

import os
import sys

with concurrent.futures.ProcessPoolExecutor() as executor:
    file_format_id = _get_file_fomrat('binary')
    for root, _dirs, files in os.walk('target_directory_path'):
        for filename in files:
            if filename.endswith('.fbx'):
                path = os.path.join(root, filename)
                executor.submit(_convert, path, file_format_id, true)

これで少しは早く、一括変換が可能になったと思います。

出力ファイルの FBX File Version を指定する

変換後のファイルは使っているFBX SDKのバージョンに依存しますが、
下記のように指定することができます。

from fbx import FbxSceneRenamer

# FBXVersion: 7500 になる
exporter.SetFileExportVersion('FBX201600', FbxSceneRenamer.eNone)
# FBXVersion: 7200 になる
exporter.SetFileExportVersion('FBX201200', FbxSceneRenamer.eNone)

指定できるバージョンは
fbxio.h で定義されている
FBX_xxxx_xx_COMPATIBLE 系の文字列です。

まとめ

まとめると、下記にアップしたもののようになります。
https://github.com/yassy0413/fbx-file-utility-python/blob/master/fbx_file_format_utility.py

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした