"""Print rig and mesh details for the locally-authored inspired creature models.""" from __future__ import annotations import json from pathlib import Path import subprocess import tempfile import bpy from mathutils import Vector ROOT = Path(__file__).resolve().parents[2] MODEL_ROOT = ROOT / "game_assets/models/shelved/yugioh" def reset_scene() -> None: if bpy.context.object and bpy.context.object.mode != "OBJECT": bpy.ops.object.mode_set(mode="OBJECT") bpy.ops.object.select_all(action="SELECT") bpy.ops.object.delete(use_global=False) def inspect_model(path: Path) -> dict[str, object]: reset_scene() source_path = path.with_suffix(".smd") with tempfile.TemporaryDirectory(prefix="thor-yugioh-inspect-") as temp_dir: converted_path = Path(temp_dir) / "model.glb" subprocess.run( ["assimp", "export", str(source_path), str(converted_path), "-f", "glb2"], check=True, capture_output=True, text=True, ) bpy.ops.import_scene.gltf(filepath=str(converted_path)) armatures = [obj for obj in bpy.context.scene.objects if obj.type == "ARMATURE"] meshes = [obj for obj in bpy.context.scene.objects if obj.type == "MESH"] bounds = [] for mesh in meshes: bounds.extend(mesh.matrix_world @ Vector(corner) for corner in mesh.bound_box) return { "asset": path.parent.name, "source": source_path.name, "armatures": [ { "name": rig.name, "bones": [bone.name for bone in rig.data.bones], "actions": [action.name for action in bpy.data.actions], } for rig in armatures ], "meshes": [mesh.name for mesh in meshes], "materials": sorted({slot.material.name for mesh in meshes for slot in mesh.material_slots if slot.material}), "bounds": { "min": [round(min(point[index] for point in bounds), 4) for index in range(3)], "max": [round(max(point[index] for point in bounds), 4) for index in range(3)], } if bounds else None, } def main() -> None: report = [inspect_model(path) for path in sorted(MODEL_ROOT.glob("*/MMD_*.fbx"))] print("YUGIOH_MODEL_REPORT=" + json.dumps(report, indent=2)) if __name__ == "__main__": main()