- autorig: landmark detection from vertex cloud (markers JSON override), mixamorig 22-bone skeleton fit, bone-heat weights + nearest-bone rescue - retarget: world-space quaternion delta transfer, hip-height scale compensation, fuzzy/explicit bone mapping (CMU map bundled) - smoke: procedural test humanoid + synthetic BVH -> rig -> retarget -> verify - deploy: push to gitea, pull + smoke on m3ultra and m1ultra Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
"""Verify a MIRPAMO output GLB: armature present, meshes skinned,
|
|
animations present with real motion. Prints a JSON report; exit 1 on failure.
|
|
|
|
Usage: blender -b --python bpy/verify_glb.py -- --in out/final.glb \
|
|
[--expect-anim] [--min-frames 10]
|
|
"""
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
import bpy
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from common import (clean_scene, count_fcurves, die, find_armature,
|
|
find_meshes, import_any, stage_args)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--in", dest="inp", required=True)
|
|
ap.add_argument("--expect-anim", action="store_true")
|
|
ap.add_argument("--min-frames", type=int, default=10)
|
|
args = ap.parse_args(stage_args())
|
|
|
|
clean_scene()
|
|
objs = import_any(args.inp)
|
|
arm = find_armature(objs)
|
|
meshes = find_meshes(objs)
|
|
|
|
report = {
|
|
"file": args.inp,
|
|
"armature": arm.name if arm else None,
|
|
"bones": len(arm.data.bones) if arm else 0,
|
|
"meshes": [m.name for m in meshes],
|
|
"skinned": [],
|
|
"actions": {},
|
|
}
|
|
|
|
if not arm:
|
|
print(json.dumps(report, indent=2))
|
|
die("no armature in output")
|
|
if not meshes:
|
|
print(json.dumps(report, indent=2))
|
|
die("no meshes in output")
|
|
|
|
for m in meshes:
|
|
has_arm_mod = any(mod.type == "ARMATURE" for mod in m.modifiers)
|
|
has_groups = len(m.vertex_groups) > 0
|
|
report["skinned"].append(
|
|
{"mesh": m.name, "armature_mod": has_arm_mod,
|
|
"vertex_groups": len(m.vertex_groups)})
|
|
if not (has_arm_mod and has_groups):
|
|
print(json.dumps(report, indent=2))
|
|
die(f"mesh {m.name} is not skinned")
|
|
|
|
for act in bpy.data.actions:
|
|
fr = act.frame_range
|
|
n_frames = int(fr[1] - fr[0]) + 1
|
|
report["actions"][act.name] = {
|
|
"frames": n_frames, "fcurves": count_fcurves(act)}
|
|
|
|
if args.expect_anim:
|
|
good = [a for a, v in report["actions"].items()
|
|
if v["frames"] >= args.min_frames and v["fcurves"] > 0]
|
|
if not good:
|
|
print(json.dumps(report, indent=2))
|
|
die(f"no action with >= {args.min_frames} frames found")
|
|
|
|
print(json.dumps(report, indent=2))
|
|
print("[mirpamo] verify OK")
|
|
|
|
|
|
main()
|