kimagre inrash

感想を書きます

.fbx のサムネイルを Blender +Pythonスクリプトで一気に作ろう!

time 2025/04/21

モデルデータである .fbx はエクスプローラ上で中身が何か表示されません。
エクスプローラ拡張はありそうな気がするけど、画像データとして出しておけばいろいろと便利になりそう。
1つずつやるのは現実的ではないので自動的に作れる方法を探ります。

FBX SDK

FBXのSDKが配布されています。
https://aps.autodesk.com/developer/overview/fbx-sdk
こちらのSampleを使えば .fbx を読み込んで表示するのは出来そう。

ざっと触ってみて使い勝手が悪かったため、断念。

Blender + スクリプト(Python)

Blenderのスクリプトを使って .fbx 読み込んでレンダリングしてそれを .png で保存して・・を繰り返せばいけそう。

  1. Blender 起動
  2. 「スクリプト作成」というタブを押す
  3. テキスト領域に下記のコードを貼り付ける
    • importRootDir に .fbx があるフォルダを指定(サブフォルダも検出されます)
    • outputRootDir に .png を吐き出すフォルダを指定
  4. カメラとライトを設置して読み込む .fbx が入る位置に設定しておく
    • レンダリングして画像を保存するのでこのカメラの中に入っていないとダメ
  5. ▶を押して実行する

import bpy
import glob
import os
import time

def bulk_import():
    importRootDir = f'D:\\FBX\\'
    outputRootDir = f'D:\\FBX\\_export\\'
    
    print(f'importRootDir:' + importRootDir)
    print(f'outputRootDir:' + outputRootDir)
    
    # fbxファイルの一覧を取得
    os.chdir(importRootDir)
    paths = glob.glob('./**/*.fbx', recursive=True)
    
    for path in paths:
        print(f'path:' + path)
        
        # importDir からの相対パスを取得
        offsetDir = os.path.dirname(path)
        offsetDir = offsetDir.replace(f'.\\', f'')
        offsetDir = offsetDir.replace(importRootDir, f'')
        
        # 出力先フォルダ作成
        outputDir = outputRootDir + offsetDir
        print(f'outputDir:' + outputDir)
        if os.path.isdir(outputDir) == False:
            os.makedirs(outputDir)
        
        # .fbx をインポート
        bpy.ops.import_scene.fbx(filepath=path, global_scale=1.0)
        
        # ファイル名から拡張子を除いたものを取得
        basename_without_ext = os.path.splitext(os.path.basename(path))[0]
        sspath = outputDir + f'\\' +basename_without_ext + f'.png'
        print(f'sspath:' + sspath)
        
        # レンダリング
        bpy.ops.render.render()
        bpy.data.images['Render Result'].save_render(filepath=sspath)
        
        cleanup()


def cleanup():
    bpy.ops.object.select_by_type(type='MESH')
    bpy.ops.object.delete(use_global=True)
    bpy.ops.object.select_by_type(type='SURFACE')
    bpy.ops.object.delete(use_global=True)
    bpy.ops.object.select_by_type(type='EMPTY')
    bpy.ops.object.delete(use_global=True)
    bpy.ops.object.select_by_type(type='ARMATURE')
    bpy.ops.object.delete(use_global=True)


# エントリーポイント
print(f'start!')
bulk_import()
print(f'finish!')

参考

https://analytics-note.xyz/programming/glob-recursive/
https://note.nkmk.me/python-os-mkdir-makedirs/#osmakedirs
https://zenn.dev/jujunjun110/articles/203ad46ba57beb

前後記事

Blenderのボーン周りメモ(自分用メモ)
Blender のアニメーション周りのメモ(自分用メモ)