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<0 or fc.array_index == array_index):
            return fc

    fc = action.fcurves.new(data_path, 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+"rotation_euler", i, group)
        pos = len(fc.keyframe_points)
        fc.keyframe_points[pos].co = [frame, euler[i]]

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]
    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 == "rotation_quaternion" or fc.data_path[-20:]==".rotation_quaternion":

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

        data_path = bone_prefix + "rotation_quaternion"
        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
convert_quaternion_to_euler(obj.animation_data.action, obj, order)
  • $\begingroup$ Thanks for this script! It does the trick, but for an object, not for bones as I need. Also, it creates Euler channels outside the object group. I think by adding some code, they could be inside the channel. And this could be important if it is modified to use it for several bones. $\endgroup$ – Mario Mey Oct 29 '15 at 16:07
  • $\begingroup$ aw nuts. I overlooked the bone part of the question. This will take a little more work as I create a new test bed. $\endgroup$ – Mutant Bob Oct 29 '15 at 16:54
  • $\begingroup$ The scripts seems to do what it has to do... but there is only one thing that I told you in last comment: all the new Euler channels are not in its respective group. Below all the bone groups, there are all the "X Euler Rotation (body)", "Y Euler Rotation (body)", "Z Euler Rotation (body)", "X Euler Rotation (hand.l)"... and so. Only that and it's perfect! $\endgroup$ – Mario Mey Oct 30 '15 at 22:51
  • $\begingroup$ I have to admit I'm not sure what you mean when you talk about groups. Maybe a screenshot could clear it up for me. $\endgroup$ – Mutant Bob Oct 31 '15 at 4:21
  • 3
    $\begingroup$ For those who are interested in this script, I modified it and converted to an addon. Now, it converts selected/all Bones from one/all Actions, in Tools Panel. Here. Thanks again, Mutant Bob! $\endgroup$ – Mario Mey Dec 15 '15 at 19:52

Here's what works for me (v. 2.78). While in Pose mode select all bones (A), press Ctrl+R, Set Rotation Mode context menu will pop up, select whatever rotation mode you want.

  • $\begingroup$ That only sets the rotation mode. It doesn't convert the f-curve and its actions. $\endgroup$ – user27640 Jul 7 '17 at 0:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.