How to convert Quaternions keyframes to Euler ones in several Actions? I created a character for a game. It has IK legs. I animated its foot and didn't realize that they were Quaterinon. I made lot of actions with it.

Now... as I will make more actions with this character, I would like to convert every Action with its rotation data to Euler. So, after changing its rotation mode, the bone would have the same visual rotation as it had with Quaternions.

There are some ways to do it by hand. In my opinion, the easiest is:


Select one Action.
Select the bone I want to convert.
Enter in Edit mode.
Duplicate it.
Go to pose mode.
Insert a keyframe LocRotScale (now, it appear as a channel in the Action).
Copy every frame from the first bone and paste them in the duplicated one (select keyframes, Ctrl-C, select channel, Ctrl-V)
Change Rotation mode to XYZ-Euler to the duplicated bone (this step has to be here. If not, it wouldn't have the correct keyframes).
Add a CopyRot constraint to the duplicated bone, copying the visual rotation from the first one.
In the first keyframe, insert VisualRotation Keyframe (selecting VisualRot in keying set).
Press Up key, press I key. Again. Again. Again till the last keyframe.
Select first bone.
Convert to Euler.
Insert a keyframe (to aquire Euler channels).
Copy every keyframe from the second bone and paste to the first.
Delete the second bone and its channel in the Action.


As you can see, it is a big deal to do this with every bone and every Action.

I think, in total, there are 3 ways to do this:


The one I said, by hand.
To make a script that make all those steps, but automatic.
To make a script that include advanced math operation to convert those Quaterion keyframes to Euler ones.
To use "Rotation mode keyframes" in every Action, to respect the correct mode. I mean, to put a Quaternion keyframe in every already done Action and put a Euler keyframe in every new action. (I didn't try this technique, I don't know if it works in BGE)


... any other?
I cobbled together the following python script: http://web.purplefrog.com/~thoth/blender/python-cookbook/convert-quaternion-keyframes-to-euler.html
Here is another update that supports both bone fcurves, and the fcurve groups.
import bpy

def get_or_create_fcurve(action, data_path, array_index=-1, group=None):
    for fc in action.fcurves:
        if fc.data_path == data_path and (array_index&lt;0 or fc.array_index == array_index):
            return fc

    fc = action.fcurves.new(data_path, index=array_index)
    fc.group = group
    return fc

def add_keyframe_euler(action, euler, frame, bone_prefix, group):
    for i in range(len(euler)):
        fc = get_or_create_fcurve(action, bone_prefix+&quot;rotation_euler&quot;, i, group)
        pos = len(fc.keyframe_points)
        fc.keyframe_points.add(1)
        fc.keyframe_points[pos].co = [frame, euler[i]]
        fc.update()


def frames_matching(action, data_path):
    frames = set()
    for fc in action.fcurves:
        if fc.data_path == data_path:
            fri = [kp.co[0] for kp in fc.keyframe_points]
            frames.update(fri)
    return frames


def fcurves_group(action, data_path):
    for fc in action.fcurves:
        if fc.data_path == data_path and fc.group is not None:
            return fc.group
    return None


def convert_quaternion_to_euler(action, obj, order):

    bone_prefixes = set()
    for fc in action.fcurves:
        if fc.data_path == &quot;rotation_quaternion&quot; or fc.data_path[-20:]==&quot;.rotation_quaternion&quot;:
            bone_prefixes.add(fc.data_path[:-19])


    for bone_prefix in bone_prefixes:
        if (bone_prefix == &quot;&quot;):
            bone = obj
        else:
            bone = eval(&quot;obj.&quot;+bone_prefix[:-1]) # I wish I knew a better way to do this

        data_path = bone_prefix + &quot;rotation_quaternion&quot;
        frames = frames_matching(action, data_path)
        group = fcurves_group(action, data_path)

        for fr in frames:
            quat = bone.rotation_quaternion.copy()
            for fc in action.fcurves:
                if fc.data_path == data_path:
                    quat[fc.array_index] = fc.evaluate(fr)
            euler = quat.to_euler(order)

            add_keyframe_euler(action, euler, fr, bone_prefix, group)
            bone.rotation_mode = order
#
#

scn = bpy.context.scene
obj = bpy.context.active_object
order='XYZ'
convert_quaternion_to_euler(obj.animation_data.action, obj, order)