Here's a function I wrote that lets you rotate a bone in edit mode and automatically corrects all animations so they maintain the same orientation:
def reorient_bone(operator, context, fixed_items, layers=(), bone_name="Bip0", location=mathutils.Vector((0,0,1)), rotation=mathutils.Euler((0,0,1)) ):
qrn = rotation.to_quaternion().conjugated()
qe = rotation.to_quaternion()
#when no object exists, or when we are in edit mode when script is run
for ob in bpy.context.scene.objects:
if type(ob.data) == bpy.types.Armature:
bpy.context.scene.objects.active = ob
bpy.ops.object.mode_set(mode='EDIT')
for bone in ob.data.edit_bones:
if bone.name == bone_name:
l = mathutils.Vector(bone.head)
m = rotation.to_matrix() * bone.matrix.to_3x3()
bone.head = mathutils.Vector((0,0,0))
bone.tail = mathutils.Vector((0,1,0))
bone.roll = 0
bone.transform(m)
bone.translate(l)
bpy.ops.object.mode_set(mode='POSE')
for action in bpy.data.actions:
for group in action.groups:
if group.name == bone_name:
#rotate the translation so the movement of the bone remains identical
for t in ("location", "quaternion"):
curves = [fcurve for fcurve in group.channels if fcurve.data_path.endswith(t)]
if curves:
num_keys = len(curves[0].keyframe_points)
for i in range(0, num_keys):
frame = curves[0].keyframe_points[i].co[0]
key = [fcurve.keyframe_points[i].co[1] for fcurve in curves]
if len(curves) == 3:
key = qrn * mathutils.Vector(key)
else:
#arbitrary rotations keeping the worldspace character of the rotation
if fixed_items == "0":
q = mathutils.Quaternion(key)
v, a = q.to_axis_angle()
v = qrn * mathutils.Vector(v)
key = mathutils.Quaternion(v, a)
#for the other case - restore rotation around rest bone change
if fixed_items == "1":
key = mathutils.Quaternion(key) * qe
for a in range(0,len(curves)):
curves[a].keyframe_points[i].co[1] = key[a]
for fcurve in curves:
fcurve.update()
#call the tangent function
loop_fcurve_tangents()
return {'FINISHED'}