From 5c2069e2842687d29b249f4717af0d044a33f087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 23 Sep 2025 16:29:11 +0200 Subject: [PATCH] Refactor: convert "Bake Action" operator to current Action API Remove the use of `action.fcurves` in the Bake Action operator, replacing it with the current API (introduced for slotted Actions in Blender 4.4). No functional changes. This is part of #146586 Pull Request: https://projects.blender.org/blender/blender/pulls/147060 --- scripts/modules/bpy_extras/anim_utils.py | 59 +++++++++++------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/scripts/modules/bpy_extras/anim_utils.py b/scripts/modules/bpy_extras/anim_utils.py index eab32d16660..17e51b8bd4d 100644 --- a/scripts/modules/bpy_extras/anim_utils.py +++ b/scripts/modules/bpy_extras/anim_utils.py @@ -390,13 +390,6 @@ def bake_action_iter( if bake_options.do_object: obj_info.append((frame, *obj_frame_info(obj))) - # ------------------------------------------------------------------------- - # Clean (store initial data) - if bake_options.do_clean and action is not None: - clean_orig_data = {fcu: {p.co[1] for p in fcu.keyframe_points} for fcu in action.fcurves} - else: - clean_orig_data = {} - # ------------------------------------------------------------------------- # Create action @@ -423,16 +416,24 @@ def bake_action_iter( if not atd.use_tweak_mode: atd.action_blend_type = 'REPLACE' + # If any data is going to be baked, there will be a channelbag created, so + # might just as well create it now and have a clear, unambiguous reference + # to it. If it is created here, it will have no F-Curves, and so certain + # loops below will just be no-ops. + channelbag: ActionChannelbag = action_ensure_channelbag_for_slot(atd.action, atd.action_slot) + + # ------------------------------------------------------------------------- + # Clean (store initial data) + if bake_options.do_clean: + clean_orig_data = {fcu: {p.co[1] for p in fcu.keyframe_points} for fcu in channelbag.fcurves} + else: + clean_orig_data = {} + # ------------------------------------------------------------------------- # Apply transformations to action # pose - lookup_fcurves = {} - assert action.is_action_layered - channelbag = action_get_channelbag_for_slot(action, atd.action_slot) - if channelbag: - # channelbag can be None if no layers or strips exist in the action. - lookup_fcurves = {(fcurve.data_path, fcurve.array_index): fcurve for fcurve in channelbag.fcurves} + lookup_fcurves = {(fcurve.data_path, fcurve.array_index): fcurve for fcurve in channelbag.fcurves} if bake_options.do_pose: for f, armature_custom_properties in armature_info: @@ -524,10 +525,10 @@ def bake_action_iter( bake_custom_properties(pbone, custom_props=custom_props[name], frame=f, group_name=name) if is_new_action: - keyframes.insert_keyframes_into_new_action(total_new_keys, action, name) + keyframes.insert_keyframes_into_new_action(total_new_keys, channelbag, name) else: keyframes.insert_keyframes_into_existing_action( - lookup_fcurves, total_new_keys, action, atd.action_slot) + lookup_fcurves, total_new_keys, channelbag) # object. TODO. multiple objects if bake_options.do_object: @@ -591,10 +592,9 @@ def bake_action_iter( bake_custom_properties(obj, custom_props=custom_props, frame=f, group_name=name) if is_new_action: - keyframes.insert_keyframes_into_new_action(total_new_keys, action, name) + keyframes.insert_keyframes_into_new_action(total_new_keys, channelbag, name) else: - keyframes.insert_keyframes_into_existing_action( - lookup_fcurves, total_new_keys, action, atd.action_slot) + keyframes.insert_keyframes_into_existing_action(lookup_fcurves, total_new_keys, channelbag) if bake_options.do_parents_clear: obj.parent = None @@ -603,7 +603,7 @@ def bake_action_iter( # Clean if bake_options.do_clean: - for fcu in action.fcurves: + for fcu in channelbag.fcurves: fcu_orig_data = clean_orig_data.get(fcu, set()) keyframe_points = fcu.keyframe_points @@ -675,15 +675,14 @@ class KeyframesCo: def insert_keyframes_into_new_action( self, total_new_keys: int, - action: Action, - action_group_name: str, + channelbag: ActionChannelbag, + group_name: str, ) -> None: """ Assumes the action is new, that it has no F-curves. Otherwise, the only difference between versions is performance and implementation simplicity. - :arg action_group_name: Name of Action Group that F-curves are added to. - :type action_group_name: str + :arg group_name: Name of the Group that F-curves are added to. """ linear_enum_values = [ bpy.types.Keyframe.bl_rna.properties["interpolation"].enum_items["LINEAR"].value @@ -694,8 +693,8 @@ class KeyframesCo: continue data_path, array_index = fc_key - keyframe_points = action.fcurves.new( - data_path, index=array_index, action_group=action_group_name + keyframe_points = channelbag.fcurves.new( + data_path, index=array_index, group_name=group_name ).keyframe_points keyframe_points.add(total_new_keys) @@ -709,18 +708,14 @@ class KeyframesCo: self, lookup_fcurves: Mapping[FCurveKey, bpy.types.FCurve], total_new_keys: int, - action: Action, - action_slot: ActionSlot, + channelbag: ActionChannelbag, ) -> None: """ Assumes the action already exists, that it might already have F-curves. Otherwise, the only difference between versions is performance and implementation simplicity. :arg lookup_fcurves: : This is only used for efficiency. - It's a substitute for ``action.fcurves.find()`` which is a potentially expensive linear search. - :type lookup_fcurves: ``Mapping[FCurveKey, bpy.types.FCurve]`` - :arg action_group_name: Name of Action Group that F-curves are added to. - :type action_group_name: str + It's a substitute for ``channelbag.fcurves.find()`` which is a potentially expensive linear search. """ linear_enum_values = [ bpy.types.Keyframe.bl_rna.properties["interpolation"].enum_items["LINEAR"].value @@ -733,8 +728,6 @@ class KeyframesCo: fcurve = lookup_fcurves.get(fc_key, None) if fcurve is None: data_path, array_index = fc_key - assert action.is_action_layered - channelbag = action_ensure_channelbag_for_slot(action, action_slot) fcurve = channelbag.fcurves.new(data_path, index=array_index) keyframe_points = fcurve.keyframe_points