Refactor: rename "Animation data-block" to "Action"

Rename "Animation data-block" to "Action" or "Layered Action", where
appropriate. Some uses of the term actually refer to the `AnimData`
struct, in which case they were left as-is.

No real functional changes, just changing some messages & descriptions.

Pull Request: https://projects.blender.org/blender/blender/pulls/124170
This commit is contained in:
Sybren A. Stüvel 2024-07-05 17:01:50 +02:00
parent c0364efec0
commit 0aa75ab57b
24 changed files with 360 additions and 360 deletions

@ -113,7 +113,7 @@ def _wm_selected_action_update(wm, context):
def register_props():
# Due to this hackyness, the WindowManager will increase the user count of
# the pointed-to Animation data-block.
# the pointed-to Action.
WindowManager.selected_action = PointerProperty(
type=bpy.types.Action,
name="Action",

@ -101,7 +101,7 @@ class Action : public ::bAction {
*/
bool is_action_layered() const;
/* Animation Layers access. */
/* Action Layers access. */
blender::Span<const Layer *> layers() const;
blender::MutableSpan<Layer *> layers();
const Layer *layer(int64_t index) const;
@ -110,7 +110,7 @@ class Action : public ::bAction {
Layer &layer_add(StringRefNull name);
/**
* Remove the layer from this animation.
* Remove the layer from this Action.
*
* After this call, the passed reference is no longer valid, as the memory
* will have been freed. Any strips on the layer will be freed too.
@ -125,7 +125,7 @@ class Action : public ::bAction {
*/
void layer_ensure_at_least_one();
/* Animation Slot access. */
/* Action Slot access. */
blender::Span<const Slot *> slots() const;
blender::MutableSpan<Slot *> slots();
const Slot *slot(int64_t index) const;
@ -146,8 +146,8 @@ class Action : public ::bAction {
* Set the slot name, ensure it is unique, and propagate the new name to
* all data-blocks that use it.
*
* This has to be done on the Animation level to ensure each slot has a
* unique name within the Animation.
* This has to be done on the Action level to ensure each slot has a
* unique name within the Action.
*
* \note This does NOT ensure the first two characters match the ID type of
* this slot. This is the caller's responsibility.
@ -191,7 +191,7 @@ class Action : public ::bAction {
/**
* Create a new slot, named after the given ID, and limited to the ID's type.
*
* Note that this assigns neither this Animation nor the new Slot to the ID. This function
* Note that this assigns neither this Action nor the new Slot to the ID. This function
* merely initializes the Slot itself to suitable values to start animating this ID.
*/
Slot &slot_add_for_id(const ID &animated_id);
@ -216,21 +216,21 @@ class Action : public ::bAction {
*/
Slot &slot_ensure_for_id(const ID &animated_id);
/** Assign this animation to the ID.
/** Assign this Action to the ID.
*
* \param slot: The slot this ID should be animated by, may be nullptr if it is to be
* assigned later. In that case, the ID will not actually receive any animation.
* \param animated_id: The ID that should be animated by this Animation data-block.
* \param animated_id: The ID that should be animated by this Action.
*
* \return whether the assignment was successful.
*/
bool assign_id(Slot *slot, ID &animated_id);
/**
* Unassign this Animation from the animated ID.
* Unassign this Action from the animated ID.
*
* \param animated_id: ID that is animated by this Animation. Calling this
* function when this ID is _not_ animated by this Animation is not allowed,
* \param animated_id: ID that is animated by this Action. Calling this
* function when this ID is _not_ animated by this Action is not allowed,
* and considered a bug.
*/
void unassign_id(ID &animated_id);
@ -238,19 +238,19 @@ class Action : public ::bAction {
/**
* Find the slot that best matches the animated ID.
*
* If the ID is already animated by this Animation, by matching this
* Animation's slots with (in order):
* If the ID is already animated by this Action, by matching this
* Action's slots with (in order):
*
* - `animated_id.adt->slot_handle`,
* - `animated_id.adt->slot_name`,
* - `animated_id.name`.
*
* Note that this is different from #slot_for_id, which does not use the
* slot name, and only works when this Animation is already assigned. */
* slot name, and only works when this Action is already assigned. */
Slot *find_suitable_slot_for(const ID &animated_id);
/**
* Return whether this Animation actually has any animation data for the given slot.
* Return whether this Action actually has any animation data for the given slot.
*/
bool is_slot_animated(slot_handle_t slot_handle) const;
@ -264,7 +264,7 @@ class Action : public ::bAction {
Layer *get_layer_for_keyframing();
protected:
/** Return the layer's index, or -1 if not found in this animation. */
/** Return the layer's index, or -1 if not found in this Action. */
int64_t find_layer_index(const Layer &layer) const;
private:
@ -283,7 +283,7 @@ class Action : public ::bAction {
/**
* Set the slot's ID type to that of the animated ID, ensure the name
* prefix is set accordingly, and that the name is unique within the
* Animation.
* Action.
*
* \note This assumes that the slot has no ID type set yet. If it does, it
* is considered a bug to call this function.
@ -575,7 +575,7 @@ class Slot : public ::ActionSlot {
/**
* Ensure the first two characters of the name match the ID type.
*
* \note This does NOT ensure name uniqueness within the Animation. That is
* \note This does NOT ensure name uniqueness within the Action. That is
* the responsibility of the caller.
*/
void name_ensure_prefix();
@ -677,7 +677,7 @@ static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag),
"DNA struct and its C++ wrapper must have the same size");
/**
* Assign the animation to the ID.
* Assign the Action to the ID.
*
* This will will make a best-effort guess as to which slot to use, in this
* order;
@ -691,9 +691,9 @@ static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag),
*
* \return `false` if the assignment was not possible (for example the ID is of a type that cannot
* be animated). If the above fall-through case of "no slot found" is reached, this function
* will still return `true` as the Animation was successfully assigned.
* will still return `true` as the Action was successfully assigned.
*/
bool assign_animation(Action &anim, ID &animated_id);
bool assign_action(Action &action, ID &animated_id);
/**
* Return whether the given Action can be assigned to the ID.
@ -706,10 +706,10 @@ bool is_action_assignable_to(const bAction *dna_action, ID_Type id_code);
/**
* Ensure that this ID is no longer animated.
*/
void unassign_animation(ID &animated_id);
void unassign_action(ID &animated_id);
/**
* Clear the animation slot of this ID.
* Clear the Action slot of this ID.
*
* `adt.slot_handle_name` is updated to reflect the current name of the
* slot, before un-assigning. This is to ensure that the stored name reflects
@ -717,14 +717,14 @@ void unassign_animation(ID &animated_id);
*
* \param animated_id: the animated ID.
*
* \note this does not clear the Animation pointer, just the slot handle.
* \note this does not clear the Action pointer, just the slot handle.
*/
void unassign_slot(ID &animated_id);
/**
* Return the Animation of this ID, or nullptr if it has none.
* Return the Action of this ID, or nullptr if it has none.
*/
Action *get_animation(ID &animated_id);
Action *get_action(ID &animated_id);
/**
* Get the Action and the Slot that animate this ID.
@ -740,33 +740,33 @@ std::optional<std::pair<Action *, Slot *>> get_action_slot_pair(ID &animated_id)
/**
* Return the F-Curves for this specific slot handle.
*
* This is just a utility function, that's intended to become obsolete when multi-layer animation
* is introduced. However, since Blender currently only supports a single layer with a single
* 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 animation is getting implemented.
* multi-layered Actions are getting implemented.
*/
Span<FCurve *> fcurves_for_animation(Action &anim, slot_handle_t slot_handle);
Span<const FCurve *> fcurves_for_animation(const Action &anim, slot_handle_t slot_handle);
Span<FCurve *> fcurves_for_action_slot(Action &action, slot_handle_t slot_handle);
Span<const FCurve *> fcurves_for_action_slot(const Action &action, slot_handle_t slot_handle);
/**
* Return all F-Curves in the Action.
*
* This works for both legacy and layered Actions.
*
* This is a utility function whose purpose is unclear after multi-layer animation is introduced.
* This is a utility function whose purpose is unclear after multi-layer Actions are introduced.
* It might still be useful, it might not be.
* The use of this function is an indicator for code that might have to be altered when
* multi-layered animation is getting implemented.
* multi-layered Actions are getting implemented.
*/
Vector<const FCurve *> fcurves_all(const Action &action);
Vector<FCurve *> fcurves_all(Action &action);
/**
* Get (or add relevant data to be able to do so) F-Curve from the given Action,
* for the given Animation Data block. This assumes that all the destinations are valid.
* Get (or add relevant data to be able to do so) an F-Curve from the given Action,
* for the given animated data-block. This assumes that all the destinations are valid.
* \param ptr: can be a null pointer.
*/
FCurve *action_fcurve_ensure(Main *bmain,

@ -5,7 +5,7 @@
/** \file
* \ingroup animrig
*
* \brief Animation data-block evaluation.
* \brief Layered Action evaluation.
*/
#pragma once
@ -26,10 +26,10 @@ namespace blender::animrig {
* \param flush_to_original: when true, look up the original data-block (assuming
* the given one is an evaluated copy) and update that too.
*/
void evaluate_and_apply_animation(PointerRNA &animated_id_ptr,
Action &animation,
slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context,
bool flush_to_original);
void evaluate_and_apply_action(PointerRNA &animated_id_ptr,
Action &action,
slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context,
bool flush_to_original);
} // namespace blender::animrig

@ -52,7 +52,7 @@ namespace blender::animrig {
namespace {
/**
* Default name for animation slots. The first two characters in the name indicate the ID type
* Default name for action slots. The first two characters in the name indicate the ID type
* of whatever is animated by it.
*
* Since the ID type may not be determined when the slot is created, the prefix starts out at
@ -95,7 +95,7 @@ template<typename T> static void grow_array(T **array, int *num, const int add_n
BLI_assert(add_num > 0);
const int new_array_num = *num + add_num;
T *new_array = reinterpret_cast<T *>(
MEM_cnew_array<T *>(new_array_num, "animrig::animation/grow_array"));
MEM_cnew_array<T *>(new_array_num, "animrig::action/grow_array"));
blender::uninitialized_relocate_n(*array, *num, new_array);
MEM_SAFE_FREE(*array);
@ -123,7 +123,7 @@ template<typename T> static void shrink_array(T **array, int *num, const int shr
*num = new_array_num;
}
/* ----- Animation implementation ----------- */
/* ----- Action implementation ----------- */
bool Action::is_empty() const
{
@ -262,19 +262,19 @@ const Slot *Action::slot_for_handle(const slot_handle_t handle) const
return nullptr;
}
static void anim_slot_name_ensure_unique(Action &animation, Slot &slot)
static void slot_name_ensure_unique(Action &action, Slot &slot)
{
/* Cannot capture parameters by reference in the lambda, as that would change its signature
* and no longer be compatible with BLI_uniquename_cb(). That's why this struct is necessary. */
struct DupNameCheckData {
Action &anim;
Action &action;
Slot &slot;
};
DupNameCheckData check_data = {animation, slot};
DupNameCheckData check_data = {action, slot};
auto check_name_is_used = [](void *arg, const char *name) -> bool {
DupNameCheckData *data = static_cast<DupNameCheckData *>(arg);
for (const Slot *slot : data->anim.slots()) {
for (const Slot *slot : data->action.slots()) {
if (slot == &data->slot) {
/* Don't compare against the slot that's being renamed. */
continue;
@ -302,9 +302,9 @@ void Action::slot_name_set(Main &bmain, Slot &slot, const StringRefNull new_name
void Action::slot_name_define(Slot &slot, const StringRefNull new_name)
{
BLI_assert_msg(StringRef(new_name).size() >= Slot::name_length_min,
"Animation Slots must be large enough for a 2-letter ID code + the display name");
"Action Slots must be large enough for a 2-letter ID code + the display name");
STRNCPY_UTF8(slot.name, new_name.c_str());
anim_slot_name_ensure_unique(*this, slot);
slot_name_ensure_unique(*this, slot);
}
void Action::slot_name_propagate(Main &bmain, const Slot &slot)
@ -322,7 +322,7 @@ void Action::slot_name_propagate(Main &bmain, const Slot &slot)
AnimData *adt = BKE_animdata_from_id(id);
if (!adt || adt->action != this) {
/* Not animated by this Animation. */
/* Not animated by this Action. */
continue;
}
if (adt->slot_handle != slot.handle) {
@ -352,7 +352,7 @@ Slot &Action::slot_allocate()
{
Slot &slot = *MEM_new<Slot>(__func__);
this->last_slot_handle++;
BLI_assert_msg(this->last_slot_handle > 0, "Animation Slot handle overflow");
BLI_assert_msg(this->last_slot_handle > 0, "Action Slot handle overflow");
slot.handle = this->last_slot_handle;
/* Set the default flags. These cannot be set via the 'DNA defaults' system,
@ -373,7 +373,7 @@ Slot &Action::slot_add()
/* Append the Slot to the Action. */
grow_array_and_append<::ActionSlot *>(&this->slot_array, &this->slot_array_num, &slot);
anim_slot_name_ensure_unique(*this, slot);
slot_name_ensure_unique(*this, slot);
/* If this is the first slot in this Action, it means that it could have
* been used as a legacy Action before. As a result, this->idroot may be
@ -444,7 +444,7 @@ bool Action::is_slot_animated(const slot_handle_t slot_handle) const
return false;
}
Span<const FCurve *> fcurves = fcurves_for_animation(*this, slot_handle);
Span<const FCurve *> fcurves = fcurves_for_action_slot(*this, slot_handle);
return !fcurves.is_empty();
}
@ -468,7 +468,7 @@ bool Action::assign_id(Slot *slot, ID &animated_id)
if (adt->action && adt->action != this) {
/* The caller should unassign the ID from its existing animation first, or
* use the top-level function `assign_animation(anim, ID)`. */
* use the top-level function `assign_action(anim, ID)`. */
return false;
}
@ -520,7 +520,7 @@ bool Action::assign_id(Slot *slot, ID &animated_id)
void Action::slot_name_ensure_prefix(Slot &slot)
{
slot.name_ensure_prefix();
anim_slot_name_ensure_unique(*this, slot);
slot_name_ensure_unique(*this, slot);
}
void Action::slot_setup_for_id(Slot &slot, const ID &animated_id)
@ -747,12 +747,12 @@ void Slot::users_invalidate(Main &bmain)
/* ----- Functions ----------- */
bool assign_animation(Action &anim, ID &animated_id)
bool assign_action(Action &action, ID &animated_id)
{
unassign_animation(animated_id);
unassign_action(animated_id);
Slot *slot = anim.find_suitable_slot_for(animated_id);
return anim.assign_id(slot, animated_id);
Slot *slot = action.find_suitable_slot_for(animated_id);
return action.assign_id(slot, animated_id);
}
bool is_action_assignable_to(const bAction *dna_action, const ID_Type id_code)
@ -779,13 +779,13 @@ bool is_action_assignable_to(const bAction *dna_action, const ID_Type id_code)
return true;
}
void unassign_animation(ID &animated_id)
void unassign_action(ID &animated_id)
{
Action *anim = get_animation(animated_id);
if (!anim) {
Action *action = get_action(animated_id);
if (!action) {
return;
}
anim->unassign_id(animated_id);
action->unassign_id(animated_id);
}
void unassign_slot(ID &animated_id)
@ -809,7 +809,7 @@ void unassign_slot(ID &animated_id)
}
/* TODO: rename to get_action(). */
Action *get_animation(ID &animated_id)
Action *get_action(ID &animated_id)
{
AnimData *adt = BKE_animdata_from_id(&animated_id);
if (!adt) {
@ -1179,14 +1179,14 @@ const FCurve *ChannelBag::fcurve_find(const StringRefNull rna_path, const int ar
/* Utility function implementations. */
static const animrig::ChannelBag *channelbag_for_animation(const Action &anim,
const slot_handle_t slot_handle)
static const animrig::ChannelBag *channelbag_for_action_slot(const Action &action,
const slot_handle_t slot_handle)
{
if (slot_handle == Slot::unassigned) {
return nullptr;
}
for (const animrig::Layer *layer : anim.layers()) {
for (const animrig::Layer *layer : action.layers()) {
for (const animrig::Strip *strip : layer->strips()) {
switch (strip->type()) {
case animrig::Strip::Type::Keyframe: {
@ -1203,25 +1203,26 @@ static const animrig::ChannelBag *channelbag_for_animation(const Action &anim,
return nullptr;
}
static animrig::ChannelBag *channelbag_for_animation(Action &anim, const slot_handle_t slot_handle)
static animrig::ChannelBag *channelbag_for_action_slot(Action &action,
const slot_handle_t slot_handle)
{
const animrig::ChannelBag *const_bag = channelbag_for_animation(const_cast<const Action &>(anim),
slot_handle);
const animrig::ChannelBag *const_bag = channelbag_for_action_slot(
const_cast<const Action &>(action), slot_handle);
return const_cast<animrig::ChannelBag *>(const_bag);
}
Span<FCurve *> fcurves_for_animation(Action &anim, const slot_handle_t slot_handle)
Span<FCurve *> fcurves_for_action_slot(Action &action, const slot_handle_t slot_handle)
{
animrig::ChannelBag *bag = channelbag_for_animation(anim, slot_handle);
animrig::ChannelBag *bag = channelbag_for_action_slot(action, slot_handle);
if (!bag) {
return {};
}
return bag->fcurves();
}
Span<const FCurve *> fcurves_for_animation(const Action &anim, const slot_handle_t slot_handle)
Span<const FCurve *> fcurves_for_action_slot(const Action &action, const slot_handle_t slot_handle)
{
const animrig::ChannelBag *bag = channelbag_for_animation(anim, slot_handle);
const animrig::ChannelBag *bag = channelbag_for_action_slot(action, slot_handle);
if (!bag) {
return {};
}

@ -27,7 +27,7 @@ namespace blender::animrig::tests {
class ActionLayersTest : public testing::Test {
public:
Main *bmain;
Action *anim;
Action *action;
Object *cube;
Object *suzanne;
@ -48,7 +48,7 @@ class ActionLayersTest : public testing::Test {
void SetUp() override
{
bmain = BKE_main_new();
anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACÄnimåtië"));
action = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACÄnimåtië"));
cube = BKE_object_add_only_object(bmain, OB_EMPTY, "Küüübus");
suzanne = BKE_object_add_only_object(bmain, OB_EMPTY, "OBSuzanne");
}
@ -61,12 +61,12 @@ class ActionLayersTest : public testing::Test {
TEST_F(ActionLayersTest, add_layer)
{
Layer &layer = anim->layer_add("layer name");
Layer &layer = action->layer_add("layer name");
EXPECT_EQ(anim->layer(0), &layer);
EXPECT_EQ(action->layer(0), &layer);
EXPECT_EQ("layer name", std::string(layer.name));
EXPECT_EQ(1.0f, layer.influence) << "Expected DNA defaults to be used.";
EXPECT_EQ(0, anim->layer_active_index)
EXPECT_EQ(0, action->layer_active_index)
<< "Expected newly added layer to become the active layer.";
ASSERT_EQ(0, layer.strips().size()) << "Expected newly added layer to have no strip.";
}
@ -76,19 +76,20 @@ TEST_F(ActionLayersTest, add_layer__reset_idroot)
/* An empty Action is a valid legacy Action, and thus can have its idroot set to a non-zero
* value. If such an Action gets a layer, it no longer is a valid legacy Action, and thus its
* idtype should be reset to zero. */
anim->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
ASSERT_NE(0, anim->idroot) << "anim->idroot should not be zero at the start of this test.";
action->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
ASSERT_NE(0, action->idroot) << "action->idroot should not be zero at the start of this test.";
anim->layer_add("layer name");
action->layer_add("layer name");
EXPECT_EQ(0, anim->idroot) << "anim->idroot should get reset when the Action becomes layered.";
EXPECT_EQ(0, action->idroot)
<< "action->idroot should get reset when the Action becomes layered.";
}
TEST_F(ActionLayersTest, remove_layer)
{
Layer &layer0 = anim->layer_add("Test Læür nul");
Layer &layer1 = anim->layer_add("Test Læür één");
Layer &layer2 = anim->layer_add("Test Læür twee");
Layer &layer0 = action->layer_add("Test Læür nul");
Layer &layer1 = action->layer_add("Test Læür één");
Layer &layer2 = action->layer_add("Test Læür twee");
/* Add some strips to check that they are freed correctly too (implicitly by the
* memory leak checker). */
@ -99,27 +100,27 @@ TEST_F(ActionLayersTest, remove_layer)
{ /* Test removing a layer that is not owned. */
Action *other_anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACOtherAnim"));
Layer &other_layer = other_anim->layer_add("Another Layer");
EXPECT_FALSE(anim->layer_remove(other_layer))
<< "Removing a layer not owned by the animation should be gracefully rejected";
EXPECT_FALSE(action->layer_remove(other_layer))
<< "Removing a layer not owned by the Action should be gracefully rejected";
BKE_id_free(bmain, &other_anim->id);
}
EXPECT_TRUE(anim->layer_remove(layer1));
EXPECT_EQ(2, anim->layers().size());
EXPECT_STREQ(layer0.name, anim->layer(0)->name);
EXPECT_STREQ(layer2.name, anim->layer(1)->name);
EXPECT_TRUE(action->layer_remove(layer1));
EXPECT_EQ(2, action->layers().size());
EXPECT_STREQ(layer0.name, action->layer(0)->name);
EXPECT_STREQ(layer2.name, action->layer(1)->name);
EXPECT_TRUE(anim->layer_remove(layer2));
EXPECT_EQ(1, anim->layers().size());
EXPECT_STREQ(layer0.name, anim->layer(0)->name);
EXPECT_TRUE(action->layer_remove(layer2));
EXPECT_EQ(1, action->layers().size());
EXPECT_STREQ(layer0.name, action->layer(0)->name);
EXPECT_TRUE(anim->layer_remove(layer0));
EXPECT_EQ(0, anim->layers().size());
EXPECT_TRUE(action->layer_remove(layer0));
EXPECT_EQ(0, action->layers().size());
}
TEST_F(ActionLayersTest, add_strip)
{
Layer &layer = anim->layer_add("Test Læür");
Layer &layer = action->layer_add("Test Læür");
Strip &strip = layer.strip_add(Strip::Type::Keyframe);
ASSERT_EQ(1, layer.strips().size());
@ -140,7 +141,7 @@ TEST_F(ActionLayersTest, add_strip)
/* Add some keys to check that also the strip data is freed correctly. */
const KeyframeSettings settings = get_keyframe_settings(false);
Slot &slot = anim->slot_add();
Slot &slot = action->slot_add();
strip.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
another_strip.as<KeyframeStrip>().keyframe_insert(
slot, {"location", 0}, {1.0f, 47.0f}, settings);
@ -148,14 +149,14 @@ TEST_F(ActionLayersTest, add_strip)
TEST_F(ActionLayersTest, remove_strip)
{
Layer &layer = anim->layer_add("Test Læür");
Layer &layer = action->layer_add("Test Læür");
Strip &strip0 = layer.strip_add(Strip::Type::Keyframe);
Strip &strip1 = layer.strip_add(Strip::Type::Keyframe);
Strip &strip2 = layer.strip_add(Strip::Type::Keyframe);
/* Add some keys to check that also the strip data is freed correctly. */
const KeyframeSettings settings = get_keyframe_settings(false);
Slot &slot = anim->slot_add();
Slot &slot = action->slot_add();
strip0.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
strip1.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
strip2.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
@ -173,7 +174,7 @@ TEST_F(ActionLayersTest, remove_strip)
EXPECT_EQ(0, layer.strips().size());
{ /* Test removing a strip that is not owned. */
Layer &other_layer = anim->layer_add("Another Layer");
Layer &other_layer = action->layer_add("Another Layer");
Strip &other_strip = other_layer.strip_add(Strip::Type::Keyframe);
EXPECT_FALSE(layer.strip_remove(other_strip))
@ -183,7 +184,7 @@ TEST_F(ActionLayersTest, remove_strip)
TEST_F(ActionLayersTest, add_remove_strip_of_concrete_type)
{
Layer &layer = anim->layer_add("Test Læür");
Layer &layer = action->layer_add("Test Læür");
KeyframeStrip &key_strip = layer.strip_add<KeyframeStrip>();
/* key_strip is of type KeyframeStrip, but should be implicitly converted to a
@ -194,8 +195,8 @@ TEST_F(ActionLayersTest, add_remove_strip_of_concrete_type)
TEST_F(ActionLayersTest, add_slot)
{
{ /* Creating an 'unused' Slot should just be called 'Slot'. */
Slot &slot = anim->slot_add();
EXPECT_EQ(1, anim->last_slot_handle);
Slot &slot = action->slot_add();
EXPECT_EQ(1, action->last_slot_handle);
EXPECT_EQ(1, slot.handle);
EXPECT_STREQ("XXSlot", slot.name);
@ -203,8 +204,8 @@ TEST_F(ActionLayersTest, add_slot)
}
{ /* Creating a Slot for a specific ID should name it after the ID. */
Slot &slot = anim->slot_add_for_id(cube->id);
EXPECT_EQ(2, anim->last_slot_handle);
Slot &slot = action->slot_add_for_id(cube->id);
EXPECT_EQ(2, action->last_slot_handle);
EXPECT_EQ(2, slot.handle);
EXPECT_STREQ(cube->id.name, slot.name);
@ -217,33 +218,34 @@ TEST_F(ActionLayersTest, add_slot__reset_idroot)
/* An empty Action is a valid legacy Action, and thus can have its idroot set
* to a non-zero value. If such an Action gets a slot, it no longer is a
* valid legacy Action, and thus its idtype should be reset to zero. */
anim->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
ASSERT_NE(0, anim->idroot) << "anim->idroot should not be zero at the start of this test.";
action->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
ASSERT_NE(0, action->idroot) << "action->idroot should not be zero at the start of this test.";
anim->slot_add();
action->slot_add();
EXPECT_EQ(0, anim->idroot) << "anim->idroot should get reset when the Action becomes layered.";
EXPECT_EQ(0, action->idroot)
<< "action->idroot should get reset when the Action becomes layered.";
}
TEST_F(ActionLayersTest, add_slot_multiple)
{
Slot &bind_cube = anim->slot_add();
Slot &bind_suzanne = anim->slot_add();
EXPECT_TRUE(anim->assign_id(&bind_cube, cube->id));
EXPECT_TRUE(anim->assign_id(&bind_suzanne, suzanne->id));
Slot &bind_cube = action->slot_add();
Slot &bind_suzanne = action->slot_add();
EXPECT_TRUE(action->assign_id(&bind_cube, cube->id));
EXPECT_TRUE(action->assign_id(&bind_suzanne, suzanne->id));
EXPECT_EQ(2, anim->last_slot_handle);
EXPECT_EQ(2, action->last_slot_handle);
EXPECT_EQ(1, bind_cube.handle);
EXPECT_EQ(2, bind_suzanne.handle);
}
TEST_F(ActionLayersTest, anim_assign_id)
TEST_F(ActionLayersTest, action_assign_id)
{
/* Assign to the only, 'virgin' Slot, should always work. */
Slot &slot_cube = anim->slot_add();
Slot &slot_cube = action->slot_add();
ASSERT_NE(nullptr, slot_cube.runtime);
ASSERT_STREQ(slot_cube.name, "XXSlot");
ASSERT_TRUE(anim->assign_id(&slot_cube, cube->id));
ASSERT_TRUE(action->assign_id(&slot_cube, cube->id));
EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
EXPECT_STREQ(slot_cube.name, "OBSlot");
EXPECT_STREQ(slot_cube.name, cube->adt->slot_name)
@ -253,7 +255,7 @@ TEST_F(ActionLayersTest, anim_assign_id)
<< "Expecting Cube to be registered as animated by its slot.";
/* Assign another ID to the same Slot. */
ASSERT_TRUE(anim->assign_id(&slot_cube, suzanne->id));
ASSERT_TRUE(action->assign_id(&slot_cube, suzanne->id));
EXPECT_STREQ(slot_cube.name, "OBSlot");
EXPECT_STREQ(slot_cube.name, cube->adt->slot_name)
<< "The slot name should be copied to the adt";
@ -261,45 +263,45 @@ TEST_F(ActionLayersTest, anim_assign_id)
EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
<< "Expecting Suzanne to be registered as animated by the Cube slot.";
{ /* Assign Cube to another animation+slot without unassigning first. */
{ /* Assign Cube to another action+slot without unassigning first. */
Action *another_anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACOtherAnim"));
Slot &another_slot = another_anim->slot_add();
ASSERT_FALSE(another_anim->assign_id(&another_slot, cube->id))
<< "Assigning animation (with this function) when already assigned should fail.";
<< "Assigning Action (with this function) when already assigned should fail.";
EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to still be registered as animated by its slot.";
}
{ /* Assign Cube to another slot of the same Animation, this should work. */
const int user_count_pre = anim->id.us;
Slot &slot_cube_2 = anim->slot_add();
ASSERT_TRUE(anim->assign_id(&slot_cube_2, cube->id));
ASSERT_EQ(anim->id.us, user_count_pre)
<< "Assigning to a different slot of the same animation should _not_ change the user "
"count of that Animation";
{ /* Assign Cube to another slot of the same Action, this should work. */
const int user_count_pre = action->id.us;
Slot &slot_cube_2 = action->slot_add();
ASSERT_TRUE(action->assign_id(&slot_cube_2, cube->id));
ASSERT_EQ(action->id.us, user_count_pre)
<< "Assigning to a different slot of the same Action should _not_ change the user "
"count of that Action";
EXPECT_FALSE(slot_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to no longer be registered as animated by the Cube slot.";
EXPECT_TRUE(slot_cube_2.users(*bmain).contains(&cube->id))
<< "Expecting Cube to be registered as animated by the 'cube_2' slot.";
}
{ /* Unassign the animation. */
const int user_count_pre = anim->id.us;
anim->unassign_id(cube->id);
ASSERT_EQ(anim->id.us, user_count_pre - 1)
<< "Unassigning an animation should lower its user count";
{ /* Unassign the Action. */
const int user_count_pre = action->id.us;
action->unassign_id(cube->id);
ASSERT_EQ(action->id.us, user_count_pre - 1)
<< "Unassigning an Action should lower its user count";
ASSERT_EQ(2, anim->slots().size()) << "Expecting the Action to have two Slots";
EXPECT_FALSE(anim->slot(0)->users(*bmain).contains(&cube->id))
ASSERT_EQ(2, action->slots().size()) << "Expecting the Action to have two Slots";
EXPECT_FALSE(action->slot(0)->users(*bmain).contains(&cube->id))
<< "Expecting Cube to no longer be registered as animated by any slot.";
EXPECT_FALSE(anim->slot(1)->users(*bmain).contains(&cube->id))
EXPECT_FALSE(action->slot(1)->users(*bmain).contains(&cube->id))
<< "Expecting Cube to no longer be registered as animated by any slot.";
}
/* Assign Cube to another 'virgin' slot. This should not cause a name
* collision between the Slots. */
Slot &another_slot_cube = anim->slot_add();
ASSERT_TRUE(anim->assign_id(&another_slot_cube, cube->id));
Slot &another_slot_cube = action->slot_add();
ASSERT_TRUE(action->assign_id(&another_slot_cube, cube->id));
EXPECT_EQ(another_slot_cube.handle, cube->adt->slot_handle);
EXPECT_STREQ("OBSlot.002", another_slot_cube.name) << "The slot should be uniquely named";
EXPECT_STREQ("OBSlot.002", cube->adt->slot_name) << "The slot name should be copied to the adt";
@ -308,7 +310,7 @@ TEST_F(ActionLayersTest, anim_assign_id)
/* Create an ID of another type. This should not be assignable to this slot. */
ID *mesh = static_cast<ID *>(BKE_id_new_nomain(ID_ME, "Mesh"));
EXPECT_FALSE(anim->assign_id(&slot_cube, *mesh))
EXPECT_FALSE(action->assign_id(&slot_cube, *mesh))
<< "Mesh should not be animatable by an Object slot";
EXPECT_FALSE(another_slot_cube.users(*bmain).contains(mesh))
<< "Expecting Mesh to not be registered as animated by the 'slot_cube' slot.";
@ -317,28 +319,28 @@ TEST_F(ActionLayersTest, anim_assign_id)
TEST_F(ActionLayersTest, rename_slot)
{
Slot &slot_cube = anim->slot_add();
ASSERT_TRUE(anim->assign_id(&slot_cube, cube->id));
Slot &slot_cube = action->slot_add();
ASSERT_TRUE(action->assign_id(&slot_cube, cube->id));
EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
EXPECT_STREQ("OBSlot", slot_cube.name);
EXPECT_STREQ(slot_cube.name, cube->adt->slot_name)
<< "The slot name should be copied to the adt";
anim->slot_name_define(slot_cube, "New Slot Name");
action->slot_name_define(slot_cube, "New Slot Name");
EXPECT_STREQ("New Slot Name", slot_cube.name);
/* At this point the slot name will not have been copied to the cube
* AnimData. However, I don't want to test for that here, as it's not exactly
* desirable behavior, but more of a side-effect of the current
* implementation. */
anim->slot_name_propagate(*bmain, slot_cube);
action->slot_name_propagate(*bmain, slot_cube);
EXPECT_STREQ("New Slot Name", cube->adt->slot_name);
/* Finally, do another rename, do NOT call the propagate function, then
* unassign. This should still result in the correct slot name being stored
* on the ADT. */
anim->slot_name_define(slot_cube, "Even Newer Name");
anim->unassign_id(cube->id);
action->slot_name_define(slot_cube, "Even Newer Name");
action->unassign_id(cube->id);
EXPECT_STREQ("Even Newer Name", cube->adt->slot_name);
}
@ -352,7 +354,7 @@ TEST_F(ActionLayersTest, slot_name_ensure_prefix)
}
};
Slot &raw_slot = anim->slot_add();
Slot &raw_slot = action->slot_add();
AccessibleSlot &slot = static_cast<AccessibleSlot &>(raw_slot);
ASSERT_STREQ("XXSlot", slot.name);
ASSERT_EQ(0, slot.idtype);
@ -367,7 +369,7 @@ TEST_F(ActionLayersTest, slot_name_ensure_prefix)
EXPECT_STREQ("CASlot", slot.name);
/* idtype ME, explicit name of other idtype. */
anim->slot_name_define(slot, "CANewName");
action->slot_name_define(slot, "CANewName");
slot.idtype = ID_ME;
slot.name_ensure_prefix();
EXPECT_STREQ("MENewName", slot.name);
@ -380,7 +382,7 @@ TEST_F(ActionLayersTest, slot_name_ensure_prefix)
TEST_F(ActionLayersTest, slot_name_prefix)
{
Slot &slot = anim->slot_add();
Slot &slot = action->slot_add();
EXPECT_EQ("XX", slot.name_prefix_for_idtype());
slot.idtype = ID_CA;
@ -389,11 +391,11 @@ TEST_F(ActionLayersTest, slot_name_prefix)
TEST_F(ActionLayersTest, rename_slot_name_collision)
{
Slot &slot1 = anim->slot_add();
Slot &slot2 = anim->slot_add();
Slot &slot1 = action->slot_add();
Slot &slot2 = action->slot_add();
anim->slot_name_define(slot1, "New Slot Name");
anim->slot_name_define(slot2, "New Slot Name");
action->slot_name_define(slot1, "New Slot Name");
action->slot_name_define(slot2, "New Slot Name");
EXPECT_STREQ("New Slot Name", slot1.name);
EXPECT_STREQ("New Slot Name.001", slot2.name);
}
@ -402,25 +404,25 @@ TEST_F(ActionLayersTest, find_suitable_slot)
{
/* ===
* Empty case, no slots exist yet and the ID doesn't even have an AnimData. */
EXPECT_EQ(nullptr, anim->find_suitable_slot_for(cube->id));
EXPECT_EQ(nullptr, action->find_suitable_slot_for(cube->id));
/* ===
* Slot exists with the same name & type as the ID, but the ID doesn't have any AnimData yet.
* These should nevertheless be matched up. */
Slot &slot = anim->slot_add();
Slot &slot = action->slot_add();
slot.handle = 327;
STRNCPY_UTF8(slot.name, "OBKüüübus");
slot.idtype = GS(cube->id.name);
EXPECT_EQ(&slot, anim->find_suitable_slot_for(cube->id));
EXPECT_EQ(&slot, action->find_suitable_slot_for(cube->id));
/* ===
* Slot exists with the same name & type as the ID, and the ID has an AnimData with the same
* slot name, but a different slot_handle. Since the Animation has not yet been
* slot name, but a different slot_handle. Since the Action has not yet been
* assigned to this ID, the slot_handle should be ignored, and the slot name used for
* matching. */
/* Create a slot with a handle that should be ignored.*/
Slot &other_slot = anim->slot_add();
Slot &other_slot = action->slot_add();
other_slot.handle = 47;
AnimData *adt = BKE_animdata_ensure_id(&cube->id);
@ -428,28 +430,28 @@ TEST_F(ActionLayersTest, find_suitable_slot)
/* Configure adt to use the handle of one slot, and the name of the other. */
adt->slot_handle = other_slot.handle;
STRNCPY_UTF8(adt->slot_name, slot.name);
EXPECT_EQ(&slot, anim->find_suitable_slot_for(cube->id));
EXPECT_EQ(&slot, action->find_suitable_slot_for(cube->id));
/* ===
* Same situation as above (AnimData has name of one slot, but the handle of another),
* except that the animation data-block has already been assigned. In this case the handle
* should take precedence. */
adt->action = anim;
id_us_plus(&anim->id);
EXPECT_EQ(&other_slot, anim->find_suitable_slot_for(cube->id));
* except that the Action has already been assigned. In this case the handle should take
* precedence. */
adt->action = action;
id_us_plus(&action->id);
EXPECT_EQ(&other_slot, action->find_suitable_slot_for(cube->id));
/* ===
* A slot exists, but doesn't match anything in the anim data of the cube. This should fall
* A slot exists, but doesn't match anything in the action data of the cube. This should fall
* back to using the ID name. */
adt->slot_handle = 161;
STRNCPY_UTF8(adt->slot_name, "¿¿What's this??");
EXPECT_EQ(&slot, anim->find_suitable_slot_for(cube->id));
EXPECT_EQ(&slot, action->find_suitable_slot_for(cube->id));
}
TEST_F(ActionLayersTest, strip)
{
constexpr float inf = std::numeric_limits<float>::infinity();
Layer &layer0 = anim->layer_add("Test Læür nul");
Layer &layer0 = action->layer_add("Test Læür nul");
Strip &strip = layer0.strip_add(Strip::Type::Keyframe);
strip.resize(-inf, inf);
@ -486,9 +488,9 @@ TEST_F(ActionLayersTest, strip)
TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
{
Slot &slot = anim->slot_add();
EXPECT_TRUE(anim->assign_id(&slot, cube->id));
Layer &layer = anim->layer_add("Kübus layer");
Slot &slot = action->slot_add();
EXPECT_TRUE(action->assign_id(&slot, cube->id));
Layer &layer = action->layer_add("Kübus layer");
Strip &strip = layer.strip_add(Strip::Type::Keyframe);
KeyframeStrip &key_strip = strip.as<KeyframeStrip>();
@ -533,38 +535,38 @@ TEST_F(ActionLayersTest, is_action_assignable_to)
EXPECT_TRUE(is_action_assignable_to(nullptr, ID_CA))
<< "nullptr Actions should be assignable to any type.";
EXPECT_TRUE(is_action_assignable_to(anim, ID_OB))
EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
<< "Empty Actions should be assignable to any type.";
EXPECT_TRUE(is_action_assignable_to(anim, ID_CA))
EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
<< "Empty Actions should be assignable to any type.";
/* Make the Action a legacy one. */
FCurve fake_fcurve;
BLI_addtail(&anim->curves, &fake_fcurve);
ASSERT_FALSE(anim->is_empty());
ASSERT_TRUE(anim->is_action_legacy());
ASSERT_EQ(0, anim->idroot);
BLI_addtail(&action->curves, &fake_fcurve);
ASSERT_FALSE(action->is_empty());
ASSERT_TRUE(action->is_action_legacy());
ASSERT_EQ(0, action->idroot);
EXPECT_TRUE(is_action_assignable_to(anim, ID_OB))
EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
<< "Legacy Actions with idroot=0 should be assignable to any type.";
EXPECT_TRUE(is_action_assignable_to(anim, ID_CA))
EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
<< "Legacy Actions with idroot=0 should be assignable to any type.";
/* Set the legacy idroot. */
anim->idroot = ID_CA;
EXPECT_FALSE(is_action_assignable_to(anim, ID_OB))
action->idroot = ID_CA;
EXPECT_FALSE(is_action_assignable_to(action, ID_OB))
<< "Legacy Actions with idroot=ID_CA should NOT be assignable to ID_OB.";
EXPECT_TRUE(is_action_assignable_to(anim, ID_CA))
EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
<< "Legacy Actions with idroot=CA should be assignable to ID_CA.";
/* Make the Action a layered one. */
BLI_poptail(&anim->curves);
anim->layer_add("layer");
ASSERT_EQ(0, anim->idroot) << "Adding a layer should clear the idroot.";
BLI_poptail(&action->curves);
action->layer_add("layer");
ASSERT_EQ(0, action->idroot) << "Adding a layer should clear the idroot.";
EXPECT_TRUE(is_action_assignable_to(anim, ID_OB))
EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
<< "Layered Actions should be assignable to any type.";
EXPECT_TRUE(is_action_assignable_to(anim, ID_CA))
EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
<< "Layered Actions should be assignable to any type.";
}

@ -127,7 +127,7 @@ void animdata_fcurve_delete(bAnimContext *ac, AnimData *adt, FCurve *fcu)
animdata_remove_empty_action(adt);
}
else {
/* TODO: support deleting FCurves from Animation data-blocks. */
/* TODO: support deleting FCurves from layered Actions. */
return;
}
}
@ -200,7 +200,7 @@ const FCurve *fcurve_find_by_rna_path(const AnimData &adt,
const Slot *slot = action.slot_for_handle(adt.slot_handle);
if (!slot) {
/* No need to inspect anything if this ID does not have an animation Slot. */
/* No need to inspect anything if this ID does not have an Action Slot. */
return nullptr;
}

@ -35,15 +35,15 @@ void apply_evaluation_result(const EvaluationResult &evaluation_result,
PointerRNA &animated_id_ptr,
bool flush_to_original);
static EvaluationResult evaluate_animation(PointerRNA &animated_id_ptr,
Action &animation,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context)
static EvaluationResult evaluate_action(PointerRNA &animated_id_ptr,
Action &action,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context)
{
EvaluationResult last_result;
/* Evaluate each layer in order. */
for (Layer *layer : animation.layers()) {
for (Layer *layer : action.layers()) {
if (layer->influence <= 0.0f) {
/* Don't bother evaluating layers without influence. */
continue;
@ -69,14 +69,14 @@ static EvaluationResult evaluate_animation(PointerRNA &animated_id_ptr,
return last_result;
}
void evaluate_and_apply_animation(PointerRNA &animated_id_ptr,
Action &animation,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context,
const bool flush_to_original)
void evaluate_and_apply_action(PointerRNA &animated_id_ptr,
Action &action,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context,
const bool flush_to_original)
{
EvaluationResult evaluation_result = evaluate_animation(
animated_id_ptr, animation, slot_handle, anim_eval_context);
EvaluationResult evaluation_result = evaluate_action(
animated_id_ptr, action, slot_handle, anim_eval_context);
if (!evaluation_result) {
return;
}

@ -54,7 +54,7 @@ class AnimatedProperty {
}
};
/* Evaluated FCurves for some animation slot.
/* Evaluated FCurves for some action slot.
* Mapping from property identifier to its float value.
*
* Can be fed to the evaluation of the next layer, mixed with another strip, or

@ -33,7 +33,7 @@ using namespace blender::animrig::internal;
class AnimationEvaluationTest : public testing::Test {
protected:
Main *bmain;
Action *anim;
Action *action;
Object *cube;
Slot *slot;
Layer *layer;
@ -60,13 +60,13 @@ class AnimationEvaluationTest : public testing::Test {
void SetUp() override
{
bmain = BKE_main_new();
anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACÄnimåtië"));
action = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACÄnimåtië"));
cube = BKE_object_add_only_object(bmain, OB_EMPTY, "Küüübus");
slot = &anim->slot_add();
anim->assign_id(slot, cube->id);
layer = &anim->layer_add("Kübus layer");
slot = &action->slot_add();
action->assign_id(slot, cube->id);
layer = &action->layer_add("Kübus layer");
/* Make it easier to predict test values. */
settings.interpolation = BEZT_IPO_LIN;

@ -163,7 +163,7 @@ void CombinedKeyingResult::generate_reports(ReportList *reports, const eReportTy
if (this->get_count(SingleKeyingResult::NO_VALID_SLOT) > 0) {
const int error_count = this->get_count(SingleKeyingResult::NO_VALID_SLOT);
errors.append(fmt::format(RPT_("Inserting keys on {:d} data-block(s) has been skipped because "
"of missing animation slots."),
"of missing action slots."),
error_count));
}

@ -307,7 +307,7 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con
* \note Return pointer parameters (`r_action`, `r_driven` and `r_special`) are all optional and
* may be NULL.
*
* \note since Animation data-blocks may have multiple layers all containing an F-Curve for this
* \note since Actions may have multiple layers all containing an F-Curve for this
* property, what is returned is a best-effort guess. The topmost layer has priority, and it is
* assumed that when it has a strip, it's infinite.
*/

@ -396,13 +396,13 @@ static void read_keyframe_strip(BlendDataReader *reader, animrig::KeyframeStrip
}
}
static void read_layers(BlendDataReader *reader, animrig::Action &anim)
static void read_layers(BlendDataReader *reader, animrig::Action &action)
{
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&anim.layer_array));
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&action.layer_array));
for (int layer_idx = 0; layer_idx < anim.layer_array_num; layer_idx++) {
BLO_read_struct(reader, ActionLayer, &anim.layer_array[layer_idx]);
ActionLayer *layer = anim.layer_array[layer_idx];
for (int layer_idx = 0; layer_idx < action.layer_array_num; layer_idx++) {
BLO_read_struct(reader, ActionLayer, &action.layer_array[layer_idx]);
ActionLayer *layer = action.layer_array[layer_idx];
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&layer->strip_array));
for (int strip_idx = 0; strip_idx < layer->strip_array_num; strip_idx++) {
@ -419,13 +419,13 @@ static void read_layers(BlendDataReader *reader, animrig::Action &anim)
}
}
static void read_slots(BlendDataReader *reader, animrig::Action &anim)
static void read_slots(BlendDataReader *reader, animrig::Action &action)
{
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&anim.slot_array));
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&action.slot_array));
for (int i = 0; i < anim.slot_array_num; i++) {
BLO_read_struct(reader, ActionSlot, &anim.slot_array[i]);
anim.slot_array[i]->wrap().blend_read_post();
for (int i = 0; i < action.slot_array_num; i++) {
BLO_read_struct(reader, ActionSlot, &action.slot_array[i]);
action.slot_array[i]->wrap().blend_read_post();
}
}

@ -205,11 +205,11 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
#ifdef WITH_ANIM_BAKLAVA
if (!act) {
animrig::unassign_animation(*id);
animrig::unassign_action(*id);
return true;
}
animrig::Action &action = act->wrap();
return animrig::assign_animation(action, *id);
return animrig::assign_action(action, *id);
#else
return animdata_set_action(reports, id, &adt->action, act);
#endif // WITH_ANIM_BAKLAVA
@ -259,7 +259,7 @@ void BKE_animdata_free(ID *id, const bool do_id_user)
/* unlink action (don't free, as it's in its own list) */
if (adt->action) {
#ifdef WITH_ANIM_BAKLAVA
blender::animrig::unassign_animation(*id);
blender::animrig::unassign_action(*id);
#else
id_us_min(&adt->action->id);
#endif
@ -270,7 +270,7 @@ void BKE_animdata_free(ID *id, const bool do_id_user)
/* TODO: Linked Actions do not support usage in the NLA yet, so work around this and cleanly
* unassign the Action by moving it back to `adt->action`. */
adt->action = adt->tmpact;
blender::animrig::unassign_animation(*id);
blender::animrig::unassign_action(*id);
#else
id_us_min(&adt->tmpact->id);
#endif

@ -3949,7 +3949,7 @@ void BKE_animsys_evaluate_animdata(ID *id,
else if (adt->action) {
blender::animrig::Action &action = adt->action->wrap();
if (action.is_action_layered()) {
blender::animrig::evaluate_and_apply_animation(
blender::animrig::evaluate_and_apply_action(
id_ptr, action, adt->slot_handle, *anim_eval_context, flush_to_original);
}
else {

@ -1574,7 +1574,7 @@ void DepsgraphRelationBuilder::build_animdata_curves(ID *id)
OperationKey animation_exit_key(id, NodeType::ANIMATION, OperationCode::ANIMATION_EXIT);
add_relation(animation_entry_key, animation_eval_key, "Init -> Eval");
add_relation(animation_eval_key, animation_exit_key, "Eval -> Exit");
/* Wire up dependency from action and Animation datablock. */
/* Wire up dependency from Actions. */
ComponentKey adt_key(id, NodeType::ANIMATION);
/* Relation from action itself. */
if (adt->action != nullptr) {

@ -44,7 +44,7 @@ static bool mode_transfer_is_animation_running(const float anim_time)
return anim_time >= 0.0f && anim_time <= MODE_TRANSFER_FLASH_LENGTH;
}
static float mode_transfer_alpha_for_animation_time_get(const float anim_time)
static float mode_transfer_alpha_for_action_slot_time_get(const float anim_time)
{
if (anim_time > MODE_TRANSFER_FLASH_LENGTH) {
return 0.0f;
@ -103,7 +103,7 @@ void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob)
float color[4];
UI_GetThemeColor3fv(TH_VERTEX_SELECT, color);
color[3] = mode_transfer_alpha_for_animation_time_get(animation_time);
color[3] = mode_transfer_alpha_for_action_slot_time_get(animation_time);
srgb_to_linearrgb_v4(color, color);
/* Alpha pre-multiply. */
mul_v3_fl(color, color[3]);

@ -481,8 +481,8 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
* - nlaOk: line or block of code to execute for NLA tracks+strips case
* - driversOk: line or block of code to execute for Drivers case
* - nlaKeysOk: line or block of code for NLA Strip Keyframes case
* - keysOk: line or block of code for Keyframes case
* - animOk: line or block of code for Keyframes from Animation data blocks case
* - legacyActionOk: line or block of code for Keyframes from legacy Actions
* - layeredActionOk: line or block of code for Keyframes from layered Actions
*
* The checks for the various cases are as follows:
* 0) top level: checks for animdata and also that all the F-Curves for the block will be visible
@ -1516,7 +1516,7 @@ static size_t animfilter_action_slot(bAnimContext *ac,
if (show_fcurves_only || expansion_is_ok) {
/* Add list elements for the F-Curves for this Slot. */
Span<FCurve *> fcurves = animrig::fcurves_for_animation(action, slot.handle);
Span<FCurve *> fcurves = animrig::fcurves_for_action_slot(action, slot.handle);
items += animfilter_fcurves_span(
ac, anim_data, fcurves, slot.handle, filter_mode, owner_id, &action.id);
}

@ -170,7 +170,7 @@ static short agrp_keyframes_loop(KeyframeEditData *ked,
/* Loop over all keyframes in the layered Action. */
static short action_layered_keyframes_loop(KeyframeEditData *ked,
animrig::Action &anim,
animrig::Action &action,
animrig::Slot *slot,
KeyframeEditFunc key_ok,
KeyframeEditFunc key_cb,
@ -181,7 +181,7 @@ static short action_layered_keyframes_loop(KeyframeEditData *ked,
return 0;
}
Span<FCurve *> fcurves = animrig::fcurves_for_animation(anim, slot->handle);
Span<FCurve *> fcurves = animrig::fcurves_for_action_slot(action, slot->handle);
for (FCurve *fcurve : fcurves) {
if (ANIM_fcurve_keyframes_loop(ked, fcurve, key_ok, key_cb, fcu_cb)) {
return 1;
@ -421,9 +421,9 @@ short ANIM_animchannel_keyframes_loop(KeyframeEditData *ked,
/* This assumes that the ALE_ACTION_LAYERED channel is shown in the dopesheet context,
* underneath the data-block that owns `ale->adt`. So that means that the loop is limited to
* the keys that belong to that slot. */
animrig::Action &anim = static_cast<bAction *>(ale->key_data)->wrap();
animrig::Slot *slot = anim.slot_for_handle(ale->adt->slot_handle);
return action_layered_keyframes_loop(ked, anim, slot, key_ok, key_cb, fcu_cb);
animrig::Action &action = static_cast<bAction *>(ale->key_data)->wrap();
animrig::Slot *slot = action.slot_for_handle(ale->adt->slot_handle);
return action_layered_keyframes_loop(ked, action, slot, key_ok, key_cb, fcu_cb);
#else
return 0;
#endif

@ -1169,7 +1169,7 @@ void action_slot_to_keylist(AnimData *adt,
blender::float2 range)
{
BLI_assert(GS(action.id.name) == ID_AC);
for (FCurve *fcurve : fcurves_for_animation(action, slot_handle)) {
for (FCurve *fcurve : fcurves_for_action_slot(action, slot_handle)) {
fcurve_to_keylist(adt, fcurve, keylist, saction_flag, range);
}
}
@ -1186,7 +1186,7 @@ void action_to_keylist(AnimData *adt,
blender::animrig::Action &action = dna_action->wrap();
/* TODO: move this into fcurves_for_animation(). */
/* TODO: move this into fcurves_for_action_slot(). */
if (action.is_action_legacy()) {
LISTBASE_FOREACH (FCurve *, fcu, &action.curves) {
fcurve_to_keylist(adt, fcu, keylist, saction_flag, range);

@ -1182,7 +1182,7 @@ typedef struct ActionStrip {
*
* This offset determines the difference between "Animation time" (which would
* typically be the same as the scene time, until the animation system
* supports strips referencing other Animation data-blocks).
* supports strips referencing other Actions).
*/
float frame_offset;

@ -118,8 +118,8 @@ static animrig::Strip &rna_data_strip(const PointerRNA *ptr)
static void rna_Action_tag_animupdate(Main *, Scene *, PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
DEG_id_tag_update(&anim.id, ID_RECALC_ANIMATION);
animrig::Action &action = rna_action(ptr);
DEG_id_tag_update(&action.id, ID_RECALC_ANIMATION);
}
static animrig::KeyframeStrip &rna_data_keyframe_strip(const PointerRNA *ptr)
@ -145,27 +145,27 @@ static void rna_iterator_array_begin(CollectionPropertyIterator *iter, MutableSp
rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr);
}
static ActionSlot *rna_Action_slots_new(bAction *anim_id,
static ActionSlot *rna_Action_slots_new(bAction *dna_action,
bContext *C,
ReportList *reports,
ID *id_for_slot)
{
animrig::Action &anim = anim_id->wrap();
animrig::Action &action = dna_action->wrap();
animrig::Slot *slot;
if (!anim.is_action_layered()) {
if (!action.is_action_layered()) {
BKE_reportf(reports,
RPT_ERROR,
"Cannot add slots to a legacy Action '%s'. Convert it to a layered Action first.",
anim.id.name + 2);
action.id.name + 2);
return nullptr;
}
if (id_for_slot) {
slot = &anim.slot_add_for_id(*id_for_slot);
slot = &action.slot_add_for_id(*id_for_slot);
}
else {
slot = &anim.slot_add();
slot = &action.slot_add();
}
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
@ -174,14 +174,14 @@ static ActionSlot *rna_Action_slots_new(bAction *anim_id,
static void rna_iterator_action_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
rna_iterator_array_begin(iter, anim.layers());
animrig::Action &action = rna_action(ptr);
rna_iterator_array_begin(iter, action.layers());
}
static int rna_iterator_action_layers_length(PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
return anim.layers().size();
animrig::Action &action = rna_action(ptr);
return action.layers().size();
}
static ActionLayer *rna_Action_layers_new(bAction *dna_action,
@ -189,24 +189,24 @@ static ActionLayer *rna_Action_layers_new(bAction *dna_action,
ReportList *reports,
const char *name)
{
animrig::Action &anim = dna_action->wrap();
animrig::Action &action = dna_action->wrap();
if (!anim.is_action_layered()) {
if (!action.is_action_layered()) {
BKE_reportf(reports,
RPT_ERROR,
"Cannot add layers to a legacy Action '%s'. Convert it to a layered Action first.",
anim.id.name + 2);
action.id.name + 2);
return nullptr;
}
if (anim.layers().size() >= 1) {
if (action.layers().size() >= 1) {
/* Not allowed to have more than one layer, for now. This limitation is in
* place until working with multiple animated IDs is fleshed out better. */
BKE_report(reports, RPT_ERROR, "An Animation may not have more than one layer");
BKE_report(reports, RPT_ERROR, "An Action may not have more than one layer");
return nullptr;
}
animrig::Layer &layer = anim.layer_add(name);
animrig::Layer &layer = action.layer_add(name);
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
return &layer;
@ -217,27 +217,27 @@ void rna_Action_layers_remove(bAction *dna_action,
ReportList *reports,
ActionLayer *dna_layer)
{
animrig::Action &anim = dna_action->wrap();
animrig::Action &action = dna_action->wrap();
animrig::Layer &layer = dna_layer->wrap();
if (!anim.layer_remove(layer)) {
BKE_report(reports, RPT_ERROR, "This layer does not belong to this animation");
if (!action.layer_remove(layer)) {
BKE_report(reports, RPT_ERROR, "This layer does not belong to this Action");
return;
}
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
DEG_id_tag_update(&anim.id, ID_RECALC_ANIMATION);
DEG_id_tag_update(&action.id, ID_RECALC_ANIMATION);
}
static void rna_iterator_animation_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
rna_iterator_array_begin(iter, anim.slots());
animrig::Action &action = rna_action(ptr);
rna_iterator_array_begin(iter, action.slots());
}
static int rna_iterator_animation_slots_length(PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
return anim.slots().size();
animrig::Action &action = rna_action(ptr);
return action.slots().size();
}
static std::optional<std::string> rna_ActionSlot_path(const PointerRNA *ptr)
@ -264,7 +264,7 @@ int rna_ActionSlot_name_display_length(PointerRNA *ptr)
static void rna_ActionSlot_name_display_set(PointerRNA *ptr, const char *name)
{
animrig::Action &anim = rna_action(ptr);
animrig::Action &action = rna_action(ptr);
animrig::Slot &slot = rna_data_slot(ptr);
const StringRef name_ref(name);
@ -275,12 +275,12 @@ static void rna_ActionSlot_name_display_set(PointerRNA *ptr, const char *name)
/* Construct the new internal name, from the slot's type and the given name. */
const std::string internal_name = slot.name_prefix_for_idtype() + name_ref;
anim.slot_name_define(slot, internal_name);
action.slot_name_define(slot, internal_name);
}
static void rna_ActionSlot_name_set(PointerRNA *ptr, const char *name)
{
animrig::Action &anim = rna_action(ptr);
animrig::Action &action = rna_action(ptr);
animrig::Slot &slot = rna_data_slot(ptr);
const StringRef name_ref(name);
@ -302,14 +302,14 @@ static void rna_ActionSlot_name_set(PointerRNA *ptr, const char *name)
}
}
anim.slot_name_define(slot, name);
action.slot_name_define(slot, name);
}
static void rna_ActionSlot_name_update(Main *bmain, Scene *, PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
animrig::Action &action = rna_action(ptr);
animrig::Slot &slot = rna_data_slot(ptr);
anim.slot_name_propagate(*bmain, slot);
action.slot_name_propagate(*bmain, slot);
}
static std::optional<std::string> rna_ActionLayer_path(const PointerRNA *ptr)
@ -356,11 +356,8 @@ ActionStrip *rna_ActionStrips_new(ActionLayer *dna_layer,
return &strip;
}
void rna_ActionStrips_remove(ID *animation_id,
ActionLayer *dna_layer,
bContext *C,
ReportList *reports,
ActionStrip *dna_strip)
void rna_ActionStrips_remove(
ID *action, ActionLayer *dna_layer, bContext *C, ReportList *reports, ActionStrip *dna_strip)
{
animrig::Layer &layer = dna_layer->wrap();
animrig::Strip &strip = dna_strip->wrap();
@ -370,7 +367,7 @@ void rna_ActionStrips_remove(ID *animation_id,
}
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
DEG_id_tag_update(animation_id, ID_RECALC_ANIMATION);
DEG_id_tag_update(action, ID_RECALC_ANIMATION);
}
static StructRNA *rna_ActionStrip_refine(PointerRNA *ptr)
@ -385,17 +382,17 @@ static StructRNA *rna_ActionStrip_refine(PointerRNA *ptr)
static std::optional<std::string> rna_ActionStrip_path(const PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
animrig::Action &action = rna_action(ptr);
animrig::Strip &strip_to_find = rna_data_strip(ptr);
for (animrig::Layer *layer : anim.layers()) {
for (animrig::Layer *layer : action.layers()) {
Span<animrig::Strip *> strips = layer->strips();
const int index = strips.first_index_try(&strip_to_find);
if (index < 0) {
continue;
}
PointerRNA layer_ptr = RNA_pointer_create(&anim.id, &RNA_ActionLayer, layer);
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.");
const std::string strip_path = fmt::format("{}.strips[{}]", *layer_path, index);
@ -1173,7 +1170,7 @@ static void rna_def_action_slots(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_srna(cprop, "ActionSlots");
srna = RNA_def_struct(brna, "ActionSlots", nullptr);
RNA_def_struct_sdna(srna, "bAction");
RNA_def_struct_ui_text(srna, "Action Slots", "Collection of animation slots");
RNA_def_struct_ui_text(srna, "Action Slots", "Collection of action slots");
/* Animation.slots.new(...) */
func = RNA_def_function(srna, "new", "rna_Action_slots_new");
@ -1190,7 +1187,7 @@ static void rna_def_action_slots(BlenderRNA *brna, PropertyRNA *cprop)
/* Clear out the PARM_REQUIRED flag, which is set by default for pointer parameters. */
RNA_def_parameter_flags(parm, PropertyFlag(0), ParameterFlag(0));
parm = RNA_def_pointer(func, "slot", "ActionSlot", "", "Newly created animation slot");
parm = RNA_def_pointer(func, "slot", "ActionSlot", "", "Newly created action slot");
RNA_def_function_return(func, parm);
}
@ -1217,7 +1214,7 @@ static void rna_def_action_layers(BlenderRNA *brna, PropertyRNA *cprop)
nullptr,
sizeof(ActionLayer::name) - 1,
"Name",
"Name of the layer, will be made unique within the Animation data-block");
"Name of the layer, will be made unique within the Action");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_pointer(func, "layer", "ActionLayer", "", "Newly created animation layer");
RNA_def_function_return(func, parm);
@ -1240,8 +1237,8 @@ static void rna_def_action_slot(BlenderRNA *brna)
RNA_def_struct_path_func(srna, "rna_ActionSlot_path");
RNA_def_struct_ui_text(
srna,
"Animation Slot",
"Identifier for a set of channels in this Animation, that can be used by a data-block "
"Action slot",
"Identifier for a set of channels in this Action, that can be used by a data-block "
"to specify what it gets animated by");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
@ -1252,7 +1249,7 @@ static void rna_def_action_slot(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna,
"Slot Name",
"Used when connecting an Animation to a data-block, to find the correct slot handle");
"Used when connecting an Action to a data-block, to find the correct slot handle");
prop = RNA_def_property(srna, "name_display", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop,
@ -1368,7 +1365,7 @@ static void rna_def_keyframestrip_channelbags(BlenderRNA *brna, PropertyRNA *cpr
RNA_def_struct_ui_text(
srna,
"Animation Channels for Slots",
"For each animation slot, a list of animation channels that are meant for that slot");
"For each action slot, a list of animation channels that are meant for that slot");
}
static void rna_def_action_keyframe_strip(BlenderRNA *brna)
@ -1378,7 +1375,7 @@ static void rna_def_action_keyframe_strip(BlenderRNA *brna)
srna = RNA_def_struct(brna, "KeyframeActionStrip", "ActionStrip");
RNA_def_struct_ui_text(
srna, "Keyframe Animation Strip", "Strip with a set of F-Curves for each animation slot");
srna, "Keyframe Animation Strip", "Strip with a set of F-Curves for each action slot");
prop = RNA_def_property(srna, "channelbags", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "ActionChannelBag");
@ -1406,7 +1403,7 @@ static void rna_def_action_keyframe_strip(BlenderRNA *brna)
0,
INT_MAX,
"Slot Handle",
"Number that identifies a specific animation slot",
"Number that identifies a specific action slot",
0,
INT_MAX);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
@ -1483,7 +1480,7 @@ static void rna_def_action_strip(BlenderRNA *brna)
"KEYFRAME",
0,
"Keyframe",
"Strip with a set of F-Curves for each animation slot"},
"Strip with a set of F-Curves for each action slot"},
{0, nullptr, 0, nullptr, nullptr},
};
@ -1503,7 +1500,7 @@ static void rna_def_channelbag_for_slot_fcurves(BlenderRNA *brna, PropertyRNA *c
RNA_def_property_srna(cprop, "ActionChannelBagFCurves");
srna = RNA_def_struct(brna, "ActionChannelBagFCurves", nullptr);
RNA_def_struct_sdna(srna, "bActionChannelBag");
RNA_def_struct_ui_text(srna, "F-Curves", "Collection of F-Curves for a specific animation slot");
RNA_def_struct_ui_text(srna, "F-Curves", "Collection of F-Curves for a specific action slot");
}
static void rna_def_action_channelbag(BlenderRNA *brna)
@ -1515,7 +1512,7 @@ static void rna_def_action_channelbag(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna,
"Animation Channel Bag",
"Collection of animation channels, typically associated with an animation slot");
"Collection of animation channels, typically associated with an action slot");
prop = RNA_def_property(srna, "slot_handle", PROP_INT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@ -1842,7 +1839,7 @@ static void rna_def_action(BlenderRNA *brna)
nullptr,
nullptr,
nullptr);
RNA_def_property_ui_text(prop, "Layers", "The list of layers that make up this Animation");
RNA_def_property_ui_text(prop, "Layers", "The list of layers that make up this Action");
rna_def_action_layers(brna, prop);
# endif // WITH_ANIM_BAKLAVA

@ -111,7 +111,7 @@ const EnumPropertyItem rna_enum_action_slot_item_new = {
"NEW",
ICON_ADD,
"New",
"Create a new animation slot for this data-block"};
"Create a new action slot for this data-block"};
const EnumPropertyItem rna_enum_action_slot_item_legacy = {
int(blender::animrig::Slot::unassigned),
"UNASSIGNED",
@ -183,11 +183,11 @@ static void rna_AnimData_action_set(PointerRNA *ptr, PointerRNA value, ReportLis
bAction *action = static_cast<bAction *>(value.data);
if (!action) {
blender::animrig::unassign_animation(animated_id);
blender::animrig::unassign_action(animated_id);
return;
}
blender::animrig::assign_animation(action->wrap(), animated_id);
blender::animrig::assign_action(action->wrap(), animated_id);
# else
ID *ownerId = ptr->owner_id;
BKE_animdata_set_action(nullptr, ownerId, static_cast<bAction *>(value.data));
@ -262,23 +262,23 @@ static void rna_AnimData_action_slot_handle_set(
return;
}
blender::animrig::Action *anim = blender::animrig::get_animation(animated_id);
if (!anim) {
/* No animation to verify the slot handle is valid. As the slot handle
* will be completely ignored when re-assigning an Animation, better to
* refuse setting it altogether. This will make bugs in Python code more obvious. */
blender::animrig::Action *action = blender::animrig::get_action(animated_id);
if (!action) {
/* No Action to verify the slot handle is valid. As the slot handle will be
* completely ignored when re-assigning an Action, better to refuse setting
* it altogether. This will make bugs in Python code more obvious. */
WM_reportf(RPT_ERROR,
"Data-block '%s' does not have an animation, cannot set slot handle",
"Data-block '%s' does not have an Action, cannot set slot handle",
animated_id.name + 2);
return;
}
blender::animrig::Slot *slot = anim->slot_for_handle(new_slot_handle);
if (!anim->assign_id(slot, animated_id)) {
blender::animrig::Slot *slot = action->slot_for_handle(new_slot_handle);
if (!action->assign_id(slot, animated_id)) {
if (slot) {
WM_reportf(RPT_ERROR,
"Action '%s' slot '%s' (%d) could not be assigned to %s",
anim->id.name + 2,
action->id.name + 2,
slot->name,
slot->handle,
animated_id.name + 2);
@ -288,7 +288,7 @@ static void rna_AnimData_action_slot_handle_set(
BLI_assert_unreachable();
WM_reportf(RPT_ERROR,
"Action '%s' slot could not be unassigned from %s",
anim->id.name + 2,
action->id.name + 2,
animated_id.name + 2);
}
return;
@ -317,46 +317,46 @@ static void rna_AnimData_action_slot_set(PointerRNA *ptr, int value)
const slot_handle_t new_slot_handle = slot_handle_t(value);
if (new_slot_handle == Slot::unassigned) {
/* No need to check with the Animation, as 'no slot' is always valid. */
/* No need to check with the Action, as 'no slot' is always valid. */
adt.slot_handle = Slot::unassigned;
return;
}
if (!adt.action) {
/* No Action to verify the slot handle is valid. As the slot handle
* will be completely ignored when re-assigning an Animation, better to
* refuse setting it altogether. This will make bugs in Python code more obvious. */
/* No Action to verify the slot handle is valid. As the slot handle will be
* completely ignored when re-assigning an Action, better to refuse setting
* it altogether. This will make bugs in Python code more obvious. */
WM_reportf(RPT_ERROR,
"Data-block '%s' does not have an Action, cannot set slot handle",
animated_id.name + 2);
return;
}
Action &anim = adt.action->wrap();
Action &action = adt.action->wrap();
Slot *slot = nullptr;
/* TODO: handle legacy Action. */
BLI_assert(anim.is_action_layered());
BLI_assert(action.is_action_layered());
if (new_slot_handle == slot_items_value_create_new) {
/* Special case for this enum item. */
slot = &anim.slot_add_for_id(animated_id);
slot = &action.slot_add_for_id(animated_id);
}
else {
slot = anim.slot_for_handle(new_slot_handle);
slot = action.slot_for_handle(new_slot_handle);
if (!slot) {
WM_reportf(RPT_ERROR,
"Animation '%s' has no slot with handle %d",
anim.id.name + 2,
"Action '%s' has no slot with handle %d",
action.id.name + 2,
new_slot_handle);
return;
}
}
if (!anim.assign_id(slot, animated_id)) {
if (!action.assign_id(slot, animated_id)) {
WM_reportf(RPT_ERROR,
"Animation '%s' slot '%s' (%d) could not be assigned to %s",
anim.id.name + 2,
"Action '%s' slot '%s' (%d) could not be assigned to %s",
action.id.name + 2,
slot->name_without_prefix().c_str(),
slot->handle,
animated_id.name + 2);
@ -384,10 +384,10 @@ static const EnumPropertyItem *rna_AnimData_action_slot_itemf(bContext * /*C*/,
EnumPropertyItem *items = nullptr;
int num_items = 0;
const Action &anim = adt.action->wrap();
const Action &action = adt.action->wrap();
bool found_assigned_slot = false;
for (const Slot *slot : anim.slots()) {
for (const Slot *slot : action.slots()) {
item.value = slot->handle;
item.identifier = slot->name;
item.name = slot->name_without_prefix().c_str();
@ -403,7 +403,7 @@ static const EnumPropertyItem *rna_AnimData_action_slot_itemf(bContext * /*C*/,
}
/* Only add the 'New' option when this is a Layered Action. */
const bool is_layered = anim.is_action_layered();
const bool is_layered = action.is_action_layered();
if (is_layered) {
RNA_enum_item_add(&items, &num_items, &rna_enum_action_slot_item_new);
}

@ -7457,8 +7457,8 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, nullptr, "use_animation_baklava", 1);
RNA_def_property_ui_text(
prop,
"New Animation Data-block",
"The new 'Animation' data-block can contain the animation for multiple data-blocks at once");
"Multi-Slot Actions",
"The new 'layered' Action can contain the animation for multiple data-blocks at once");
RNA_def_property_update(prop, 0, "rna_userdef_update");
}

@ -20,61 +20,61 @@ class ActionSlotAssignmentTest(unittest.TestCase):
def test_action_assignment(self):
# Create new Action.
anim = bpy.data.actions.new('TestAction')
self.assertEqual(0, anim.users)
action = bpy.data.actions.new('TestAction')
self.assertEqual(0, action.users)
# Assign the animation to the cube,
cube = bpy.data.objects['Cube']
cube_adt = cube.animation_data_create()
cube_adt.action = anim
self.assertEqual(1, anim.users)
cube_adt.action = action
self.assertEqual(1, action.users)
# Assign the animation to the camera as well.
camera = bpy.data.objects['Camera']
camera_adt = camera.animation_data_create()
camera_adt.action = anim
self.assertEqual(2, anim.users)
camera_adt.action = action
self.assertEqual(2, action.users)
# Unassigning should decrement the user count.
cube_adt.action = None
self.assertEqual(1, anim.users)
self.assertEqual(1, action.users)
# Deleting the camera should also decrement the user count.
bpy.data.objects.remove(camera)
self.assertEqual(0, anim.users)
self.assertEqual(0, action.users)
def test_slot_assignment(self):
# Create new Action.
anim = bpy.data.actions.new('TestAction')
self.assertEqual(0, anim.users)
action = bpy.data.actions.new('TestAction')
self.assertEqual(0, action.users)
# Assign the animation to the cube,
# Assign the Action to the cube,
cube = bpy.data.objects['Cube']
cube_adt = cube.animation_data_create()
cube_adt.action = anim
bind_cube = anim.slots.new(for_id=cube)
cube_adt.action_slot_handle = bind_cube.handle
self.assertEqual(cube_adt.action_slot_handle, bind_cube.handle)
cube_adt.action = action
slot_cube = action.slots.new(for_id=cube)
cube_adt.action_slot_handle = slot_cube.handle
self.assertEqual(cube_adt.action_slot_handle, slot_cube.handle)
# Assign the animation to the camera as well.
# Assign the Action to the camera as well.
camera = bpy.data.objects['Camera']
bind_camera = anim.slots.new(for_id=camera)
slot_camera = action.slots.new(for_id=camera)
camera_adt = camera.animation_data_create()
camera_adt.action = anim
self.assertEqual(camera_adt.action_slot_handle, bind_camera.handle)
camera_adt.action = action
self.assertEqual(camera_adt.action_slot_handle, slot_camera.handle)
# Unassigning should keep the slot name.
cube_adt.action = None
self.assertEqual(cube_adt.action_slot_name, bind_cube.name)
self.assertEqual(cube_adt.action_slot_name, slot_cube.name)
# It should not be possible to set the slot handle while the animation is unassigned.
bind_extra = anim.slots.new()
cube_adt.action_slot_handle = bind_extra.handle
self.assertNotEqual(cube_adt.action_slot_handle, bind_extra.handle)
# It should not be possible to set the slot handle while the Action is unassigned.
slot_extra = action.slots.new()
cube_adt.action_slot_handle = slot_extra.handle
self.assertNotEqual(cube_adt.action_slot_handle, slot_extra.handle)
class LimitationsTest(unittest.TestCase):
"""Test artificial limitations for the Animation data-block.
"""Test artificial limitations for the layered Action.
Certain limitations are in place to keep development & testing focused.
"""
@ -85,22 +85,22 @@ class LimitationsTest(unittest.TestCase):
anims.remove(anims[0])
def test_initial_layers(self):
"""Test that upon creation an Animation has no layers/strips."""
anim = bpy.data.actions.new('TestAction')
self.assertEqual([], anim.layers[:])
"""Test that upon creation an Action has no layers/strips."""
action = bpy.data.actions.new('TestAction')
self.assertEqual([], action.layers[:])
def test_limited_layers_strips(self):
"""Test that there can only be one layer with one strip."""
anim = bpy.data.actions.new('TestAction')
layer = anim.layers.new(name="Layer")
action = bpy.data.actions.new('TestAction')
layer = action.layers.new(name="Layer")
self.assertEqual([], layer.strips[:])
strip = layer.strips.new(type='KEYFRAME')
# Adding a 2nd layer should be forbidden.
with self.assertRaises(RuntimeError):
anim.layers.new(name="Forbidden Layer")
self.assertEqual([layer], anim.layers[:])
action.layers.new(name="Forbidden Layer")
self.assertEqual([layer], action.layers[:])
# Adding a 2nd strip should be forbidden.
with self.assertRaises(RuntimeError):
@ -110,8 +110,8 @@ class LimitationsTest(unittest.TestCase):
def test_limited_strip_api(self):
"""Test that strips have no frame start/end/offset properties."""
anim = bpy.data.actions.new('TestAction')
layer = anim.layers.new(name="Layer")
action = bpy.data.actions.new('TestAction')
layer = action.layers.new(name="Layer")
strip = layer.strips.new(type='KEYFRAME')
self.assertFalse(hasattr(strip, 'frame_start'))
@ -171,9 +171,9 @@ class DataPathTest(unittest.TestCase):
anims.remove(anims[0])
def test_repr(self):
anim = bpy.data.actions.new('TestAction')
action = bpy.data.actions.new('TestAction')
layer = anim.layers.new(name="Layer")
layer = action.layers.new(name="Layer")
self.assertEqual("bpy.data.actions['TestAction'].layers[\"Layer\"]", repr(layer))
strip = layer.strips.new(type='KEYFRAME')