From 1395abc502ec2801b243c50f0ee4f83a392f3937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 3 Oct 2025 15:25:27 +0200 Subject: [PATCH] Python: remove legacy Action API Remove the legacy (aka backward-compatible) API from `bpy.types.Action`: - `action.fcurves` - `action.groups` - `action.id_root` These have been deprecated since the introduction of Slotted Actions in Blender 4.4. They operated on a subset of the Action's data, which was not guaranteed to be the correct subset, given the Action's new possibilities. This was to give add-on developers time to migrate to the new API, and has always been intended to be removed in Blender 5.0. See #146586 for more info. Pull Request: https://projects.blender.org/blender/blender/pulls/146626 --- source/blender/makesrna/intern/rna_action.cc | 558 ------------------- 1 file changed, 558 deletions(-) diff --git a/source/blender/makesrna/intern/rna_action.cc b/source/blender/makesrna/intern/rna_action.cc index 6baefe068b3..c647274af48 100644 --- a/source/blender/makesrna/intern/rna_action.cc +++ b/source/blender/makesrna/intern/rna_action.cc @@ -912,362 +912,6 @@ static PointerRNA rna_ActionGroup_channels_get(CollectionPropertyIterator *iter) return RNA_pointer_create_with_parent(iter->parent, &RNA_FCurve, fcurve); } -/* Use the backward-compatible API only when we're working with the action as a - * layered action. */ -static bool use_backward_compatible_api(animrig::Action &action) -{ - return !animrig::legacy::action_treat_as_legacy(action); -} - -static void rna_iterator_Action_groups_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - animrig::Action &action = rna_action(ptr); - - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag) { - return; - } - rna_iterator_array_begin(iter, ptr, channelbag->channel_groups()); - } - else { - rna_iterator_listbase_begin(iter, ptr, &action.groups, nullptr); - } -} -static void rna_iterator_Action_groups_next(CollectionPropertyIterator *iter) -{ - animrig::Action &action = rna_action(&iter->parent); - if (use_backward_compatible_api(action)) { - rna_iterator_array_next(iter); - } - else { - rna_iterator_listbase_next(iter); - } -} -static void rna_iterator_Action_groups_end(CollectionPropertyIterator *iter) -{ - animrig::Action &action = rna_action(&iter->parent); - if (use_backward_compatible_api(action)) { - rna_iterator_array_end(iter); - } - else { - rna_iterator_listbase_end(iter); - } -} -static PointerRNA rna_iterator_Action_groups_get(CollectionPropertyIterator *iter) -{ - animrig::Action &action = rna_action(&iter->parent); - bActionGroup *group; - if (use_backward_compatible_api(action)) { - group = static_cast(rna_iterator_array_dereference_get(iter)); - } - else { - group = static_cast(rna_iterator_listbase_get(iter)); - } - - return RNA_pointer_create_with_parent(iter->parent, &RNA_ActionGroup, group); -} -static int rna_iterator_Action_groups_length(PointerRNA *ptr) -{ - animrig::Action &action = rna_action(ptr); - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag) { - return 0; - } - return channelbag->channel_groups().size(); - } - return BLI_listbase_count(&action.groups); -} - -static bActionGroup *rna_Action_groups_new(bAction *act, const char name[]) -{ - bActionGroup *group; - - animrig::Action &action = act->wrap(); - if (use_backward_compatible_api(action)) { - animrig::Channelbag &channelbag = animrig::legacy::channelbag_ensure(action); - group = &channelbag.channel_group_create(name); - } - else { - group = action_groups_add_new(act, name); - } - - /* I (Sybren) expected that the commented-out notifier below was missing. - * However, the animation filtering code (`animfilter_act_group()`) hides - * empty groups. Because this group is newly created, it's not shown in the - * UI, and thus there is no need to send notifiers. - * - * WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN | NA_ADDED, nullptr); */ - - return group; -} - -static void rna_Action_groups_remove(bAction *act, ReportList *reports, PointerRNA *agrp_ptr) -{ - bActionGroup *agrp = static_cast(agrp_ptr->data); - BLI_assert(agrp); /* Ensured by RNA flag PROP_NEVER_NULL. */ - - animrig::Action &action = act->wrap(); - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag || !channelbag->channel_group_remove(*agrp)) { - BKE_reportf(reports, - RPT_ERROR, - "Action group '%s' not found in action '%s'", - agrp->name, - act->id.name + 2); - } - - /* Removing a group also removes its F-Curves. */ - DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH); - WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr); - - return; - } - - FCurve *fcu, *fcn; - - /* try to remove the F-Curve from the action */ - if (BLI_remlink_safe(&act->groups, agrp) == false) { - BKE_reportf(reports, - RPT_ERROR, - "Action group '%s' not found in action '%s'", - agrp->name, - act->id.name + 2); - return; - } - - /* Move every one of the group's F-Curves out into the Action again. */ - for (fcu = static_cast(agrp->channels.first); (fcu) && (fcu->grp == agrp); fcu = fcn) { - fcn = fcu->next; - - /* remove from group */ - action_groups_remove_channel(act, fcu); - - /* tack onto the end */ - BLI_addtail(&act->curves, fcu); - } - - MEM_freeN(agrp); - agrp_ptr->invalidate(); - - DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH); - WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr); -} - -static void rna_iterator_Action_fcurves_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - animrig::Action &action = rna_action(ptr); - - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag) { - return; - } - rna_iterator_array_begin(iter, ptr, channelbag->fcurves()); - } - else { - rna_iterator_listbase_begin(iter, ptr, &action.curves, nullptr); - } -} -static void rna_iterator_Action_fcurves_next(CollectionPropertyIterator *iter) -{ - animrig::Action &action = rna_action(&iter->parent); - if (use_backward_compatible_api(action)) { - rna_iterator_array_next(iter); - } - else { - rna_iterator_listbase_next(iter); - } -} -static void rna_iterator_Action_fcurves_end(CollectionPropertyIterator *iter) -{ - animrig::Action &action = rna_action(&iter->parent); - if (use_backward_compatible_api(action)) { - rna_iterator_array_end(iter); - } - else { - rna_iterator_listbase_end(iter); - } -} -static PointerRNA rna_iterator_Action_fcurves_get(CollectionPropertyIterator *iter) -{ - animrig::Action &action = rna_action(&iter->parent); - FCurve *fcurve; - if (use_backward_compatible_api(action)) { - fcurve = static_cast(rna_iterator_array_dereference_get(iter)); - } - else { - fcurve = static_cast(rna_iterator_listbase_get(iter)); - } - - return RNA_pointer_create_with_parent(iter->parent, &RNA_FCurve, fcurve); -} -static int rna_iterator_Action_fcurves_length(PointerRNA *ptr) -{ - animrig::Action &action = rna_action(ptr); - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag) { - return 0; - } - return channelbag->fcurves().size(); - } - return BLI_listbase_count(&action.curves); -} - -static FCurve *rna_Action_fcurve_new(bAction *act, - Main *bmain, - ReportList *reports, - const char *data_path, - int index, - const char *group) -{ - BLI_assert(data_path != nullptr); - if (data_path[0] == '\0') { - BKE_report(reports, RPT_ERROR, "F-Curve data path empty, invalid argument"); - return nullptr; - } - - animrig::FCurveDescriptor fcurve_descriptor = {data_path, index}; - if (group && group[0] != '\0') { - fcurve_descriptor.channel_group = group; - } - - animrig::Action &action = act->wrap(); - if (use_backward_compatible_api(action)) { - /* Add the F-Curve to the channelbag for the first slot. */ - animrig::Channelbag &channelbag = animrig::legacy::channelbag_ensure(action); - FCurve *fcurve = channelbag.fcurve_create_unique(bmain, fcurve_descriptor); - if (!fcurve) { - /* The only reason fcurve_create_unique() returns nullptr is when the curve - * already exists. */ - - /* This is using the same error as below, as this is mimicking the legacy API. */ - BKE_reportf(reports, - RPT_ERROR, - "F-Curve '%s[%d]' already exists in action '%s'", - data_path, - index, - act->id.name + 2); - return nullptr; - } - - return fcurve; - } - - /* Annoying, check if this exists. */ - if (blender::animrig::fcurve_find_in_action(act, fcurve_descriptor)) { - BKE_reportf(reports, - RPT_ERROR, - "F-Curve '%s[%d]' already exists in action '%s'", - data_path, - index, - act->id.name + 2); - return nullptr; - } - return blender::animrig::action_fcurve_ensure_legacy( - bmain, - act, - fcurve_descriptor.channel_group ? fcurve_descriptor.channel_group->c_str() : nullptr, - nullptr, - fcurve_descriptor); -} - -static FCurve *rna_Action_fcurve_find(bAction *act, - ReportList *reports, - const char *data_path, - int index) -{ - if (data_path[0] == '\0') { - BKE_report(reports, RPT_ERROR, "F-Curve data path empty, invalid argument"); - return nullptr; - } - - animrig::Action &action = act->wrap(); - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag) { - return nullptr; - } - return channelbag->fcurve_find({data_path, index}); - } - - /* Returns nullptr if not found. */ - return animrig::fcurve_find_in_action(act, {data_path, index}); -} - -static void rna_Action_fcurve_remove(bAction *act, ReportList *reports, PointerRNA *fcu_ptr) -{ - FCurve *fcu = static_cast(fcu_ptr->data); - - animrig::Action &action = act->wrap(); - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag) { - BKE_reportf(reports, RPT_ERROR, "F-Curve not found in action '%s'", act->id.name + 2); - return; - } - if (!channelbag->fcurve_remove(*fcu)) { - BKE_reportf(reports, RPT_ERROR, "F-Curve not found in action '%s'", act->id.name + 2); - return; - } - fcu_ptr->invalidate(); - - DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH); - WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr); - - return; - } - - if (fcu->grp) { - if (BLI_findindex(&act->groups, fcu->grp) == -1) { - BKE_reportf(reports, - RPT_ERROR, - "F-Curve's action group '%s' not found in action '%s'", - fcu->grp->name, - act->id.name + 2); - return; - } - - action_groups_remove_channel(act, fcu); - BKE_fcurve_free(fcu); - fcu_ptr->invalidate(); - } - else { - if (BLI_findindex(&act->curves, fcu) == -1) { - BKE_reportf(reports, RPT_ERROR, "F-Curve not found in action '%s'", act->id.name + 2); - return; - } - - BLI_remlink(&act->curves, fcu); - BKE_fcurve_free(fcu); - fcu_ptr->invalidate(); - } - - DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH); - WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr); -} - -static void rna_Action_fcurve_clear(bAction *act) -{ - animrig::Action &action = act->wrap(); - if (use_backward_compatible_api(action)) { - animrig::Channelbag *channelbag = animrig::legacy::channelbag_get(action); - if (!channelbag) { - /* Nothing to clear, so the post-condition of not having F-Curves is fulfilled. */ - return; - } - channelbag->fcurves_clear(); - } - else { - BKE_action_fcurves_clear(act); - } - - WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr); -} - static TimeMarker *rna_Action_pose_markers_new(bAction *act, const char name[]) { TimeMarker *marker = MEM_callocN("TimeMarker"); @@ -1673,55 +1317,6 @@ static void rna_ActionSlot_target_id_type_set(PointerRNA *ptr, int value) action.slot_idtype_define(slot, ID_Type(value)); } -/** - * For API backwards compatibility with pre-layered-actions (Blender 4.3 and - * earlier), we treat `Action.id_root` as a proxy for the `target_id_type` - * property (`idtype` in DNA) of the Action's first Slot. - * - * If the Action has no slots, then we fallback to returning 'unspecified' (0). - */ -static int rna_Action_id_root_get(PointerRNA *ptr) -{ - animrig::Action &action = reinterpret_cast(ptr->owner_id)->wrap(); - - if (action.slots().is_empty()) { - return 0; - } - - return action.slot(0)->idtype; -} - -/** - * For API backwards compatibility with pre-layered-actions (Blender 4.3 and - * earlier), we treat `Action.id_root` as a proxy for the `target_id_type` - * property (`idtype` in DNA) of the Action's first Slot. - * - * If the Action has no slots, then a legacy slot is created and its - * `target_id_type` is set. - */ -static void rna_Action_id_root_set(PointerRNA *ptr, int value) -{ - animrig::Action &action = reinterpret_cast(ptr->owner_id)->wrap(); - - animrig::Slot &slot = animrig::legacy::slot_ensure(action); - action.slot_idtype_define(slot, ID_Type(value)); -} - -static void rna_Action_id_root_update(Main *bmain, Scene *, PointerRNA *ptr) -{ - animrig::Action &action = rna_action(ptr); - - if (action.slots().is_empty()) { - /* Nothing to do: id_root can't be set without at least one slot, so no - * change was possible that would necessitate an update. */ - return; - } - - /* Setting id_root actually sets the target ID type of the first slot, so it's - * the resulting changes to the first slot that we need to propagate. */ - action.slot_identifier_propagate(*bmain, *action.slot(0)); -} - #else static void rna_def_dopesheet(BlenderRNA *brna) @@ -2758,110 +2353,6 @@ static void rna_def_action_group(BlenderRNA *brna) /* =========================== Legacy Action interface =========================== */ -/* fcurve.keyframe_points */ -static void rna_def_action_groups(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "ActionGroups"); - srna = RNA_def_struct(brna, "ActionGroups", nullptr); - RNA_def_struct_sdna(srna, "bAction"); - RNA_def_struct_ui_text(srna, "Action Groups", "Collection of action groups"); - RNA_def_property_collection_funcs(cprop, - "rna_iterator_Action_groups_begin", - "rna_iterator_Action_groups_next", - "rna_iterator_Action_groups_end", - "rna_iterator_Action_groups_get", - "rna_iterator_Action_groups_length", - nullptr, - nullptr, - nullptr); - - func = RNA_def_function(srna, "new", "rna_Action_groups_new"); - RNA_def_function_ui_description(func, "Create a new action group and add it to the action"); - parm = RNA_def_string(func, "name", "Group", 0, "", "New name for the action group"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_pointer(func, "action_group", "ActionGroup", "", "Newly created action group"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_Action_groups_remove"); - RNA_def_function_ui_description(func, "Remove action group"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "action_group", "ActionGroup", "", "Action group to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); -} - -static void rna_def_action_fcurves(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "ActionFCurves"); - srna = RNA_def_struct(brna, "ActionFCurves", nullptr); - RNA_def_struct_sdna(srna, "bAction"); - RNA_def_struct_ui_text( - srna, - "Action F-Curves", - "Collection of action F-Curves. Note that this is a legacy API that is unaware of action " - "slots, and will only consider the F-Curves for this action's first slot"); - RNA_def_property_collection_funcs(cprop, - "rna_iterator_Action_fcurves_begin", - "rna_iterator_Action_fcurves_next", - "rna_iterator_Action_fcurves_end", - "rna_iterator_Action_fcurves_get", - "rna_iterator_Action_fcurves_length", - nullptr, - nullptr, - nullptr); - - /* Action.fcurves.new(...) */ - func = RNA_def_function(srna, "new", "rna_Action_fcurve_new"); - RNA_def_function_ui_description(func, - "Add an F-Curve for the first slot of this action, creating the " - "necessary layer, strip, and slot if necessary"); - RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN); - parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path to use"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Array index", 0, INT_MAX); - RNA_def_string( - func, "action_group", nullptr, 0, "Action Group", "Acton group to add this F-Curve into"); - - parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "Newly created F-Curve"); - RNA_def_function_return(func, parm); - - /* Action.fcurves.find(...) */ - func = RNA_def_function(srna, "find", "rna_Action_fcurve_find"); - RNA_def_function_ui_description( - func, - "Find an F-Curve. Note that this function performs a linear scan " - "of all F-Curves for the action's first slot."); - RNA_def_function_flag(func, FUNC_USE_REPORTS); - parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Array index", 0, INT_MAX); - parm = RNA_def_pointer( - func, "fcurve", "FCurve", "", "The found F-Curve, or None if it does not exist"); - RNA_def_function_return(func, parm); - - /* Action.fcurves.remove(...) */ - func = RNA_def_function(srna, "remove", "rna_Action_fcurve_remove"); - RNA_def_function_ui_description(func, "Remove the F-Curve from the action's first slot"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "F-Curve to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); - - /* Action.fcurves.clear() */ - func = RNA_def_function(srna, "clear", "rna_Action_fcurve_clear"); - RNA_def_function_ui_description(func, "Remove all F-Curves from the action's first slot"); -} - static void rna_def_action_pose_markers(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2910,53 +2401,6 @@ static void rna_def_action_pose_markers(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_ui_text(prop, "Active Pose Marker Index", "Index of active pose marker"); } -/* Access to 'legacy' Action features, like the top-level F-Curves, the corresponding F-Curve - * groups, and the top-level id_root. */ -static void rna_def_action_legacy(BlenderRNA *brna, StructRNA *srna) -{ - PropertyRNA *prop; - - /* collections */ - prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, nullptr, "curves", nullptr); - RNA_def_property_struct_type(prop, "FCurve"); - RNA_def_property_ui_text( - prop, - "F-Curves", - "Legacy API, for backward compatibility with code that does not handle slotted actions yet. " - "This collection contains the F-Curves for the action's first slot"); - rna_def_action_fcurves(brna, prop); - - prop = RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, nullptr, "groups", nullptr); - RNA_def_property_struct_type(prop, "ActionGroup"); - RNA_def_property_ui_text( - prop, - "Groups", - "Legacy API, for backward compatibility with code that does not handle slotted actions yet. " - "This collection contains the F-Curve groups for the action's first slot"); - rna_def_action_groups(brna, prop); - - /* special "type" limiter - should not really be edited in general, - * but is still available/editable in 'emergencies' */ - prop = RNA_def_property(srna, "id_root", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "idroot"); - RNA_def_property_enum_items(prop, default_ActionSlot_target_id_type_items); - RNA_def_property_enum_funcs(prop, - "rna_Action_id_root_get", - "rna_Action_id_root_set", - "rna_ActionSlot_target_id_type_itemf"); - RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_Action_id_root_update"); - RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT); - RNA_def_property_ui_text( - prop, - "ID Root Type", - "Legacy API, for backward compatibility with code that does not handle slotted actions yet. " - "Type of data-block that the action's first slot can be used on. Do not change unless you " - "know what you are doing"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); -} - static void rna_def_action(BlenderRNA *brna) { StructRNA *srna; @@ -3141,8 +2585,6 @@ static void rna_def_action(BlenderRNA *brna) parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "The found or created F-Curve"); RNA_def_function_return(func, parm); - rna_def_action_legacy(brna, srna); - /* API calls */ RNA_api_action(srna); }