The problem: I animate frames 1–12 of a 24-frame walk cycle, then want frames 13–24 to be the L/R-flipped version of what I just did. Doing it manually is paste-pose-flipped twelve times, then checking whether the heel strike lands right. Slow and I always miss something.
So I wrote a script. Bone name flipping works, location X-mirroring is fine. The rotation part is where I'm skeptical:
import bpy
def flip_bone_name(name):
for a, b in [('.L', '.R'), ('.R', '.L'), ('_L', '_R'), ('_R', '_L')]:
if name.endswith(a):
return name[:-len(a)] + b
return name
def mirror_range(obj, src_start, src_end):
half = src_end - src_start + 1
scene = bpy.context.scene
for frame in range(src_start, src_end + 1):
scene.frame_set(frame)
target_frame = frame + half
for bone in obj.pose.bones:
mirror_name = flip_bone_name(bone.name)
if mirror_name not in obj.pose.bones:
continue
dst = obj.pose.bones[mirror_name]
loc = bone.location.copy()
loc.x *= -1
dst.location = loc
dst.keyframe_insert('location', frame=target_frame)
if bone.rotation_mode == 'QUATERNION':
q = bone.rotation_quaternion
dst.rotation_quaternion = q.__class__(q.w, -q.x, q.y, q.z)
dst.keyframe_insert('rotation_quaternion', frame=target_frame)
else:
r = bone.rotation_euler.copy()
r.x *= -1
r.z *= -1
dst.rotation_euler = r
dst.keyframe_insert('rotation_euler', frame=target_frame)
obj = bpy.context.active_object
mirror_range(obj, src_start=1, src_end=12)
Most bones look correct. But a couple of my foot controllers come out slightly twisted. The heel contact looks right but there's a small rotation error on the toe pivot. I think the problem is that some bones have a rest orientation that doesn't align with world X, so negating local X isn't the same as mirroring across the character's actual symmetry plane.
Blender's own Paste Pose Flipped handles this correctly, it must use the bone's rest pose or the armature matrix as a reference. Does anyone know what it's actually doing internally? Or is there a cleaner Python approach that doesn't bake in orientation assumptions? Also curious if anyone's tackled scale mirroring. My rig uses squash-and-stretch on some IK chains and I haven't touched that part yet.