Files
AIGame/Tools/model_tools/bl_audit_lmgirl_fbx_axes.py

42 lines
2.0 KiB
Python

"""Audit FBX axis metadata, raw geometry and evaluated Blender world bounds."""
import sys,json
from pathlib import Path
import bpy
import numpy as np
from io_scene_fbx import parse_fbx
ROOT=Path(__file__).resolve().parents[2]
OUT=ROOT/'Tools/output/lmgirl_tpose_studio/mixamo_upload'
def properties(element):
block=next((e for e in element.elems if e.id==b'Properties70'),None)
return {p.props[0].decode():[v.decode(errors='replace') if isinstance(v,bytes) else v for v in p.props[4:]] for p in block.elems} if block else {}
def bounds(points):
p=np.asarray(points,dtype=float)
return dict(min=p.min(0).tolist(),max=p.max(0).tolist(),span=np.ptp(p,axis=0).tolist())
def audit(path):
tree,version=parse_fbx.parse(str(path))
settings=next(e for e in tree.elems if e.id==b'GlobalSettings')
objects=next(e for e in tree.elems if e.id==b'Objects')
raw=[]
for e in objects.elems:
if e.id==b'Model':raw.append(dict(type='Model',name=str(e.props[1]),properties=properties(e)))
if e.id==b'Geometry':
vs=next((x for x in e.elems if x.id==b'Vertices'),None)
if vs:raw.append(dict(type='Geometry',name=str(e.props[1]),bounds=bounds(np.asarray(vs.props[0]).reshape(-1,3))))
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.fbx(filepath=str(path),use_anim=False)
imported=[]
for o in bpy.context.scene.objects:
if o.type=='MESH':imported.append(dict(name=o.name,vertices=len(o.data.vertices),triangles=sum(len(p.vertices)-2 for p in o.data.polygons),
rotation=list(o.rotation_euler),matrix=[list(r) for r in o.matrix_world],worldBounds=bounds([o.matrix_world@v.co for v in o.data.vertices])))
return dict(path=str(path),fbxVersion=version,globalSettings=properties(settings),raw=raw,imported=imported)
paths=[Path(p) for p in sys.argv[sys.argv.index('--')+1:]]
OUT.mkdir(parents=True,exist_ok=True)
report=[audit(p) for p in paths]
(OUT/'axis_audit.json').write_text(json.dumps(report,indent=2),encoding='utf-8')
print(json.dumps(report,indent=2))