""" Spike T-1089 / Synty Sidekick intake — step 1: rest-pose recon. Imports the Sidekick garment FBX (with its armature) and our Quaternius armature.glb + average_m body segments, then prints: - world-space bone head/tail positions for the 12 vertex-group bones in BOTH armatures + per-bone delta - bounding boxes of the garment vs our torso-region body segments - object transforms as-imported (scale factors etc.) Run: tooling/blender --background --python \ spikes/synty-intake/scripts/01_recon_rest_pose.py """ import bpy import json import os REPO = "/var/mnt/data/projects/settled-reach" GARMENT_FBX = os.path.join( REPO, "spikes/synty-intake/raw/Assets/Synty/SidekickCharacters/Resources", "Meshes/Outfits/Starter/SK_SCFI_CIVL_09_10TORS_HU01.fbx") OUR_ARMATURE_GLB = os.path.join(REPO, "client/assets/characters/skeleton/armature.glb") BODY_DIR = os.path.join(REPO, "client/assets/characters/bodies/average_m") BODY_SEGS = ["seg_torso", "seg_hips", "seg_neck", "seg_arm_upper_l", "seg_arm_upper_r"] BOUND_BONES = ["pelvis", "thigh_l", "thigh_r", "spine_01", "spine_02", "spine_03", "neck_01", "head", "clavicle_l", "upperarm_l", "clavicle_r", "upperarm_r"] OUT_JSON = os.path.join(REPO, "spikes/synty-intake/out/recon_rest_pose.json") def clear_scene(): bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete(use_global=False) def import_new(op, path, **kw): before = set(bpy.data.objects) op(filepath=path, **kw) return list(set(bpy.data.objects) - before) def find_armature(objs): for o in objs: if o.type == 'ARMATURE': return o return None def bone_world(arm_obj, name): b = arm_obj.data.bones.get(name) if b is None: return None mw = arm_obj.matrix_world h = mw @ b.head_local t = mw @ b.tail_local return {"head": [round(v, 5) for v in h], "tail": [round(v, 5) for v in t]} def mesh_world_bbox(obj): import mathutils pts = [obj.matrix_world @ mathutils.Vector(c) for c in obj.bound_box] lo = [round(min(p[i] for p in pts), 5) for i in range(3)] hi = [round(max(p[i] for p in pts), 5) for i in range(3)] return {"min": lo, "max": hi, "dims": [round(hi[i] - lo[i], 5) for i in range(3)]} clear_scene() report = {} # --- garment FBX --- g_objs = import_new(bpy.ops.import_scene.fbx, GARMENT_FBX) g_arm = find_armature(g_objs) g_meshes = [o for o in g_objs if o.type == 'MESH'] report["garment_objects"] = [ {"name": o.name, "type": o.type, "scale": [round(s, 6) for s in o.scale], "location": [round(v, 6) for v in o.location]} for o in g_objs] report["sidekick_bones"] = {} for bn in BOUND_BONES: report["sidekick_bones"][bn] = bone_world(g_arm, bn) report["garment_meshes"] = {} for m in g_meshes: report["garment_meshes"][m.name] = { "verts": len(m.data.vertices), "bbox": mesh_world_bbox(m), "vgroups": [vg.name for vg in m.vertex_groups], "shape_keys": ([kb.name for kb in m.data.shape_keys.key_blocks] if m.data.shape_keys else []), } # --- our armature --- o_objs = import_new(bpy.ops.import_scene.gltf, OUR_ARMATURE_GLB) o_arm = find_armature(o_objs) report["our_armature_bone_count"] = len(o_arm.data.bones) report["our_bones"] = {} deltas = {} for bn in BOUND_BONES: ours = bn if bn != "head" else "Head" info = bone_world(o_arm, ours) report["our_bones"][ours] = info sk = report["sidekick_bones"][bn] if info and sk: d = [round(sk["head"][i] - info["head"][i], 5) for i in range(3)] mag = round(sum(x * x for x in d) ** 0.5, 5) deltas[bn] = {"delta": d, "magnitude_m": mag} report["bone_head_deltas"] = deltas # --- our body segments (proportion reference) --- report["our_body_segments"] = {} for seg in BODY_SEGS: path = os.path.join(BODY_DIR, seg + ".glb") objs = import_new(bpy.ops.import_scene.gltf, path) for m in objs: if m.type == 'MESH': report["our_body_segments"][seg] = { "verts": len(m.data.vertices), "bbox": mesh_world_bbox(m), "vgroups": [vg.name for vg in m.vertex_groups], } os.makedirs(os.path.dirname(OUT_JSON), exist_ok=True) with open(OUT_JSON, "w") as f: json.dump(report, f, indent=2) print("\n=== RECON SUMMARY ===") print(json.dumps(deltas, indent=2)) print("Garment bbox:", json.dumps( {k: v["bbox"]["dims"] for k, v in report["garment_meshes"].items()})) print("Body seg bbox:", json.dumps( {k: v["bbox"]["dims"] for k, v in report["our_body_segments"].items()})) print("Report written:", OUT_JSON)