Anim: add RNA code for ChannelBags
RNA API for creating & removing channelbags, as well as a path function to construct RNA paths for channelbags. ```python action = bpy.data.actions.new('TestAction') slot = action.slots.new() slot.name = 'OBTest' layer = action.layers.new(name="Layer") strip = layer.strips.new(type='KEYFRAME') # New in this commit: channelbag = strip.channelbags.new(slot) strip.channelbags.remove(channelbag) ``` Pull Request: https://projects.blender.org/blender/blender/pulls/124793
This commit is contained in:
parent
3ecfa5659e
commit
7360ce4bcc
@ -657,6 +657,19 @@ class KeyframeStrip : public ::KeyframeActionStrip {
|
||||
* Should only be called when there is no `ChannelBag` for this slot yet.
|
||||
*/
|
||||
ChannelBag &channelbag_for_slot_add(const Slot &slot);
|
||||
|
||||
/**
|
||||
* Remove the ChannelBag from this slot.
|
||||
*
|
||||
* After this call the reference is no longer valid, as the memory will have been freed.
|
||||
*
|
||||
* \return true when the ChannelBag was found & removed, false if it wasn't found.
|
||||
*/
|
||||
bool channelbag_remove(ChannelBag &channelbag_to_remove);
|
||||
|
||||
/** Return the channelbag's index, or -1 if there is none for this slot handle. */
|
||||
int64_t find_channelbag_index(const ChannelBag &channelbag) const;
|
||||
|
||||
/**
|
||||
* Find an FCurve for this slot + RNA path + array index combination.
|
||||
*
|
||||
|
@ -1043,6 +1043,15 @@ const ChannelBag *KeyframeStrip::channelbag_for_slot(const slot_handle_t slot_ha
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
int64_t KeyframeStrip::find_channelbag_index(const ChannelBag &channelbag_to_remove) const
|
||||
{
|
||||
for (int64_t index = 0; index < this->channelbag_array_num; index++) {
|
||||
if (this->channelbag(index) == &channelbag_to_remove) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
ChannelBag *KeyframeStrip::channelbag_for_slot(const slot_handle_t slot_handle)
|
||||
{
|
||||
const auto *const_this = const_cast<const KeyframeStrip *>(this);
|
||||
@ -1072,6 +1081,28 @@ ChannelBag &KeyframeStrip::channelbag_for_slot_add(const Slot &slot)
|
||||
return channels;
|
||||
}
|
||||
|
||||
static void channelbag_ptr_destructor(ActionChannelBag **dna_channelbag_ptr)
|
||||
{
|
||||
ChannelBag &channelbag = (*dna_channelbag_ptr)->wrap();
|
||||
MEM_delete(&channelbag);
|
||||
};
|
||||
|
||||
bool KeyframeStrip::channelbag_remove(ChannelBag &channelbag_to_remove)
|
||||
{
|
||||
const int64_t channelbag_index = this->find_channelbag_index(channelbag_to_remove);
|
||||
if (channelbag_index < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dna::array::remove_index(&this->channelbag_array,
|
||||
&this->channelbag_array_num,
|
||||
nullptr,
|
||||
channelbag_index,
|
||||
channelbag_ptr_destructor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FCurve *KeyframeStrip::fcurve_find(const Slot &slot, const FCurveDescriptor fcurve_descriptor)
|
||||
{
|
||||
ChannelBag *channels = this->channelbag_for_slot(slot);
|
||||
|
@ -451,6 +451,45 @@ static int rna_iterator_keyframestrip_channelbags_length(PointerRNA *ptr)
|
||||
return key_strip.channelbags().size();
|
||||
}
|
||||
|
||||
static ActionChannelBag *rna_ChannelBags_new(KeyframeActionStrip *dna_strip,
|
||||
bContext *C,
|
||||
ReportList *reports,
|
||||
ActionSlot *dna_slot)
|
||||
{
|
||||
animrig::KeyframeStrip &key_strip = dna_strip->wrap();
|
||||
animrig::Slot &slot = dna_slot->wrap();
|
||||
|
||||
if (key_strip.channelbag_for_slot(slot) != nullptr) {
|
||||
BKE_report(reports, RPT_ERROR, "A channelbag for this slot already exists");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ChannelBag &channelbag = key_strip.channelbag_for_slot_add(slot);
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
|
||||
/* No need to tag the depsgraph, as there is no new animation yet. */
|
||||
|
||||
return &channelbag;
|
||||
}
|
||||
|
||||
static void rna_ChannelBags_remove(ID *action,
|
||||
KeyframeActionStrip *dna_strip,
|
||||
bContext *C,
|
||||
ReportList *reports,
|
||||
ActionChannelBag *dna_channelbag)
|
||||
{
|
||||
animrig::KeyframeStrip &key_strip = dna_strip->wrap();
|
||||
animrig::ChannelBag &channelbag = dna_channelbag->wrap();
|
||||
|
||||
if (!key_strip.channelbag_remove(channelbag)) {
|
||||
BKE_report(reports, RPT_ERROR, "This channelbag does not belong to this strip");
|
||||
return;
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
|
||||
DEG_id_tag_update(action, ID_RECALC_ANIMATION);
|
||||
}
|
||||
|
||||
static bool rna_KeyframeActionStrip_key_insert(ID *id,
|
||||
KeyframeActionStrip *dna_strip,
|
||||
Main *bmain,
|
||||
@ -481,6 +520,34 @@ static bool rna_KeyframeActionStrip_key_insert(ID *id,
|
||||
return ok;
|
||||
}
|
||||
|
||||
static std::optional<std::string> rna_ActionChannelBag_path(const PointerRNA *ptr)
|
||||
{
|
||||
animrig::Action &action = rna_action(ptr);
|
||||
animrig::ChannelBag &cbag_to_find = rna_data_channelbag(ptr);
|
||||
|
||||
for (animrig::Layer *layer : action.layers()) {
|
||||
for (int64_t strip_index : layer->strips().index_range()) {
|
||||
const animrig::Strip *strip = layer->strip(strip_index);
|
||||
if (!strip->is<animrig::KeyframeStrip>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const animrig::KeyframeStrip &key_strip = strip->as<animrig::KeyframeStrip>();
|
||||
const int64_t index = key_strip.find_channelbag_index(cbag_to_find);
|
||||
if (index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PointerRNA layer_ptr = RNA_pointer_create(&action.id, &RNA_ActionLayer, layer);
|
||||
const std::optional<std::string> layer_path = rna_ActionLayer_path(&layer_ptr);
|
||||
BLI_assert_msg(layer_path, "Every animation layer should have a valid RNA path.");
|
||||
return fmt::format("{}.strips[{}].channelbags[{}]", *layer_path, strip_index, index);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static void rna_iterator_ChannelBag_fcurves_begin(CollectionPropertyIterator *iter,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
@ -1433,6 +1500,9 @@ static void rna_def_keyframestrip_channelbags(BlenderRNA *brna, PropertyRNA *cpr
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
RNA_def_property_srna(cprop, "ActionChannelBags");
|
||||
srna = RNA_def_struct(brna, "ActionChannelBags", nullptr);
|
||||
RNA_def_struct_sdna(srna, "KeyframeActionStrip");
|
||||
@ -1440,6 +1510,30 @@ static void rna_def_keyframestrip_channelbags(BlenderRNA *brna, PropertyRNA *cpr
|
||||
srna,
|
||||
"Animation Channels for Slots",
|
||||
"For each action slot, a list of animation channels that are meant for that slot");
|
||||
|
||||
/* KeyframeStrip.channelbags.new(slot=...) */
|
||||
func = RNA_def_function(srna, "new", "rna_ChannelBags_new");
|
||||
RNA_def_function_ui_description(
|
||||
func,
|
||||
"Add a new channelbag to the strip, to contain animation channels for a specific slot");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
|
||||
parm = RNA_def_pointer(func,
|
||||
"slot",
|
||||
"ActionSlot",
|
||||
"Action Slot",
|
||||
"The slot that should be animated by this channelbag");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
|
||||
/* Return value. */
|
||||
parm = RNA_def_pointer(func, "channelbag", "ActionChannelBag", "", "Newly created channelbag");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
/* KeyframeStrip.channelbags.remove(strip) */
|
||||
func = RNA_def_function(srna, "remove", "rna_ChannelBags_remove");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
|
||||
RNA_def_function_ui_description(func, "Remove the channelbag from the strip");
|
||||
parm = RNA_def_pointer(func, "channelbag", "ActionChannelBag", "", "The channelbag to remove");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
}
|
||||
|
||||
static void rna_def_action_keyframe_strip(BlenderRNA *brna)
|
||||
@ -1587,6 +1681,7 @@ static void rna_def_action_channelbag(BlenderRNA *brna)
|
||||
srna,
|
||||
"Animation Channel Bag",
|
||||
"Collection of animation channels, typically associated with an action slot");
|
||||
RNA_def_struct_path_func(srna, "rna_ActionChannelBag_path");
|
||||
|
||||
prop = RNA_def_property(srna, "slot_handle", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
@ -164,6 +164,30 @@ class TestLegacyLayered(unittest.TestCase):
|
||||
self.assertSequenceEqual([], act.groups)
|
||||
|
||||
|
||||
class ChannelBagsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
anims = bpy.data.actions
|
||||
while anims:
|
||||
anims.remove(anims[0])
|
||||
|
||||
def test_create_remove_channelbag(self):
|
||||
action = bpy.data.actions.new('TestAction')
|
||||
|
||||
slot = action.slots.new()
|
||||
slot.name = 'OBTest'
|
||||
|
||||
layer = action.layers.new(name="Layer")
|
||||
strip = layer.strips.new(type='KEYFRAME')
|
||||
channelbag = strip.channelbags.new(slot)
|
||||
|
||||
strip.key_insert(slot, "location", 1, 47.0, 327.0)
|
||||
self.assertEqual("location", channelbag.fcurves[0].data_path,
|
||||
"Keys for the channelbag's slot should go into the channelbag")
|
||||
|
||||
strip.channelbags.remove(channelbag)
|
||||
self.assertEqual([], list(strip.channelbags))
|
||||
|
||||
|
||||
class DataPathTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
anims = bpy.data.actions
|
||||
@ -173,12 +197,19 @@ class DataPathTest(unittest.TestCase):
|
||||
def test_repr(self):
|
||||
action = bpy.data.actions.new('TestAction')
|
||||
|
||||
slot = action.slots.new()
|
||||
slot.name = 'OBTest'
|
||||
self.assertEqual("bpy.data.actions['TestAction'].slots[\"OBTest\"]", repr(slot))
|
||||
|
||||
layer = action.layers.new(name="Layer")
|
||||
self.assertEqual("bpy.data.actions['TestAction'].layers[\"Layer\"]", repr(layer))
|
||||
|
||||
strip = layer.strips.new(type='KEYFRAME')
|
||||
self.assertEqual("bpy.data.actions['TestAction'].layers[\"Layer\"].strips[0]", repr(strip))
|
||||
|
||||
channelbag = strip.channelbags.new(slot)
|
||||
self.assertEqual("bpy.data.actions['TestAction'].layers[\"Layer\"].strips[0].channelbags[0]", repr(channelbag))
|
||||
|
||||
|
||||
def main():
|
||||
global args
|
||||
|
Loading…
x
Reference in New Issue
Block a user