Refactor: misc improvements to the channel groups code

Addressing post-landed review comments from @dr.sybren in #125774.

These changes are all refactors and cleanups, and should have no functional changes.

Pull Request: https://projects.blender.org/blender/blender/pulls/126822
This commit is contained in:
Nathan Vegdahl
2024-08-29 19:12:45 +02:00
committed by Nathan Vegdahl
parent df59028d0b
commit 1c1e389d4b
9 changed files with 206 additions and 180 deletions

View File

@@ -832,68 +832,88 @@ class ChannelBag : public ::ActionChannelBag {
* It specifically does *not* maintain any of the semantic invariants of the
* group array or its relationship to the fcurves.
*
* Both `collapse_channel_group_gaps()` and
* `update_fcurve_channel_group_pointers()` should be called at some point
* after this to restore the semantic invariants.
* `restore_channel_group_invariants()` should be called at some point after
* this to restore the semantic invariants.
*
* \see `collapse_channel_group_gaps()`
*
* \see `update_fcurve_channel_group_pointers()`
* \see `restore_channel_group_invariants()`
*/
void channel_group_remove_raw(int channel_group_index);
/**
* Move channel groups' fcurve spans so that there are no gaps between them,
* and to start at the first fcurve.
* Restore invariants related to channel groups.
*
* This does *not* alter the order of the channel groups nor the number of
* fcurves in each group. It simply changes the start indices of each group so
* that the groups are packed together at the start of the fcurves.
* This restores critical invariants and should be called (at some point) any
* time that groups are explicitly modified or that group membership of
* fcurves might change implicitly (e.g. due to moving/adding/removing
* fcurves).
*
* The specific invariants restored by this method are:
* 1. All grouped fcurves should come before all non-grouped fcurves.
* 2. All fcurves should point back to the group they belong to (if any) via
* their `grp` pointer.
*
* This function assumes that the fcurves are already in the correct group
* order (so the first N belong to the first group, which is also of length N,
* etc.). The groups are then updated so their starting index matches this.
* Then the fcurves' `grp` pointer is updated, so that any changes in group
* membership is correctly reflected.
*
* For example, if the mapping of groups to fcurves looks like this (g* are
* the groups, dots indicate ungrouped areas, and f* are the fcurves, so e.g.
* f1 and f2 are part of group g0):
* group g0 currently contains f1 and f2, but ought to contain f0 and f1):
*
* ```
* ..| g0 |..|g1|.....| g2 |..
* |..| g0 |..|g1|.....| g2 |..|
* |f0|f1|f2|f3|f4|f5|f6|f7|f8|f9|
* ```
*
* Then after calling this function they will look like this:
*
* ```
* | g0 |g1| g2 |..............
* | g0 |g1| g2 |..............|
* |f0|f1|f2|f3|f4|f5|f6|f7|f8|f9|
* ```
*
* Note that this specifically does *not* move the fcurves, and therefore this
* alters fcurve membership in a way that depends on how the groups are
* shifted. It also does not update the group pointers inside the fcurves, so
* `update_fcurve_channel_group_pointers()` must be called at some point after
* this to fix those up.
* Note that this specifically does *not* move the fcurves, but rather moves
* the groups *over* the fcurves, changing membership.
*
* This upholds critical invariants and should be called any time gaps might
* be introduced (changing the fcurve span of or removing a group).
*
* \see `update_fcurve_channel_group_pointers()`
* The `grp` pointers in the fcurves are then updated to reflect their new
* group membership, using the groups as the source of truth.
*/
void collapse_channel_group_gaps();
void restore_channel_group_invariants();
};
static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag),
"DNA struct and its C++ wrapper must have the same size");
/**
* A group of channels within a ChannelBag.
*
* This does *not* own the fcurves--the ChannelBag does. This just groups
* fcurves for organizational purposes, e.g. for use in the channel list in the
* animation editors.
*
* Usage of this wrapper typically indicates that the group is part of a layered
* action. However, the underlying `bActionGroup` struct is also used by legacy
* actions.
*/
class ChannelGroup : public ::bActionGroup {
public:
/**
* Determine whether this channel group is from a legacy action or a layered action.
*
* \return True if it's from a legacy action, false if it's from a layered action.
*/
bool is_legacy() const;
/**
* Updates all fcurves to point at the channel group that they belong to.
*
* The indices in the channel groups are considered the source of truth, and
* the pointers in the fcurves are simply updated to match those. Fcurves
* that don't belong to a group will have their group pointer set to null.
*
* This upholds critical invariants and should always be called after
* modifications to either the array of fcurves (changing the array position
* of, adding, or removing fcurves) or to the array of groups (changing the
* fcurve span of, removing, or adding groups).
* Get the fcurves in this channel group.
*/
void update_fcurve_channel_group_pointers();
Span<FCurve *> fcurves();
Span<const FCurve *> fcurves() const;
};
static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag),
static_assert(sizeof(ChannelGroup) == sizeof(::bActionGroup),
"DNA struct and its C++ wrapper must have the same size");
/**
@@ -979,20 +999,6 @@ const animrig::ChannelBag *channelbag_for_action_slot(const Action &action,
slot_handle_t slot_handle);
animrig::ChannelBag *channelbag_for_action_slot(Action &action, slot_handle_t slot_handle);
/**
* Return the channel groups for this specific slot handle.
*
* This is just a utility function, that's intended to become obsolete when multi-layer Actions
* are introduced. However, since Blender currently only supports a single layer with a single
* strip, of a single type, this function can be used.
*
* The use of this function is also an indicator for code that will have to be altered when
* multi-layered Actions are getting implemented.
*/
Span<bActionGroup *> channel_groups_for_action_slot(Action &action, slot_handle_t slot_handle);
Span<const bActionGroup *> channel_groups_for_action_slot(const Action &action,
slot_handle_t slot_handle);
/**
* Return the F-Curves for this specific slot handle.
*
@@ -1162,6 +1168,15 @@ void action_deselect_keys(Action &action);
/* Wrap functions for the DNA structs. */
inline blender::animrig::ChannelGroup &bActionGroup::wrap()
{
return *reinterpret_cast<blender::animrig::ChannelGroup *>(this);
}
inline const blender::animrig::ChannelGroup &bActionGroup::wrap() const
{
return *reinterpret_cast<const blender::animrig::ChannelGroup *>(this);
}
inline blender::animrig::Action &bAction::wrap()
{
return *reinterpret_cast<blender::animrig::Action *>(this);

View File

@@ -1263,15 +1263,14 @@ FCurve &ChannelBag::fcurve_create(Main *bmain, FCurveDescriptor fcurve_descripto
bActionGroup *group = fcurve_descriptor.channel_group.has_value() ?
&this->channel_group_ensure(*fcurve_descriptor.channel_group) :
nullptr;
int insert_index = group ? group->fcurve_range_start + group->fcurve_range_length :
this->fcurve_array_num;
const int insert_index = group ? group->fcurve_range_start + group->fcurve_range_length :
this->fcurve_array_num;
BLI_assert(insert_index <= this->fcurve_array_num);
grow_array_and_insert(&this->fcurve_array, &this->fcurve_array_num, insert_index, new_fcurve);
if (group) {
group->fcurve_range_length += 1;
this->collapse_channel_group_gaps();
this->update_fcurve_channel_group_pointers();
this->restore_channel_group_invariants();
}
if (bmain) {
@@ -1295,15 +1294,14 @@ bool ChannelBag::fcurve_remove(FCurve &fcurve_to_remove)
const int group_index = this->channel_group_containing_index(fcurve_index);
if (group_index != -1) {
bActionGroup *group = this->channel_groups()[group_index];
bActionGroup *group = this->channel_group(group_index);
group->fcurve_range_length -= 1;
if (group->fcurve_range_length <= 0) {
const int group_index = this->channel_groups().as_span().first_index_try(group);
this->channel_group_remove_raw(group_index);
}
this->collapse_channel_group_gaps();
this->update_fcurve_channel_group_pointers();
this->restore_channel_group_invariants();
}
dna::array::remove_index(
@@ -1508,7 +1506,12 @@ bActionGroup &ChannelBag::channel_group_create(StringRefNull name)
/* Make it selected. */
new_group->flag = AGRP_SELECTED;
/* Ensure it has a unique name. */
/* Ensure it has a unique name.
*
* Note that this only happens here (upon creation). The user can later rename
* groups to have duplicate names. This is stupid, but it's how the legacy
* system worked, and at the time of writing this code we're just trying to
* match that system's behavior, even when it's goofy.*/
std::string unique_name = BLI_uniquename_cb(
[&](const StringRef name) {
for (bActionGroup *group : this->channel_groups()) {
@@ -1558,8 +1561,7 @@ bool ChannelBag::channel_group_remove(bActionGroup &group)
to_index);
this->channel_group_remove_raw(group_index);
this->collapse_channel_group_gaps();
this->update_fcurve_channel_group_pointers();
this->restore_channel_group_invariants();
return true;
}
@@ -1572,38 +1574,66 @@ void ChannelBag::channel_group_remove_raw(const int group_index)
shrink_array_and_remove(&this->group_array, &this->group_array_num, group_index);
}
void ChannelBag::collapse_channel_group_gaps()
void ChannelBag::restore_channel_group_invariants()
{
int index = 0;
/* Shift channel groups. */
{
int start_index = 0;
for (bActionGroup *group : this->channel_groups()) {
group->fcurve_range_start = start_index;
start_index += group->fcurve_range_length;
}
for (bActionGroup *group : this->channel_groups()) {
group->fcurve_range_start = index;
index += group->fcurve_range_length;
/* Double-check that this didn't push any of the groups off the end of the
* fcurve array. */
BLI_assert(start_index <= this->fcurve_array_num);
}
BLI_assert(index <= this->fcurve_array_num);
}
void ChannelBag::update_fcurve_channel_group_pointers()
{
Span<bActionGroup *> groups = this->channel_groups();
for (bActionGroup *group : groups) {
for (FCurve *fcurve :
this->fcurves().slice(group->fcurve_range_start, group->fcurve_range_length))
{
fcurve->grp = group;
/* Recompute fcurves' group pointers. */
{
for (FCurve *fcurve : this->fcurves()) {
fcurve->grp = nullptr;
}
for (bActionGroup *group : this->channel_groups()) {
for (FCurve *fcurve : group->wrap().fcurves()) {
fcurve->grp = group;
}
}
}
}
int first_ungrouped_fcurve_index = 0;
if (!groups.is_empty()) {
first_ungrouped_fcurve_index = groups.last()->fcurve_range_start +
groups.last()->fcurve_range_length;
bool ChannelGroup::is_legacy() const
{
const bool group_is_legacy = this->channel_bag == nullptr;
BLI_assert_msg(group_is_legacy || this->channels.first == nullptr,
"Layered-action channel group has legacy-action data.");
return group_is_legacy;
}
Span<FCurve *> ChannelGroup::fcurves()
{
BLI_assert(!this->is_legacy());
if (this->fcurve_range_length == 0) {
return {};
}
for (FCurve *fcurve : this->fcurves().drop_front(first_ungrouped_fcurve_index)) {
fcurve->grp = nullptr;
return this->channel_bag->wrap().fcurves().slice(this->fcurve_range_start,
this->fcurve_range_length);
}
Span<const FCurve *> ChannelGroup::fcurves() const
{
BLI_assert(!this->is_legacy());
if (this->fcurve_range_length == 0) {
return {};
}
return this->channel_bag->wrap().fcurves().slice(this->fcurve_range_start,
this->fcurve_range_length);
}
/* Utility function implementations. */
@@ -1641,28 +1671,6 @@ animrig::ChannelBag *channelbag_for_action_slot(Action &action, const slot_handl
return const_cast<animrig::ChannelBag *>(const_bag);
}
Span<bActionGroup *> channel_groups_for_action_slot(Action &action,
const slot_handle_t slot_handle)
{
assert_baklava_phase_1_invariants(action);
animrig::ChannelBag *bag = channelbag_for_action_slot(action, slot_handle);
if (!bag) {
return {};
}
return bag->channel_groups();
}
Span<const bActionGroup *> channel_groups_for_action_slot(const Action &action,
const slot_handle_t slot_handle)
{
assert_baklava_phase_1_invariants(action);
const animrig::ChannelBag *bag = channelbag_for_action_slot(action, slot_handle);
if (!bag) {
return {};
}
return bag->channel_groups();
}
Span<FCurve *> fcurves_for_action_slot(Action &action, const slot_handle_t slot_handle)
{
assert_baklava_phase_1_invariants(action);
@@ -1889,7 +1897,6 @@ bool action_fcurve_remove(Action &action, FCurve &fcu)
bool ChannelBag::fcurve_assign_to_channel_group(FCurve &fcurve, bActionGroup &to_group)
{
if (this->channel_groups().as_span().first_index_try(&to_group) == -1) {
return false;
}
@@ -1899,16 +1906,18 @@ bool ChannelBag::fcurve_assign_to_channel_group(FCurve &fcurve, bActionGroup &to
return false;
}
const int from_group_index = this->channel_group_containing_index(fcurve_index);
if (from_group_index != -1) {
bActionGroup *from_group = this->channel_groups()[from_group_index];
if (from_group == &to_group) {
return true;
}
if (fcurve.grp == &to_group) {
return true;
}
from_group->fcurve_range_length--;
if (from_group->fcurve_range_length == 0) {
this->channel_group_remove_raw(from_group_index);
/* Remove fcurve from old group, if it belongs to one. */
if (fcurve.grp != nullptr) {
bActionGroup *old_group = fcurve.grp;
old_group->fcurve_range_length--;
if (old_group->fcurve_range_length == 0) {
const int old_group_index = this->channel_groups().as_span().first_index_try(old_group);
this->channel_group_remove_raw(old_group_index);
}
}
@@ -1919,8 +1928,7 @@ bool ChannelBag::fcurve_assign_to_channel_group(FCurve &fcurve, bActionGroup &to
to_group.fcurve_range_start + to_group.fcurve_range_length);
to_group.fcurve_range_length++;
this->collapse_channel_group_gaps();
this->update_fcurve_channel_group_pointers();
this->restore_channel_group_invariants();
return true;
}

View File

@@ -386,15 +386,11 @@ static void action_blend_write_make_legacy_channel_groups_listbase(
* `action_blend_write_make_legacy_fcurves_listbase()`, so that they function
* properly as a list. */
for (bActionGroup *group : channel_groups) {
if (group->fcurve_range_length == 0) {
Span<FCurve *> fcurves = group->wrap().fcurves();
if (fcurves.is_empty()) {
group->channels = {nullptr, nullptr};
continue;
}
Span<FCurve *> fcurves = group->channel_bag->wrap().fcurves();
group->channels = {
fcurves[group->fcurve_range_start],
fcurves[group->fcurve_range_start + group->fcurve_range_length - 1],
};
group->channels = {fcurves.first(), fcurves.last()};
}
/* Determine the prev/next pointers on the elements. */
@@ -485,11 +481,11 @@ static void action_blend_write(BlendWriter *writer, ID *id, const void *id_addre
* forward-compat legacy data is also written, and vice-versa. Both have
* pointers to each other that won't resolve properly when loaded in older
* Blender versions if only one is written. */
Span<FCurve *> fcurves = fcurves_for_action_slot(action, first_slot.handle);
action_blend_write_make_legacy_fcurves_listbase(action.curves, fcurves);
Span<bActionGroup *> channel_groups = channel_groups_for_action_slot(action,
first_slot.handle);
action_blend_write_make_legacy_channel_groups_listbase(action.groups, channel_groups);
animrig::ChannelBag *bag = channelbag_for_action_slot(action, first_slot.handle);
if (bag) {
action_blend_write_make_legacy_fcurves_listbase(action.curves, bag->fcurves());
action_blend_write_make_legacy_channel_groups_listbase(action.groups, bag->channel_groups());
}
}
#else
/* Built without Baklava, so ensure that the written data is clean. This should not change
@@ -550,20 +546,13 @@ static void action_blend_write(BlendWriter *writer, ID *id, const void *id_addre
#ifdef WITH_ANIM_BAKLAVA
static void read_channel_group(BlendDataReader *reader, bActionGroup &channel_group)
{
/* Remap non-owning pointer. */
channel_group.channel_bag = static_cast<ActionChannelBag *>(BLO_read_get_new_data_address_no_us(
reader, channel_group.channel_bag, sizeof(ActionChannelBag)));
}
static void read_channelbag(BlendDataReader *reader, animrig::ChannelBag &channelbag)
{
BLO_read_pointer_array(
reader, channelbag.group_array_num, reinterpret_cast<void **>(&channelbag.group_array));
for (int i = 0; i < channelbag.group_array_num; i++) {
BLO_read_struct(reader, bActionGroup, &channelbag.group_array[i]);
read_channel_group(reader, *channelbag.group_array[i]);
channelbag.group_array[i]->channel_bag = &channelbag;
/* Clear the legacy channels #ListBase, since it will have been set for some
* groups for forward compatibility.

View File

@@ -1515,8 +1515,7 @@ static size_t animfilter_act_group(bAnimContext *ac,
}
else {
BLI_assert(agrp->channel_bag != nullptr);
Span<FCurve *> fcurves = agrp->channel_bag->wrap().fcurves().slice(
agrp->fcurve_range_start, agrp->fcurve_range_length);
Span<FCurve *> fcurves = agrp->wrap().fcurves();
tmp_items += animfilter_fcurves_span(
ac, &tmp_data, fcurves, slot_handle, filter_mode, owner_id, &act->id);
}
@@ -1584,6 +1583,7 @@ static size_t animfilter_action_slot(bAnimContext *ac,
const bool is_action_mode = (ac->spacetype == SPACE_ACTION &&
ac->dopesheet_mode == SACTCONT_ACTION);
const bool show_fcurves_only = (filter_mode & ANIMFILTER_FCURVESONLY);
const bool show_active_group_only = filter_mode & ANIMFILTER_ACTGROUPED;
const bool include_summary_channels = (filter_mode & ANIMFILTER_LIST_CHANNELS);
const bool show_slot_channel = (is_action_mode && selection_ok_for_slot && !show_fcurves_only &&
include_summary_channels);
@@ -1620,7 +1620,7 @@ static size_t animfilter_action_slot(bAnimContext *ac,
}
/* Add ungrouped channels. */
if (!(filter_mode & ANIMFILTER_ACTGROUPED)) {
if (!show_active_group_only) {
int first_ungrouped_fcurve_index = 0;
if (!channel_bag->channel_groups().is_empty()) {
const bActionGroup *last_group = channel_bag->channel_groups().last();

View File

@@ -155,7 +155,7 @@ static short agrp_keyframes_loop(KeyframeEditData *ked,
}
/* Legacy actions. */
if (agrp->channels.first && agrp->channels.last) {
if (agrp->wrap().is_legacy()) {
LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
if (fcu->grp == agrp) {
if (ANIM_fcurve_keyframes_loop(ked, fcu, key_ok, key_cb, fcu_cb)) {
@@ -167,9 +167,6 @@ static short agrp_keyframes_loop(KeyframeEditData *ked,
}
/* Layered actions. */
if (agrp->channel_bag == nullptr) {
return 0;
}
animrig::ChannelBag channel_bag = agrp->channel_bag->wrap();
Span<FCurve *> fcurves = channel_bag.fcurves().slice(agrp->fcurve_range_start,
agrp->fcurve_range_length);

View File

@@ -1201,7 +1201,7 @@ void action_group_to_keylist(AnimData *adt,
}
/* Legacy actions. */
if (agrp->channels.first && agrp->channels.last) {
if (agrp->wrap().is_legacy()) {
LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
if (fcu->grp != agrp) {
break;
@@ -1212,9 +1212,6 @@ void action_group_to_keylist(AnimData *adt,
}
/* Layered actions. */
if (agrp->channel_bag == nullptr) {
return;
}
animrig::ChannelBag channel_bag = agrp->channel_bag->wrap();
Span<FCurve *> fcurves = channel_bag.fcurves().slice(agrp->fcurve_range_start,
agrp->fcurve_range_length);

View File

@@ -56,6 +56,7 @@ class Action;
class Slot;
class SlotRuntime;
class ChannelBag;
class ChannelGroup;
class KeyframeStrip;
class Layer;
class Strip;
@@ -689,6 +690,12 @@ typedef struct bActionGroup {
*
* This specifies that span as a range of items in a ChannelBag's fcurve
* array.
*
* Note that empty groups (`fcurve_range_length == 0`) are allowed, and they
* still have a position in the fcurves array, as specified by
* `fcurve_range_start`. You can imagine these cases as a zero-width range
* that sits at the border between the element at `fcurve_range_start` and the
* element just before it.
*/
int fcurve_range_start;
int fcurve_range_length;
@@ -713,6 +720,11 @@ typedef struct bActionGroup {
/** Color set to use when customCol == -1. */
ThemeWireColor cs;
#ifdef __cplusplus
blender::animrig::ChannelGroup &wrap();
const blender::animrig::ChannelGroup &wrap() const;
#endif
} bActionGroup;
/* Action Group flags */
@@ -1244,8 +1256,9 @@ typedef struct ActionChannelBag {
* for membership is the information in the channel groups here.
*
* Invariants:
* 1. The groups are stored in this array in the same order as their indices
* into the fcurve array.
* 1. The groups are sorted by thier `fcurve_range_start` field. In other
* words, they are in the same order as their starting positions in the
* fcurve array.
* 2. The grouped fcurves are tightly packed, starting at the first fcurve and
* having no gaps of ungrouped fcurves between them. Ungrouped fcurves come
* at the end, after all of the grouped fcurves. */

View File

@@ -711,27 +711,31 @@ static void rna_ActionGroup_channels_begin(CollectionPropertyIterator *iter, Poi
iter->internal.custom = custom_iter;
/* Group from a layered action. */
if (group->channel_bag != nullptr) {
MutableSpan<FCurve *> fcurves = group->channel_bag->wrap().fcurves();
/* We handle both the listbase (legacy action) and array (layered action)
* cases below. The code for each is based on the code in
* `rna_iterator_listbase_begin()` and `rna_iterator_array_begin()`,
* respectively. */
custom_iter->tag = ActionGroupChannelsIterator::ARRAY;
custom_iter->array.ptr = reinterpret_cast<char *>(fcurves.data() + group->fcurve_range_start);
custom_iter->array.endptr = reinterpret_cast<char *>(
fcurves.data() + group->fcurve_range_start + group->fcurve_range_length);
custom_iter->array.itemsize = sizeof(FCurve *);
custom_iter->array.length = group->fcurve_range_length;
iter->valid = group->fcurve_range_length != 0;
/* Group from a legacy action. */
if (group->wrap().is_legacy()) {
custom_iter->tag = ActionGroupChannelsIterator::LISTBASE;
custom_iter->listbase.link = static_cast<Link *>(group->channels.first);
iter->valid = custom_iter->listbase.link != nullptr;
return;
}
/* Group from a legacy action. */
custom_iter->tag = ActionGroupChannelsIterator::LISTBASE;
custom_iter->listbase.link = static_cast<Link *>(group->channels.first);
/* Group from a layered action. */
MutableSpan<FCurve *> fcurves = group->channel_bag->wrap().fcurves();
iter->valid = custom_iter->listbase.link != nullptr;
custom_iter->tag = ActionGroupChannelsIterator::ARRAY;
custom_iter->array.ptr = reinterpret_cast<char *>(fcurves.data() + group->fcurve_range_start);
custom_iter->array.endptr = reinterpret_cast<char *>(fcurves.data() + group->fcurve_range_start +
group->fcurve_range_length);
custom_iter->array.itemsize = sizeof(FCurve *);
custom_iter->array.length = group->fcurve_range_length;
iter->valid = group->fcurve_range_length > 0;
}
static void rna_ActionGroup_channels_end(CollectionPropertyIterator *iter)
@@ -747,6 +751,9 @@ static void rna_ActionGroup_channels_next(CollectionPropertyIterator *iter)
ActionGroupChannelsIterator *custom_iter = static_cast<ActionGroupChannelsIterator *>(
iter->internal.custom);
/* The code for both cases here is written based on the code in
* `rna_iterator_array_next()` and `rna_iterator_listbase_next()`,
* respectively. */
switch (custom_iter->tag) {
case ActionGroupChannelsIterator::ARRAY: {
custom_iter->array.ptr += custom_iter->array.itemsize;

View File

@@ -645,8 +645,9 @@ static void rna_FCurve_group_set(PointerRNA *ptr, PointerRNA value, ReportList *
}
blender::animrig::Action &action = act->wrap();
/* Legacy action. */
if (!action.is_action_layered()) {
/* Legacy action. */
/* make sure F-Curve exists in this action first, otherwise we could still have been tricked */
if (BLI_findindex(&act->curves, fcu) == -1) {
@@ -668,24 +669,23 @@ static void rna_FCurve_group_set(PointerRNA *ptr, PointerRNA value, ReportList *
* (or else will corrupt groups). */
BLI_addtail(&act->curves, fcu);
}
return;
}
else {
/* Layered action. */
bActionGroup *group = static_cast<bActionGroup *>(value.data);
blender::animrig::ChannelBag *channel_bag;
{
ActionChannelBag *tmp = group->channel_bag;
BLI_assert(tmp != nullptr);
channel_bag = &tmp->wrap();
}
/* Layered action. */
bActionGroup *group = static_cast<bActionGroup *>(value.data);
if (!channel_bag->fcurve_assign_to_channel_group(*fcu, *group)) {
printf("ERROR: F-Curve (%p) doesn't belong to the same channel bag as channel group '%s'\n",
fcu,
group->name);
return;
}
BLI_assert(group->channel_bag != nullptr);
blender::animrig::ChannelBag &channel_bag = group->channel_bag->wrap();
if (!channel_bag.fcurve_assign_to_channel_group(*fcu, *group)) {
printf(
"ERROR: F-Curve (datapath: '%s') doesn't belong to the same channel bag as "
"channel group '%s'\n",
fcu->rna_path,
group->name);
return;
}
}