"""Compare Unity evaluation against raw FBX joint trajectories, pelvis-relative.""" import json,math from pathlib import Path ROOT=Path(__file__).resolve().parents[2] OUT=ROOT/'Tools/output/campguide_studio/playback_audit' reference=json.loads((OUT/'fbx_reference.json').read_text(encoding='utf-8'))[1] comparison=json.loads((OUT/'comparison.json').read_text(encoding='utf-8')) def sub(a,b):return [a[i]-b[i] for i in range(3)] def length(a):return math.sqrt(sum(v*v for v in a)) def unity(p):return [-p[0],p[2],-p[1]] def angle(a,b):return math.degrees(math.acos(max(-1,min(1,sum(a[i]*b[i] for i in range(3))/(length(a)*length(b)))))) report=[] for entry in comparison['samples']: maximum=0;feet_max=0;angles=[];gaps=[] for frame,pose in enumerate(entry['frames']): raw={name:unity(p) for name,p in reference['samples'][frame]['world'].items()} for name,p in pose.items(): delta=sub(sub(p,pose['mixamorig:Hips']),sub(raw[name],raw['mixamorig:Hips'])) error=length(delta);maximum=max(maximum,error) if name.endswith('Foot'):feet_max=max(feet_max,error) for side in ('Left','Right'): ankle='mixamorig:'+side+'Foot';toe='mixamorig:'+side+'ToeBase' angles.append(angle(sub(pose[toe],pose[ankle]),sub(raw[toe],raw[ankle]))) gaps.append(pose['mixamorig:RightFoot'][0]-pose['mixamorig:LeftFoot'][0]) report.append(dict(variant=entry['tag'],max_joint_error_m=maximum,max_ankle_error_m=feet_max, max_foot_direction_error_degrees=max(angles),min_signed_ankle_gap_m=min(gaps),crossed_frames=sum(g<0 for g in gaps))) print(json.dumps(report,indent=2)) assert report[0]['max_joint_error_m']<.001,'Generic reference does not match the original FBX' (OUT/'summary.json').write_text(json.dumps(report,indent=2),encoding='utf-8')