2
$\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$

1 Answer 1

2
$\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)
$\endgroup$
8
  • 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$
    – user56873
    Mar 29, 2020 at 19:04
  • 1
    $\begingroup$ Cheers, added pose bone selection. $\endgroup$
    – batFINGER
    Mar 29, 2020 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$
    – user56873
    Mar 29, 2020 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, 2020 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$
    – user56873
    Mar 29, 2020 at 20:44

You must log in to answer this question.