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:
committed by
Nathan Vegdahl
parent
df59028d0b
commit
1c1e389d4b
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user