15

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?

Mario Mey
  • 378
  • 1
  • 3
  • 10

4 Answers4

10

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, 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+"rotation_euler", 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)

Heckel
  • 113
  • 5
Mutant Bob
  • 9,243
  • 2
  • 29
  • 55
  • 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.

    – Mario Mey Oct 29 '15 at 16:07
  • aw nuts. I overlooked the bone part of the question. This will take a little more work as I create a new test bed. – Mutant Bob Oct 29 '15 at 16:54
  • 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! – Mario Mey Oct 30 '15 at 22:51
  • 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. – Mutant Bob Oct 31 '15 at 04:21
  • It's ok, it is not common to call them "groups". When you see a bone name in DopeSheet or in F-Curves, it is has no bone data, it is just a group of channels. And those channels have the information about the bone. You can group and ungroup them (Ctrl-G / Alt-G or in Channel menu). Then, the created Euler channels should be inside its respective bone group. – Mario Mey Nov 01 '15 at 12:37
  • OK, based on the way it is represented in the API, I would call these fcurve groups, and I think the newest script will put any newly-created euler curves in the same group as the quaternion source (but existing fcurves will not be regrouped). – Mutant Bob Nov 02 '15 at 18:25
  • Yes, man, it does exactly what I need. I know this is a really particular need and maybe there's nobody else needing this... but I think it could be converted to an addon and uploaded somewhere. I don't have the time right now, but I would do it. Also, changing some code, it could work on every Action in the file: don't care, I can do it. Thank you very much! – Mario Mey Nov 05 '15 at 12:15
  • And, with some additional lines, the Quaternions channels could be deleted, to make the file smaller... – Mario Mey Nov 05 '15 at 12:43
  • 3
    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! – Mario Mey Dec 15 '15 at 19:52
  • 1
    I know it is not proper etiquette to necro a dead discussion like this, but I really want to express my thanks for this script. I've been pulling my hair out trying to find an answer for converting quats animations to euler and this script is working for me in Blender 2.92. This in actual fact is still a problem and it is not a dead topic or discussion, even 6 years later. So, Thank you so much for this!!! – PteJack May 13 '21 at 19:55
  • @PteJack, thank you for your words. I'm glad it is usefull for you. Thanks to Atkay, I realized (today) that this code is inside Rigify, as a part of it. I mean, it's already inside Blender download file. – Mario Mey Oct 05 '21 at 19:12
4

With Rigify you can also convert rotation keyframes from any rotation mode to an other. At least it worked for me good so far. Just enable the Addon in preferences, then go to Pose > Convert Rotation Modes

I hope it's helpful.

enter image description here

enter image description here

Atti
  • 263
  • 2
  • 8
  • 3
    Just wow! JUST WOW. I realized that it is my and Mutant Bob's code! Look inside this file in Blender folder: 2.93/scripts/addons/rigify/rot_mode.py. There is my name! In other words... that's our code. I'm happy! – Mario Mey Oct 05 '21 at 18:53
  • Here is my GUI: https://imgur.com/a/YEvdFgA and here is Rigify's: https://imgur.com/a/uzeQcVJ. You can add this last capture to your answer . – Mario Mey Oct 05 '21 at 18:57
  • 1
    Nice, then i would say you did a top-of-the-line-job there :) For me it was really helpful for converting old quarternion animation data. Thanks :) – Atti Nov 02 '21 at 12:52
1

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.

Glider
  • 11
  • 1
  • That only sets the rotation mode. It doesn't convert the f-curve and its actions. –  Jul 07 '17 at 00:01
0

use this script for bone rotation conversion. simple and easy to read just replace the name of your action and bone

import bpy

action = bpy.data.actions["Hand.RAction"] eulerDataPath = 'pose.bones["CATRigRArmPalm"].rotation_euler' quatPath = 'pose.bones["CATRigRArmPalm"].rotation_quaternion' obj = bpy.context.object bone = obj.pose.bones["CATRigRArmPalm"] bone.rotation_mode = 'XYZ' for fc in action.fcurves: for keyframe in fc.keyframe_points: if fc.data_path == eulerDataPath: ftime = int(keyframe.co.x) bpy.context.scene.frame_set(ftime) bone.rotation_mode = 'XYZ' obj.keyframe_insert(data_path=eulerDataPath,frame = ftime) bone.rotation_mode = 'QUATERNION' continue

bone.rotation_mode = 'XYZ'