"""Fit only offending new sole vertices by 1mm in game space; leave rig, UV and clips unchanged.""" import bpy,sys,json,hashlib from pathlib import Path from mathutils import Vector from mathutils.kdtree import KDTree import numpy as np argv=sys.argv[sys.argv.index('--')+1:] folder=Path(argv[0]).resolve() name=argv[1] if len(argv)>1 else 'GreatHornStudio' # GreatHorn's reviewed fit touched exactly two soles; other actors fit whatever their own constraints flag. expected=int(argv[2]) if len(argv)>2 else (2 if name=='GreatHornStudio' else -1) rigged=folder/(name+'_Rigged.fbx') bpy.ops.wm.read_factory_settings(use_empty=True);bpy.ops.import_scene.fbx(filepath=str(rigged)) arm=next(o for o in bpy.context.scene.objects if o.type=='ARMATURE');low=next(o for o in bpy.context.scene.objects if o.type=='MESH');arm.animation_data_clear();arm.data.pose_position='REST';bpy.context.view_layer.update() world=[low.matrix_world@v.co for v in low.data.vertices];lo=Vector([min(v[i] for v in world) for i in range(3)]);hi=Vector([max(v[i] for v in world) for i in range(3)]);center=Vector(((lo.x+hi.x)/2,(lo.y+hi.y)/2,lo.z));height=hi.z-lo.z data=json.loads((folder/'foot_constraints.json').read_text(encoding='utf-8'));frames=np.array(data['frames']);targets=[] for j,v in enumerate(data['vertices']): weights=np.array(v['weights']) if weights.sum()>.95 and (frames[:,j,:]@weights).min()<-.003:targets.append(v) assert expected<0 or len(targets)==expected,'Re-inspect a changed sole fit instead of applying stale edits' normal=[(p-center)/height for p in world];candidates=[] for sx in (-1,1): for sz in (-1,1): tree=KDTree(len(normal)) for i,p in enumerate(normal):tree.insert(Vector((sx*p.x,p.z,sz*p.y)),i) tree.balance();errors=[tree.find(Vector(v['normalizedBind']))[2] for v in data['vertices']];candidates.append((max(errors),sx,sz,tree)) error,sx,sz,tree=min(candidates,key=lambda x:x[0]);assert error<.0001,('Unity/Blender bind coordinate mismatch',error) uv=lambda:hashlib.sha256(np.array([tuple(v.uv) for v in low.data.uv_layers.active.data],dtype='