wrote a Blender script to auto-bake IK to FK chains before FBX export — tired of doing this by hand

164 views 0 replies

Every single time. I finish a rig, animate the whole thing with IK legs and arms, then open Unity and stare at a skeleton that just sits in T-pose because I forgot to bake before export. Again. So I finally wrote a script to handle it. It detects IK-constrained bones, walks the full chain, selects them, bakes visual transforms to keyframes, and strips the constraints.

import bpy

def get_ik_bone_names(armature_obj):
    ik_bones = set()
    for bone in armature_obj.pose.bones:
        for constraint in bone.constraints:
            if constraint.type == 'IK':
                chain_len = constraint.chain_count
                b = bone
                for _ in range(chain_len if chain_len > 0 else 10):
                    ik_bones.add(b.name)
                    if b.parent is None:
                        break
                    b = b.parent
    return list(ik_bones)


def bake_ik_to_fk(obj, frame_start=None, frame_end=None):
    if obj is None or obj.type != 'ARMATURE':
        print("Select an armature first.")
        return

    scene = bpy.context.scene
    fs = frame_start if frame_start is not None else scene.frame_start
    fe = frame_end if frame_end is not None else scene.frame_end

    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.mode_set(mode='POSE')

    ik_bone_names = get_ik_bone_names(obj)
    if not ik_bone_names:
        print(f"No IK constraints found on {obj.name}, nothing to bake.")
        bpy.ops.object.mode_set(mode='OBJECT')
        return

    bpy.ops.pose.select_all(action='DESELECT')
    for name in ik_bone_names:
        if name in obj.pose.bones:
            obj.pose.bones[name].bone.select = True

    bpy.ops.nla.bake(
        frame_start=fs,
        frame_end=fe,
        only_selected=True,
        visual_keying=True,
        clear_constraints=True,
        use_current_action=True,
        bake_types={'POSE'}
    )

    bpy.ops.object.mode_set(mode='OBJECT')
    print(f"Baked {len(ik_bone_names)} bones: {ik_bone_names}")


bake_ik_to_fk(bpy.context.active_object)

The key flag is visual_keying=True. Without it you get the pre-constraint values, which is completely useless. clear_constraints=True strips the IK solver afterward so you're not exporting dead constraint data that some importers complain about.

A few things I ran into:

  • Bakes every frame, so it gets slow on long takes. Anything over ~250 frames I'll manually step-down the frame range or downsample the keys after.
  • If your IK chain has a pole target bone, the chain-walking logic above doesn't grab it. You'd need to pull constraint.pole_target and add that bone separately. Haven't had drift issues without it but it probably matters on some rigs.
  • This only touches the active action. NLA multi-action setups need each action baked separately, which I haven't automated yet.

That NLA case is what I'd love help with. My current thought is muting all NLA tracks, soloing each strip in turn, baking, then restoring state, but it feels brittle. Anyone doing this more cleanly? Also curious whether people just rely on the FBX exporter's built-in bake_anim=True option instead of pre-baking in Blender. I've had it miss tail bones on longer chains and stopped trusting it.

Moonjump
Forum Search Shader Sandbox
Sign In Register