1
$\begingroup$

When baking an action we can select which bones to bake, by choosing selected only option. But can you have more control over which types of channels to bake? For example if I would like to only bake translation and rotation channels of the bones, is this possible to achieve with or without scripting? Locking channels in the transform panel for each bone prevents those channels from being keyframed but they still get baked.

$\endgroup$
3
$\begingroup$

Script Solution

Can look at the datapath of an fcurve of an action. Here is the fcurves of default armature object of single pose bone with a LocRotScale action

>>> a = C.object.animation_data.action

>>> for fc in a.fcurves:
...     fc.data_path
...     
'pose.bones["Bone"].location'
'pose.bones["Bone"].location'
'pose.bones["Bone"].location'
'pose.bones["Bone"].rotation_quaternion'
'pose.bones["Bone"].rotation_quaternion'
'pose.bones["Bone"].rotation_quaternion'
'pose.bones["Bone"].rotation_quaternion'
'pose.bones["Bone"].scale'
'pose.bones["Bone"].scale'
'pose.bones["Bone"].scale'

A script to convert any fcurve of an action to samples (bake it) that has a datapath that ends with "location" or "rotation_quaternion"

import bpy

context = bpy.context

ob = context.object
action = ob.animation_data.action

to_bake = ("location",
           "rotation_quaternion",
           )

fcs = (fc for fc in action.fcurves 
       if any(fc.data_path.endswith(p) for p in to_bake))

for fc in fcs:
    fc.convert_to_samples(*action.frame_range)

Further to this, can make a lookup dictionary of the fcurves, then use it based on selected pose bones

The first part of the datapath from a pose bone

>>> C.active_pose_bone.path_from_id()
'pose.bones["Bone"]'

Similar to above, but only baking the location and quat rot of selected pose bones.

import bpy
from collections import defaultdict

context = bpy.context

ob = context.object
action = ob.animation_data.action

to_bake = ("location",
           "rotation_quaternion",
           )

fcurves = defaultdict(list)

for fc in action.fcurves:
    fcurves[fc.data_path].append(fc)

for pb in context.selected_pose_bones:
    path = pb.path_from_id()
    for sfx in to_bake:
        fcs = fcurves[f"{path}.{sfx}"]
        for fc in fcs:
            print("Bake", fc.data_path, "xyzw"[fc.array_index]) 
            fc.convert_to_samples(*action.frame_range)
| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ this is exactly what I needed thanks a lot! I was using bpy.ops.nla.bake() command to bake previously. Aside from it being restrictive, I don't like using these "context specific" commands in my script. Being able to bake each fcurve separately as needed and not having to rely on the correct context is really nice! $\endgroup$ – Lenny White Mar 29 at 19:04
  • 1
    $\begingroup$ Cheers, added pose bone selection. $\endgroup$ – batFINGER Mar 29 at 19:13
  • $\begingroup$ Awesome, thanks for the addition! Can I ask, converting to samples works nicely, but the baked keyframes do not seem to get visualised in the dopesheet, like they do with default baking. In graph editor I can see the curve but I can't seem to be able to tweak it. $\endgroup$ – Lenny White Mar 29 at 19:30
  • 1
    $\begingroup$ Same result as bake audio to fcurve. AFAIK Have to convert back to keyframes to tweak. $\endgroup$ – batFINGER Mar 29 at 19:32
  • $\begingroup$ Baking curves this way unfortunately doesn't seem to bake the bone constraint transformations as well. I guess I'll have to use bpy.ops.nla.bake() if I want that. $\endgroup$ – Lenny White Mar 29 at 20:44

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.