Anim: add iterator over Action Slot uses, with references

Add an iterator `foreach_action_slot_use_with_references(ID, callback)`
that provides references to the found `bAction` pointer and slot handle.
That way the callback can assign another Action or slot when it sees
fit, without having to know exactly what the source of this info is
(could be a direct assignment, but also an NLA strip or Action
Constraint).

Ref: #127844
Pull Request: https://projects.blender.org/blender/blender/pulls/127871
This commit is contained in:
Sybren A. Stüvel
2024-09-20 10:30:15 +02:00
parent 8ae944026e
commit b51d60a226
3 changed files with 80 additions and 3 deletions

View File

@@ -57,4 +57,17 @@ bool foreach_action_slot_use(
const ID &animated_id,
FunctionRef<bool(const Action &action, slot_handle_t slot_handle)> callback);
/**
* Same as foreach_action_slot_use(), except that it reports some pointers so the callback can
* modify which Action/slot is assigned.
*
* \see blender::animrig::generic_assign_action
* \see blender::animrig::generic_assign_action_slot
* \see blender::animrig::generic_assign_action_slot_handle
*/
bool foreach_action_slot_use_with_references(
ID &animated_id,
FunctionRef<bool(bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_name)>
callback);
} // namespace blender::animrig

View File

@@ -51,12 +51,31 @@ bool foreach_action_slot_use(
const ID &animated_id,
FunctionRef<bool(const Action &action, slot_handle_t slot_handle)> callback)
{
const auto forward_to_callback = [&](bAction *&action_ptr_ref,
const slot_handle_t &slot_handle_ref,
char * /*slot_name*/) -> bool {
if (!action_ptr_ref) {
return true;
}
return callback(const_cast<const Action &>(action_ptr_ref->wrap()), slot_handle_ref);
};
return foreach_action_slot_use_with_references(const_cast<ID &>(animated_id),
forward_to_callback);
}
bool foreach_action_slot_use_with_references(
ID &animated_id,
FunctionRef<bool(bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_name)>
callback)
{
AnimData *adt = BKE_animdata_from_id(&animated_id);
if (adt) {
if (adt->action) {
/* Direct assignment. */
if (!callback(adt->action->wrap(), adt->slot_handle)) {
if (!callback(adt->action, adt->slot_handle, adt->slot_name)) {
return false;
}
}
@@ -64,7 +83,7 @@ bool foreach_action_slot_use(
/* NLA strips. */
const bool looped_until_last_strip = bke::nla::foreach_strip_adt(*adt, [&](NlaStrip *strip) {
if (strip->act) {
if (!callback(strip->act->wrap(), strip->action_slot_handle)) {
if (!callback(strip->act, strip->action_slot_handle, strip->action_slot_name)) {
return false;
}
}
@@ -96,7 +115,9 @@ bool foreach_action_slot_use(
if (!constraint_data->act) {
return true;
}
return callback(constraint_data->act->wrap(), constraint_data->action_slot_handle);
return callback(constraint_data->act,
constraint_data->action_slot_handle,
constraint_data->action_slot_name);
};
/* Visit Object constraints. */

View File

@@ -107,4 +107,47 @@ TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
});
ASSERT_TRUE(invalid_slot_fcurves.is_empty());
}
TEST_F(ActionIteratorsTest, foreach_action_slot_use_with_references)
{
/* Create a cube and assign the Action + a slot. */
Object *cube = static_cast<Object *>(BKE_id_new(bmain, ID_OB, "OBCube"));
Slot *slot_cube = assign_action_ensure_slot_for_keying(*action, cube->id);
ASSERT_NE(slot_cube, nullptr);
/* Create another Action with slot to assign. */
Action &other_action =
static_cast<bAction *>(BKE_id_new(bmain, ID_AC, "ACAnotherAction"))->wrap();
Slot &another_slot = other_action.slot_add();
std::optional<ActionSlotAssignmentResult> slot_assignment_result;
const auto assign_other_action =
[&](bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_name) -> bool {
/* Assign the other Action. */
generic_assign_action(cube->id, &other_action, action_ptr_ref, slot_handle_ref, slot_name);
/* Assign the slot of the other Action. */
slot_assignment_result = generic_assign_action_slot(
&another_slot, cube->id, action_ptr_ref, slot_handle_ref, slot_name);
return true;
};
foreach_action_slot_use_with_references(cube->id, assign_other_action);
/* Check the result, the slot assignment should have been changed. */
ASSERT_TRUE(slot_assignment_result.has_value());
EXPECT_EQ(ActionSlotAssignmentResult::OK, slot_assignment_result.value());
std::optional<std::pair<Action *, Slot *>> action_and_slot = get_action_slot_pair(cube->id);
ASSERT_TRUE(action_and_slot.has_value());
EXPECT_EQ(&other_action, action_and_slot->first)
<< "Expected Action " << other_action.id.name << " but found "
<< action_and_slot->first->id.name;
EXPECT_EQ(&another_slot, action_and_slot->second)
<< "Expected Slot " << another_slot.name << " but found " << action_and_slot->second->name;
}
} // namespace blender::animrig::tests