Fix #137041: Bugs with Slotted Actions and the Available Keying Set

Make the 'Available' keying set look at the F-Curves for the assigned
slot, instead of the backward-compatible API (which only sees the F-Curves
for the first slot).

Pull Request: https://projects.blender.org/blender/blender/pulls/137131
This commit is contained in:
Sybren A. Stüvel
2025-04-08 11:10:46 +02:00
parent a753d68271
commit 9605cbde6f
4 changed files with 30 additions and 4 deletions

View File

@@ -75,12 +75,19 @@ class BakeOptions:
"""Bake custom properties."""
def action_get_channelbag_for_slot(action: Action, slot: ActionSlot) -> ActionChannelbag | None:
def action_get_channelbag_for_slot(action: Action | None, slot: ActionSlot | None) -> ActionChannelbag | None:
"""
Returns the first channelbag found for the slot.
In case there are multiple layers or strips they are iterated until a
channelbag for that slot is found. In case no matching channelbag is found, returns None.
"""
if not action or not slot:
# This is just for convenience so that you can call
# action_get_channelbag_for_slot(adt.action, adt.action_slot) and check
# the return value for None, without having to also check the action and
# the slot for None.
return None
for layer in action.layers:
for strip in layer.strips:
channelbag = strip.channelbag(slot)

View File

@@ -25,6 +25,8 @@ __all__ = (
import bpy
from bpy_extras import anim_utils
###########################
# General Utilities
@@ -117,7 +119,11 @@ def RKS_GEN_available(_ksi, _context, ks, data):
# for each F-Curve, include a path to key it
# NOTE: we don't need to set the group settings here
for fcu in adt.action.fcurves:
cbag = anim_utils.action_get_channelbag_for_slot(adt.action, adt.action_slot)
if not cbag:
return
for fcu in cbag.fcurves:
if basePath:
if basePath in fcu.data_path:
ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)

View File

@@ -17,6 +17,7 @@ are supported.
import bpy
import keyingsets_utils
from bpy.types import KeyingSetInfo
from bpy_extras import anim_utils
###############################
# Built-In KeyingSets
@@ -355,7 +356,13 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
ob = context.active_object
if ob:
# TODO: this fails if one animation-less object is active, but many others are selected
return ob.animation_data and ob.animation_data.action
adt = ob.animation_data
if not adt:
return False
cbag = anim_utils.action_get_channelbag_for_slot(adt.action, adt.action_slot)
if not cbag:
return False
return bool(cbag.fcurves)
else:
return bool(context.selected_objects)

View File

@@ -745,9 +745,15 @@ static void rna_Channelbag_group_remove(ActionChannelbag *dna_channelbag,
static ActionChannelbag *rna_ActionStrip_channelbag(ID *dna_action_id,
ActionStrip *self,
ReportList *reports,
const ActionSlot *dna_slot,
const bool ensure)
{
if (!dna_slot) {
BKE_report(reports, RPT_ERROR, "Cannot return channelbag when slot is None");
return nullptr;
}
animrig::Action &action = reinterpret_cast<bAction *>(dna_action_id)->wrap();
animrig::StripKeyframeData &strip_data = self->wrap().data<animrig::StripKeyframeData>(action);
const animrig::Slot &slot = dna_slot->wrap();
@@ -2377,7 +2383,7 @@ static void rna_def_action_keyframe_strip(BlenderRNA *brna)
/* Strip.channelbag(...). */
func = RNA_def_function(srna, "channelbag", "rna_ActionStrip_channelbag");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Find the ActionChannelbag for a specific Slot");
parm = RNA_def_pointer(
func, "slot", "ActionSlot", "Slot", "The slot for which to find the channelbag");