diff --git a/pipeline/strip_skin.py b/pipeline/strip_skin.py index e3205784..a252868c 100644 --- a/pipeline/strip_skin.py +++ b/pipeline/strip_skin.py @@ -23,7 +23,9 @@ out = Path(args.out).expanduser() out.mkdir(parents=True, exist_ok=True) for clip in args.clips.split(","): clip = Path(clip).expanduser() - dest = out / clip.name + # GLB out: Blender 5's FBX exporter flattens slotted actions to identity; + # the glTF exporter is slot-native and MIRPAMO imports glb clips fine. + dest = (out / clip.name).with_suffix(".glb") if dest.exists(): print("SKIP", clip.name) continue @@ -33,13 +35,33 @@ for clip in args.clips.split(","): if o.type == "MESH": bpy.data.objects.remove(o, do_unlink=True) # normalise numbered Mixamo namespaces (mixamorig7: -> mixamorig:) so - # downstream mappers (MIRPAMO retarget) match by exact name + # downstream mappers (MIRPAMO retarget) match by exact name. + # NOTE: bone rename does NOT fix up slotted-action fcurve paths in + # Blender 5 — rewrite them explicitly or the action drives nothing. import re + + def all_fcurves(act): + if hasattr(act, "fcurves") and len(getattr(act, "fcurves", [])): + return list(act.fcurves) + out = [] + for layer in act.layers: + for strip in layer.strips: + for cb in strip.channelbags: + out.extend(cb.fcurves) + return out + for o in bpy.data.objects: if o.type == "ARMATURE": for b in o.data.bones: b.name = re.sub(r"^mixamorig\d+:", "mixamorig:", b.name) - bpy.ops.export_scene.fbx(filepath=str(dest), use_selection=False, - add_leaf_bones=False, bake_anim=True) + for act in bpy.data.actions: + for fc in all_fcurves(act): + if '"' in fc.data_path: + bone = fc.data_path.split('"')[1] + fixed = re.sub(r"^mixamorig\d+:", "mixamorig:", bone) + if fixed != bone: + fc.data_path = fc.data_path.replace('"%s"' % bone, '"%s"' % fixed) + bpy.ops.export_scene.gltf(filepath=str(dest), export_format="GLB", + export_animations=True) print("STRIPPED", clip.name) print("DONE")