69 lines
2.3 KiB
Python
69 lines
2.3 KiB
Python
"""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()
|