Files
Alien_Sniper/tools/convert_glb_to_fbx.py
T
2026-07-21 18:34:32 +08:00

87 lines
3.1 KiB
Python

"""Batch-convert GLB files to FBX through Blender.
Run this file with Blender's Python, not the system Python. The script keeps
the source folder layout and writes an errors.csv next to the output files.
"""
from __future__ import annotations
import argparse
import csv
import sys
from pathlib import Path
import bpy
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--source", required=True, type=Path)
parser.add_argument("--destination", required=True, type=Path)
parser.add_argument("--overwrite", action="store_true")
return parser.parse_args(sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else [])
def clear_scene() -> None:
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)
for collection in (bpy.data.meshes, bpy.data.materials, bpy.data.images, bpy.data.armatures):
for item in list(collection):
collection.remove(item)
def main() -> int:
args = parse_args()
source = args.source.resolve()
destination = args.destination.resolve()
if not source.is_dir():
print(f"ERROR: source folder does not exist: {source}")
return 2
if source == destination:
print("ERROR: destination must be different from source")
return 2
glb_files = sorted(source.rglob("*.glb"))
destination.mkdir(parents=True, exist_ok=True)
errors: list[tuple[str, str]] = []
converted = skipped = 0
for index, input_file in enumerate(glb_files, start=1):
output_file = destination / input_file.relative_to(source).with_suffix(".fbx")
if output_file.exists() and not args.overwrite:
print(f"[{index}/{len(glb_files)}] SKIP {input_file.name}")
skipped += 1
continue
output_file.parent.mkdir(parents=True, exist_ok=True)
try:
clear_scene()
bpy.ops.import_scene.gltf(filepath=str(input_file))
bpy.ops.export_scene.fbx(
filepath=str(output_file),
use_selection=False,
object_types={"EMPTY", "CAMERA", "LIGHT", "ARMATURE", "MESH", "OTHER"},
use_mesh_modifiers=True,
add_leaf_bones=False,
path_mode="COPY",
embed_textures=True,
)
converted += 1
print(f"[{index}/{len(glb_files)}] OK {input_file.name}")
except Exception as exc: # continue converting the remaining assets
errors.append((str(input_file), repr(exc)))
print(f"[{index}/{len(glb_files)}] FAIL {input_file.name}: {exc}")
if errors:
error_log = destination / "errors.csv"
with error_log.open("w", newline="", encoding="utf-8-sig") as handle:
writer = csv.writer(handle)
writer.writerow(("source_file", "error"))
writer.writerows(errors)
print(f"Error log: {error_log}")
print(f"Finished: {converted} converted, {skipped} skipped, {len(errors)} failed.")
return 1 if errors else 0
if __name__ == "__main__":
raise SystemExit(main())