Refactor: rename 'Action Binding' to 'Action Slot'

Rename 'Binding' to 'Slot'. The old term was causing all kind of
confusion, and 'slot' was considered to be a better term for the
intended functionality.

This commit breaks existing blend files that were using the new layered
Action for their animation. The animation data will be lost due to the
rename, as there is no versioning code or DNA renaming logic. At this
time the new system is still marked as experimental, so shouldn't be
used for anything serious anyway.

Pull Request: https://projects.blender.org/blender/blender/pulls/124170
This commit is contained in:
Sybren A. Stüvel 2024-07-05 16:59:34 +02:00
parent 7f6feb2872
commit c0364efec0
46 changed files with 1195 additions and 1215 deletions

@ -38,7 +38,7 @@ def dopesheet_filter(layout, context):
row = layout.row(align=True)
if is_action_editor and context.preferences.experimental.use_animation_baklava:
row.prop(dopesheet, "show_all_bindings", text="")
row.prop(dopesheet, "show_all_slots", text="")
row.prop(dopesheet, "show_only_selected", text="")
row.prop(dopesheet, "show_hidden", text="")

@ -50,27 +50,27 @@ class VIEW3D_PT_animation_layers(Panel):
col = layout.column(align=False)
anim = adt and adt.action
if anim:
binding_sub = col.column(align=True)
slot_sub = col.column(align=True)
# Binding selector.
row = binding_sub.row(align=True)
row.prop(adt, "action_binding", text="Binding")
row.operator("anim.binding_unassign_object", text="", icon='X')
# Slot selector.
row = slot_sub.row(align=True)
row.prop(adt, "action_slot", text="Slot")
row.operator("anim.slot_unassign_object", text="", icon='X')
binding = anim.bindings.get(adt.action_binding, None)
if binding:
binding_sub.prop(binding, "name_display", text="Name")
slot = anim.slots.get(adt.action_slot, None)
if slot:
slot_sub.prop(slot, "name_display", text="Name")
internal_sub = binding_sub.box().column(align=True)
internal_sub = slot_sub.box().column(align=True)
internal_sub.active = False
internal_sub.prop(adt, "action_binding_handle", text="handle")
if binding:
internal_sub.prop(binding, "name", text="Internal Name")
internal_sub.prop(adt, "action_slot_handle", text="handle")
if slot:
internal_sub.prop(slot, "name", text="Internal Name")
if adt:
col.prop(adt, "action_binding_name", text="ADT Binding Name")
col.prop(adt, "action_slot_name", text="ADT Slot Name")
else:
col.label(text="ADT Binding Name: -")
col.label(text="ADT Slot Name: -")
layout.separator()

@ -36,10 +36,10 @@ namespace blender::animrig {
/* Forward declarations for the types defined later in this file. */
class Layer;
class Strip;
class Binding;
class Slot;
/* Use an alias for the Binding handle type to help disambiguate function parameters. */
using binding_handle_t = decltype(::ActionBinding::handle);
/* Use an alias for the Slot handle type to help disambiguate function parameters. */
using slot_handle_t = decltype(::ActionSlot::handle);
/**
* Container of animation data for one or more animated IDs.
@ -50,7 +50,7 @@ using binding_handle_t = decltype(::ActionBinding::handle);
* Temporary limitation: each Action can only contain one Layer.
*
* Which sub-set of that data drives the animation of which ID is determined by
* which Binding is associated with that ID.
* which Slot is associated with that ID.
*
* \note This wrapper class for the `bAction` DNA struct only has functionality
* for the layered animation data. The legacy F-Curves (in `bAction::curves`)
@ -61,7 +61,7 @@ using binding_handle_t = decltype(::ActionBinding::handle);
* for both.
*
* \see AnimData::action
* \see AnimData::binding_handle
* \see AnimData::slot_handle
*/
class Action : public ::bAction {
public:
@ -76,7 +76,7 @@ class Action : public ::bAction {
/**
* Return whether this Action has any data at all.
*
* \return true when `bAction::layer_array` and `bAction::binding_array`, as well as
* \return true when `bAction::layer_array` and `bAction::slot_array`, as well as
* the legacy `curves` list, are empty.
*/
bool is_empty() const;
@ -85,7 +85,7 @@ class Action : public ::bAction {
*
* - Animation data is stored in `bAction::curves`.
* - Evaluated equally for all data-blocks that reference this Action.
* - Binding handle is ignored.
* - Slot handle is ignored.
*
* \note An empty Action is valid as both a legacy and layered Action. Code that only supports
* layered Actions should assert on `is_action_layered()`.
@ -95,7 +95,7 @@ class Action : public ::bAction {
* Return whether this is a layered Action.
*
* - Animation data is stored in `bAction::layer_array`.
* - Evaluated for data-blocks based on their binding handle.
* - Evaluated for data-blocks based on their slot handle.
*
* \note An empty Action is valid as both a legacy and layered Action.
*/
@ -125,106 +125,106 @@ class Action : public ::bAction {
*/
void layer_ensure_at_least_one();
/* Animation Binding access. */
blender::Span<const Binding *> bindings() const;
blender::MutableSpan<Binding *> bindings();
const Binding *binding(int64_t index) const;
Binding *binding(int64_t index);
/* Animation Slot access. */
blender::Span<const Slot *> slots() const;
blender::MutableSpan<Slot *> slots();
const Slot *slot(int64_t index) const;
Slot *slot(int64_t index);
/**
* Return the Binding with the given handle.
* Return the Slot with the given handle.
*
* \param handle can be `Binding::unassigned`, in which case `nullptr` is returned.
* \param handle can be `Slot::unassigned`, in which case `nullptr` is returned.
*
* \return `nullptr` when the binding cannot be found, so either the handle was
* `Binding::unassigned` or some value that does not match any Binding in this Action.
* \return `nullptr` when the slot cannot be found, so either the handle was
* `Slot::unassigned` or some value that does not match any Slot in this Action.
*/
Binding *binding_for_handle(binding_handle_t handle);
const Binding *binding_for_handle(binding_handle_t handle) const;
Slot *slot_for_handle(slot_handle_t handle);
const Slot *slot_for_handle(slot_handle_t handle) const;
/**
* Set the binding name, ensure it is unique, and propagate the new name to
* 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 binding has a
* This has to be done on the Animation level to ensure each slot has a
* unique name within the Animation.
*
* \note This does NOT ensure the first two characters match the ID type of
* this binding. This is the caller's responsibility.
* this slot. This is the caller's responsibility.
*
* \see Action::binding_name_define
* \see Action::binding_name_propagate
* \see Action::slot_name_define
* \see Action::slot_name_propagate
*/
void binding_name_set(Main &bmain, Binding &binding, StringRefNull new_name);
void slot_name_set(Main &bmain, Slot &slot, StringRefNull new_name);
/**
* Set the binding name, and ensure it is unique.
* Set the slot name, and ensure it is unique.
*
* \note This does NOT ensure the first two characters match the ID type of
* this binding. This is the caller's responsibility.
* this slot. This is the caller's responsibility.
*
* \see Action::binding_name_set
* \see Action::binding_name_propagate
* \see Action::slot_name_set
* \see Action::slot_name_propagate
*/
void binding_name_define(Binding &binding, StringRefNull new_name);
void slot_name_define(Slot &slot, StringRefNull new_name);
/**
* Update the `AnimData::action_binding_name` field of any ID that is animated by
* this Binding.
* Update the `AnimData::action_slot_name` field of any ID that is animated by
* this Slot.
*
* Should be called after `binding_name_define(binding)`. This is implemented as a separate
* Should be called after `slot_name_define(slot)`. This is implemented as a separate
* function due to the need to access `bmain`, which is available in the RNA on-property-update
* handler, but not in the RNA property setter.
*/
void binding_name_propagate(Main &bmain, const Binding &binding);
void slot_name_propagate(Main &bmain, const Slot &slot);
Binding *binding_find_by_name(StringRefNull binding_name);
Slot *slot_find_by_name(StringRefNull slot_name);
/**
* Create a new, unused Binding.
* Create a new, unused Slot.
*
* The returned binding will be suitable for any ID type. After binding to an
* The returned slot will be suitable for any ID type. After slot to an
* ID, it be limited to that ID's type.
*/
Binding &binding_add();
Slot &slot_add();
/**
* Create a new binding, named after the given ID, and limited to the ID's type.
* 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 Binding to the ID. This function
* merely initializes the Binding itself to suitable values to start animating this ID.
* Note that this assigns neither this Animation nor the new Slot to the ID. This function
* merely initializes the Slot itself to suitable values to start animating this ID.
*/
Binding &binding_add_for_id(const ID &animated_id);
Slot &slot_add_for_id(const ID &animated_id);
/**
* Ensure that an appropriate Binding exists for the given ID.
* Ensure that an appropriate Slot exists for the given ID.
*
* If a suitable Binding can be found, that Binding is returned. Otherwise,
* If a suitable Slot can be found, that Slot is returned. Otherwise,
* one is created.
*
* This is essentially a wrapper for `find_suitable_binding_for()` and
* `binding_add_for_id()`, and follows their semantics. Notably, like both of
* This is essentially a wrapper for `find_suitable_slot_for()` and
* `slot_add_for_id()`, and follows their semantics. Notably, like both of
* those methods, this Action does not need to already be assigned to the ID.
* And like `find_suitable_binding_for()`, if this Action *is* already
* assigned to the ID with a valid Binding, that Binding is returned.
* And like `find_suitable_slot_for()`, if this Action *is* already
* assigned to the ID with a valid Slot, that Slot is returned.
*
* Note that this assigns neither this Action nor the Binding to the ID. This
* merely ensures that an appropriate Binding exists.
* Note that this assigns neither this Action nor the Slot to the ID. This
* merely ensures that an appropriate Slot exists.
*
* \see `Action::find_suitable_binding_for()`
* \see `Action::binding_add_for_id()`
* \see `Action::find_suitable_slot_for()`
* \see `Action::slot_add_for_id()`
*/
Binding &binding_ensure_for_id(const ID &animated_id);
Slot &slot_ensure_for_id(const ID &animated_id);
/** Assign this animation to the ID.
*
* \param binding: The binding this ID should be animated by, may be nullptr if it is to be
* \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.
*
* \return whether the assignment was successful.
*/
bool assign_id(Binding *binding, ID &animated_id);
bool assign_id(Slot *slot, ID &animated_id);
/**
* Unassign this Animation from the animated ID.
@ -236,23 +236,23 @@ class Action : public ::bAction {
void unassign_id(ID &animated_id);
/**
* Find the binding that best matches the animated ID.
* Find the slot that best matches the animated ID.
*
* If the ID is already animated by this Animation, by matching this
* Animation's bindings with (in order):
* Animation's slots with (in order):
*
* - `animated_id.adt->binding_handle`,
* - `animated_id.adt->binding_name`,
* - `animated_id.adt->slot_handle`,
* - `animated_id.adt->slot_name`,
* - `animated_id.name`.
*
* Note that this is different from #binding_for_id, which does not use the
* binding name, and only works when this Animation is already assigned. */
Binding *find_suitable_binding_for(const ID &animated_id);
* 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 *find_suitable_slot_for(const ID &animated_id);
/**
* Return whether this Animation actually has any animation data for the given binding.
* Return whether this Animation actually has any animation data for the given slot.
*/
bool is_binding_animated(binding_handle_t binding_handle) const;
bool is_slot_animated(slot_handle_t slot_handle) const;
/**
* Get the layer that should be used for user-level keyframe insertion.
@ -268,27 +268,27 @@ class Action : public ::bAction {
int64_t find_layer_index(const Layer &layer) const;
private:
Binding &binding_allocate();
Slot &slot_allocate();
/**
* Ensure the binding name prefix matches its ID type.
* Ensure the slot name prefix matches its ID type.
*
* This ensures that the first two characters match the ID type of
* this binding.
* this slot.
*
* \see Action::binding_name_propagate
* \see Action::slot_name_propagate
*/
void binding_name_ensure_prefix(Binding &binding);
void slot_name_ensure_prefix(Slot &slot);
/**
* Set the binding's ID type to that of the animated ID, ensure the name
* 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.
*
* \note This assumes that the binding has no ID type set yet. If it does, it
* \note This assumes that the slot has no ID type set yet. If it does, it
* is considered a bug to call this function.
*/
void binding_setup_for_id(Binding &binding, const ID &animated_id);
void slot_setup_for_id(Slot &slot, const ID &animated_id);
};
static_assert(sizeof(Action) == sizeof(::bAction),
"DNA struct and its C++ wrapper must have the same size");
@ -456,44 +456,44 @@ static_assert(sizeof(Layer) == sizeof(::ActionLayer),
ENUM_OPERATORS(Layer::Flags, Layer::Flags::Enabled);
/**
* Identifier for a sub-set of the animation data inside an Animation data-block.
* Identifier for a sub-set of the animation data inside an Action.
*
* An animatable ID specifies both an `Animation*` and an `ActionBinding::handle`
* An animatable ID specifies both an `Action*` and an `ActionSlot::handle`
* to identify which F-Curves (and in the future other animation data) it will
* be animated by.
*
* This is called a 'binding' because it binds the animatable ID to the sub-set
* This is called a 'slot' because it binds the animatable ID to the sub-set
* of animation data that should animate it.
*
* \see AnimData::binding_handle
* \see AnimData::slot_handle
*/
class Binding : public ::ActionBinding {
class Slot : public ::ActionSlot {
public:
Binding();
Binding(const Binding &other);
~Binding();
Slot();
Slot(const Slot &other);
~Slot();
/**
* Update the Binding after reading it from a blend file.
* Update the Slot after reading it from a blend file.
*
* This is a low-level function and should not typically be used. It's only here to let
* blenkernel allocate the runtime struct when reading a Binding from disk, without having to
* blenkernel allocate the runtime struct when reading a Slot from disk, without having to
* share the struct definition itself. */
void blend_read_post();
/**
* Binding handle value indicating that there is no binding assigned.
* Slot handle value indicating that there is no slot assigned.
*/
constexpr static binding_handle_t unassigned = 0;
constexpr static slot_handle_t unassigned = 0;
/**
* Binding names consist of a two-character ID code, then the display name.
* Slot names consist of a two-character ID code, then the display name.
* This means that the minimum length of a valid name is 3 characters.
*/
constexpr static int name_length_min = 3;
/**
* Return the name prefix for the Binding's type.
* Return the name prefix for the Slot's type.
*
* This is the ID name prefix, so "OB" for objects, "CA" for cameras, etc.
*/
@ -506,10 +506,10 @@ class Binding : public ::ActionBinding {
*/
StringRefNull name_without_prefix() const;
/** Return whether this Binding is usable by this ID type. */
/** Return whether this Slot is usable by this ID type. */
bool is_suitable_for(const ID &animated_id) const;
/** Return whether this Binding has an `idtype` set. */
/** Return whether this Slot has an `idtype` set. */
bool has_idtype() const;
/* Flags access. */
@ -519,7 +519,7 @@ class Binding : public ::ActionBinding {
/** Selected in animation editors. */
Selected = (1 << 1),
/* When adding/removing a flag, also update the ENUM_OPERATORS() invocation,
* all the way below the Binding class. */
* all the way below the Slot class. */
};
Flags flags() const;
bool is_expanded() const;
@ -527,7 +527,7 @@ class Binding : public ::ActionBinding {
bool is_selected() const;
void set_selected(bool selected);
/** Return the set of IDs that are animated by this Binding. */
/** Return the set of IDs that are animated by this Slot. */
Span<ID *> users(Main &bmain) const;
/**
@ -538,20 +538,20 @@ class Binding : public ::ActionBinding {
* This is a low-level function, and should only be used when calling `users(bmain)` is not
* appropriate.
*
* \see Binding::users(Main &bmain)
* \see Slot::users(Main &bmain)
*/
Vector<ID *> runtime_users();
/**
* Register this ID as animated by this Binding.
* Register this ID as animated by this Slot.
*
* This is a low-level function and should not typically be used.
* Use #Action::assign_id(binding, animated_id) instead.
* Use #Action::assign_id(slot, animated_id) instead.
*/
void users_add(ID &animated_id);
/**
* Register this ID as no longer animated by this Binding.
* Register this ID as no longer animated by this Slot.
*
* This is a low-level function and should not typically be used.
* Use #Action::assign_id(nullptr, animated_id) instead.
@ -563,9 +563,9 @@ class Binding : public ::ActionBinding {
*
* This is typically not necessary, and only called from low-level code.
*
* \note This static method invalidates all user caches of all Action Bindings.
* \note This static method invalidates all user caches of all Action Slots.
*
* \see blender::animrig::internal::rebuild_binding_user_cache()
* \see blender::animrig::internal::rebuild_slot_user_cache()
*/
static void users_invalidate(Main &bmain);
@ -580,12 +580,12 @@ class Binding : public ::ActionBinding {
*/
void name_ensure_prefix();
};
static_assert(sizeof(Binding) == sizeof(::ActionBinding),
static_assert(sizeof(Slot) == sizeof(::ActionSlot),
"DNA struct and its C++ wrapper must have the same size");
ENUM_OPERATORS(Binding::Flags, Binding::Flags::Selected);
ENUM_OPERATORS(Slot::Flags, Slot::Flags::Selected);
/**
* KeyframeStrips effectively contain a bag of F-Curves for each Binding.
* KeyframeStrips effectively contain a bag of F-Curves for each Slot.
*/
class KeyframeStrip : public ::KeyframeActionStrip {
public:
@ -612,39 +612,39 @@ class KeyframeStrip : public ::KeyframeActionStrip {
ChannelBag *channelbag(int64_t index);
/**
* Find the animation channels for this binding.
* Find the animation channels for this slot.
*
* \return nullptr if there is none yet for this binding.
* \return nullptr if there is none yet for this slot.
*/
const ChannelBag *channelbag_for_binding(const Binding &binding) const;
ChannelBag *channelbag_for_binding(const Binding &binding);
const ChannelBag *channelbag_for_binding(binding_handle_t binding_handle) const;
ChannelBag *channelbag_for_binding(binding_handle_t binding_handle);
const ChannelBag *channelbag_for_slot(const Slot &slot) const;
ChannelBag *channelbag_for_slot(const Slot &slot);
const ChannelBag *channelbag_for_slot(slot_handle_t slot_handle) const;
ChannelBag *channelbag_for_slot(slot_handle_t slot_handle);
/**
* Add the animation channels for this binding.
* Add the animation channels for this slot.
*
* Should only be called when there is no `ChannelBag` for this binding yet.
* Should only be called when there is no `ChannelBag` for this slot yet.
*/
ChannelBag &channelbag_for_binding_add(const Binding &binding);
ChannelBag &channelbag_for_slot_add(const Slot &slot);
/**
* Find an FCurve for this binding + RNA path + array index combination.
* Find an FCurve for this slot + RNA path + array index combination.
*
* If it cannot be found, `nullptr` is returned.
*/
FCurve *fcurve_find(const Binding &binding, FCurveDescriptor fcurve_descriptor);
FCurve *fcurve_find(const Slot &slot, FCurveDescriptor fcurve_descriptor);
/**
* Find an FCurve for this binding + RNA path + array index combination.
* Find an FCurve for this slot + RNA path + array index combination.
*
* If it cannot be found, a new one is created.
*
* \param `prop_subtype` The subtype of the property this fcurve is for, if
* available.
*/
FCurve &fcurve_find_or_create(const Binding &binding, FCurveDescriptor fcurve_descriptor);
FCurve &fcurve_find_or_create(const Slot &slot, FCurveDescriptor fcurve_descriptor);
SingleKeyingResult keyframe_insert(const Binding &binding,
SingleKeyingResult keyframe_insert(const Slot &slot,
FCurveDescriptor fcurve_descriptor,
float2 time_value,
const KeyframeSettings &settings,
@ -657,7 +657,7 @@ template<> KeyframeStrip &Strip::as<KeyframeStrip>();
template<> const KeyframeStrip &Strip::as<KeyframeStrip>() const;
/**
* Collection of F-Curves, intended for a specific Binding handle.
* Collection of F-Curves, intended for a specific Slot handle.
*/
class ChannelBag : public ::ActionChannelBag {
public:
@ -679,18 +679,18 @@ static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag),
/**
* Assign the animation to the ID.
*
* This will will make a best-effort guess as to which binding to use, in this
* This will will make a best-effort guess as to which slot to use, in this
* order;
*
* - By binding handle.
* - By slot handle.
* - By fallback string.
* - By the ID's name (matching against the binding name).
* - If the above do not find a suitable binding, the animated ID will not
* receive any animation and the caller is responsible for creating a binding
* - By the ID's name (matching against the slot name).
* - If the above do not find a suitable slot, the animated ID will not
* receive any animation and the caller is responsible for creating a slot
* and assigning it.
*
* \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 binding found" is reached, this function
* 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.
*/
bool assign_animation(Action &anim, ID &animated_id);
@ -709,17 +709,17 @@ bool is_action_assignable_to(const bAction *dna_action, ID_Type id_code);
void unassign_animation(ID &animated_id);
/**
* Clear the animation binding of this ID.
* Clear the animation slot of this ID.
*
* `adt.binding_handle_name` is updated to reflect the current name of the
* binding, before un-assigning. This is to ensure that the stored name reflects
* the actual binding that was used, making re-binding trivial.
* `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
* the actual slot that was used, making re-slot trivial.
*
* \param animated_id: the animated ID.
*
* \note this does not clear the Animation pointer, just the binding handle.
* \note this does not clear the Animation pointer, just the slot handle.
*/
void unassign_binding(ID &animated_id);
void unassign_slot(ID &animated_id);
/**
* Return the Animation of this ID, or nullptr if it has none.
@ -727,18 +727,18 @@ void unassign_binding(ID &animated_id);
Action *get_animation(ID &animated_id);
/**
* Get the Action and the Binding that animate this ID.
* Get the Action and the Slot that animate this ID.
*
* \return One of two options:
* - pair<Action, Binding> when an Action and a Binding are assigned. In other
* words, when this ID is actually animated by this Action+Binding pair.
* - pair<Action, Slot> when an Action and a Slot are assigned. In other
* words, when this ID is actually animated by this Action+Slot pair.
* - nullopt: when this ID is not animated. This can have several causes: not
* an animatable type, no Action assigned, or no Binding assigned.
* an animatable type, no Action assigned, or no Slot assigned.
*/
std::optional<std::pair<Action *, Binding *>> get_action_binding_pair(ID &animated_id);
std::optional<std::pair<Action *, Slot *>> get_action_slot_pair(ID &animated_id);
/**
* Return the F-Curves for this specific binding handle.
* 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
@ -747,8 +747,8 @@ std::optional<std::pair<Action *, Binding *>> get_action_binding_pair(ID &animat
* The use of this function is also an indicator for code that will have to be altered when
* multi-layered animation is getting implemented.
*/
Span<FCurve *> fcurves_for_animation(Action &anim, binding_handle_t binding_handle);
Span<const FCurve *> fcurves_for_animation(const Action &anim, binding_handle_t binding_handle);
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);
/**
* Return all F-Curves in the Action.
@ -833,13 +833,13 @@ inline const blender::animrig::Layer &ActionLayer::wrap() const
return *reinterpret_cast<const blender::animrig::Layer *>(this);
}
inline blender::animrig::Binding &ActionBinding::wrap()
inline blender::animrig::Slot &ActionSlot::wrap()
{
return *reinterpret_cast<blender::animrig::Binding *>(this);
return *reinterpret_cast<blender::animrig::Slot *>(this);
}
inline const blender::animrig::Binding &ActionBinding::wrap() const
inline const blender::animrig::Slot &ActionSlot::wrap() const
{
return *reinterpret_cast<const blender::animrig::Binding *>(this);
return *reinterpret_cast<const blender::animrig::Slot *>(this);
}
inline blender::animrig::Strip &ActionStrip::wrap()

@ -21,14 +21,14 @@ namespace blender::animrig {
/**
* Top level animation evaluation function.
*
* Animate the given ID, using the animation data-block and the given binding.
* Animate the given ID, using the layered Action and the given slot.
*
* \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,
binding_handle_t binding_handle,
slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context,
bool flush_to_original);

@ -57,7 +57,7 @@ enum class SingleKeyingResult {
ID_NOT_ANIMATABLE,
NO_VALID_LAYER,
NO_VALID_STRIP,
NO_VALID_BINDING,
NO_VALID_SLOT,
CANNOT_RESOLVE_PATH,
/* Make sure to always keep this at the end of the enum. */
_KEYING_RESULT_MAX,

@ -52,14 +52,14 @@ namespace blender::animrig {
namespace {
/**
* Default name for animation bindings. The first two characters in the name indicate the ID type
* Default name for animation 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 binding is created, the prefix starts out at
* XX. Note that no code should use this XX value; use Binding::has_idtype() instead.
* Since the ID type may not be determined when the slot is created, the prefix starts out at
* XX. Note that no code should use this XX value; use Slot::has_idtype() instead.
*/
constexpr const char *binding_default_name = "Binding";
constexpr const char *binding_unbound_prefix = "XX";
constexpr const char *slot_default_name = "Slot";
constexpr const char *slot_unbound_prefix = "XX";
constexpr const char *layer_default_name = "Layer";
@ -127,19 +127,19 @@ template<typename T> static void shrink_array(T **array, int *num, const int shr
bool Action::is_empty() const
{
return this->layer_array_num == 0 && this->binding_array_num == 0 &&
return this->layer_array_num == 0 && this->slot_array_num == 0 &&
BLI_listbase_is_empty(&this->curves);
}
bool Action::is_action_legacy() const
{
/* This is a valid legacy Action only if there is no layered info. */
return this->layer_array_num == 0 && this->binding_array_num == 0;
return this->layer_array_num == 0 && this->slot_array_num == 0;
}
bool Action::is_action_layered() const
{
/* This is a valid layered Action if there is ANY layered info (because that
* takes precedence) or when there is no legacy info. */
return this->layer_array_num > 0 || this->binding_array_num > 0 ||
return this->layer_array_num > 0 || this->slot_array_num > 0 ||
BLI_listbase_is_empty(&this->curves);
}
@ -223,93 +223,91 @@ int64_t Action::find_layer_index(const Layer &layer) const
return -1;
}
blender::Span<const Binding *> Action::bindings() const
blender::Span<const Slot *> Action::slots() const
{
return blender::Span<Binding *>{reinterpret_cast<Binding **>(this->binding_array),
this->binding_array_num};
return blender::Span<Slot *>{reinterpret_cast<Slot **>(this->slot_array), this->slot_array_num};
}
blender::MutableSpan<Binding *> Action::bindings()
blender::MutableSpan<Slot *> Action::slots()
{
return blender::MutableSpan<Binding *>{reinterpret_cast<Binding **>(this->binding_array),
this->binding_array_num};
return blender::MutableSpan<Slot *>{reinterpret_cast<Slot **>(this->slot_array),
this->slot_array_num};
}
const Binding *Action::binding(const int64_t index) const
const Slot *Action::slot(const int64_t index) const
{
return &this->binding_array[index]->wrap();
return &this->slot_array[index]->wrap();
}
Binding *Action::binding(const int64_t index)
Slot *Action::slot(const int64_t index)
{
return &this->binding_array[index]->wrap();
return &this->slot_array[index]->wrap();
}
Binding *Action::binding_for_handle(const binding_handle_t handle)
Slot *Action::slot_for_handle(const slot_handle_t handle)
{
const Binding *binding = const_cast<const Action *>(this)->binding_for_handle(handle);
return const_cast<Binding *>(binding);
const Slot *slot = const_cast<const Action *>(this)->slot_for_handle(handle);
return const_cast<Slot *>(slot);
}
const Binding *Action::binding_for_handle(const binding_handle_t handle) const
const Slot *Action::slot_for_handle(const slot_handle_t handle) const
{
if (handle == Binding::unassigned) {
if (handle == Slot::unassigned) {
return nullptr;
}
/* TODO: implement hash-map lookup. */
for (const Binding *binding : bindings()) {
if (binding->handle == handle) {
return binding;
for (const Slot *slot : slots()) {
if (slot->handle == handle) {
return slot;
}
}
return nullptr;
}
static void anim_binding_name_ensure_unique(Action &animation, Binding &binding)
static void anim_slot_name_ensure_unique(Action &animation, 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;
Binding &binding;
Slot &slot;
};
DupNameCheckData check_data = {animation, binding};
DupNameCheckData check_data = {animation, slot};
auto check_name_is_used = [](void *arg, const char *name) -> bool {
DupNameCheckData *data = static_cast<DupNameCheckData *>(arg);
for (const Binding *binding : data->anim.bindings()) {
if (binding == &data->binding) {
/* Don't compare against the binding that's being renamed. */
for (const Slot *slot : data->anim.slots()) {
if (slot == &data->slot) {
/* Don't compare against the slot that's being renamed. */
continue;
}
if (STREQ(binding->name, name)) {
if (STREQ(slot->name, name)) {
return true;
}
}
return false;
};
BLI_uniquename_cb(check_name_is_used, &check_data, "", '.', binding.name, sizeof(binding.name));
BLI_uniquename_cb(check_name_is_used, &check_data, "", '.', slot.name, sizeof(slot.name));
}
/* TODO: maybe this function should only set the 'name without prefix' aka the 'display name'. That
* way only `this->id_type` is responsible for the prefix. I (Sybren) think that's easier to
* determine when the code is a bit more mature, and we can see what the majority of the calls to
* this function actually do/need. */
void Action::binding_name_set(Main &bmain, Binding &binding, const StringRefNull new_name)
void Action::slot_name_set(Main &bmain, Slot &slot, const StringRefNull new_name)
{
this->binding_name_define(binding, new_name);
this->binding_name_propagate(bmain, binding);
this->slot_name_define(slot, new_name);
this->slot_name_propagate(bmain, slot);
}
void Action::binding_name_define(Binding &binding, const StringRefNull new_name)
void Action::slot_name_define(Slot &slot, const StringRefNull new_name)
{
BLI_assert_msg(
StringRef(new_name).size() >= Binding::name_length_min,
"Animation Bindings must be large enough for a 2-letter ID code + the display name");
STRNCPY_UTF8(binding.name, new_name.c_str());
anim_binding_name_ensure_unique(*this, binding);
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");
STRNCPY_UTF8(slot.name, new_name.c_str());
anim_slot_name_ensure_unique(*this, slot);
}
void Action::binding_name_propagate(Main &bmain, const Binding &binding)
void Action::slot_name_propagate(Main &bmain, const Slot &slot)
{
/* Just loop over all animatable IDs in the main database. */
ListBase *lb;
@ -327,58 +325,57 @@ void Action::binding_name_propagate(Main &bmain, const Binding &binding)
/* Not animated by this Animation. */
continue;
}
if (adt->binding_handle != binding.handle) {
/* Not animated by this Binding. */
if (adt->slot_handle != slot.handle) {
/* Not animated by this Slot. */
continue;
}
/* Ensure the Binding name on the AnimData is correct. */
STRNCPY_UTF8(adt->binding_name, binding.name);
/* Ensure the Slot name on the AnimData is correct. */
STRNCPY_UTF8(adt->slot_name, slot.name);
}
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_LISTBASE_END;
}
Binding *Action::binding_find_by_name(const StringRefNull binding_name)
Slot *Action::slot_find_by_name(const StringRefNull slot_name)
{
for (Binding *binding : bindings()) {
if (STREQ(binding->name, binding_name.c_str())) {
return binding;
for (Slot *slot : slots()) {
if (STREQ(slot->name, slot_name.c_str())) {
return slot;
}
}
return nullptr;
}
Binding &Action::binding_allocate()
Slot &Action::slot_allocate()
{
Binding &binding = *MEM_new<Binding>(__func__);
this->last_binding_handle++;
BLI_assert_msg(this->last_binding_handle > 0, "Animation Binding handle overflow");
binding.handle = this->last_binding_handle;
Slot &slot = *MEM_new<Slot>(__func__);
this->last_slot_handle++;
BLI_assert_msg(this->last_slot_handle > 0, "Animation Slot handle overflow");
slot.handle = this->last_slot_handle;
/* Set the default flags. These cannot be set via the 'DNA defaults' system,
* as that would require knowing which bit corresponds with which flag. That's
* only known to the C++ wrapper code. */
binding.set_expanded(true);
return binding;
slot.set_expanded(true);
return slot;
}
Binding &Action::binding_add()
Slot &Action::slot_add()
{
Binding &binding = this->binding_allocate();
Slot &slot = this->slot_allocate();
/* Assign the default name and the 'unbound' name prefix. */
STRNCPY_UTF8(binding.name, binding_unbound_prefix);
BLI_strncpy_utf8(binding.name + 2, DATA_(binding_default_name), ARRAY_SIZE(binding.name) - 2);
STRNCPY_UTF8(slot.name, slot_unbound_prefix);
BLI_strncpy_utf8(slot.name + 2, DATA_(slot_default_name), ARRAY_SIZE(slot.name) - 2);
/* Append the Binding to the animation data-block. */
grow_array_and_append<::ActionBinding *>(
&this->binding_array, &this->binding_array_num, &binding);
/* Append the Slot to the Action. */
grow_array_and_append<::ActionSlot *>(&this->slot_array, &this->slot_array_num, &slot);
anim_binding_name_ensure_unique(*this, binding);
anim_slot_name_ensure_unique(*this, slot);
/* If this is the first binding in this Action, it means that it could have
/* 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
* non-zero while it should be zero for layered Actions.
*
@ -386,68 +383,68 @@ Binding &Action::binding_add()
* there is no check for whether this is actually the first layer. */
this->idroot = 0;
return binding;
return slot;
}
Binding &Action::binding_add_for_id(const ID &animated_id)
Slot &Action::slot_add_for_id(const ID &animated_id)
{
Binding &binding = this->binding_add();
Slot &slot = this->slot_add();
binding.idtype = GS(animated_id.name);
this->binding_name_define(binding, animated_id.name);
slot.idtype = GS(animated_id.name);
this->slot_name_define(slot, animated_id.name);
/* No need to call anim.binding_name_propagate() as nothing will be using
* this brand new Binding yet. */
/* No need to call anim.slot_name_propagate() as nothing will be using
* this brand new Slot yet. */
return binding;
return slot;
}
Binding &Action::binding_ensure_for_id(const ID &animated_id)
Slot &Action::slot_ensure_for_id(const ID &animated_id)
{
if (Binding *binding = this->find_suitable_binding_for(animated_id)) {
return *binding;
if (Slot *slot = this->find_suitable_slot_for(animated_id)) {
return *slot;
}
return this->binding_add_for_id(animated_id);
return this->slot_add_for_id(animated_id);
}
Binding *Action::find_suitable_binding_for(const ID &animated_id)
Slot *Action::find_suitable_slot_for(const ID &animated_id)
{
AnimData *adt = BKE_animdata_from_id(&animated_id);
/* The binding handle is only valid when this action has already been
/* The slot handle is only valid when this action has already been
* assigned. Otherwise it's meaningless. */
if (adt && adt->action == this) {
Binding *binding = this->binding_for_handle(adt->binding_handle);
if (binding && binding->is_suitable_for(animated_id)) {
return binding;
Slot *slot = this->slot_for_handle(adt->slot_handle);
if (slot && slot->is_suitable_for(animated_id)) {
return slot;
}
}
/* Try the binding name from the AnimData, if it is set. */
if (adt && adt->binding_name[0]) {
Binding *binding = this->binding_find_by_name(adt->binding_name);
if (binding && binding->is_suitable_for(animated_id)) {
return binding;
/* Try the slot name from the AnimData, if it is set. */
if (adt && adt->slot_name[0]) {
Slot *slot = this->slot_find_by_name(adt->slot_name);
if (slot && slot->is_suitable_for(animated_id)) {
return slot;
}
}
/* As a last resort, search for the ID name. */
Binding *binding = this->binding_find_by_name(animated_id.name);
if (binding && binding->is_suitable_for(animated_id)) {
return binding;
Slot *slot = this->slot_find_by_name(animated_id.name);
if (slot && slot->is_suitable_for(animated_id)) {
return slot;
}
return nullptr;
}
bool Action::is_binding_animated(const binding_handle_t binding_handle) const
bool Action::is_slot_animated(const slot_handle_t slot_handle) const
{
if (binding_handle == Binding::unassigned) {
if (slot_handle == Slot::unassigned) {
return false;
}
Span<const FCurve *> fcurves = fcurves_for_animation(*this, binding_handle);
Span<const FCurve *> fcurves = fcurves_for_animation(*this, slot_handle);
return !fcurves.is_empty();
}
@ -462,7 +459,7 @@ Layer *Action::get_layer_for_keyframing()
return this->layer(0);
}
bool Action::assign_id(Binding *binding, ID &animated_id)
bool Action::assign_id(Slot *slot, ID &animated_id)
{
AnimData *adt = BKE_animdata_ensure_id(&animated_id);
if (!adt) {
@ -475,24 +472,24 @@ bool Action::assign_id(Binding *binding, ID &animated_id)
return false;
}
/* Check that the new Binding is suitable, before changing `adt`. */
if (binding && !binding->is_suitable_for(animated_id)) {
/* Check that the new Slot is suitable, before changing `adt`. */
if (slot && !slot->is_suitable_for(animated_id)) {
return false;
}
/* Unassign any previously-assigned Binding. */
Binding *binding_to_unassign = this->binding_for_handle(adt->binding_handle);
if (binding_to_unassign) {
binding_to_unassign->users_remove(animated_id);
/* Unassign any previously-assigned Slot. */
Slot *slot_to_unassign = this->slot_for_handle(adt->slot_handle);
if (slot_to_unassign) {
slot_to_unassign->users_remove(animated_id);
/* Before unassigning, make sure that the stored Binding name is up to date. The binding name
/* Before unassigning, make sure that the stored Slot name is up to date. The slot name
* might have changed in a way that wasn't copied into the ADT yet (for example when the
* Action is linked from another file), so better copy the name to be sure that it can be
* transparently reassigned later.
*
* TODO: Replace this with a BLI_assert() that the name is as expected, and "simply" ensure
* this name is always correct. */
STRNCPY_UTF8(adt->binding_name, binding_to_unassign->name);
STRNCPY_UTF8(adt->slot_name, slot_to_unassign->name);
}
/* Assign the Action itself. */
@ -504,37 +501,37 @@ bool Action::assign_id(Binding *binding, ID &animated_id)
adt->action = this;
}
/* Assign the Binding. */
if (binding) {
this->binding_setup_for_id(*binding, animated_id);
adt->binding_handle = binding->handle;
binding->users_add(animated_id);
/* Assign the Slot. */
if (slot) {
this->slot_setup_for_id(*slot, animated_id);
adt->slot_handle = slot->handle;
slot->users_add(animated_id);
/* Always make sure the ID's binding name matches the assigned binding. */
STRNCPY_UTF8(adt->binding_name, binding->name);
/* Always make sure the ID's slot name matches the assigned slot. */
STRNCPY_UTF8(adt->slot_name, slot->name);
}
else {
adt->binding_handle = Binding::unassigned;
adt->slot_handle = Slot::unassigned;
}
return true;
}
void Action::binding_name_ensure_prefix(Binding &binding)
void Action::slot_name_ensure_prefix(Slot &slot)
{
binding.name_ensure_prefix();
anim_binding_name_ensure_unique(*this, binding);
slot.name_ensure_prefix();
anim_slot_name_ensure_unique(*this, slot);
}
void Action::binding_setup_for_id(Binding &binding, const ID &animated_id)
void Action::slot_setup_for_id(Slot &slot, const ID &animated_id)
{
if (binding.has_idtype()) {
BLI_assert(binding.idtype == GS(animated_id.name));
if (slot.has_idtype()) {
BLI_assert(slot.idtype == GS(animated_id.name));
return;
}
binding.idtype = GS(animated_id.name);
this->binding_name_ensure_prefix(binding);
slot.idtype = GS(animated_id.name);
this->slot_name_ensure_prefix(slot);
}
void Action::unassign_id(ID &animated_id)
@ -543,7 +540,7 @@ void Action::unassign_id(ID &animated_id)
BLI_assert_msg(adt, "ID is not animated at all");
BLI_assert_msg(adt->action == this, "ID is not assigned to this Animation");
/* Unassign the Binding first. */
/* Unassign the Slot first. */
this->assign_id(nullptr, animated_id);
/* Unassign the Action itself. */
@ -632,105 +629,105 @@ int64_t Layer::find_strip_index(const Strip &strip) const
return -1;
}
/* ----- ActionBinding implementation ----------- */
/* ----- ActionSlot implementation ----------- */
Binding::Binding()
Slot::Slot()
{
memset(this, 0, sizeof(*this));
this->runtime = MEM_new<BindingRuntime>(__func__);
this->runtime = MEM_new<SlotRuntime>(__func__);
}
Binding::Binding(const Binding &other)
Slot::Slot(const Slot &other)
{
memset(this, 0, sizeof(*this));
STRNCPY(this->name, other.name);
this->idtype = other.idtype;
this->handle = other.handle;
this->runtime = MEM_new<BindingRuntime>(__func__);
this->runtime = MEM_new<SlotRuntime>(__func__);
}
Binding::~Binding()
Slot::~Slot()
{
MEM_delete(this->runtime);
}
void Binding::blend_read_post()
void Slot::blend_read_post()
{
BLI_assert(!this->runtime);
this->runtime = MEM_new<BindingRuntime>(__func__);
this->runtime = MEM_new<SlotRuntime>(__func__);
}
bool Binding::is_suitable_for(const ID &animated_id) const
bool Slot::is_suitable_for(const ID &animated_id) const
{
if (!this->has_idtype()) {
/* Without specific ID type set, this Binding can animate any ID. */
/* Without specific ID type set, this Slot can animate any ID. */
return true;
}
/* Check that the ID type is compatible with this binding. */
/* Check that the ID type is compatible with this slot. */
const int animated_idtype = GS(animated_id.name);
return this->idtype == animated_idtype;
}
bool Binding::has_idtype() const
bool Slot::has_idtype() const
{
return this->idtype != 0;
}
Binding::Flags Binding::flags() const
Slot::Flags Slot::flags() const
{
return static_cast<Binding::Flags>(this->binding_flags);
return static_cast<Slot::Flags>(this->slot_flags);
}
bool Binding::is_expanded() const
bool Slot::is_expanded() const
{
return this->binding_flags & uint8_t(Flags::Expanded);
return this->slot_flags & uint8_t(Flags::Expanded);
}
void Binding::set_expanded(const bool expanded)
void Slot::set_expanded(const bool expanded)
{
if (expanded) {
this->binding_flags |= uint8_t(Flags::Expanded);
this->slot_flags |= uint8_t(Flags::Expanded);
}
else {
this->binding_flags &= ~(uint8_t(Flags::Expanded));
this->slot_flags &= ~(uint8_t(Flags::Expanded));
}
}
bool Binding::is_selected() const
bool Slot::is_selected() const
{
return this->binding_flags & uint8_t(Flags::Selected);
return this->slot_flags & uint8_t(Flags::Selected);
}
void Binding::set_selected(const bool selected)
void Slot::set_selected(const bool selected)
{
if (selected) {
this->binding_flags |= uint8_t(Flags::Selected);
this->slot_flags |= uint8_t(Flags::Selected);
}
else {
this->binding_flags &= ~(uint8_t(Flags::Selected));
this->slot_flags &= ~(uint8_t(Flags::Selected));
}
}
Span<ID *> Binding::users(Main &bmain) const
Span<ID *> Slot::users(Main &bmain) const
{
if (bmain.is_action_binding_to_id_map_dirty) {
internal::rebuild_binding_user_cache(bmain);
if (bmain.is_action_slot_to_id_map_dirty) {
internal::rebuild_slot_user_cache(bmain);
}
BLI_assert(this->runtime);
return this->runtime->users.as_span();
}
Vector<ID *> Binding::runtime_users()
Vector<ID *> Slot::runtime_users()
{
BLI_assert_msg(this->runtime, "Binding::runtime should always be allocated");
BLI_assert_msg(this->runtime, "Slot::runtime should always be allocated");
return this->runtime->users;
}
void Binding::users_add(ID &animated_id)
void Slot::users_add(ID &animated_id)
{
BLI_assert(this->runtime);
this->runtime->users.append_non_duplicates(&animated_id);
}
void Binding::users_remove(ID &animated_id)
void Slot::users_remove(ID &animated_id)
{
BLI_assert(this->runtime);
Vector<ID *> &users = this->runtime->users;
@ -743,9 +740,9 @@ void Binding::users_remove(ID &animated_id)
users.remove_and_reorder(vector_index);
}
void Binding::users_invalidate(Main &bmain)
void Slot::users_invalidate(Main &bmain)
{
bmain.is_action_binding_to_id_map_dirty = true;
bmain.is_action_slot_to_id_map_dirty = true;
}
/* ----- Functions ----------- */
@ -754,8 +751,8 @@ bool assign_animation(Action &anim, ID &animated_id)
{
unassign_animation(animated_id);
Binding *binding = anim.find_suitable_binding_for(animated_id);
return anim.assign_id(binding, animated_id);
Slot *slot = anim.find_suitable_slot_for(animated_id);
return anim.assign_id(slot, animated_id);
}
bool is_action_assignable_to(const bAction *dna_action, const ID_Type id_code)
@ -791,22 +788,22 @@ void unassign_animation(ID &animated_id)
anim->unassign_id(animated_id);
}
void unassign_binding(ID &animated_id)
void unassign_slot(ID &animated_id)
{
AnimData *adt = BKE_animdata_from_id(&animated_id);
BLI_assert_msg(adt, "Cannot unassign an Action Binding from a non-animated ID.");
BLI_assert_msg(adt, "Cannot unassign an Action Slot from a non-animated ID.");
if (!adt) {
return;
}
if (!adt->action) {
/* Nothing assigned. */
BLI_assert_msg(adt->binding_handle == Binding::unassigned,
"Binding handle should be 'unassigned' when no Action is assigned");
BLI_assert_msg(adt->slot_handle == Slot::unassigned,
"Slot handle should be 'unassigned' when no Action is assigned");
return;
}
/* Assign the 'nullptr' binding, effectively unassigning it. */
/* Assign the 'nullptr' slot, effectively unassigning it. */
Action &action = adt->action->wrap();
action.assign_id(nullptr, animated_id);
}
@ -824,7 +821,7 @@ Action *get_animation(ID &animated_id)
return &adt->action->wrap();
}
std::optional<std::pair<Action *, Binding *>> get_action_binding_pair(ID &animated_id)
std::optional<std::pair<Action *, Slot *>> get_action_slot_pair(ID &animated_id)
{
AnimData *adt = BKE_animdata_from_id(&animated_id);
if (!adt || !adt->action) {
@ -833,19 +830,19 @@ std::optional<std::pair<Action *, Binding *>> get_action_binding_pair(ID &animat
}
Action &action = adt->action->wrap();
Binding *binding = action.binding_for_handle(adt->binding_handle);
if (!binding) {
Slot *slot = action.slot_for_handle(adt->slot_handle);
if (!slot) {
/* Will not receive any animation from this Action. */
return std::nullopt;
}
return std::make_pair(&action, binding);
return std::make_pair(&action, slot);
}
std::string Binding::name_prefix_for_idtype() const
std::string Slot::name_prefix_for_idtype() const
{
if (!this->has_idtype()) {
return binding_unbound_prefix;
return slot_unbound_prefix;
}
char name[3] = {0};
@ -853,7 +850,7 @@ std::string Binding::name_prefix_for_idtype() const
return name;
}
StringRefNull Binding::name_without_prefix() const
StringRefNull Slot::name_without_prefix() const
{
BLI_assert(StringRef(this->name).size() >= name_length_min);
@ -864,7 +861,7 @@ StringRefNull Binding::name_without_prefix() const
return this->name + 2;
}
void Binding::name_ensure_prefix()
void Slot::name_ensure_prefix()
{
BLI_assert(StringRef(this->name).size() >= name_length_min);
@ -876,8 +873,8 @@ void Binding::name_ensure_prefix()
if (!this->has_idtype()) {
/* A zero idtype is not going to convert to a two-character string, so we
* need to explicitly assign the default prefix. */
this->name[0] = binding_unbound_prefix[0];
this->name[1] = binding_unbound_prefix[1];
this->name[0] = slot_unbound_prefix[0];
this->name[1] = slot_unbound_prefix[1];
return;
}
@ -954,8 +951,8 @@ KeyframeStrip::KeyframeStrip(const KeyframeStrip &other)
KeyframeStrip::~KeyframeStrip()
{
for (ChannelBag *channelbag_for_binding : this->channelbags()) {
MEM_delete(channelbag_for_binding);
for (ChannelBag *channelbag_for_slot : this->channelbags()) {
MEM_delete(channelbag_for_slot);
}
MEM_SAFE_FREE(this->channelbags_array);
this->channelbags_array_num = 0;
@ -1001,38 +998,37 @@ ChannelBag *KeyframeStrip::channelbag(const int64_t index)
{
return &this->channelbags_array[index]->wrap();
}
const ChannelBag *KeyframeStrip::channelbag_for_binding(
const binding_handle_t binding_handle) const
const ChannelBag *KeyframeStrip::channelbag_for_slot(const slot_handle_t slot_handle) const
{
for (const ChannelBag *channels : this->channelbags()) {
if (channels->binding_handle == binding_handle) {
if (channels->slot_handle == slot_handle) {
return channels;
}
}
return nullptr;
}
ChannelBag *KeyframeStrip::channelbag_for_binding(const binding_handle_t binding_handle)
ChannelBag *KeyframeStrip::channelbag_for_slot(const slot_handle_t slot_handle)
{
const auto *const_this = const_cast<const KeyframeStrip *>(this);
const auto *const_channels = const_this->channelbag_for_binding(binding_handle);
const auto *const_channels = const_this->channelbag_for_slot(slot_handle);
return const_cast<ChannelBag *>(const_channels);
}
const ChannelBag *KeyframeStrip::channelbag_for_binding(const Binding &binding) const
const ChannelBag *KeyframeStrip::channelbag_for_slot(const Slot &slot) const
{
return this->channelbag_for_binding(binding.handle);
return this->channelbag_for_slot(slot.handle);
}
ChannelBag *KeyframeStrip::channelbag_for_binding(const Binding &binding)
ChannelBag *KeyframeStrip::channelbag_for_slot(const Slot &slot)
{
return this->channelbag_for_binding(binding.handle);
return this->channelbag_for_slot(slot.handle);
}
ChannelBag &KeyframeStrip::channelbag_for_binding_add(const Binding &binding)
ChannelBag &KeyframeStrip::channelbag_for_slot_add(const Slot &slot)
{
BLI_assert_msg(channelbag_for_binding(binding) == nullptr,
"Cannot add chans-for-binding for already-registered binding");
BLI_assert_msg(channelbag_for_slot(slot) == nullptr,
"Cannot add chans-for-slot for already-registered slot");
ChannelBag &channels = MEM_new<ActionChannelBag>(__func__)->wrap();
channels.binding_handle = binding.handle;
channels.slot_handle = slot.handle;
grow_array_and_append<ActionChannelBag *>(
&this->channelbags_array, &this->channelbags_array_num, &channels);
@ -1040,10 +1036,9 @@ ChannelBag &KeyframeStrip::channelbag_for_binding_add(const Binding &binding)
return channels;
}
FCurve *KeyframeStrip::fcurve_find(const Binding &binding,
const FCurveDescriptor fcurve_descriptor)
FCurve *KeyframeStrip::fcurve_find(const Slot &slot, const FCurveDescriptor fcurve_descriptor)
{
ChannelBag *channels = this->channelbag_for_binding(binding);
ChannelBag *channels = this->channelbag_for_slot(slot);
if (channels == nullptr) {
return nullptr;
}
@ -1063,18 +1058,18 @@ FCurve *KeyframeStrip::fcurve_find(const Binding &binding,
return nullptr;
}
FCurve &KeyframeStrip::fcurve_find_or_create(const Binding &binding,
FCurve &KeyframeStrip::fcurve_find_or_create(const Slot &slot,
const FCurveDescriptor fcurve_descriptor)
{
if (FCurve *existing_fcurve = this->fcurve_find(binding, fcurve_descriptor)) {
if (FCurve *existing_fcurve = this->fcurve_find(slot, fcurve_descriptor)) {
return *existing_fcurve;
}
FCurve *new_fcurve = create_fcurve_for_channel(fcurve_descriptor);
ChannelBag *channels = this->channelbag_for_binding(binding);
ChannelBag *channels = this->channelbag_for_slot(slot);
if (channels == nullptr) {
channels = &this->channelbag_for_binding_add(binding);
channels = &this->channelbag_for_slot_add(slot);
}
if (channels->fcurve_array_num == 0) {
@ -1085,7 +1080,7 @@ FCurve &KeyframeStrip::fcurve_find_or_create(const Binding &binding,
return *new_fcurve;
}
SingleKeyingResult KeyframeStrip::keyframe_insert(const Binding &binding,
SingleKeyingResult KeyframeStrip::keyframe_insert(const Slot &slot,
const FCurveDescriptor fcurve_descriptor,
const float2 time_value,
const KeyframeSettings &settings,
@ -1094,25 +1089,25 @@ SingleKeyingResult KeyframeStrip::keyframe_insert(const Binding &binding,
/* Get the fcurve, or create one if it doesn't exist and the keying flags
* allow. */
FCurve *fcurve = key_insertion_may_create_fcurve(insert_key_flags) ?
&this->fcurve_find_or_create(binding, fcurve_descriptor) :
this->fcurve_find(binding, fcurve_descriptor);
&this->fcurve_find_or_create(slot, fcurve_descriptor) :
this->fcurve_find(slot, fcurve_descriptor);
if (!fcurve) {
std::fprintf(stderr,
"FCurve %s[%d] for binding %s was not created due to either the Only Insert "
"FCurve %s[%d] for slot %s was not created due to either the Only Insert "
"Available setting or Replace keyframing mode.\n",
fcurve_descriptor.rna_path.c_str(),
fcurve_descriptor.array_index,
binding.name);
slot.name);
return SingleKeyingResult::CANNOT_CREATE_FCURVE;
}
if (!BKE_fcurve_is_keyframable(fcurve)) {
/* TODO: handle this properly, in a way that can be communicated to the user. */
std::fprintf(stderr,
"FCurve %s[%d] for binding %s doesn't allow inserting keys.\n",
"FCurve %s[%d] for slot %s doesn't allow inserting keys.\n",
fcurve_descriptor.rna_path.c_str(),
fcurve_descriptor.array_index,
binding.name);
slot.name);
return SingleKeyingResult::FCURVE_NOT_KEYFRAMEABLE;
}
@ -1121,10 +1116,10 @@ SingleKeyingResult KeyframeStrip::keyframe_insert(const Binding &binding,
if (insert_vert_result != SingleKeyingResult::SUCCESS) {
std::fprintf(stderr,
"Could not insert key into FCurve %s[%d] for binding %s.\n",
"Could not insert key into FCurve %s[%d] for slot %s.\n",
fcurve_descriptor.rna_path.c_str(),
fcurve_descriptor.array_index,
binding.name);
slot.name);
return insert_vert_result;
}
@ -1135,7 +1130,7 @@ SingleKeyingResult KeyframeStrip::keyframe_insert(const Binding &binding,
ChannelBag::ChannelBag(const ChannelBag &other)
{
this->binding_handle = other.binding_handle;
this->slot_handle = other.slot_handle;
this->fcurve_array_num = other.fcurve_array_num;
this->fcurve_array = MEM_cnew_array<FCurve *>(other.fcurve_array_num, __func__);
@ -1185,9 +1180,9 @@ 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 binding_handle_t binding_handle)
const slot_handle_t slot_handle)
{
if (binding_handle == Binding::unassigned) {
if (slot_handle == Slot::unassigned) {
return nullptr;
}
@ -1196,7 +1191,7 @@ static const animrig::ChannelBag *channelbag_for_animation(const Action &anim,
switch (strip->type()) {
case animrig::Strip::Type::Keyframe: {
const animrig::KeyframeStrip &key_strip = strip->as<animrig::KeyframeStrip>();
const animrig::ChannelBag *bag = key_strip.channelbag_for_binding(binding_handle);
const animrig::ChannelBag *bag = key_strip.channelbag_for_slot(slot_handle);
if (bag) {
return bag;
}
@ -1208,27 +1203,25 @@ static const animrig::ChannelBag *channelbag_for_animation(const Action &anim,
return nullptr;
}
static animrig::ChannelBag *channelbag_for_animation(Action &anim,
const binding_handle_t binding_handle)
static animrig::ChannelBag *channelbag_for_animation(Action &anim, const slot_handle_t slot_handle)
{
const animrig::ChannelBag *const_bag = channelbag_for_animation(const_cast<const Action &>(anim),
binding_handle);
slot_handle);
return const_cast<animrig::ChannelBag *>(const_bag);
}
Span<FCurve *> fcurves_for_animation(Action &anim, const binding_handle_t binding_handle)
Span<FCurve *> fcurves_for_animation(Action &anim, const slot_handle_t slot_handle)
{
animrig::ChannelBag *bag = channelbag_for_animation(anim, binding_handle);
animrig::ChannelBag *bag = channelbag_for_animation(anim, slot_handle);
if (!bag) {
return {};
}
return bag->fcurves();
}
Span<const FCurve *> fcurves_for_animation(const Action &anim,
const binding_handle_t binding_handle)
Span<const FCurve *> fcurves_for_animation(const Action &anim, const slot_handle_t slot_handle)
{
const animrig::ChannelBag *bag = channelbag_for_animation(anim, binding_handle);
const animrig::ChannelBag *bag = channelbag_for_animation(anim, slot_handle);
if (!bag) {
return {};
}

@ -5,7 +5,7 @@
/** \file
* \ingroup animrig
*
* \brief Internal C++ functions to deal with Actions, Bindings, and their runtime data.
* \brief Internal C++ functions to deal with Actions, Slots, and their runtime data.
*/
#include "BKE_anim_data.hh"
@ -21,28 +21,28 @@
namespace blender::animrig::internal {
/**
* Rebuild the binding user cache for a specific bmain.
* Rebuild the slot user cache for a specific bmain.
*/
void rebuild_binding_user_cache(Main &bmain)
void rebuild_slot_user_cache(Main &bmain)
{
/* Loop over all Actions and clear their bindings' user cache. */
/* Loop over all Actions and clear their slots' user cache. */
LISTBASE_FOREACH (bAction *, dna_action, &bmain.actions) {
Action &action = dna_action->wrap();
for (Binding *binding : action.bindings()) {
BLI_assert_msg(binding->runtime, "Binding::runtime should always be allocated");
binding->runtime->users.clear();
for (Slot *slot : action.slots()) {
BLI_assert_msg(slot->runtime, "Slot::runtime should always be allocated");
slot->runtime->users.clear();
}
}
/* Mark all Bindings as clear. This is a bit of a lie, because the code below still has to run.
* However, this is a necessity to make the `binding.users_add(*id)` call work without triggering
/* Mark all Slots as clear. This is a bit of a lie, because the code below still has to run.
* However, this is a necessity to make the `slot.users_add(*id)` call work without triggering
* an infinite recursion.
*
* The alternative would be to go around the `binding.users_add()` function and access the
* The alternative would be to go around the `slot.users_add()` function and access the
* runtime directly, but this is IMO a bit cleaner. */
bmain.is_action_binding_to_id_map_dirty = false;
bmain.is_action_slot_to_id_map_dirty = false;
/* Loop over all IDs to cache their binding usage. */
/* Loop over all IDs to cache their slot usage. */
ListBase *ids_of_idtype;
ID *id;
FOREACH_MAIN_LISTBASE_BEGIN (&bmain, ids_of_idtype) {
@ -55,13 +55,13 @@ void rebuild_binding_user_cache(Main &bmain)
FOREACH_MAIN_LISTBASE_ID_BEGIN (ids_of_idtype, id) {
BLI_assert(id_can_have_animdata(id));
std::optional<std::pair<Action *, Binding *>> action_binding = get_action_binding_pair(*id);
if (!action_binding) {
std::optional<std::pair<Action *, Slot *>> action_slot = get_action_slot_pair(*id);
if (!action_slot) {
continue;
}
Binding &binding = *action_binding->second;
binding.users_add(*id);
Slot &slot = *action_slot->second;
slot.users_add(*id);
}
FOREACH_MAIN_LISTBASE_ID_END;
}

@ -5,7 +5,7 @@
/** \file
* \ingroup animrig
*
* \brief Internal C++ functions to deal with Actions, Bindings, and their runtime data.
* \brief Internal C++ functions to deal with Actions, Slots, and their runtime data.
*/
#pragma once
@ -20,12 +20,12 @@ namespace blender::animrig {
* Not placed in the 'internal' namespace, as this type is forward-declared in
* DNA_action_types.h, and that shouldn't reference the internal namespace.
*/
class BindingRuntime {
class SlotRuntime {
public:
/**
* Cache of pointers to the IDs that are animated by this binding.
* Cache of pointers to the IDs that are animated by this slot.
*
* Note that this is a vector for simplicity, as the majority of the bindings
* Note that this is a vector for simplicity, as the majority of the slots
* will have zero or one user. Semantically it's treated as a set: order
* doesn't matter, and it has no duplicate entries.
*
@ -37,11 +37,11 @@ class BindingRuntime {
namespace internal {
/**
* Rebuild the BindingRuntime::users cache of all Bindings in all Actions.
* Rebuild the SlotRuntime::users cache of all Slots in all Actions.
*
* The reason that all binding users are re-cached at once is two-fold:
* The reason that all slot users are re-cached at once is two-fold:
*
* 1. Regardless of how many binding caches are rebuilt, this function will need
* 1. Regardless of how many slot caches are rebuilt, this function will need
* to loop over all IDs anyway.
* 2. Deletion of IDs may be hard to detect otherwise. This is a bit of a weak
* argument, as if this is not implemented properly (i.e. not un-assigning
@ -49,7 +49,7 @@ namespace internal {
* rebuild will not be triggered. In any case, because the rebuild is global,
* any subsequent call at least ensures correctness even with such bugs.
*/
void rebuild_binding_user_cache(Main &bmain);
void rebuild_slot_user_cache(Main &bmain);
} // namespace internal

@ -140,10 +140,10 @@ 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);
Binding &binding = anim->binding_add();
strip.as<KeyframeStrip>().keyframe_insert(binding, {"location", 0}, {1.0f, 47.0f}, settings);
Slot &slot = anim->slot_add();
strip.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
another_strip.as<KeyframeStrip>().keyframe_insert(
binding, {"location", 0}, {1.0f, 47.0f}, settings);
slot, {"location", 0}, {1.0f, 47.0f}, settings);
}
TEST_F(ActionLayersTest, remove_strip)
@ -155,10 +155,10 @@ TEST_F(ActionLayersTest, remove_strip)
/* Add some keys to check that also the strip data is freed correctly. */
const KeyframeSettings settings = get_keyframe_settings(false);
Binding &binding = anim->binding_add();
strip0.as<KeyframeStrip>().keyframe_insert(binding, {"location", 0}, {1.0f, 47.0f}, settings);
strip1.as<KeyframeStrip>().keyframe_insert(binding, {"location", 0}, {1.0f, 47.0f}, settings);
strip2.as<KeyframeStrip>().keyframe_insert(binding, {"location", 0}, {1.0f, 47.0f}, settings);
Slot &slot = anim->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);
EXPECT_TRUE(layer.strip_remove(strip1));
EXPECT_EQ(2, layer.strips().size());
@ -191,96 +191,96 @@ TEST_F(ActionLayersTest, add_remove_strip_of_concrete_type)
EXPECT_TRUE(layer.strip_remove(key_strip));
}
TEST_F(ActionLayersTest, add_binding)
TEST_F(ActionLayersTest, add_slot)
{
{ /* Creating an 'unused' Binding should just be called 'Binding'. */
Binding &binding = anim->binding_add();
EXPECT_EQ(1, anim->last_binding_handle);
EXPECT_EQ(1, binding.handle);
{ /* Creating an 'unused' Slot should just be called 'Slot'. */
Slot &slot = anim->slot_add();
EXPECT_EQ(1, anim->last_slot_handle);
EXPECT_EQ(1, slot.handle);
EXPECT_STREQ("XXBinding", binding.name);
EXPECT_EQ(0, binding.idtype);
EXPECT_STREQ("XXSlot", slot.name);
EXPECT_EQ(0, slot.idtype);
}
{ /* Creating a Binding for a specific ID should name it after the ID. */
Binding &binding = anim->binding_add_for_id(cube->id);
EXPECT_EQ(2, anim->last_binding_handle);
EXPECT_EQ(2, binding.handle);
{ /* 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);
EXPECT_EQ(2, slot.handle);
EXPECT_STREQ(cube->id.name, binding.name);
EXPECT_EQ(ID_OB, binding.idtype);
EXPECT_STREQ(cube->id.name, slot.name);
EXPECT_EQ(ID_OB, slot.idtype);
}
}
TEST_F(ActionLayersTest, add_binding__reset_idroot)
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 binding, it no longer is a
* 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.";
anim->binding_add();
anim->slot_add();
EXPECT_EQ(0, anim->idroot) << "anim->idroot should get reset when the Action becomes layered.";
}
TEST_F(ActionLayersTest, add_binding_multiple)
TEST_F(ActionLayersTest, add_slot_multiple)
{
Binding &bind_cube = anim->binding_add();
Binding &bind_suzanne = anim->binding_add();
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));
EXPECT_EQ(2, anim->last_binding_handle);
EXPECT_EQ(2, anim->last_slot_handle);
EXPECT_EQ(1, bind_cube.handle);
EXPECT_EQ(2, bind_suzanne.handle);
}
TEST_F(ActionLayersTest, anim_assign_id)
{
/* Assign to the only, 'virgin' Binding, should always work. */
Binding &binding_cube = anim->binding_add();
ASSERT_NE(nullptr, binding_cube.runtime);
ASSERT_STREQ(binding_cube.name, "XXBinding");
ASSERT_TRUE(anim->assign_id(&binding_cube, cube->id));
EXPECT_EQ(binding_cube.handle, cube->adt->binding_handle);
EXPECT_STREQ(binding_cube.name, "OBBinding");
EXPECT_STREQ(binding_cube.name, cube->adt->binding_name)
<< "The binding name should be copied to the adt";
/* Assign to the only, 'virgin' Slot, should always work. */
Slot &slot_cube = anim->slot_add();
ASSERT_NE(nullptr, slot_cube.runtime);
ASSERT_STREQ(slot_cube.name, "XXSlot");
ASSERT_TRUE(anim->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)
<< "The slot name should be copied to the adt";
EXPECT_TRUE(binding_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to be registered as animated by its binding.";
EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to be registered as animated by its slot.";
/* Assign another ID to the same Binding. */
ASSERT_TRUE(anim->assign_id(&binding_cube, suzanne->id));
EXPECT_STREQ(binding_cube.name, "OBBinding");
EXPECT_STREQ(binding_cube.name, cube->adt->binding_name)
<< "The binding name should be copied to the adt";
/* Assign another ID to the same Slot. */
ASSERT_TRUE(anim->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";
EXPECT_TRUE(binding_cube.users(*bmain).contains(&cube->id))
<< "Expecting Suzanne to be registered as animated by the Cube binding.";
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+binding without unassigning first. */
{ /* Assign Cube to another animation+slot without unassigning first. */
Action *another_anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACOtherAnim"));
Binding &another_binding = another_anim->binding_add();
ASSERT_FALSE(another_anim->assign_id(&another_binding, cube->id))
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.";
EXPECT_TRUE(binding_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to still be registered as animated by its binding.";
EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to still be registered as animated by its slot.";
}
{ /* Assign Cube to another binding of the same Animation, this should work. */
{ /* Assign Cube to another slot of the same Animation, this should work. */
const int user_count_pre = anim->id.us;
Binding &binding_cube_2 = anim->binding_add();
ASSERT_TRUE(anim->assign_id(&binding_cube_2, cube->id));
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 binding of the same animation should _not_ change the user "
<< "Assigning to a different slot of the same animation should _not_ change the user "
"count of that Animation";
EXPECT_FALSE(binding_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to no longer be registered as animated by the Cube binding.";
EXPECT_TRUE(binding_cube_2.users(*bmain).contains(&cube->id))
<< "Expecting Cube to be registered as animated by the 'cube_2' binding.";
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. */
@ -289,163 +289,161 @@ TEST_F(ActionLayersTest, anim_assign_id)
ASSERT_EQ(anim->id.us, user_count_pre - 1)
<< "Unassigning an animation should lower its user count";
ASSERT_EQ(2, anim->bindings().size()) << "Expecting the Action to have two Bindings";
EXPECT_FALSE(anim->binding(0)->users(*bmain).contains(&cube->id))
<< "Expecting Cube to no longer be registered as animated by any binding.";
EXPECT_FALSE(anim->binding(1)->users(*bmain).contains(&cube->id))
<< "Expecting Cube to no longer be registered as animated by any binding.";
ASSERT_EQ(2, anim->slots().size()) << "Expecting the Action to have two Slots";
EXPECT_FALSE(anim->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))
<< "Expecting Cube to no longer be registered as animated by any slot.";
}
/* Assign Cube to another 'virgin' binding. This should not cause a name
* collision between the Bindings. */
Binding &another_binding_cube = anim->binding_add();
ASSERT_TRUE(anim->assign_id(&another_binding_cube, cube->id));
EXPECT_EQ(another_binding_cube.handle, cube->adt->binding_handle);
EXPECT_STREQ("OBBinding.002", another_binding_cube.name)
<< "The binding should be uniquely named";
EXPECT_STREQ("OBBinding.002", cube->adt->binding_name)
<< "The binding name should be copied to the adt";
EXPECT_TRUE(another_binding_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to be registered as animated by the 'another_binding_cube' binding.";
/* 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));
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";
EXPECT_TRUE(another_slot_cube.users(*bmain).contains(&cube->id))
<< "Expecting Cube to be registered as animated by the 'another_slot_cube' slot.";
/* Create an ID of another type. This should not be assignable to this binding. */
/* 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(&binding_cube, *mesh))
<< "Mesh should not be animatable by an Object binding";
EXPECT_FALSE(another_binding_cube.users(*bmain).contains(mesh))
<< "Expecting Mesh to not be registered as animated by the 'binding_cube' binding.";
EXPECT_FALSE(anim->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.";
BKE_id_free(nullptr, mesh);
}
TEST_F(ActionLayersTest, rename_binding)
TEST_F(ActionLayersTest, rename_slot)
{
Binding &binding_cube = anim->binding_add();
ASSERT_TRUE(anim->assign_id(&binding_cube, cube->id));
EXPECT_EQ(binding_cube.handle, cube->adt->binding_handle);
EXPECT_STREQ("OBBinding", binding_cube.name);
EXPECT_STREQ(binding_cube.name, cube->adt->binding_name)
<< "The binding name should be copied to the adt";
Slot &slot_cube = anim->slot_add();
ASSERT_TRUE(anim->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->binding_name_define(binding_cube, "New Binding Name");
EXPECT_STREQ("New Binding Name", binding_cube.name);
/* At this point the binding name will not have been copied to the cube
anim->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->binding_name_propagate(*bmain, binding_cube);
EXPECT_STREQ("New Binding Name", cube->adt->binding_name);
anim->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 binding name being stored
* unassign. This should still result in the correct slot name being stored
* on the ADT. */
anim->binding_name_define(binding_cube, "Even Newer Name");
anim->slot_name_define(slot_cube, "Even Newer Name");
anim->unassign_id(cube->id);
EXPECT_STREQ("Even Newer Name", cube->adt->binding_name);
EXPECT_STREQ("Even Newer Name", cube->adt->slot_name);
}
TEST_F(ActionLayersTest, binding_name_ensure_prefix)
TEST_F(ActionLayersTest, slot_name_ensure_prefix)
{
class AccessibleBinding : public Binding {
class AccessibleSlot : public Slot {
public:
void name_ensure_prefix()
{
Binding::name_ensure_prefix();
Slot::name_ensure_prefix();
}
};
Binding &raw_binding = anim->binding_add();
AccessibleBinding &binding = static_cast<AccessibleBinding &>(raw_binding);
ASSERT_STREQ("XXBinding", binding.name);
ASSERT_EQ(0, binding.idtype);
Slot &raw_slot = anim->slot_add();
AccessibleSlot &slot = static_cast<AccessibleSlot &>(raw_slot);
ASSERT_STREQ("XXSlot", slot.name);
ASSERT_EQ(0, slot.idtype);
/* Check defaults, idtype zeroed. */
binding.name_ensure_prefix();
EXPECT_STREQ("XXBinding", binding.name);
slot.name_ensure_prefix();
EXPECT_STREQ("XXSlot", slot.name);
/* idtype CA, default name. */
binding.idtype = ID_CA;
binding.name_ensure_prefix();
EXPECT_STREQ("CABinding", binding.name);
slot.idtype = ID_CA;
slot.name_ensure_prefix();
EXPECT_STREQ("CASlot", slot.name);
/* idtype ME, explicit name of other idtype. */
anim->binding_name_define(binding, "CANewName");
binding.idtype = ID_ME;
binding.name_ensure_prefix();
EXPECT_STREQ("MENewName", binding.name);
anim->slot_name_define(slot, "CANewName");
slot.idtype = ID_ME;
slot.name_ensure_prefix();
EXPECT_STREQ("MENewName", slot.name);
/* Zeroing out idtype. */
binding.idtype = 0;
binding.name_ensure_prefix();
EXPECT_STREQ("XXNewName", binding.name);
slot.idtype = 0;
slot.name_ensure_prefix();
EXPECT_STREQ("XXNewName", slot.name);
}
TEST_F(ActionLayersTest, binding_name_prefix)
TEST_F(ActionLayersTest, slot_name_prefix)
{
Binding &binding = anim->binding_add();
EXPECT_EQ("XX", binding.name_prefix_for_idtype());
Slot &slot = anim->slot_add();
EXPECT_EQ("XX", slot.name_prefix_for_idtype());
binding.idtype = ID_CA;
EXPECT_EQ("CA", binding.name_prefix_for_idtype());
slot.idtype = ID_CA;
EXPECT_EQ("CA", slot.name_prefix_for_idtype());
}
TEST_F(ActionLayersTest, rename_binding_name_collision)
TEST_F(ActionLayersTest, rename_slot_name_collision)
{
Binding &binding1 = anim->binding_add();
Binding &binding2 = anim->binding_add();
Slot &slot1 = anim->slot_add();
Slot &slot2 = anim->slot_add();
anim->binding_name_define(binding1, "New Binding Name");
anim->binding_name_define(binding2, "New Binding Name");
EXPECT_STREQ("New Binding Name", binding1.name);
EXPECT_STREQ("New Binding Name.001", binding2.name);
anim->slot_name_define(slot1, "New Slot Name");
anim->slot_name_define(slot2, "New Slot Name");
EXPECT_STREQ("New Slot Name", slot1.name);
EXPECT_STREQ("New Slot Name.001", slot2.name);
}
TEST_F(ActionLayersTest, find_suitable_binding)
TEST_F(ActionLayersTest, find_suitable_slot)
{
/* ===
* Empty case, no bindings exist yet and the ID doesn't even have an AnimData. */
EXPECT_EQ(nullptr, anim->find_suitable_binding_for(cube->id));
* 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));
/* ===
* Binding exists with the same name & type as the ID, but the ID doesn't have any AnimData yet.
* 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. */
Binding &binding = anim->binding_add();
binding.handle = 327;
STRNCPY_UTF8(binding.name, "OBKüüübus");
binding.idtype = GS(cube->id.name);
EXPECT_EQ(&binding, anim->find_suitable_binding_for(cube->id));
Slot &slot = anim->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));
/* ===
* Binding exists with the same name & type as the ID, and the ID has an AnimData with the same
* binding name, but a different binding_handle. Since the Animation has not yet been
* assigned to this ID, the binding_handle should be ignored, and the binding name used for
* 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
* assigned to this ID, the slot_handle should be ignored, and the slot name used for
* matching. */
/* Create a binding with a handle that should be ignored.*/
Binding &other_binding = anim->binding_add();
other_binding.handle = 47;
/* Create a slot with a handle that should be ignored.*/
Slot &other_slot = anim->slot_add();
other_slot.handle = 47;
AnimData *adt = BKE_animdata_ensure_id(&cube->id);
adt->action = nullptr;
/* Configure adt to use the handle of one binding, and the name of the other. */
adt->binding_handle = other_binding.handle;
STRNCPY_UTF8(adt->binding_name, binding.name);
EXPECT_EQ(&binding, anim->find_suitable_binding_for(cube->id));
/* 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));
/* ===
* Same situation as above (AnimData has name of one binding, but the handle of another),
* 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_binding, anim->find_suitable_binding_for(cube->id));
EXPECT_EQ(&other_slot, anim->find_suitable_slot_for(cube->id));
/* ===
* A binding 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 anim data of the cube. This should fall
* back to using the ID name. */
adt->binding_handle = 161;
STRNCPY_UTF8(adt->binding_name, "¿¿What's this??");
EXPECT_EQ(&binding, anim->find_suitable_binding_for(cube->id));
adt->slot_handle = 161;
STRNCPY_UTF8(adt->slot_name, "¿¿What's this??");
EXPECT_EQ(&slot, anim->find_suitable_slot_for(cube->id));
}
TEST_F(ActionLayersTest, strip)
@ -488,8 +486,8 @@ TEST_F(ActionLayersTest, strip)
TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
{
Binding &binding = anim->binding_add();
EXPECT_TRUE(anim->assign_id(&binding, cube->id));
Slot &slot = anim->slot_add();
EXPECT_TRUE(anim->assign_id(&slot, cube->id));
Layer &layer = anim->layer_add("Kübus layer");
Strip &strip = layer.strip_add(Strip::Type::Keyframe);
@ -497,23 +495,23 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
const KeyframeSettings settings = get_keyframe_settings(false);
SingleKeyingResult result_loc_a = key_strip.keyframe_insert(
binding, {"location", 0}, {1.0f, 47.0f}, settings);
slot, {"location", 0}, {1.0f, 47.0f}, settings);
ASSERT_EQ(SingleKeyingResult::SUCCESS, result_loc_a)
<< "Expected keyframe insertion to be successful";
/* Check the strip was created correctly, with the channels for the binding. */
/* Check the strip was created correctly, with the channels for the slot. */
ASSERT_EQ(1, key_strip.channelbags().size());
ChannelBag *channels = key_strip.channelbag(0);
EXPECT_EQ(binding.handle, channels->binding_handle);
EXPECT_EQ(slot.handle, channels->slot_handle);
/* Insert a second key, should insert into the same FCurve as before. */
SingleKeyingResult result_loc_b = key_strip.keyframe_insert(
binding, {"location", 0}, {5.0f, 47.1f}, settings);
slot, {"location", 0}, {5.0f, 47.1f}, settings);
EXPECT_EQ(SingleKeyingResult::SUCCESS, result_loc_b);
ASSERT_EQ(1, channels->fcurves().size()) << "Expect insertion with the same (binding/rna "
ASSERT_EQ(1, channels->fcurves().size()) << "Expect insertion with the same (slot/rna "
"path/array index) tuple to go into the same FCurve";
EXPECT_EQ(2, channels->fcurves()[0]->totvert)
<< "Expect insertion with the same (binding/rna path/array index) tuple to go into the same "
<< "Expect insertion with the same (slot/rna path/array index) tuple to go into the same "
"FCurve";
EXPECT_EQ(47.0f, evaluate_fcurve(channels->fcurves()[0], 1.0f));
@ -521,7 +519,7 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
/* Insert another key for another property, should create another FCurve. */
SingleKeyingResult result_rot = key_strip.keyframe_insert(
binding, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings);
slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings);
EXPECT_EQ(SingleKeyingResult::SUCCESS, result_rot);
ASSERT_EQ(2, channels->fcurves().size()) << "Expected a second FCurve to be created.";
EXPECT_EQ(2, channels->fcurves()[0]->totvert);

@ -198,14 +198,14 @@ const FCurve *fcurve_find_by_rna_path(const AnimData &adt,
const Action &action = adt.action->wrap();
BLI_assert(action.is_action_layered());
const Binding *binding = action.binding_for_handle(adt.binding_handle);
if (!binding) {
/* No need to inspect anything if this ID does not have an animation Binding. */
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. */
return nullptr;
}
/* No check for the binding's ID type. Not only do we not have the actual ID
* to do this check, but also, since the Action and the binding have been
/* No check for the slot's ID type. Not only do we not have the actual ID
* to do this check, but also, since the Action and the slot have been
* assigned, just trust that it's valid. */
/* Iterate the layers top-down, as higher-up animation overrides (or at least can override)
@ -218,11 +218,11 @@ const FCurve *fcurve_find_by_rna_path(const AnimData &adt,
switch (strip->type()) {
case Strip::Type::Keyframe: {
const KeyframeStrip &key_strip = strip->as<KeyframeStrip>();
const ChannelBag *channelbag_for_binding = key_strip.channelbag_for_binding(*binding);
if (!channelbag_for_binding) {
const ChannelBag *channelbag_for_slot = key_strip.channelbag_for_slot(*slot);
if (!channelbag_for_slot) {
continue;
}
const FCurve *fcu = channelbag_for_binding->fcurve_find(rna_path, array_index);
const FCurve *fcu = channelbag_for_slot->fcurve_find(rna_path, array_index);
if (!fcu) {
continue;
}

@ -37,7 +37,7 @@ void apply_evaluation_result(const EvaluationResult &evaluation_result,
static EvaluationResult evaluate_animation(PointerRNA &animated_id_ptr,
Action &animation,
const binding_handle_t binding_handle,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context)
{
EvaluationResult last_result;
@ -49,7 +49,7 @@ static EvaluationResult evaluate_animation(PointerRNA &animated_id_ptr,
continue;
}
auto layer_result = evaluate_layer(animated_id_ptr, *layer, binding_handle, anim_eval_context);
auto layer_result = evaluate_layer(animated_id_ptr, *layer, slot_handle, anim_eval_context);
if (!layer_result) {
continue;
}
@ -71,12 +71,12 @@ static EvaluationResult evaluate_animation(PointerRNA &animated_id_ptr,
void evaluate_and_apply_animation(PointerRNA &animated_id_ptr,
Action &animation,
const binding_handle_t binding_handle,
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, binding_handle, anim_eval_context);
animated_id_ptr, animation, slot_handle, anim_eval_context);
if (!evaluation_result) {
return;
}
@ -132,16 +132,16 @@ static void animsys_write_orig_anim_rna(PointerRNA *ptr,
static EvaluationResult evaluate_keyframe_strip(PointerRNA &animated_id_ptr,
KeyframeStrip &key_strip,
const binding_handle_t binding_handle,
const slot_handle_t slot_handle,
const AnimationEvalContext &offset_eval_context)
{
ChannelBag *channelbag_for_binding = key_strip.channelbag_for_binding(binding_handle);
if (!channelbag_for_binding) {
ChannelBag *channelbag_for_slot = key_strip.channelbag_for_slot(slot_handle);
if (!channelbag_for_slot) {
return {};
}
EvaluationResult evaluation_result;
for (FCurve *fcu : channelbag_for_binding->fcurves()) {
for (FCurve *fcu : channelbag_for_slot->fcurves()) {
/* Blatant copy of animsys_evaluate_fcurves(). */
if (!is_fcurve_evaluatable(fcu)) {
@ -191,7 +191,7 @@ void apply_evaluation_result(const EvaluationResult &evaluation_result,
static EvaluationResult evaluate_strip(PointerRNA &animated_id_ptr,
Strip &strip,
const binding_handle_t binding_handle,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context)
{
AnimationEvalContext offset_eval_context = anim_eval_context;
@ -202,8 +202,7 @@ static EvaluationResult evaluate_strip(PointerRNA &animated_id_ptr,
switch (strip.type()) {
case Strip::Type::Keyframe: {
KeyframeStrip &key_strip = strip.as<KeyframeStrip>();
return evaluate_keyframe_strip(
animated_id_ptr, key_strip, binding_handle, offset_eval_context);
return evaluate_keyframe_strip(animated_id_ptr, key_strip, slot_handle, offset_eval_context);
}
}
@ -262,7 +261,7 @@ namespace internal {
EvaluationResult evaluate_layer(PointerRNA &animated_id_ptr,
Layer &layer,
const binding_handle_t binding_handle,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context)
{
/* TODO: implement cross-blending between overlapping strips. For now, this is not supported.
@ -279,7 +278,7 @@ EvaluationResult evaluate_layer(PointerRNA &animated_id_ptr,
}
const EvaluationResult strip_result = evaluate_strip(
animated_id_ptr, *strip, binding_handle, anim_eval_context);
animated_id_ptr, *strip, slot_handle, anim_eval_context);
if (!strip_result) {
continue;
}

@ -54,7 +54,7 @@ class AnimatedProperty {
}
};
/* Evaluated FCurves for some animation binding.
/* Evaluated FCurves for some animation slot.
* Mapping from property identifier to its float value.
*
* Can be fed to the evaluation of the next layer, mixed with another strip, or
@ -116,13 +116,13 @@ class EvaluationResult {
};
/**
* Evaluate the animation data on the given layer, for the given binding. This
* Evaluate the animation data on the given layer, for the given slot. This
* just returns the evaluation result, without taking any other layers,
* blending, influence, etc. into account.
*/
EvaluationResult evaluate_layer(PointerRNA &animated_id_ptr,
Layer &layer,
binding_handle_t binding_handle,
slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context);
} // namespace blender::animrig::internal

@ -35,7 +35,7 @@ class AnimationEvaluationTest : public testing::Test {
Main *bmain;
Action *anim;
Object *cube;
Binding *binding;
Slot *slot;
Layer *layer;
KeyframeSettings settings = get_keyframe_settings(false);
@ -64,8 +64,8 @@ class AnimationEvaluationTest : public testing::Test {
cube = BKE_object_add_only_object(bmain, OB_EMPTY, "Küüübus");
binding = &anim->binding_add();
anim->assign_id(binding, cube->id);
slot = &anim->slot_add();
anim->assign_id(slot, cube->id);
layer = &anim->layer_add("Kübus layer");
/* Make it easier to predict test values. */
@ -86,7 +86,7 @@ class AnimationEvaluationTest : public testing::Test {
{
anim_eval_context.eval_time = eval_time;
EvaluationResult result = evaluate_layer(
cube_rna_ptr, *layer, binding->handle, anim_eval_context);
cube_rna_ptr, *layer, slot->handle, anim_eval_context);
const AnimatedProperty *loc0_result = result.lookup_ptr(PropIdentifier(rna_path, array_index));
if (!loc0_result) {
@ -149,10 +149,10 @@ TEST_F(AnimationEvaluationTest, evaluate_layer__keyframes)
KeyframeStrip &key_strip = strip.as<KeyframeStrip>();
/* Set some keys. */
key_strip.keyframe_insert(*binding, {"location", 0}, {1.0f, 47.1f}, settings);
key_strip.keyframe_insert(*binding, {"location", 0}, {5.0f, 47.5f}, settings);
key_strip.keyframe_insert(*binding, {"rotation_euler", 1}, {1.0f, 0.0f}, settings);
key_strip.keyframe_insert(*binding, {"rotation_euler", 1}, {5.0f, 3.14f}, settings);
key_strip.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.1f}, settings);
key_strip.keyframe_insert(*slot, {"location", 0}, {5.0f, 47.5f}, settings);
key_strip.keyframe_insert(*slot, {"rotation_euler", 1}, {1.0f, 0.0f}, settings);
key_strip.keyframe_insert(*slot, {"rotation_euler", 1}, {5.0f, 3.14f}, settings);
/* Set the animated properties to some values. These should not be overwritten
* by the evaluation itself. */
@ -165,8 +165,7 @@ TEST_F(AnimationEvaluationTest, evaluate_layer__keyframes)
/* Evaluate. */
anim_eval_context.eval_time = 3.0f;
EvaluationResult result = evaluate_layer(
cube_rna_ptr, *layer, binding->handle, anim_eval_context);
EvaluationResult result = evaluate_layer(cube_rna_ptr, *layer, slot->handle, anim_eval_context);
/* Check the result. */
ASSERT_FALSE(result.is_empty());
@ -190,9 +189,9 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__single_strip)
/* Set some keys. */
KeyframeStrip &key_strip = strip.as<KeyframeStrip>();
key_strip.keyframe_insert(*binding, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip.keyframe_insert(*binding, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip.keyframe_insert(*binding, {"location", 0}, {10.0f, 48.0f}, settings);
key_strip.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
/* Evaluate the layer to see how it handles the boundaries + something in between. */
EXPECT_TRUE(test_evaluate_layer("location", 0, {1.0f, 47.0f}));
@ -214,15 +213,15 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__nonoverlapping)
/* Set some keys. */
{
KeyframeStrip &key_strip1 = strip1.as<KeyframeStrip>();
key_strip1.keyframe_insert(*binding, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip1.keyframe_insert(*binding, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip1.keyframe_insert(*binding, {"location", 0}, {10.0f, 48.0f}, settings);
key_strip1.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip1.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip1.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
}
{
KeyframeStrip &key_strip2 = strip2.as<KeyframeStrip>();
key_strip2.keyframe_insert(*binding, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip2.keyframe_insert(*binding, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip2.keyframe_insert(*binding, {"location", 0}, {10.0f, 48.0f}, settings);
key_strip2.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip2.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip2.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
}
/* Check Strip 1. */
@ -254,15 +253,15 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__overlapping_edge)
/* Set some keys. */
{
KeyframeStrip &key_strip1 = strip1.as<KeyframeStrip>();
key_strip1.keyframe_insert(*binding, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip1.keyframe_insert(*binding, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip1.keyframe_insert(*binding, {"location", 0}, {10.0f, 48.0f}, settings);
key_strip1.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip1.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip1.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
}
{
KeyframeStrip &key_strip2 = strip2.as<KeyframeStrip>();
key_strip2.keyframe_insert(*binding, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip2.keyframe_insert(*binding, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip2.keyframe_insert(*binding, {"location", 0}, {10.0f, 48.0f}, settings);
key_strip2.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
key_strip2.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
key_strip2.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
}
/* Check Strip 1. */

@ -160,10 +160,10 @@ void CombinedKeyingResult::generate_reports(ReportList *reports, const eReportTy
error_count));
}
if (this->get_count(SingleKeyingResult::NO_VALID_BINDING) > 0) {
const int error_count = this->get_count(SingleKeyingResult::NO_VALID_BINDING);
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 bindings."),
"of missing animation slots."),
error_count));
}
@ -855,7 +855,7 @@ struct KeyInsertData {
};
static SingleKeyingResult insert_key_layer(Layer &layer,
const Binding &binding,
const Slot &slot,
const std::string &rna_path,
const std::optional<PropertySubType> prop_subtype,
const KeyInsertData &key_data,
@ -866,7 +866,7 @@ static SingleKeyingResult insert_key_layer(Layer &layer,
BLI_assert(layer.strips().size() == 1);
Strip *strip = layer.strip(0);
return strip->as<KeyframeStrip>().keyframe_insert(binding,
return strip->as<KeyframeStrip>().keyframe_insert(slot,
{rna_path, key_data.array_index, prop_subtype},
key_data.position,
key_settings,
@ -882,12 +882,12 @@ static CombinedKeyingResult insert_key_layered_action(Action &action,
{
BLI_assert(action.is_action_layered());
Binding &binding = action.binding_ensure_for_id(*rna_pointer->owner_id);
const bool success = action.assign_id(&binding, *rna_pointer->owner_id);
Slot &slot = action.slot_ensure_for_id(*rna_pointer->owner_id);
const bool success = action.assign_id(&slot, *rna_pointer->owner_id);
UNUSED_VARS_NDEBUG(success);
BLI_assert_msg(
success,
"The conditions that would cause this Binding assigment to fail (such as the ID not being "
"The conditions that would cause this Slot assigment to fail (such as the ID not being "
"animatible) should have been caught and handled by higher-level functions.");
/* Ensure that at least one layer exists. If not, create the default layer
@ -912,8 +912,8 @@ static CombinedKeyingResult insert_key_layered_action(Action &action,
rna_pointer, rna_path.path.c_str(), &ptr, &prop);
if (!path_resolved) {
std::fprintf(stderr,
"Failed to insert key on binding %s due to unresolved RNA path: %s\n",
binding.name,
"Failed to insert key on slot %s due to unresolved RNA path: %s\n",
slot.name,
rna_path.path.c_str());
combined_result.add(SingleKeyingResult::CANNOT_RESOLVE_PATH);
continue;
@ -933,7 +933,7 @@ static CombinedKeyingResult insert_key_layered_action(Action &action,
const KeyInsertData key_data = {{scene_frame, rna_values[property_index]}, property_index};
const SingleKeyingResult result = insert_key_layer(*layer,
binding,
slot,
*rna_path_id_to_prop,
prop_subtype,
key_data,

@ -134,7 +134,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__non_array_property)
/* First time should create:
* - AnimData
* - Action
* - Binding
* - Slot
* - Layer
* - Infinite KeyframeStrip
* - FCurve with a single key
@ -153,13 +153,13 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__non_array_property)
ASSERT_NE(nullptr, object->adt->action);
Action &action = object->adt->action->wrap();
/* The action has a binding, it's named properly, and it's correctly assigned
/* The action has a slot, it's named properly, and it's correctly assigned
* to the object. */
ASSERT_EQ(1, action.bindings().size());
Binding *binding = action.binding(0);
EXPECT_STREQ(object->id.name, binding->name);
EXPECT_STREQ(object->adt->binding_name, binding->name);
EXPECT_EQ(object->adt->binding_handle, binding->handle);
ASSERT_EQ(1, action.slots().size());
Slot *slot = action.slot(0);
EXPECT_STREQ(object->id.name, slot->name);
EXPECT_STREQ(object->adt->slot_name, slot->name);
EXPECT_EQ(object->adt->slot_handle, slot->handle);
/* We have the default layer and strip. */
ASSERT_TRUE(action.is_action_layered());
@ -171,8 +171,8 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__non_array_property)
ASSERT_EQ(Strip::Type::Keyframe, strip->type());
KeyframeStrip *keyframe_strip = &strip->as<KeyframeStrip>();
/* We have a channel bag for the binding. */
ChannelBag *channel_bag = keyframe_strip->channelbag_for_binding(*binding);
/* We have a channel bag for the slot. */
ChannelBag *channel_bag = keyframe_strip->channelbag_for_slot(*slot);
ASSERT_NE(nullptr, channel_bag);
/* The fcurves in the channel bag are what we expect. */
@ -240,7 +240,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__single_element)
ASSERT_NE(nullptr, object->adt);
ASSERT_NE(nullptr, object->adt->action);
Action &action = object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
@ -273,7 +273,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__all_elements)
ASSERT_NE(nullptr, object->adt);
ASSERT_NE(nullptr, object->adt->action);
Action &action = object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
@ -311,7 +311,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__pose_bone_rna_pointer)
ASSERT_NE(nullptr, armature_object->adt);
ASSERT_NE(nullptr, armature_object->adt->action);
Action &action = armature_object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
@ -345,7 +345,7 @@ TEST_F(KeyframingTest, insert_keyframes__pose_bone_owner_id_pointer)
ASSERT_NE(nullptr, armature_object->adt);
ASSERT_NE(nullptr, armature_object->adt->action);
Action &action = armature_object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
@ -383,7 +383,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__multiple_properties)
ASSERT_NE(nullptr, object->adt);
ASSERT_NE(nullptr, object->adt->action);
Action &action = object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
@ -408,7 +408,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__multiple_ids)
AnimationEvalContext anim_eval_context = {nullptr, 1.0};
/* First object should crate the action and get a binding and channel bag. */
/* First object should crate the action and get a slot and channel bag. */
const CombinedKeyingResult result_1 = insert_keyframes(bmain,
&object_rna_pointer,
std::nullopt,
@ -422,12 +422,12 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__multiple_ids)
ASSERT_NE(nullptr, object->adt->action);
Action &action = object->adt->action->wrap();
/* The action has a binding and it's assigned to the first object. */
ASSERT_EQ(1, action.bindings().size());
Binding *binding_1 = action.binding_for_handle(object->adt->binding_handle);
ASSERT_NE(nullptr, binding_1);
EXPECT_STREQ(object->id.name, binding_1->name);
EXPECT_STREQ(object->adt->binding_name, binding_1->name);
/* The action has a slot and it's assigned to the first object. */
ASSERT_EQ(1, action.slots().size());
Slot *slot_1 = action.slot_for_handle(object->adt->slot_handle);
ASSERT_NE(nullptr, slot_1);
EXPECT_STREQ(object->id.name, slot_1->name);
EXPECT_STREQ(object->adt->slot_name, slot_1->name);
/* Get the keyframe strip. */
ASSERT_TRUE(action.is_action_layered());
@ -435,16 +435,16 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__multiple_ids)
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
/* We have a single channel bag, and it's for the first object's binding. */
/* We have a single channel bag, and it's for the first object's slot. */
ASSERT_EQ(1, strip->channelbags().size());
ChannelBag *channel_bag_1 = strip->channelbag_for_binding(*binding_1);
ChannelBag *channel_bag_1 = strip->channelbag_for_slot(*slot_1);
ASSERT_NE(nullptr, channel_bag_1);
/* Assign the action to the second object, with no binding. */
/* Assign the action to the second object, with no slot. */
action.assign_id(nullptr, armature_object->id);
/* Keying the second object should go into the same action, creating a new
* binding and channel bag. */
* slot and channel bag. */
const CombinedKeyingResult result_2 = insert_keyframes(bmain,
&armature_object_rna_pointer,
std::nullopt,
@ -455,14 +455,14 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__multiple_ids)
INSERTKEY_NOFLAGS);
EXPECT_EQ(1, result_2.get_count(SingleKeyingResult::SUCCESS));
ASSERT_EQ(2, action.bindings().size());
Binding *binding_2 = action.binding_for_handle(armature_object->adt->binding_handle);
ASSERT_NE(nullptr, binding_2);
EXPECT_STREQ(armature_object->id.name, binding_2->name);
EXPECT_STREQ(armature_object->adt->binding_name, binding_2->name);
ASSERT_EQ(2, action.slots().size());
Slot *slot_2 = action.slot_for_handle(armature_object->adt->slot_handle);
ASSERT_NE(nullptr, slot_2);
EXPECT_STREQ(armature_object->id.name, slot_2->name);
EXPECT_STREQ(armature_object->adt->slot_name, slot_2->name);
ASSERT_EQ(2, strip->channelbags().size());
ChannelBag *channel_bag_2 = strip->channelbag_for_binding(*binding_2);
ChannelBag *channel_bag_2 = strip->channelbag_for_slot(*slot_2);
ASSERT_NE(nullptr, channel_bag_2);
}
@ -540,10 +540,10 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__only_available)
/* If an action is created at all, it should be the default action with one
* layer and an infinite keyframe strip. */
Action &action = object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
EXPECT_EQ(object->adt->binding_handle, action.binding(0)->handle);
EXPECT_EQ(object->adt->slot_handle, action.slot(0)->handle);
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
ASSERT_EQ(0, strip->channelbags().size());
@ -621,7 +621,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__only_replace)
ASSERT_NE(nullptr, object->adt);
ASSERT_NE(nullptr, object->adt->action);
Action &action = object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();
@ -702,7 +702,7 @@ TEST_F(KeyframingTest, insert_keyframes__layered_action__only_needed)
ASSERT_NE(nullptr, object->adt);
ASSERT_NE(nullptr, object->adt->action);
Action &action = object->adt->action->wrap();
ASSERT_EQ(1, action.bindings().size());
ASSERT_EQ(1, action.slots().size());
ASSERT_EQ(1, action.layers().size());
ASSERT_EQ(1, action.layer(0)->strips().size());
KeyframeStrip *strip = &action.layer(0)->strip(0)->as<KeyframeStrip>();

@ -184,18 +184,18 @@ struct Main {
bool is_global_main;
/**
* True if the Action Binding-to-ID mapping is dirty.
* True if the Action Slot-to-ID mapping is dirty.
*
* If this flag is set, the next call to `animrig::Binding::users(bmain)` and related functions
* will trigger a rebuild of the Binding-to-ID mapping. Since constructing this mapping requires
* If this flag is set, the next call to `animrig::Slot::users(bmain)` and related functions
* will trigger a rebuild of the Slot-to-ID mapping. Since constructing this mapping requires
* a full scan of the animatable IDs in this `Main` anyway, it is kept as a flag here.
*
* \note This flag should not be set directly. Use animrig::Binding::users_invalidate() instead.
* That way the handling of this flag is limited to the code in animrig::Binding.
* \note This flag should not be set directly. Use animrig::Slot::users_invalidate() instead.
* That way the handling of this flag is limited to the code in animrig::Slot.
*
* \see blender::animrig::Binding::users_invalidate(Main &bmain)
* \see blender::animrig::Slot::users_invalidate(Main &bmain)
*/
bool is_action_binding_to_id_map_dirty;
bool is_action_slot_to_id_map_dirty;
BlendThumbnail *blen_thumb;

@ -156,8 +156,8 @@ static void action_copy_data(Main * /*bmain*/,
/* Copy all simple properties. */
action_dst.layer_array_num = action_src.layer_array_num;
action_dst.layer_active_index = action_src.layer_active_index;
action_dst.binding_array_num = action_src.binding_array_num;
action_dst.last_binding_handle = action_src.last_binding_handle;
action_dst.slot_array_num = action_src.slot_array_num;
action_dst.last_slot_handle = action_src.last_slot_handle;
/* Layers. */
action_dst.layer_array = MEM_cnew_array<ActionLayer *>(action_src.layer_array_num, __func__);
@ -165,11 +165,10 @@ static void action_copy_data(Main * /*bmain*/,
action_dst.layer_array[i] = MEM_new<animrig::Layer>(__func__, *action_src.layer(i));
}
/* Bindings. */
action_dst.binding_array = MEM_cnew_array<ActionBinding *>(action_src.binding_array_num,
__func__);
for (int i : action_src.bindings().index_range()) {
action_dst.binding_array[i] = MEM_new<animrig::Binding>(__func__, *action_src.binding(i));
/* Slots. */
action_dst.slot_array = MEM_cnew_array<ActionSlot *>(action_src.slot_array_num, __func__);
for (int i : action_src.slots().index_range()) {
action_dst.slot_array[i] = MEM_new<animrig::Slot>(__func__, *action_src.slot(i));
}
if (flag & LIB_ID_COPY_NO_PREVIEW) {
@ -192,12 +191,12 @@ static void action_free_data(ID *id)
MEM_SAFE_FREE(action.layer_array);
action.layer_array_num = 0;
/* Free bindings. */
for (animrig::Binding *binding : action.bindings()) {
MEM_delete(binding);
/* Free slots. */
for (animrig::Slot *slot : action.slots()) {
MEM_delete(slot);
}
MEM_SAFE_FREE(action.binding_array);
action.binding_array_num = 0;
MEM_SAFE_FREE(action.slot_array);
action.slot_array_num = 0;
/* Free legacy F-Curves & groups. */
BKE_fcurves_free(&action.curves);
@ -239,35 +238,35 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data)
* it's possible to report invalid pointers, which should be avoided at all
* time. */
if (bmain) {
for (animrig::Binding *binding : action.bindings()) {
for (ID *binding_user : binding->users(*bmain)) {
BKE_LIB_FOREACHID_PROCESS_ID(data, binding_user, idwalk_flags);
for (animrig::Slot *slot : action.slots()) {
for (ID *slot_user : slot->users(*bmain)) {
BKE_LIB_FOREACHID_PROCESS_ID(data, slot_user, idwalk_flags);
}
}
}
}
else if (bmain && !bmain->is_action_binding_to_id_map_dirty) {
else if (bmain && !bmain->is_action_slot_to_id_map_dirty) {
/* Because BKE_library_foreach_ID_link() can be called with bmain=nullptr,
* there are cases where we do not know which `main` this is called for. An example is in
* `deg_eval_copy_on_write.cc`, function `deg_expand_eval_copy_datablock`.
*
* Also if the cache is already dirty, we shouldn't loop over the pointers in there. If we
* were to call `binding->users(*bmain)` in that case, it would rebuild the cache. But then
* were to call `slot->users(*bmain)` in that case, it would rebuild the cache. But then
* another ID using the same Action may also trigger a rebuild of the cache, because another
* user pointer changed, forcing way too many rebuilds of the user map. */
bool should_invalidate = false;
for (animrig::Binding *binding : action.bindings()) {
for (ID *binding_user : binding->runtime_users()) {
ID *old_pointer = binding_user;
BKE_LIB_FOREACHID_PROCESS_ID(data, binding_user, idwalk_flags);
/* If binding_user changed, the cache should be invalidated. */
should_invalidate |= (binding_user != old_pointer);
for (animrig::Slot *slot : action.slots()) {
for (ID *slot_user : slot->runtime_users()) {
ID *old_pointer = slot_user;
BKE_LIB_FOREACHID_PROCESS_ID(data, slot_user, idwalk_flags);
/* If slot_user changed, the cache should be invalidated. */
should_invalidate |= (slot_user != old_pointer);
}
}
if (should_invalidate) {
animrig::Binding::users_invalidate(*bmain);
animrig::Slot::users_invalidate(*bmain);
}
}
@ -339,16 +338,16 @@ static void write_layers(BlendWriter *writer, Span<animrig::Layer *> layers)
}
}
static void write_bindings(BlendWriter *writer, Span<animrig::Binding *> bindings)
static void write_slots(BlendWriter *writer, Span<animrig::Slot *> slots)
{
BLO_write_pointer_array(writer, bindings.size(), bindings.data());
for (animrig::Binding *binding : bindings) {
BLO_write_pointer_array(writer, slots.size(), slots.data());
for (animrig::Slot *slot : slots) {
/* Make a shallow copy using the C type, so that no new runtime struct is
* allocated for the copy. */
ActionBinding shallow_copy = *binding;
ActionSlot shallow_copy = *slot;
shallow_copy.runtime = nullptr;
BLO_write_struct_at_address(writer, ActionBinding, binding, &shallow_copy);
BLO_write_struct_at_address(writer, ActionSlot, slot, &shallow_copy);
}
}
@ -361,7 +360,7 @@ static void action_blend_write(BlendWriter *writer, ID *id, const void *id_addre
/* Write layered Action data. */
write_layers(writer, action.layers());
write_bindings(writer, action.bindings());
write_slots(writer, action.slots());
/* Write legacy F-Curves & groups. */
BKE_fcurve_blend_write_listbase(writer, &action.curves);
@ -420,13 +419,13 @@ static void read_layers(BlendDataReader *reader, animrig::Action &anim)
}
}
static void read_bindings(BlendDataReader *reader, animrig::Action &anim)
static void read_slots(BlendDataReader *reader, animrig::Action &anim)
{
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&anim.binding_array));
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&anim.slot_array));
for (int i = 0; i < anim.binding_array_num; i++) {
BLO_read_struct(reader, ActionBinding, &anim.binding_array[i]);
anim.binding_array[i]->wrap().blend_read_post();
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();
}
}
@ -435,7 +434,7 @@ static void action_blend_read_data(BlendDataReader *reader, ID *id)
animrig::Action &action = reinterpret_cast<bAction *>(id)->wrap();
read_layers(reader, action);
read_bindings(reader, action);
read_slots(reader, action);
/* Read legacy data. */
BLO_read_struct_list(reader, FCurve, &action.curves);

@ -307,7 +307,7 @@ bool BKE_animdata_id_is_animated(const ID *id)
if (adt->action) {
const blender::animrig::Action &action = adt->action->wrap();
if (action.is_action_layered() && action.is_binding_animated(adt->binding_handle)) {
if (action.is_action_layered() && action.is_slot_animated(adt->slot_handle)) {
return true;
}
if (action.is_action_legacy() && !BLI_listbase_is_empty(&action.curves)) {
@ -402,17 +402,17 @@ AnimData *BKE_animdata_copy_in_lib(Main *bmain,
const bool is_main = (flag & LIB_ID_CREATE_NO_MAIN) == 0;
if (is_main) {
/* Action references were changed, so the Binding-to-user map is incomplete now. Only necessary
/* Action references were changed, so the Slot-to-user map is incomplete now. Only necessary
* when this happens in the main database though, as the user cache only tracks original IDs,
* not evaluated copies.
*
* This function does not have access to the animated ID, so it cannot just add that ID to the
* binding's users, hence the invalidation of the users map.
* slot's users, hence the invalidation of the users map.
*
* TODO: refactor to pass the owner ID to this function, and just add it to the Binding's
* TODO: refactor to pass the owner ID to this function, and just add it to the Slot's
* users. */
if (bmain) {
blender::animrig::Binding::users_invalidate(*bmain);
blender::animrig::Slot::users_invalidate(*bmain);
}
}

@ -802,7 +802,7 @@ static void action_idcode_patch_check(ID *id, bAction *act)
#ifdef WITH_ANIM_BAKLAVA
if (act->wrap().is_action_layered()) {
/* Layered Actions can always be assigned to any ID. It's actually the Binding that is limited
/* Layered Actions can always be assigned to any ID. It's actually the Slot that is limited
* to an ID type (similar to legacy Actions). Layered Actions are evaluated differently,
* though, and their evaluation shouldn't end up here. At the moment of writing it can still
* happen through NLA evaluation, though, so there's no assert here to prevent this. */
@ -3950,7 +3950,7 @@ void BKE_animsys_evaluate_animdata(ID *id,
blender::animrig::Action &action = adt->action->wrap();
if (action.is_action_layered()) {
blender::animrig::evaluate_and_apply_animation(
id_ptr, action, adt->binding_handle, *anim_eval_context, flush_to_original);
id_ptr, action, adt->slot_handle, *anim_eval_context, flush_to_original);
}
else {
animsys_evaluate_action(&id_ptr, adt->action, anim_eval_context, flush_to_original);

@ -55,10 +55,10 @@ void BKE_main_init(Main &bmain)
BLI_spin_init(reinterpret_cast<SpinLock *>(bmain.lock));
bmain.is_global_main = false;
/* Just rebuilding the Action Binding to ID* map once is likely cheaper than,
/* Just rebuilding the Action Slot to ID* map once is likely cheaper than,
* for every ID, when it's loaded from disk, check whether it's animated or
* not, and then figure out which Main it went into, and then set the flag. */
bmain.is_action_binding_to_id_map_dirty = true;
bmain.is_action_slot_to_id_map_dirty = true;
}
void BKE_main_clear(Main &bmain)

@ -1591,7 +1591,7 @@ void DepsgraphRelationBuilder::build_animdata_curves(ID *id)
BLI_assert(operation_from != nullptr);
/* Build relations from animation operation to properties it changes. */
if (adt->action != nullptr) {
build_animdata_action_targets(id, adt->binding_handle, adt_key, operation_from, adt->action);
build_animdata_action_targets(id, adt->slot_handle, adt_key, operation_from, adt->action);
}
LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
build_animdata_nlastrip_targets(id, adt_key, operation_from, &nlt->strips);
@ -1648,7 +1648,7 @@ void DepsgraphRelationBuilder::build_animdata_curves_targets(ID *id,
}
void DepsgraphRelationBuilder::build_animdata_action_targets(ID *id,
const int32_t binding_handle,
const int32_t slot_handle,
ComponentKey &adt_key,
OperationNode *operation_from,
bAction *dna_action)
@ -1667,9 +1667,9 @@ void DepsgraphRelationBuilder::build_animdata_action_targets(ID *id,
}
#ifdef WITH_ANIM_BAKLAVA
const animrig::Binding *binding = action.binding_for_handle(binding_handle);
if (binding == nullptr) {
/* If there's no matching binding, there's no Action dependency. */
const animrig::Slot *slot = action.slot_for_handle(slot_handle);
if (slot == nullptr) {
/* If there's no matching slot, there's no Action dependency. */
return;
}
@ -1680,7 +1680,7 @@ void DepsgraphRelationBuilder::build_animdata_action_targets(ID *id,
switch (strip->type()) {
case animrig::Strip::Type::Keyframe: {
animrig::KeyframeStrip &keyframe_strip = strip->as<animrig::KeyframeStrip>();
animrig::ChannelBag *channels = keyframe_strip.channelbag_for_binding(*binding);
animrig::ChannelBag *channels = keyframe_strip.channelbag_for_slot(*slot);
if (channels == nullptr) {
/* Go to next strip. */
break;
@ -1694,7 +1694,7 @@ void DepsgraphRelationBuilder::build_animdata_action_targets(ID *id,
}
}
#else
UNUSED_VARS(binding_handle);
UNUSED_VARS(slot_handle);
#endif
}
@ -1714,9 +1714,9 @@ void DepsgraphRelationBuilder::build_animdata_nlastrip_targets(ID *id,
/* TODO: add NLA support for layered actions. */
continue;
}
/* TODO: get binding handle from the owning ID. */
const animrig::binding_handle_t binding_handle = animrig::Binding::unassigned;
build_animdata_action_targets(id, binding_handle, adt_key, operation_from, strip->act);
/* TODO: get slot handle from the owning ID. */
const animrig::slot_handle_t slot_handle = animrig::Slot::unassigned;
build_animdata_action_targets(id, slot_handle, adt_key, operation_from, strip->act);
}
else if (strip->strips.first != nullptr) {
build_animdata_nlastrip_targets(id, adt_key, operation_from, &strip->strips);

@ -175,7 +175,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
OperationNode *operation_from,
ListBase *curves);
virtual void build_animdata_action_targets(ID *id,
int32_t binding_handle,
int32_t slot_handle,
ComponentKey &adt_key,
OperationNode *operation_from,
bAction *action);

@ -976,15 +976,15 @@ static void acf_fcurve_name(bAnimListElem *ale, char *name)
FCurve *fcurve = static_cast<FCurve *>(ale->data);
if (ale->fcurve_owner_id && GS(ale->fcurve_owner_id->name) == ID_AC &&
ale->binding_handle != Binding::unassigned)
ale->slot_handle != Slot::unassigned)
{
/* F-Curve comes from a layered Action. This means that we cannot trust `ale->id` or
* `ale->adt`, because in the Action Editor those are set to whatever object has this Action
* assigned. This F-Curve may be for a different binding, though, and thus might be animating a
* assigned. This F-Curve may be for a different slot, though, and thus might be animating a
* entirely different ID type. */
const Action &action = reinterpret_cast<bAction *>(ale->fcurve_owner_id)->wrap();
const Binding *binding = action.binding_for_handle(ale->binding_handle);
if (!binding) {
const Slot *slot = action.slot_for_handle(ale->slot_handle);
if (!slot) {
/* Defer to the default F-Curve resolution, but without the animated ID
* pointer, as it's likely to be wrong anyway. */
getname_anim_fcurve(name, nullptr, fcurve);
@ -992,7 +992,7 @@ static void acf_fcurve_name(bAnimListElem *ale, char *name)
}
BLI_assert(ale->bmain);
const std::string fcurve_name = getname_anim_fcurve_bound(*ale->bmain, *binding, *fcurve);
const std::string fcurve_name = getname_anim_fcurve_bound(*ale->bmain, *slot, *fcurve);
const size_t num_chars_copied = fcurve_name.copy(name, std::string::npos);
name[num_chars_copied] = '\0';
@ -1359,38 +1359,36 @@ static bAnimChannelType ACF_FILLANIM = {
/*setting_ptr*/ acf_fillanim_setting_ptr,
};
static void acf_action_binding_name(bAnimListElem *ale, char *r_name)
static void acf_action_slot_name(bAnimListElem *ale, char *r_name)
{
const animrig::Binding *binding = static_cast<animrig::Binding *>(ale->data);
if (!binding) {
/* Trying to getting the binding's name without a binding is a bug. */
const animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
if (!slot) {
/* Trying to getting the slot's name without a slot is a bug. */
BLI_assert_unreachable();
BLI_strncpy(r_name, "-nil-", ANIM_CHAN_NAME_SIZE);
return;
}
BLI_strncpy(r_name, binding->name_without_prefix().c_str(), ANIM_CHAN_NAME_SIZE);
BLI_strncpy(r_name, slot->name_without_prefix().c_str(), ANIM_CHAN_NAME_SIZE);
}
static bool acf_action_binding_name_prop(bAnimListElem *ale,
PointerRNA *r_ptr,
PropertyRNA **r_prop)
static bool acf_action_slot_name_prop(bAnimListElem *ale, PointerRNA *r_ptr, PropertyRNA **r_prop)
{
animrig::Binding *binding = static_cast<animrig::Binding *>(ale->data);
animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
BLI_assert(GS(ale->fcurve_owner_id->name) == ID_AC);
*r_ptr = RNA_pointer_create(ale->fcurve_owner_id, &RNA_ActionBinding, binding);
*r_ptr = RNA_pointer_create(ale->fcurve_owner_id, &RNA_ActionSlot, slot);
*r_prop = RNA_struct_find_property(r_ptr, "name_display");
return (*r_prop != nullptr);
}
static int acf_action_binding_icon(bAnimListElem *ale)
static int acf_action_slot_icon(bAnimListElem *ale)
{
animrig::Binding *binding = static_cast<animrig::Binding *>(ale->data);
return UI_icon_from_idcode(binding->idtype);
animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
return UI_icon_from_idcode(slot->idtype);
}
static bool acf_action_binding_setting_valid(bAnimContext * /*ac*/,
static bool acf_action_slot_setting_valid(bAnimContext * /*ac*/,
bAnimListElem * /*ale*/,
const eAnimChannel_Settings setting)
{
@ -1404,7 +1402,7 @@ static bool acf_action_binding_setting_valid(bAnimContext * /*ac*/,
}
}
static int acf_action_binding_setting_flag(bAnimContext * /*ac*/,
static int acf_action_slot_setting_flag(bAnimContext * /*ac*/,
eAnimChannel_Settings setting,
bool *r_neg)
{
@ -1412,23 +1410,23 @@ static int acf_action_binding_setting_flag(bAnimContext * /*ac*/,
switch (setting) {
case ACHANNEL_SETTING_SELECT:
return int(animrig::Binding::Flags::Selected);
return int(animrig::Slot::Flags::Selected);
case ACHANNEL_SETTING_EXPAND:
return int(animrig::Binding::Flags::Expanded);
return int(animrig::Slot::Flags::Expanded);
default:
return 0;
}
}
static void *acf_action_binding_setting_ptr(bAnimListElem *ale,
static void *acf_action_slot_setting_ptr(bAnimListElem *ale,
eAnimChannel_Settings /*setting*/,
short *r_type)
{
animrig::Binding *binding = static_cast<animrig::Binding *>(ale->data);
return GET_ACF_FLAG_PTR(binding->binding_flags, r_type);
animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
return GET_ACF_FLAG_PTR(slot->slot_flags, r_type);
}
static bAnimChannelType ACF_ACTION_BINDING = {
/*channel_type_name*/ "Action Binding",
static bAnimChannelType ACF_ACTION_SLOT = {
/*channel_type_name*/ "Action Slot",
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
@ -1437,13 +1435,13 @@ static bAnimChannelType ACF_ACTION_BINDING = {
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ acf_generic_group_offset,
/*name*/ acf_action_binding_name,
/*name_prop*/ acf_action_binding_name_prop,
/*icon*/ acf_action_binding_icon,
/*name*/ acf_action_slot_name,
/*name_prop*/ acf_action_slot_name_prop,
/*icon*/ acf_action_slot_icon,
/*has_setting*/ acf_action_binding_setting_valid,
/*setting_flag*/ acf_action_binding_setting_flag,
/*setting_ptr*/ acf_action_binding_setting_ptr,
/*has_setting*/ acf_action_slot_setting_valid,
/*setting_flag*/ acf_action_slot_setting_flag,
/*setting_ptr*/ acf_action_slot_setting_ptr,
};
#endif // WITH_ANIM_BAKLAVA
@ -4491,7 +4489,7 @@ static void ANIM_init_channel_typeinfo_data()
#ifdef WITH_ANIM_BAKLAVA
animchannelTypeInfo[type++] = &ACF_FILLANIM; /* Object's Layered Action Expander */
animchannelTypeInfo[type++] = &ACF_ACTION_BINDING; /* Action Binding Expander */
animchannelTypeInfo[type++] = &ACF_ACTION_SLOT; /* Action Slot Expander */
#else
animchannelTypeInfo[type++] = nullptr;
animchannelTypeInfo[type++] = nullptr;
@ -4539,8 +4537,8 @@ static void ANIM_init_channel_typeinfo_data()
#ifdef WITH_ANIM_BAKLAVA
BLI_assert_msg(animchannelTypeInfo[ANIMTYPE_FILLACT_LAYERED] == &ACF_FILLANIM,
"ANIMTYPE_FILLACT_LAYERED does not match ACF_FILLANIM");
BLI_assert_msg(animchannelTypeInfo[ANIMTYPE_ACTION_BINDING] == &ACF_ACTION_BINDING,
"ANIMTYPE_ACTION_BINDING does not match ACF_ACTION_BINDING");
BLI_assert_msg(animchannelTypeInfo[ANIMTYPE_ACTION_SLOT] == &ACF_ACTION_SLOT,
"ANIMTYPE_ACTION_SLOT does not match ACF_ACTION_SLOT");
#endif
}
}

@ -198,7 +198,7 @@ static bool get_channel_bounds(bAnimContext *ac,
case ALE_ACT:
case ALE_GROUP:
case ALE_ACTION_LAYERED:
case ALE_ACTION_BINDING:
case ALE_ACTION_SLOT:
case ALE_GREASE_PENCIL_DATA:
case ALE_GREASE_PENCIL_GROUP:
return false;
@ -312,7 +312,7 @@ void ANIM_set_active_channel(bAnimContext *ac,
case ANIMTYPE_SUMMARY:
case ANIMTYPE_SCENE:
case ANIMTYPE_OBJECT:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
case ANIMTYPE_NLACONTROLS:
case ANIMTYPE_FILLDRIVERS:
case ANIMTYPE_DSNTREE:
@ -453,7 +453,7 @@ bool ANIM_is_active_channel(bAnimListElem *ale)
case ANIMTYPE_SUMMARY:
case ANIMTYPE_SCENE:
case ANIMTYPE_OBJECT:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
case ANIMTYPE_NLACONTROLS:
case ANIMTYPE_FILLDRIVERS:
case ANIMTYPE_SHAPEKEY:
@ -554,9 +554,9 @@ static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListB
return ACHANNEL_SETFLAG_CLEAR;
}
break;
case ANIMTYPE_ACTION_BINDING: {
case ANIMTYPE_ACTION_SLOT: {
using namespace blender::animrig;
if (static_cast<Binding *>(ale->data)->is_selected()) {
if (static_cast<Slot *>(ale->data)->is_selected()) {
return ACHANNEL_SETFLAG_CLEAR;
}
break;
@ -721,9 +721,9 @@ static void anim_channels_select_set(bAnimContext *ac,
nlt->flag &= ~NLATRACK_ACTIVE;
break;
}
case ANIMTYPE_ACTION_BINDING: {
animrig::Binding *binding = static_cast<animrig::Binding *>(ale->data);
templated_selection_state_update(*binding, sel);
case ANIMTYPE_ACTION_SLOT: {
animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
templated_selection_state_update(*slot, sel);
break;
}
case ANIMTYPE_FILLACTD: /* Action Expander */
@ -2374,7 +2374,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator * /*op*/)
case ANIMTYPE_GROUP:
case ANIMTYPE_NLACONTROLS:
case ANIMTYPE_FILLACT_LAYERED:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
case ANIMTYPE_FILLACTD:
case ANIMTYPE_FILLDRIVERS:
case ANIMTYPE_DSMAT:
@ -3189,10 +3189,10 @@ static void box_select_anim_channels(bAnimContext *ac, rcti *rect, short selectm
ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
break;
}
case ANIMTYPE_ACTION_BINDING: {
case ANIMTYPE_ACTION_SLOT: {
using namespace blender::animrig;
Binding *binding = static_cast<Binding *>(ale->data);
templated_selection_state_update(*binding, eAnimChannels_SetFlag(selectmode));
Slot *slot = static_cast<Slot *>(ale->data);
templated_selection_state_update(*slot, eAnimChannels_SetFlag(selectmode));
break;
}
case ANIMTYPE_NONE:

@ -298,7 +298,7 @@ void ANIM_sync_animchannels_to_data(const bContext *C)
case ANIMTYPE_NLACONTROLS:
case ANIMTYPE_NLACURVE:
case ANIMTYPE_FILLACT_LAYERED:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
case ANIMTYPE_FILLACTD:
case ANIMTYPE_FILLDRIVERS:
case ANIMTYPE_DSMAT:

@ -674,14 +674,14 @@ static bAnimListElem *make_new_animlistelem(
ale->datatype = ALE_ACTION_LAYERED;
break;
}
case ANIMTYPE_ACTION_BINDING: {
animrig::Binding *binding = static_cast<animrig::Binding *>(data);
ale->flag = binding->binding_flags;
case ANIMTYPE_ACTION_SLOT: {
animrig::Slot *slot = static_cast<animrig::Slot *>(data);
ale->flag = slot->slot_flags;
BLI_assert_msg(GS(fcurve_owner_id->name) == ID_AC, "fcurve_owner_id should be an Action");
/* ale->data = the binding itself, key_data = the Action. */
/* ale->data = the slot itself, key_data = the Action. */
ale->key_data = fcurve_owner_id;
ale->datatype = ALE_ACTION_BINDING;
ale->datatype = ALE_ACTION_SLOT;
break;
}
case ANIMTYPE_FILLACTD: {
@ -1311,13 +1311,13 @@ static size_t animfilter_fcurves(bAnimContext *ac,
/**
* Add `bAnimListElem`s to `anim_data` for each F-Curve in `fcurves`.
*
* \param binding_handle The binding handle that these F-Curves animate. This is
* used later to look up the ID* of the user of the binding, which in turn is
* \param slot_handle The slot handle that these F-Curves animate. This is
* used later to look up the ID* of the user of the slot, which in turn is
* used to construct a suitable F-Curve label for in the channels list.
*
* \param owner_id The ID whose 'animdata->action' pointer was followed to get to
* these F-Curves. This ID may be animated by a different binding than referenced by
* `binding_handle`, so do _not_ treat this as "the ID animated by these F-Curves".
* these F-Curves. This ID may be animated by a different slot than referenced by
* `slot_handle`, so do _not_ treat this as "the ID animated by these F-Curves".
*
* \param fcurve_owner_id The ID that holds these F-Curves. Typically an Action, but can be any ID,
* for example in the case of drivers.
@ -1325,7 +1325,7 @@ static size_t animfilter_fcurves(bAnimContext *ac,
static size_t animfilter_fcurves_span(bAnimContext *ac,
ListBase * /*bAnimListElem*/ anim_data,
Span<FCurve *> fcurves,
const animrig::binding_handle_t binding_handle,
const animrig::slot_handle_t slot_handle,
const eAnimFilter_Flags filter_mode,
ID *owner_id,
ID *fcurve_owner_id)
@ -1359,17 +1359,17 @@ static size_t animfilter_fcurves_span(bAnimContext *ac,
bAnimListElem *ale = make_new_animlistelem(
ac->bmain, fcu, ANIMTYPE_FCURVE, owner_id, fcurve_owner_id);
/* bAnimListElem::binding_handle is exposed as int32_t and not as binding_handle_t, so better
/* bAnimListElem::slot_handle is exposed as int32_t and not as slot_handle_t, so better
* ensure that these are still equivalent.
* TODO: move to another part of the code. */
static_assert(
std::is_same_v<decltype(ActionBinding::handle), decltype(bAnimListElem::binding_handle)>);
std::is_same_v<decltype(ActionSlot::handle), decltype(bAnimListElem::slot_handle)>);
/* Note that this might not be the same as ale->adt->binding_handle. The reason this F-Curve is
/* Note that this might not be the same as ale->adt->slot_handle. The reason this F-Curve is
* shown could be because it's in the Action editor, showing ale->adt->action with _all_
* bindings, and this F-Curve could be from a different binding than what's used by the owner
* slots, and this F-Curve could be from a different slot than what's used by the owner
* of `ale->adt`. */
ale->binding_handle = binding_handle;
ale->slot_handle = slot_handle;
BLI_addtail(anim_data, ale);
num_items++;
@ -1473,12 +1473,12 @@ static size_t animfilter_act_group(bAnimContext *ac,
}
/**
* Add a channel for each Binding, with their FCurves when the Binding is expanded.
* Add a channel for each Slot, with their FCurves when the Slot is expanded.
*/
static size_t animfilter_action_binding(bAnimContext *ac,
static size_t animfilter_action_slot(bAnimContext *ac,
ListBase *anim_data,
animrig::Action &action,
animrig::Binding &binding,
animrig::Slot &slot,
const eAnimFilter_Flags filter_mode,
ID *owner_id)
{
@ -1492,40 +1492,39 @@ static size_t animfilter_action_binding(bAnimContext *ac,
const bool selection_matters = filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL);
const bool must_be_selected = filter_mode & ANIMFILTER_SEL;
const bool selection_ok_for_binding = !selection_matters ||
binding.is_selected() == must_be_selected;
const bool selection_ok_for_slot = !selection_matters || slot.is_selected() == must_be_selected;
int items = 0;
/* Add a list element for the Binding itself, but only if in Action mode. The Dopesheet mode
* shouldn't display Bindings, as F-Curves are always shown in the context of the animated ID
/* Add a list element for the Slot itself, but only if in Action mode. The Dopesheet mode
* shouldn't display Slots, as F-Curves are always shown in the context of the animated ID
* anyway. */
const bool is_action_mode = (ac->mode == SACTCONT_ACTION);
const bool show_fcurves_only = (filter_mode & ANIMFILTER_FCURVESONLY);
const bool include_summary_channels = (filter_mode & ANIMFILTER_LIST_CHANNELS);
const bool show_binding_channel = (is_action_mode && selection_ok_for_binding &&
!show_fcurves_only && include_summary_channels);
if (show_binding_channel) {
ANIMCHANNEL_NEW_CHANNEL(ac->bmain, &binding, ANIMTYPE_ACTION_BINDING, owner_id, &action.id);
const bool show_slot_channel = (is_action_mode && selection_ok_for_slot && !show_fcurves_only &&
include_summary_channels);
if (show_slot_channel) {
ANIMCHANNEL_NEW_CHANNEL(ac->bmain, &slot, ANIMTYPE_ACTION_SLOT, owner_id, &action.id);
items++;
}
/* If the 'list visible' flag is used, the expansion state of the Binding
/* If the 'list visible' flag is used, the expansion state of the Slot
* matters. Otherwise the sub-channels can always be listed. */
const bool visible_only = (filter_mode & ANIMFILTER_LIST_VISIBLE);
const bool expansion_is_ok = !visible_only || !show_binding_channel || binding.is_expanded();
const bool expansion_is_ok = !visible_only || !show_slot_channel || slot.is_expanded();
if (show_fcurves_only || expansion_is_ok) {
/* Add list elements for the F-Curves for this Binding. */
Span<FCurve *> fcurves = animrig::fcurves_for_animation(action, binding.handle);
/* Add list elements for the F-Curves for this Slot. */
Span<FCurve *> fcurves = animrig::fcurves_for_animation(action, slot.handle);
items += animfilter_fcurves_span(
ac, anim_data, fcurves, binding.handle, filter_mode, owner_id, &action.id);
ac, anim_data, fcurves, slot.handle, filter_mode, owner_id, &action.id);
}
return items;
}
static size_t animfilter_action_bindings(bAnimContext *ac,
static size_t animfilter_action_slots(bAnimContext *ac,
ListBase *anim_data,
animrig::Action &action,
const eAnimFilter_Flags filter_mode,
@ -1540,9 +1539,9 @@ static size_t animfilter_action_bindings(bAnimContext *ac,
}
int num_items = 0;
for (animrig::Binding *binding : action.bindings()) {
BLI_assert(binding);
num_items += animfilter_action_binding(ac, anim_data, action, *binding, filter_mode, owner_id);
for (animrig::Slot *slot : action.slots()) {
BLI_assert(slot);
num_items += animfilter_action_slot(ac, anim_data, action, *slot, filter_mode, owner_id);
}
return num_items;
@ -1551,7 +1550,7 @@ static size_t animfilter_action_bindings(bAnimContext *ac,
static size_t animfilter_action(bAnimContext *ac,
ListBase *anim_data,
animrig::Action &action,
const animrig::binding_handle_t binding_handle,
const animrig::slot_handle_t slot_handle,
const eAnimFilter_Flags filter_mode,
ID *owner_id)
{
@ -1595,20 +1594,20 @@ static size_t animfilter_action(bAnimContext *ac,
/* For now we don't show layers anywhere, just the contained F-Curves. */
/* Only show all Bindings in Action editor mode. Otherwise the F-Curves ought to be displayed
/* Only show all Slots in Action editor mode. Otherwise the F-Curves ought to be displayed
* underneath their animated ID anyway. */
const bool is_action_mode = (ac->mode == SACTCONT_ACTION);
const bool show_all_bindings = (ac->ads->filterflag & ADS_FILTER_ALL_BINDINGS);
if (is_action_mode && show_all_bindings) {
return animfilter_action_bindings(ac, anim_data, action, filter_mode, owner_id);
const bool show_all_slots = (ac->ads->filterflag & ADS_FILTER_ALL_SLOTS);
if (is_action_mode && show_all_slots) {
return animfilter_action_slots(ac, anim_data, action, filter_mode, owner_id);
}
animrig::Binding *binding = action.binding_for_handle(binding_handle);
if (!binding) {
/* Can happen when an Action is assigned, but not a Binding. */
animrig::Slot *slot = action.slot_for_handle(slot_handle);
if (!slot) {
/* Can happen when an Action is assigned, but not a Slot. */
return 0;
}
return animfilter_action_binding(ac, anim_data, action, *binding, filter_mode, owner_id);
return animfilter_action_slot(ac, anim_data, action, *slot, filter_mode, owner_id);
}
/* Include NLA-Data for NLA-Editor:
@ -1810,7 +1809,7 @@ static size_t animfilter_block_data(bAnimContext *ac,
items += animfilter_action(ac,
anim_data,
adt->action->wrap(),
adt->binding_handle,
adt->slot_handle,
eAnimFilter_Flags(filter_mode),
id);
},
@ -1818,7 +1817,7 @@ static size_t animfilter_block_data(bAnimContext *ac,
items += animfilter_action(ac,
anim_data,
adt->action->wrap(),
adt->binding_handle,
adt->slot_handle,
eAnimFilter_Flags(filter_mode),
id);
});
@ -1903,7 +1902,7 @@ static size_t animdata_filter_shapekey(bAnimContext *ac,
items = animfilter_action(ac,
anim_data,
key->adt->action->wrap(),
key->adt->binding_handle,
key->adt->slot_handle,
eAnimFilter_Flags(filter_mode),
(ID *)key);
}
@ -3783,9 +3782,8 @@ size_t ANIM_animdata_filter(bAnimContext *ac,
"This code assumes the Action editor shows the Action of the active object");
animrig::Action &action = static_cast<bAction *>(data)->wrap();
const animrig::binding_handle_t binding_handle = obact->adt->binding_handle;
items += animfilter_action(
ac, anim_data, action, binding_handle, filter_mode, (ID *)obact);
const animrig::slot_handle_t slot_handle = obact->adt->slot_handle;
items += animfilter_action(ac, anim_data, action, slot_handle, filter_mode, (ID *)obact);
}
}

@ -64,17 +64,17 @@ class ActionFilterTest : public testing::Test {
}
};
TEST_F(ActionFilterTest, bindings_expanded_or_not)
TEST_F(ActionFilterTest, slots_expanded_or_not)
{
Binding &bind_cube = action->binding_add();
Binding &bind_suzanne = action->binding_add();
Slot &bind_cube = action->slot_add();
Slot &bind_suzanne = action->slot_add();
ASSERT_TRUE(action->assign_id(&bind_cube, cube->id));
ASSERT_TRUE(action->assign_id(&bind_suzanne, suzanne->id));
Layer &layer = action->layer_add("Kübus layer");
KeyframeStrip &key_strip = layer.strip_add(Strip::Type::Keyframe).as<KeyframeStrip>();
/* Create multiple FCurves for multiple Bindings. */
/* Create multiple FCurves for multiple Slots. */
const KeyframeSettings settings = get_keyframe_settings(false);
ASSERT_EQ(SingleKeyingResult::SUCCESS,
key_strip.keyframe_insert(bind_cube, {"location", 0}, {1.0f, 0.25f}, settings));
@ -93,8 +93,8 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
/* Mock an bAnimContext for the Animation editor, with the above Animation showing. */
SpaceAction saction = {0};
saction.action = action;
saction.action_binding_handle = bind_cube.handle;
saction.ads.filterflag = ADS_FILTER_ALL_BINDINGS;
saction.action_slot_handle = bind_cube.handle;
saction.ads.filterflag = ADS_FILTER_ALL_SLOTS;
bAnimContext ac = {0};
ac.datatype = ANIMCONT_ACTION;
@ -104,11 +104,11 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
ac.obact = cube;
ac.ads = &saction.ads;
{ /* Test with collapsed bindings. */
{ /* Test with collapsed slots. */
bind_cube.set_expanded(false);
bind_suzanne.set_expanded(false);
/* This should produce 2 bindings and no FCurves. */
/* This should produce 2 slots and no FCurves. */
ListBase anim_data = {nullptr, nullptr};
eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
@ -119,33 +119,33 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
EXPECT_EQ(2, BLI_listbase_count(&anim_data));
ASSERT_GE(num_entries, 1)
<< "Missing 1st ANIMTYPE_ACTION_BINDING entry, stopping to prevent crash";
<< "Missing 1st ANIMTYPE_ACTION_SLOT entry, stopping to prevent crash";
const bAnimListElem *first_ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, 0));
EXPECT_EQ(ANIMTYPE_ACTION_BINDING, first_ale->type);
EXPECT_EQ(ALE_ACTION_BINDING, first_ale->datatype);
EXPECT_EQ(ANIMTYPE_ACTION_SLOT, first_ale->type);
EXPECT_EQ(ALE_ACTION_SLOT, first_ale->datatype);
EXPECT_EQ(&cube->id, first_ale->id) << "id should be the animated ID (" << cube->id.name
<< ") but is (" << first_ale->id->name << ")";
EXPECT_EQ(cube->adt, first_ale->adt) << "adt should be the animated ID's animation data";
EXPECT_EQ(&action->id, first_ale->fcurve_owner_id) << "fcurve_owner_id should be the Action";
EXPECT_EQ(&action->id, first_ale->key_data) << "key_data should be the Action";
EXPECT_EQ(&bind_cube, first_ale->data);
EXPECT_EQ(bind_cube.binding_flags, first_ale->flag);
EXPECT_EQ(bind_cube.slot_flags, first_ale->flag);
ASSERT_GE(num_entries, 2)
<< "Missing 2nd ANIMTYPE_ACTION_BINDING entry, stopping to prevent crash";
<< "Missing 2nd ANIMTYPE_ACTION_SLOT entry, stopping to prevent crash";
const bAnimListElem *second_ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, 1));
EXPECT_EQ(ANIMTYPE_ACTION_BINDING, second_ale->type);
EXPECT_EQ(ANIMTYPE_ACTION_SLOT, second_ale->type);
EXPECT_EQ(&bind_suzanne, second_ale->data);
/* Assume the rest is set correctly, as it's the same code as tested above. */
ANIM_animdata_freelist(&anim_data);
}
{ /* Test with one expanded and one collapsed binding. */
{ /* Test with one expanded and one collapsed slot. */
bind_cube.set_expanded(true);
bind_suzanne.set_expanded(false);
/* This should produce 2 bindings and 2 FCurves. */
/* This should produce 2 slots and 2 FCurves. */
ListBase anim_data = {nullptr, nullptr};
eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
@ -155,10 +155,10 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
EXPECT_EQ(4, num_entries);
EXPECT_EQ(4, BLI_listbase_count(&anim_data));
/* First should be Cube binding. */
/* First should be Cube slot. */
ASSERT_GE(num_entries, 1) << "Missing 1st ale, stopping to prevent crash";
const bAnimListElem *ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, 0));
EXPECT_EQ(ANIMTYPE_ACTION_BINDING, ale->type);
EXPECT_EQ(ANIMTYPE_ACTION_SLOT, ale->type);
EXPECT_EQ(&bind_cube, ale->data);
/* After that the Cube's FCurves. */
@ -166,24 +166,24 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, 1));
EXPECT_EQ(ANIMTYPE_FCURVE, ale->type);
EXPECT_EQ(fcu_cube_loc_x, ale->data);
EXPECT_EQ(bind_cube.handle, ale->binding_handle);
EXPECT_EQ(bind_cube.handle, ale->slot_handle);
ASSERT_GE(num_entries, 3) << "Missing 3rd ale, stopping to prevent crash";
ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, 2));
EXPECT_EQ(ANIMTYPE_FCURVE, ale->type);
EXPECT_EQ(fcu_cube_loc_y, ale->data);
EXPECT_EQ(bind_cube.handle, ale->binding_handle);
EXPECT_EQ(bind_cube.handle, ale->slot_handle);
/* And finally the Suzanne binding. */
/* And finally the Suzanne slot. */
ASSERT_GE(num_entries, 4) << "Missing 4th ale, stopping to prevent crash";
ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, 3));
EXPECT_EQ(ANIMTYPE_ACTION_BINDING, ale->type);
EXPECT_EQ(ANIMTYPE_ACTION_SLOT, ale->type);
EXPECT_EQ(&bind_suzanne, ale->data);
ANIM_animdata_freelist(&anim_data);
}
{ /* Test one expanded and one collapsed binding, and one Binding and one FCurve selected. */
{ /* Test one expanded and one collapsed slot, and one Slot and one FCurve selected. */
bind_cube.set_expanded(true);
bind_cube.set_selected(false);
bind_suzanne.set_expanded(false);
@ -192,7 +192,7 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
fcu_cube_loc_x->flag &= ~FCURVE_SELECTED;
fcu_cube_loc_y->flag |= FCURVE_SELECTED;
/* This should produce 1 binding and 1 FCurve. */
/* This should produce 1 slot and 1 FCurve. */
ListBase anim_data = {nullptr, nullptr};
eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
@ -207,9 +207,9 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
EXPECT_EQ(ANIMTYPE_FCURVE, ale->type);
EXPECT_EQ(fcu_cube_loc_y, ale->data);
/* Second the Suzanne binding, as that's the only selected binding. */
/* Second the Suzanne slot, as that's the only selected slot. */
ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, 1));
EXPECT_EQ(ANIMTYPE_ACTION_BINDING, ale->type);
EXPECT_EQ(ANIMTYPE_ACTION_SLOT, ale->type);
EXPECT_EQ(&bind_suzanne, ale->data);
ANIM_animdata_freelist(&anim_data);
@ -218,8 +218,8 @@ TEST_F(ActionFilterTest, bindings_expanded_or_not)
TEST_F(ActionFilterTest, layered_action_active_fcurves)
{
Binding &bind_cube = action->binding_add();
/* The Action+Binding has to be assigned to what the bAnimContext thinks is the active Object.
Slot &bind_cube = action->slot_add();
/* The Action+Slot has to be assigned to what the bAnimContext thinks is the active Object.
* See the BLI_assert_msg() call in the ANIMCONT_ACTION case of ANIM_animdata_filter(). */
ASSERT_TRUE(action->assign_id(&bind_cube, cube->id));
@ -244,8 +244,8 @@ TEST_F(ActionFilterTest, layered_action_active_fcurves)
/* Mock an bAnimContext for the Action editor. */
SpaceAction saction = {0};
saction.action = action;
saction.action_binding_handle = bind_cube.handle;
saction.ads.filterflag = ADS_FILTER_ALL_BINDINGS;
saction.action_slot_handle = bind_cube.handle;
saction.ads.filterflag = ADS_FILTER_ALL_SLOTS;
bAnimContext ac = {0};
ac.datatype = ANIMCONT_ACTION;

@ -207,7 +207,7 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
}
std::string getname_anim_fcurve_bound(Main &bmain,
const blender::animrig::Binding &binding,
const blender::animrig::Slot &slot,
FCurve &fcurve)
{
/* TODO: Refactor to avoid this variable. */
@ -215,8 +215,8 @@ std::string getname_anim_fcurve_bound(Main &bmain,
char name_buffer[name_maxncpy];
name_buffer[0] = '\0';
/* Check the Binding's users to see if we can find an ID* that can resolve the F-Curve. */
for (ID *user : binding.users(bmain)) {
/* Check the Slot's users to see if we can find an ID* that can resolve the F-Curve. */
for (ID *user : slot.users(bmain)) {
const int icon = getname_anim_fcurve(name_buffer, user, &fcurve);
if (icon) {
/* Managed to find a name! */
@ -224,21 +224,21 @@ std::string getname_anim_fcurve_bound(Main &bmain,
}
}
if (!binding.users(bmain).is_empty()) {
/* This binding is assigned to at least one ID, and still the property it animates could not be
if (!slot.users(bmain).is_empty()) {
/* This slot is assigned to at least one ID, and still the property it animates could not be
* found. There is no use in continuing. */
fcurve.flag |= FCURVE_DISABLED;
return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
}
/* If this part of the code is hit, the binding is not assigned to anything. The remainder of
/* If this part of the code is hit, the slot is not assigned to anything. The remainder of
* this function is all a best-effort attempt. Because of that, it will not set the
* FCURVE_DISABLED flag on the F-Curve, as having unassigned animation data is not an error (and
* that flag indicates an error). */
/* Fall back to the ID type of the binding for simple properties. */
if (!binding.has_idtype()) {
/* The Binding has never been assigned to any ID, so we don't even know what type of ID it is
/* Fall back to the ID type of the slot for simple properties. */
if (!slot.has_idtype()) {
/* The Slot has never been assigned to any ID, so we don't even know what type of ID it is
* meant for. */
return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
}
@ -248,8 +248,8 @@ std::string getname_anim_fcurve_bound(Main &bmain,
return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
}
/* Find the StructRNA for this Binding's ID type. */
StructRNA *srna = ID_code_to_RNA_type(binding.idtype);
/* Find the StructRNA for this Slot's ID type. */
StructRNA *srna = ID_code_to_RNA_type(slot.idtype);
if (!srna) {
return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
}

@ -713,10 +713,10 @@ static void ANIM_OT_scene_range_frame(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Bindings
/** \name Slots
* \{ */
static bool binding_unassign_object_poll(bContext *C)
static bool slot_unassign_object_poll(bContext *C)
{
Object *object = CTX_data_active_object(C);
if (!object) {
@ -728,31 +728,31 @@ static bool binding_unassign_object_poll(bContext *C)
return false;
}
return adt->binding_handle != blender::animrig::Binding::unassigned;
return adt->slot_handle != blender::animrig::Slot::unassigned;
}
static int binding_unassign_object_exec(bContext *C, wmOperator * /*op*/)
static int slot_unassign_object_exec(bContext *C, wmOperator * /*op*/)
{
using namespace blender;
Object *object = CTX_data_active_object(C);
animrig::unassign_binding(object->id);
animrig::unassign_slot(object->id);
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
return OPERATOR_FINISHED;
}
static void ANIM_OT_binding_unassign_object(wmOperatorType *ot)
static void ANIM_OT_slot_unassign_object(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Unassign Binding";
ot->idname = "ANIM_OT_binding_unassign_object";
ot->name = "Unassign Slot";
ot->idname = "ANIM_OT_slot_unassign_object";
ot->description =
"Clear the assigned action binding, effectively making this data-block non-animated";
"Clear the assigned action slot, effectively making this data-block non-animated";
/* api callbacks */
ot->exec = binding_unassign_object_exec;
ot->poll = binding_unassign_object_poll;
ot->exec = slot_unassign_object_exec;
ot->poll = slot_unassign_object_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@ -805,7 +805,7 @@ void ED_operatortypes_anim()
WM_operatortype_append(ANIM_OT_keying_set_active_set);
WM_operatortype_append(ANIM_OT_binding_unassign_object);
WM_operatortype_append(ANIM_OT_slot_unassign_object);
}
void ED_keymap_anim(wmKeyConfig *keyconf)

@ -389,7 +389,7 @@ enum class ChannelType {
OBJECT,
FCURVE,
ACTION_LAYERED,
ACTION_BINDING,
ACTION_SLOT,
ACTION_LEGACY,
ACTION_GROUP,
GREASE_PENCIL_CELS,
@ -417,7 +417,7 @@ struct ChannelListElement {
AnimData *adt;
FCurve *fcu;
bAction *act;
animrig::Binding *action_binding;
animrig::Slot *action_slot;
bActionGroup *agrp;
bGPDlayer *gpl;
const GreasePencilLayer *grease_pencil_layer;
@ -449,12 +449,12 @@ static void build_channel_keylist(ChannelListElement *elem, blender::float2 rang
action_to_keylist(elem->adt, elem->act, elem->keylist, elem->saction_flag, range);
break;
}
case ChannelType::ACTION_BINDING: {
case ChannelType::ACTION_SLOT: {
BLI_assert(elem->act);
BLI_assert(elem->action_binding);
action_binding_to_keylist(elem->adt,
BLI_assert(elem->action_slot);
action_slot_to_keylist(elem->adt,
elem->act->wrap(),
elem->action_binding->handle,
elem->action_slot->handle,
elem->keylist,
elem->saction_flag,
range);
@ -748,10 +748,10 @@ void ED_add_action_layered_channel(ChannelDrawList *channel_list,
draw_elem->channel_locked = locked;
}
void ED_add_action_binding_channel(ChannelDrawList *channel_list,
void ED_add_action_slot_channel(ChannelDrawList *channel_list,
AnimData *adt,
animrig::Action &action,
animrig::Binding &binding,
animrig::Slot &slot,
const float ypos,
const float yscale_fac,
int saction_flag)
@ -760,10 +760,10 @@ void ED_add_action_binding_channel(ChannelDrawList *channel_list,
saction_flag &= ~SACTION_SHOW_EXTREMES;
ChannelListElement *draw_elem = channel_list_add_element(
channel_list, ChannelType::ACTION_BINDING, ypos, yscale_fac, eSAction_Flag(saction_flag));
channel_list, ChannelType::ACTION_SLOT, ypos, yscale_fac, eSAction_Flag(saction_flag));
draw_elem->adt = adt;
draw_elem->act = &action;
draw_elem->action_binding = &binding;
draw_elem->action_slot = &slot;
draw_elem->channel_locked = locked;
}

@ -171,17 +171,17 @@ 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::Binding *binding,
animrig::Slot *slot,
KeyframeEditFunc key_ok,
KeyframeEditFunc key_cb,
FcuEditFunc fcu_cb)
{
if (!binding) {
if (!slot) {
/* Valid situation, and will not have any FCurves. */
return 0;
}
Span<FCurve *> fcurves = animrig::fcurves_for_animation(anim, binding->handle);
Span<FCurve *> fcurves = animrig::fcurves_for_animation(anim, slot->handle);
for (FCurve *fcurve : fcurves) {
if (ANIM_fcurve_keyframes_loop(ked, fcurve, key_ok, key_cb, fcu_cb)) {
return 1;
@ -420,20 +420,20 @@ short ANIM_animchannel_keyframes_loop(KeyframeEditData *ked,
#ifdef WITH_ANIM_BAKLAVA
/* 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 binding. */
* the keys that belong to that slot. */
animrig::Action &anim = static_cast<bAction *>(ale->key_data)->wrap();
animrig::Binding *binding = anim.binding_for_handle(ale->adt->binding_handle);
return action_layered_keyframes_loop(ked, anim, binding, key_ok, key_cb, fcu_cb);
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);
#else
return 0;
#endif
}
case ALE_ACTION_BINDING: {
case ALE_ACTION_SLOT: {
#ifdef WITH_ANIM_BAKLAVA
animrig::Action *action = static_cast<animrig::Action *>(ale->key_data);
BLI_assert(action);
animrig::Binding *binding = static_cast<animrig::Binding *>(ale->data);
return action_layered_keyframes_loop(ked, *action, binding, key_ok, key_cb, fcu_cb);
animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
return action_layered_keyframes_loop(ked, *action, slot, key_ok, key_cb, fcu_cb);
#else
return 0;
#endif
@ -486,7 +486,7 @@ short ANIM_animchanneldata_keyframes_loop(KeyframeEditData *ked,
case ALE_GROUP: /* action group */
return agrp_keyframes_loop(ked, (bActionGroup *)data, key_ok, key_cb, fcu_cb);
case ALE_ACTION_LAYERED:
case ALE_ACTION_BINDING:
case ALE_ACTION_SLOT:
/* This function is only used in nlaedit_apply_scale_exec(). Since the NLA has no support for
* layered Actions in strips, there is no need to implement this here. */
return 0;

@ -1161,15 +1161,15 @@ void action_group_to_keylist(AnimData *adt,
}
}
void action_binding_to_keylist(AnimData *adt,
void action_slot_to_keylist(AnimData *adt,
animrig::Action &action,
const animrig::binding_handle_t binding_handle,
const animrig::slot_handle_t slot_handle,
AnimKeylist *keylist,
const int saction_flag,
blender::float2 range)
{
BLI_assert(GS(action.id.name) == ID_AC);
for (FCurve *fcurve : fcurves_for_animation(action, binding_handle)) {
for (FCurve *fcurve : fcurves_for_animation(action, slot_handle)) {
fcurve_to_keylist(adt, fcurve, keylist, saction_flag, range);
}
}
@ -1195,10 +1195,10 @@ void action_to_keylist(AnimData *adt,
}
/**
* Assumption: the animation is bound to adt->binding_handle. This assumption will break when we
* have things like reference strips, where the strip can reference another binding handle.
* Assumption: the animation is bound to adt->slot_handle. This assumption will break when we
* have things like reference strips, where the strip can reference another slot handle.
*/
action_binding_to_keylist(adt, action, adt->binding_handle, keylist, saction_flag, range);
action_slot_to_keylist(adt, action, adt->slot_handle, keylist, saction_flag, range);
}
void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active)

@ -153,7 +153,7 @@ enum eAnim_ChannelType {
ANIMTYPE_NLACURVE,
ANIMTYPE_FILLACT_LAYERED, /* Layered Actions. */
ANIMTYPE_ACTION_BINDING,
ANIMTYPE_ACTION_SLOT,
ANIMTYPE_FILLACTD, /* Legacy Actions. */
ANIMTYPE_FILLDRIVERS,
@ -214,7 +214,7 @@ enum eAnim_KeyType {
ALE_ACT, /* Action summary (legacy). */
ALE_GROUP, /* Action Group summary (legacy). */
ALE_ACTION_LAYERED, /* Action summary (layered). */
ALE_ACTION_BINDING, /* Action binding summary. */
ALE_ACTION_SLOT, /* Action slot summary. */
ALE_GREASE_PENCIL_CEL, /* Grease Pencil Cels. */
ALE_GREASE_PENCIL_DATA, /* Grease Pencil Cels summary. */
@ -256,9 +256,9 @@ struct bAnimListElem {
/** for un-named data, the index of the data in its collection */
int index;
/**
* For data that is owned by a specific binding, its handle.
* For data that is owned by a specific slot, its handle.
*
* This is not declared as blender::animrig::binding_handle_t to avoid all the users of this
* This is not declared as blender::animrig::slot_handle_t to avoid all the users of this
* header file to get the animrig module as extra dependency (which would spread to the undo
* system, lineart, etc). It's probably best to split off this struct definition from the rest of
* this header, as most code that uses this header doesn't need to know the definition of this
@ -266,7 +266,7 @@ struct bAnimListElem {
*
* TODO: split off into separate header file.
*/
int32_t binding_handle;
int32_t slot_handle;
/** Tag the element for updating. */
eAnim_Update_Flags update;
@ -899,12 +899,12 @@ bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *c
int getname_anim_fcurve(char *name, ID *id, FCurve *fcu);
/**
* Get the name of an F-Curve that's animating a specific binding.
* Get the name of an F-Curve that's animating a specific slot.
*
* This function iterates the Binding's users to find an ID that allows it to resolve its RNA path.
* This function iterates the Slot's users to find an ID that allows it to resolve its RNA path.
*/
std::string getname_anim_fcurve_bound(Main &bmain,
const blender::animrig::Binding &binding,
const blender::animrig::Slot &slot,
FCurve &fcurve);
/**

@ -79,11 +79,11 @@ void ED_add_action_layered_channel(ChannelDrawList *channel_list,
const float ypos,
const float yscale_fac,
int saction_flag);
/* Action Binding summary. */
void ED_add_action_binding_channel(ChannelDrawList *channel_list,
/* Action Slot summary. */
void ED_add_action_slot_channel(ChannelDrawList *channel_list,
AnimData *adt,
blender::animrig::Action &action,
blender::animrig::Binding &binding,
blender::animrig::Slot &slot,
float ypos,
float yscale_fac,
int saction_flag);

@ -34,7 +34,7 @@ struct bGPdata;
namespace blender::animrig {
class Action;
class Binding;
class Slot;
} // namespace blender::animrig
/* ****************************** Base Structs ****************************** */
@ -171,9 +171,9 @@ void action_group_to_keylist(AnimData *adt,
/* Action */
void action_to_keylist(
AnimData *adt, bAction *act, AnimKeylist *keylist, int saction_flag, blender::float2 range);
void action_binding_to_keylist(AnimData *adt,
void action_slot_to_keylist(AnimData *adt,
blender::animrig::Action &action,
blender::animrig::binding_handle_t binding_handle,
blender::animrig::slot_handle_t slot_handle,
AnimKeylist *keylist,
int saction_flag,
blender::float2 range);

@ -380,11 +380,11 @@ static void draw_keyframes(bAnimContext *ac,
scale_factor,
action_flag);
break;
case ALE_ACTION_BINDING:
ED_add_action_binding_channel(draw_list,
case ALE_ACTION_SLOT:
ED_add_action_slot_channel(draw_list,
adt,
static_cast<bAction *>(ale->key_data)->wrap(),
*static_cast<animrig::Binding *>(ale->data),
*static_cast<animrig::Slot *>(ale->data),
ycenter,
scale_factor,
action_flag);

@ -119,12 +119,12 @@ static void actkeys_list_element_to_keylist(bAnimContext *ac,
action_to_keylist(adt, action, keylist, 0, range);
break;
}
case ALE_ACTION_BINDING: {
case ALE_ACTION_SLOT: {
animrig::Action *action = static_cast<animrig::Action *>(ale->key_data);
animrig::Binding *binding = static_cast<animrig::Binding *>(ale->data);
animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
BLI_assert(action);
BLI_assert(binding);
action_binding_to_keylist(adt, *action, binding->handle, keylist, 0, range);
BLI_assert(slot);
action_slot_to_keylist(adt, *action, slot->handle, keylist, 0, range);
break;
}
case ALE_ACT: {

@ -170,7 +170,7 @@ bool nla_panel_context(const bContext *C,
case ANIMTYPE_NLACONTROLS:
case ANIMTYPE_NLACURVE:
case ANIMTYPE_FILLACT_LAYERED:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
case ANIMTYPE_FILLACTD:
case ANIMTYPE_FILLDRIVERS:
case ANIMTYPE_DSMCLIP:

@ -917,7 +917,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
case ANIMTYPE_NLACONTROLS:
case ANIMTYPE_NLACURVE:
case ANIMTYPE_FILLACT_LAYERED:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
case ANIMTYPE_FILLACTD:
case ANIMTYPE_FILLDRIVERS:
case ANIMTYPE_DSMAT:

@ -264,7 +264,7 @@ static int mouse_nla_tracks(bContext *C, bAnimContext *ac, int track_index, shor
break;
}
case ANIMTYPE_FILLACT_LAYERED:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
/* The NLA doesn't support layered Actions. */
break;
default:

@ -680,7 +680,7 @@ static void createTransActionData(bContext *C, TransInfo *t)
case ANIMTYPE_GROUP:
case ANIMTYPE_NLACONTROLS:
case ANIMTYPE_FILLACT_LAYERED:
case ANIMTYPE_ACTION_BINDING:
case ANIMTYPE_ACTION_SLOT:
case ANIMTYPE_FILLACTD:
case ANIMTYPE_FILLDRIVERS:
case ANIMTYPE_DSMAT:

@ -44,8 +44,8 @@ typedef struct GPUVertBufHandle GPUVertBufHandle;
/* Forward declarations so the actual declarations can happen top-down. */
struct ActionLayer;
struct ActionBinding;
struct ActionBinding_runtime;
struct ActionSlot;
struct ActionSlot_runtime;
struct ActionStrip;
struct ActionChannelBag;
@ -53,16 +53,16 @@ struct ActionChannelBag;
#ifdef __cplusplus
namespace blender::animrig {
class Action;
class Binding;
class BindingRuntime;
class Slot;
class SlotRuntime;
class ChannelBag;
class KeyframeStrip;
class Layer;
class Strip;
} // namespace blender::animrig
using ActionBindingRuntimeHandle = blender::animrig::BindingRuntime;
using ActionSlotRuntimeHandle = blender::animrig::SlotRuntime;
#else
typedef struct ActionBindingRuntimeHandle ActionBindingRuntimeHandle;
typedef struct ActionSlotRuntimeHandle ActionSlotRuntimeHandle;
#endif
/* ************************************************ */
@ -741,9 +741,9 @@ typedef struct bAction {
int layer_array_num;
int layer_active_index; /* Index into layer_array, -1 means 'no active'. */
struct ActionBinding **binding_array; /* Array of 'binding_array_num` bindings. */
int binding_array_num;
int32_t last_binding_handle;
struct ActionSlot **slot_array; /* Array of 'slot_array_num` slots. */
int slot_array_num;
int32_t last_slot_handle;
/* Note about legacy animation data:
*
@ -854,10 +854,10 @@ typedef enum eDopeSheet_FilterFlag {
ADS_FILTER_SUMMARY = (1 << 4),
/**
* Show all Action bindings; if not set, only show the Binding of the
* Show all Action slots; if not set, only show the Slot of the
* data-block that's being animated by the Action.
*/
ADS_FILTER_ALL_BINDINGS = (1 << 5),
ADS_FILTER_ALL_SLOTS = (1 << 5),
/* datatype-based filtering */
ADS_FILTER_NOSHAPEKEYS = (1 << 6),
@ -946,9 +946,9 @@ typedef struct SpaceAction {
/** Copied to region. */
View2D v2d DNA_DEPRECATED;
/** The currently active action and its binding. */
/** The currently active action and its slot. */
bAction *action;
int32_t action_binding_handle;
int32_t action_slot_handle;
char _pad2[4];
/** The currently active context (when not showing action). */
@ -1120,49 +1120,49 @@ typedef struct ActionLayer {
} ActionLayer;
/**
* \see #blender::animrig::Binding
* \see #blender::animrig::Slot
*/
typedef struct ActionBinding {
typedef struct ActionSlot {
/**
* Typically the ID name this binding was created for, including the two
* Typically the ID name this slot was created for, including the two
* letters indicating the ID type.
*
* \see #AnimData::binding_name
* \see #AnimData::slot_name
*/
char name[66]; /* MAX_ID_NAME */
uint8_t _pad0[2];
/**
* Type of ID-blocks that this binding can be assigned to.
* Type of ID-blocks that this slot can be assigned to.
* If 0, will be set to whatever ID is first assigned.
*/
int idtype;
/**
* Identifier of this Binding within the Animation data-block.
* Identifier of this Slot within the Action.
*
* This number allows reorganization of the #bAction::binding_array without
* This number allows reorganization of the #bAction::slot_array without
* invalidating references. Also these remain valid when copy-on-evaluate
* copies are made.
*
* Only valid within the Animation data-block that owns this Binding.
* Only valid within the Action that owns this Slot.
*
* \see #blender::animrig::Action::binding_for_handle()
* \see #blender::animrig::Action::slot_for_handle()
*/
int32_t handle;
/** \see #blender::animrig::Binding::flags() */
int8_t binding_flags;
/** \see #blender::animrig::Slot::flags() */
int8_t slot_flags;
uint8_t _pad1[3];
/** Runtime data. Set to nullptr when writing to disk. */
ActionBindingRuntimeHandle *runtime;
ActionSlotRuntimeHandle *runtime;
#ifdef __cplusplus
blender::animrig::Binding &wrap();
const blender::animrig::Binding &wrap() const;
blender::animrig::Slot &wrap();
const blender::animrig::Slot &wrap() const;
#endif
} ActionBinding;
} ActionSlot;
/**
* \see #blender::animrig::Strip
@ -1215,13 +1215,13 @@ typedef struct KeyframeActionStrip {
* \see #blender::animrig::ChannelBag
*/
typedef struct ActionChannelBag {
int32_t binding_handle;
int32_t slot_handle;
int fcurve_array_num;
struct FCurve **fcurve_array; /* Array of 'fcurve_array_num' FCurves. */
/* TODO: Design & implement a way to integrate other channel types as well,
* and still have them map to a certain binding */
* and still have them map to a certain slot */
#ifdef __cplusplus
blender::animrig::ChannelBag &wrap();
const blender::animrig::ChannelBag &wrap() const;
@ -1230,10 +1230,9 @@ typedef struct ActionChannelBag {
#ifdef __cplusplus
/* Some static assertions that things that should have the same type actually do. */
static_assert(std::is_same_v<decltype(ActionSlot::handle), decltype(bAction::last_slot_handle)>);
static_assert(
std::is_same_v<decltype(ActionBinding::handle), decltype(bAction::last_binding_handle)>);
std::is_same_v<decltype(ActionSlot::handle), decltype(ActionChannelBag::slot_handle)>);
static_assert(
std::is_same_v<decltype(ActionBinding::handle), decltype(ActionChannelBag::binding_handle)>);
static_assert(
std::is_same_v<decltype(ActionBinding::handle), decltype(SpaceAction::action_binding_handle)>);
std::is_same_v<decltype(ActionSlot::handle), decltype(SpaceAction::action_slot_handle)>);
#endif

@ -1114,19 +1114,19 @@ typedef struct AnimData {
bAction *action;
/**
* Identifier for which ActionBinding of the above Animation is actually animating this
* Identifier for which ActionSlot of the above Animation is actually animating this
* data-block.
*
* Do not set this directly, use one of the assignment functions in ANIM_action.hh instead.
*/
int32_t binding_handle;
int32_t slot_handle;
/**
* Binding name, primarily used for mapping to the right binding when assigning
* another Animation data-block. Should be the same type as #ActionBinding::name.
* Slot name, primarily used for mapping to the right slot when assigning
* another Action. Should be the same type as #ActionSlot::name.
*
* \see #ActionBinding::name
* \see #ActionSlot::name
*/
char binding_name[66]; /* MAX_ID_NAME */
char slot_name[66]; /* MAX_ID_NAME */
uint8_t _pad0[2];
/**
@ -1177,8 +1177,8 @@ typedef struct AnimData {
#ifdef __cplusplus
/* Some static assertions that things that should have the same type actually do. */
static_assert(std::is_same_v<decltype(ActionBinding::handle), decltype(AnimData::binding_handle)>);
static_assert(std::is_same_v<decltype(ActionBinding::name), decltype(AnimData::binding_name)>);
static_assert(std::is_same_v<decltype(ActionSlot::handle), decltype(AnimData::slot_handle)>);
static_assert(std::is_same_v<decltype(ActionSlot::name), decltype(AnimData::slot_name)>);
#endif
/* Animation Data settings (mostly for NLA) */

@ -101,9 +101,9 @@ static animrig::Action &rna_action(const PointerRNA *ptr)
return reinterpret_cast<bAction *>(ptr->owner_id)->wrap();
}
static animrig::Binding &rna_data_binding(const PointerRNA *ptr)
static animrig::Slot &rna_data_slot(const PointerRNA *ptr)
{
return reinterpret_cast<ActionBinding *>(ptr->data)->wrap();
return reinterpret_cast<ActionSlot *>(ptr->data)->wrap();
}
static animrig::Layer &rna_data_layer(const PointerRNA *ptr)
@ -145,32 +145,31 @@ static void rna_iterator_array_begin(CollectionPropertyIterator *iter, MutableSp
rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr);
}
static ActionBinding *rna_Action_bindings_new(bAction *anim_id,
static ActionSlot *rna_Action_slots_new(bAction *anim_id,
bContext *C,
ReportList *reports,
ID *id_for_binding)
ID *id_for_slot)
{
animrig::Action &anim = anim_id->wrap();
animrig::Binding *binding;
animrig::Slot *slot;
if (!anim.is_action_layered()) {
BKE_reportf(
reports,
BKE_reportf(reports,
RPT_ERROR,
"Cannot add bindings to a legacy Action '%s'. Convert it to a layered Action first.",
"Cannot add slots to a legacy Action '%s'. Convert it to a layered Action first.",
anim.id.name + 2);
return nullptr;
}
if (id_for_binding) {
binding = &anim.binding_add_for_id(*id_for_binding);
if (id_for_slot) {
slot = &anim.slot_add_for_id(*id_for_slot);
}
else {
binding = &anim.binding_add();
slot = &anim.slot_add();
}
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
return binding;
return slot;
}
static void rna_iterator_action_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
@ -229,89 +228,88 @@ void rna_Action_layers_remove(bAction *dna_action,
DEG_id_tag_update(&anim.id, ID_RECALC_ANIMATION);
}
static void rna_iterator_animation_bindings_begin(CollectionPropertyIterator *iter,
PointerRNA *ptr)
static void rna_iterator_animation_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
rna_iterator_array_begin(iter, anim.bindings());
rna_iterator_array_begin(iter, anim.slots());
}
static int rna_iterator_animation_bindings_length(PointerRNA *ptr)
static int rna_iterator_animation_slots_length(PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
return anim.bindings().size();
return anim.slots().size();
}
static std::optional<std::string> rna_ActionBinding_path(const PointerRNA *ptr)
static std::optional<std::string> rna_ActionSlot_path(const PointerRNA *ptr)
{
animrig::Binding &binding = rna_data_binding(ptr);
animrig::Slot &slot = rna_data_slot(ptr);
char name_esc[sizeof(binding.name) * 2];
BLI_str_escape(name_esc, binding.name, sizeof(name_esc));
return fmt::format("bindings[\"{}\"]", name_esc);
char name_esc[sizeof(slot.name) * 2];
BLI_str_escape(name_esc, slot.name, sizeof(name_esc));
return fmt::format("slots[\"{}\"]", name_esc);
}
/* Name functions that ignore the first two ID characters */
void rna_ActionBinding_name_display_get(PointerRNA *ptr, char *value)
void rna_ActionSlot_name_display_get(PointerRNA *ptr, char *value)
{
animrig::Binding &binding = rna_data_binding(ptr);
binding.name_without_prefix().unsafe_copy(value);
animrig::Slot &slot = rna_data_slot(ptr);
slot.name_without_prefix().unsafe_copy(value);
}
int rna_ActionBinding_name_display_length(PointerRNA *ptr)
int rna_ActionSlot_name_display_length(PointerRNA *ptr)
{
animrig::Binding &binding = rna_data_binding(ptr);
return binding.name_without_prefix().size();
animrig::Slot &slot = rna_data_slot(ptr);
return slot.name_without_prefix().size();
}
static void rna_ActionBinding_name_display_set(PointerRNA *ptr, const char *name)
static void rna_ActionSlot_name_display_set(PointerRNA *ptr, const char *name)
{
animrig::Action &anim = rna_action(ptr);
animrig::Binding &binding = rna_data_binding(ptr);
animrig::Slot &slot = rna_data_slot(ptr);
const StringRef name_ref(name);
if (name_ref.is_empty()) {
WM_report(RPT_ERROR, "Animation binding display names cannot be empty");
WM_report(RPT_ERROR, "Action slot display names cannot be empty");
return;
}
/* Construct the new internal name, from the binding's type and the given name. */
const std::string internal_name = binding.name_prefix_for_idtype() + name_ref;
anim.binding_name_define(binding, internal_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);
}
static void rna_ActionBinding_name_set(PointerRNA *ptr, const char *name)
static void rna_ActionSlot_name_set(PointerRNA *ptr, const char *name)
{
animrig::Action &anim = rna_action(ptr);
animrig::Binding &binding = rna_data_binding(ptr);
animrig::Slot &slot = rna_data_slot(ptr);
const StringRef name_ref(name);
if (name_ref.size() < animrig::Binding::name_length_min) {
WM_report(RPT_ERROR, "Animation binding names should be at least three characters");
if (name_ref.size() < animrig::Slot::name_length_min) {
WM_report(RPT_ERROR, "Action slot names should be at least three characters");
return;
}
if (binding.has_idtype()) {
if (slot.has_idtype()) {
/* Check if the new name is going to be compatible with the already-established ID type. */
const std::string expect_prefix = binding.name_prefix_for_idtype();
const std::string expect_prefix = slot.name_prefix_for_idtype();
if (!name_ref.startswith(expect_prefix)) {
const std::string new_prefix = name_ref.substr(0, 2);
WM_reportf(RPT_WARNING,
"Animation binding renamed to unexpected prefix \"%s\" (expected \"%s\").\n",
"Action slot renamed to unexpected prefix \"%s\" (expected \"%s\").\n",
new_prefix.c_str(),
expect_prefix.c_str());
}
}
anim.binding_name_define(binding, name);
anim.slot_name_define(slot, name);
}
static void rna_ActionBinding_name_update(Main *bmain, Scene *, PointerRNA *ptr)
static void rna_ActionSlot_name_update(Main *bmain, Scene *, PointerRNA *ptr)
{
animrig::Action &anim = rna_action(ptr);
animrig::Binding &binding = rna_data_binding(ptr);
anim.binding_name_propagate(*bmain, binding);
animrig::Slot &slot = rna_data_slot(ptr);
anim.slot_name_propagate(*bmain, slot);
}
static std::optional<std::string> rna_ActionLayer_path(const PointerRNA *ptr)
@ -424,23 +422,23 @@ static bool rna_KeyframeActionStrip_key_insert(ID *id,
KeyframeActionStrip *dna_strip,
Main *bmain,
ReportList *reports,
ActionBinding *dna_binding,
ActionSlot *dna_slot,
const char *rna_path,
const int array_index,
const float value,
const float time)
{
if (dna_binding == nullptr) {
BKE_report(reports, RPT_ERROR, "Binding cannot be None");
if (dna_slot == nullptr) {
BKE_report(reports, RPT_ERROR, "Slot cannot be None");
return false;
}
animrig::KeyframeStrip &key_strip = dna_strip->wrap();
const animrig::Binding &binding = dna_binding->wrap();
const animrig::Slot &slot = dna_slot->wrap();
const animrig::KeyframeSettings settings = animrig::get_keyframe_settings(true);
const animrig::SingleKeyingResult result = key_strip.keyframe_insert(
binding, {rna_path, array_index}, {time, value}, settings, INSERTKEY_NOFLAGS);
slot, {rna_path, array_index}, {time, value}, settings, INSERTKEY_NOFLAGS);
const bool ok = result == animrig::SingleKeyingResult::SUCCESS;
if (ok) {
@ -463,11 +461,11 @@ static int rna_iterator_ChannelBag_fcurves_length(PointerRNA *ptr)
return bag.fcurves().size();
}
static ActionChannelBag *rna_KeyframeActionStrip_channels(
KeyframeActionStrip *self, const animrig::binding_handle_t binding_handle)
static ActionChannelBag *rna_KeyframeActionStrip_channels(KeyframeActionStrip *self,
const animrig::slot_handle_t slot_handle)
{
animrig::KeyframeStrip &key_strip = self->wrap();
return key_strip.channelbag_for_binding(binding_handle);
return key_strip.channelbag_for_slot(slot_handle);
}
# endif // WITH_ANIM_BAKLAVA
@ -877,10 +875,10 @@ static void rna_def_dopesheet(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, nullptr);
prop = RNA_def_property(srna, "show_all_bindings", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "filterflag", ADS_FILTER_ALL_BINDINGS);
RNA_def_property_ui_text(prop, "Show All Bindings", "Show all the Action's Bindings");
RNA_def_property_ui_icon(prop, ICON_LINKED, 0); /* TODO: select icon for Bindings. */
prop = RNA_def_property(srna, "show_all_slots", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "filterflag", ADS_FILTER_ALL_SLOTS);
RNA_def_property_ui_text(prop, "Show All Slots", "Show all the Action's Slots");
RNA_def_property_ui_icon(prop, ICON_LINKED, 0); /* TODO: select icon for Slots. */
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, nullptr);
prop = RNA_def_property(srna, "show_hidden", PROP_BOOLEAN, PROP_NONE);
@ -1165,34 +1163,34 @@ static void rna_def_dopesheet(BlenderRNA *brna)
# ifdef WITH_ANIM_BAKLAVA
static void rna_def_action_bindings(BlenderRNA *brna, PropertyRNA *cprop)
static void rna_def_action_slots(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
FunctionRNA *func;
PropertyRNA *parm;
RNA_def_property_srna(cprop, "ActionBindings");
srna = RNA_def_struct(brna, "ActionBindings", nullptr);
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 Bindings", "Collection of animation bindings");
RNA_def_struct_ui_text(srna, "Action Slots", "Collection of animation slots");
/* Animation.bindings.new(...) */
func = RNA_def_function(srna, "new", "rna_Action_bindings_new");
RNA_def_function_ui_description(func, "Add a binding to the animation");
/* Animation.slots.new(...) */
func = RNA_def_function(srna, "new", "rna_Action_slots_new");
RNA_def_function_ui_description(func, "Add a slot to the animation");
RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
parm = RNA_def_pointer(
func,
"for_id",
"ID",
"Data-Block",
"If given, the new binding will be named after this data-block, and limited to animating "
"If given, the new slot will be named after this data-block, and limited to animating "
"data-blocks of its type. If ommitted, limiting the ID type will happen as soon as the "
"binding is assigned");
"slot is assigned");
/* 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, "binding", "ActionBinding", "", "Newly created animation binding");
parm = RNA_def_pointer(func, "slot", "ActionSlot", "", "Newly created animation slot");
RNA_def_function_return(func, parm);
}
@ -1233,49 +1231,49 @@ static void rna_def_action_layers(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
}
static void rna_def_action_binding(BlenderRNA *brna)
static void rna_def_action_slot(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "ActionBinding", nullptr);
RNA_def_struct_path_func(srna, "rna_ActionBinding_path");
srna = RNA_def_struct(brna, "ActionSlot", nullptr);
RNA_def_struct_path_func(srna, "rna_ActionSlot_path");
RNA_def_struct_ui_text(
srna,
"Animation Binding",
"Animation Slot",
"Identifier for a set of channels in this Animation, 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);
RNA_def_struct_name_property(srna, prop);
RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_ActionBinding_name_set");
RNA_def_property_string_maxlength(prop, sizeof(ActionBinding::name) - 2);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_ActionBinding_name_update");
RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_ActionSlot_name_set");
RNA_def_property_string_maxlength(prop, sizeof(ActionSlot::name) - 2);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_ActionSlot_name_update");
RNA_def_struct_ui_text(
srna,
"Binding Name",
"Used when connecting an Animation to a data-block, to find the correct binding handle");
"Slot Name",
"Used when connecting an Animation 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,
"rna_ActionBinding_name_display_get",
"rna_ActionBinding_name_display_length",
"rna_ActionBinding_name_display_set");
RNA_def_property_string_maxlength(prop, sizeof(ActionBinding::name) - 2);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_ActionBinding_name_update");
"rna_ActionSlot_name_display_get",
"rna_ActionSlot_name_display_length",
"rna_ActionSlot_name_display_set");
RNA_def_property_string_maxlength(prop, sizeof(ActionSlot::name) - 2);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_ActionSlot_name_update");
RNA_def_struct_ui_text(
srna,
"Binding Display Name",
"Name of the binding for showing in the interface. It is the name, without the first two "
"Slot Display Name",
"Name of the slot for showing in the interface. It is the name, without the first two "
"characters that identify what kind of data-block it animates");
prop = RNA_def_property(srna, "handle", PROP_INT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_struct_ui_text(srna,
"Binding Handle",
"Number specific to this Binding, unique within the Animation data-block"
"Slot Handle",
"Number specific to this Slot, unique within the Action"
"This is used, for example, on a KeyframeActionStrip to look up the "
"ActionChannelBag for this Binding");
"ActionChannelBag for this Slot");
}
static void rna_def_ActionLayer_strips(BlenderRNA *brna, PropertyRNA *cprop)
@ -1369,8 +1367,8 @@ static void rna_def_keyframestrip_channelbags(BlenderRNA *brna, PropertyRNA *cpr
RNA_def_struct_sdna(srna, "KeyframeActionStrip");
RNA_def_struct_ui_text(
srna,
"Animation Channels for Bindings",
"For each animation binding, a list of animation channels that are meant for that binding");
"Animation Channels for Slots",
"For each animation slot, a list of animation channels that are meant for that slot");
}
static void rna_def_action_keyframe_strip(BlenderRNA *brna)
@ -1380,7 +1378,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 binding");
srna, "Keyframe Animation Strip", "Strip with a set of F-Curves for each animation slot");
prop = RNA_def_property(srna, "channelbags", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "ActionChannelBag");
@ -1401,14 +1399,14 @@ static void rna_def_action_keyframe_strip(BlenderRNA *brna)
/* KeyframeStrip.channels(...). */
func = RNA_def_function(srna, "channels", "rna_KeyframeActionStrip_channels");
RNA_def_function_ui_description(func, "Find the ActionChannelBag for a specific Binding");
RNA_def_function_ui_description(func, "Find the ActionChannelBag for a specific Slot");
parm = RNA_def_int(func,
"binding_handle",
"slot_handle",
0,
0,
INT_MAX,
"Binding Handle",
"Number that identifies a specific animation binding",
"Slot Handle",
"Number that identifies a specific animation slot",
0,
INT_MAX);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
@ -1420,10 +1418,10 @@ static void rna_def_action_keyframe_strip(BlenderRNA *brna)
func = RNA_def_function(srna, "key_insert", "rna_KeyframeActionStrip_key_insert");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_pointer(func,
"binding",
"ActionBinding",
"Binding",
"The binding that identifies which 'thing' should be keyed");
"slot",
"ActionSlot",
"Slot",
"The slot that identifies which 'thing' should be keyed");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path");
@ -1485,7 +1483,7 @@ static void rna_def_action_strip(BlenderRNA *brna)
"KEYFRAME",
0,
"Keyframe",
"Strip with a set of F-Curves for each animation binding"},
"Strip with a set of F-Curves for each animation slot"},
{0, nullptr, 0, nullptr, nullptr},
};
@ -1498,15 +1496,14 @@ static void rna_def_action_strip(BlenderRNA *brna)
rna_def_action_keyframe_strip(brna);
}
static void rna_def_channelbag_for_binding_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
static void rna_def_channelbag_for_slot_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
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 binding");
RNA_def_struct_ui_text(srna, "F-Curves", "Collection of F-Curves for a specific animation slot");
}
static void rna_def_action_channelbag(BlenderRNA *brna)
@ -1518,9 +1515,9 @@ 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 binding");
"Collection of animation channels, typically associated with an animation slot");
prop = RNA_def_property(srna, "binding_handle", PROP_INT, PROP_NONE);
prop = RNA_def_property(srna, "slot_handle", PROP_INT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE);
@ -1534,8 +1531,8 @@ static void rna_def_action_channelbag(BlenderRNA *brna)
nullptr,
nullptr);
RNA_def_property_struct_type(prop, "FCurve");
RNA_def_property_ui_text(prop, "F-Curves", "The individual F-Curves that animate the binding");
rna_def_channelbag_for_binding_fcurves(brna, prop);
RNA_def_property_ui_text(prop, "F-Curves", "The individual F-Curves that animate the slot");
rna_def_channelbag_for_slot_fcurves(brna, prop);
}
# endif // WITH_ANIM_BAKLAVA
@ -1790,13 +1787,13 @@ static void rna_def_action(BlenderRNA *brna)
# ifdef WITH_ANIM_BAKLAVA
/* Properties. */
prop = RNA_def_property(srna, "last_binding_handle", PROP_INT, PROP_NONE);
prop = RNA_def_property(srna, "last_slot_handle", PROP_INT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "is_empty", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(
prop, "Is Empty", "False when there is any Layer, Binding, or legacy F-Curve");
prop, "Is Empty", "False when there is any Layer, Slot, or legacy F-Curve");
RNA_def_property_boolean_funcs(prop, "rna_Action_is_empty_get", nullptr);
prop = RNA_def_property(srna, "is_action_legacy", PROP_BOOLEAN, PROP_NONE);
@ -1804,7 +1801,7 @@ static void rna_def_action(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Is Legacy Action",
"Return whether this is a legacy Action. Legacy Actions have no layers or bindings. An "
"Return whether this is a legacy Action. Legacy Actions have no layers or slots. An "
"empty Action considered as both a 'legacy' and a 'layered' Action");
RNA_def_property_boolean_funcs(prop, "rna_Action_is_action_legacy_get", nullptr);
@ -1820,19 +1817,19 @@ static void rna_def_action(BlenderRNA *brna)
/* Collection properties. */
# ifdef WITH_ANIM_BAKLAVA
prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "ActionBinding");
prop = RNA_def_property(srna, "slots", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "ActionSlot");
RNA_def_property_collection_funcs(prop,
"rna_iterator_animation_bindings_begin",
"rna_iterator_animation_slots_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_dereference_get",
"rna_iterator_animation_bindings_length",
"rna_iterator_animation_slots_length",
nullptr,
nullptr,
nullptr);
RNA_def_property_ui_text(prop, "Bindings", "The list of bindings in this animation data-block");
rna_def_action_bindings(brna, prop);
RNA_def_property_ui_text(prop, "Slots", "The list of slots in this Action");
rna_def_action_slots(brna, prop);
prop = RNA_def_property(srna, "layers", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "ActionLayer");
@ -1944,7 +1941,7 @@ void RNA_def_action(BlenderRNA *brna)
rna_def_dopesheet(brna);
# ifdef WITH_ANIM_BAKLAVA
rna_def_action_binding(brna);
rna_def_action_slot(brna);
rna_def_action_layer(brna);
rna_def_action_strip(brna);
rna_def_action_channelbag(brna);

@ -105,28 +105,28 @@ const EnumPropertyItem rna_enum_keying_flag_api_items[] = {
#ifdef WITH_ANIM_BAKLAVA
# ifdef RNA_RUNTIME
constexpr int binding_items_value_create_new = -1;
const EnumPropertyItem rna_enum_action_binding_item_new = {
binding_items_value_create_new,
constexpr int slot_items_value_create_new = -1;
const EnumPropertyItem rna_enum_action_slot_item_new = {
slot_items_value_create_new,
"NEW",
ICON_ADD,
"New",
"Create a new animation binding for this data-block"};
const EnumPropertyItem rna_enum_action_binding_item_legacy = {
int(blender::animrig::Binding::unassigned),
"Create a new animation slot for this data-block"};
const EnumPropertyItem rna_enum_action_slot_item_legacy = {
int(blender::animrig::Slot::unassigned),
"UNASSIGNED",
0,
"Legacy Action",
"This is a legacy Action, which does not support bindings."};
"This is a legacy Action, which does not support slots."};
# endif
const EnumPropertyItem rna_enum_action_binding_item_none = {
int(blender::animrig::Binding::unassigned),
const EnumPropertyItem rna_enum_action_slot_item_none = {
int(blender::animrig::Slot::unassigned),
"UNASSIGNED",
0,
"None",
"Not assigned any binding, and thus not animated."};
const EnumPropertyItem rna_enum_action_binding_items[] = {
rna_enum_action_binding_item_none,
"Not assigned any slot, and thus not animated."};
const EnumPropertyItem rna_enum_action_slot_items[] = {
rna_enum_action_slot_item_none,
{0, nullptr, 0, nullptr, nullptr},
};
#endif // WITH_ANIM_BAKLAVA
@ -246,8 +246,8 @@ bool rna_AnimData_tweakmode_override_apply(Main * /*bmain*/,
}
# ifdef WITH_ANIM_BAKLAVA
static void rna_AnimData_action_binding_handle_set(
PointerRNA *ptr, const blender::animrig::binding_handle_t new_binding_handle)
static void rna_AnimData_action_slot_handle_set(
PointerRNA *ptr, const blender::animrig::slot_handle_t new_slot_handle)
{
BLI_assert(ptr->owner_id);
ID &animated_id = *ptr->owner_id;
@ -264,30 +264,30 @@ static void rna_AnimData_action_binding_handle_set(
blender::animrig::Action *anim = blender::animrig::get_animation(animated_id);
if (!anim) {
/* No animation to verify the binding handle is valid. As the binding handle
/* 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. */
WM_reportf(RPT_ERROR,
"Data-block '%s' does not have an animation, cannot set binding handle",
"Data-block '%s' does not have an animation, cannot set slot handle",
animated_id.name + 2);
return;
}
blender::animrig::Binding *binding = anim->binding_for_handle(new_binding_handle);
if (!anim->assign_id(binding, animated_id)) {
if (binding) {
blender::animrig::Slot *slot = anim->slot_for_handle(new_slot_handle);
if (!anim->assign_id(slot, animated_id)) {
if (slot) {
WM_reportf(RPT_ERROR,
"Action '%s' binding '%s' (%d) could not be assigned to %s",
"Action '%s' slot '%s' (%d) could not be assigned to %s",
anim->id.name + 2,
binding->name,
binding->handle,
slot->name,
slot->handle,
animated_id.name + 2);
}
else {
/* This is highly unexpected, as unassigning a Binding should always be allowed. */
/* This is highly unexpected, as unassigning a Slot should always be allowed. */
BLI_assert_unreachable();
WM_reportf(RPT_ERROR,
"Action '%s' binding could not be unassigned from %s",
"Action '%s' slot could not be unassigned from %s",
anim->id.name + 2,
animated_id.name + 2);
}
@ -300,65 +300,65 @@ static AnimData &rna_animdata(const PointerRNA *ptr)
return *reinterpret_cast<AnimData *>(ptr->data);
}
static int rna_AnimData_action_binding_get(PointerRNA *ptr)
static int rna_AnimData_action_slot_get(PointerRNA *ptr)
{
AnimData &adt = rna_animdata(ptr);
return adt.binding_handle;
return adt.slot_handle;
}
static void rna_AnimData_action_binding_set(PointerRNA *ptr, int value)
static void rna_AnimData_action_slot_set(PointerRNA *ptr, int value)
{
using blender::animrig::Action;
using blender::animrig::Binding;
using blender::animrig::binding_handle_t;
using blender::animrig::Slot;
using blender::animrig::slot_handle_t;
AnimData &adt = rna_animdata(ptr);
ID &animated_id = *ptr->owner_id;
const binding_handle_t new_binding_handle = binding_handle_t(value);
if (new_binding_handle == Binding::unassigned) {
/* No need to check with the Animation, as 'no binding' is always valid. */
adt.binding_handle = Binding::unassigned;
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. */
adt.slot_handle = Slot::unassigned;
return;
}
if (!adt.action) {
/* No Action to verify the binding handle is valid. As the binding handle
/* 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. */
WM_reportf(RPT_ERROR,
"Data-block '%s' does not have an Action, cannot set binding handle",
"Data-block '%s' does not have an Action, cannot set slot handle",
animated_id.name + 2);
return;
}
Action &anim = adt.action->wrap();
Binding *binding = nullptr;
Slot *slot = nullptr;
/* TODO: handle legacy Action. */
BLI_assert(anim.is_action_layered());
if (new_binding_handle == binding_items_value_create_new) {
if (new_slot_handle == slot_items_value_create_new) {
/* Special case for this enum item. */
binding = &anim.binding_add_for_id(animated_id);
slot = &anim.slot_add_for_id(animated_id);
}
else {
binding = anim.binding_for_handle(new_binding_handle);
if (!binding) {
slot = anim.slot_for_handle(new_slot_handle);
if (!slot) {
WM_reportf(RPT_ERROR,
"Animation '%s' has no binding with handle %d",
"Animation '%s' has no slot with handle %d",
anim.id.name + 2,
new_binding_handle);
new_slot_handle);
return;
}
}
if (!anim.assign_id(binding, animated_id)) {
if (!anim.assign_id(slot, animated_id)) {
WM_reportf(RPT_ERROR,
"Animation '%s' binding '%s' (%d) could not be assigned to %s",
"Animation '%s' slot '%s' (%d) could not be assigned to %s",
anim.id.name + 2,
binding->name_without_prefix().c_str(),
binding->handle,
slot->name_without_prefix().c_str(),
slot->handle,
animated_id.name + 2);
return;
}
@ -366,18 +366,18 @@ static void rna_AnimData_action_binding_set(PointerRNA *ptr, int value)
WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN, nullptr);
}
static const EnumPropertyItem *rna_AnimData_action_binding_itemf(bContext * /*C*/,
static const EnumPropertyItem *rna_AnimData_action_slot_itemf(bContext * /*C*/,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
bool *r_free)
{
using blender::animrig::Action;
using blender::animrig::Binding;
using blender::animrig::Slot;
AnimData &adt = rna_animdata(ptr);
if (!adt.action) {
*r_free = false;
return rna_enum_action_binding_items;
return rna_enum_action_slot_items;
}
EnumPropertyItem item = {0};
@ -386,16 +386,16 @@ static const EnumPropertyItem *rna_AnimData_action_binding_itemf(bContext * /*C*
const Action &anim = adt.action->wrap();
bool found_assigned_binding = false;
for (const Binding *binding : anim.bindings()) {
item.value = binding->handle;
item.identifier = binding->name;
item.name = binding->name_without_prefix().c_str();
item.icon = UI_icon_from_idcode(binding->idtype);
bool found_assigned_slot = false;
for (const Slot *slot : anim.slots()) {
item.value = slot->handle;
item.identifier = slot->name;
item.name = slot->name_without_prefix().c_str();
item.icon = UI_icon_from_idcode(slot->idtype);
item.description = "";
RNA_enum_item_add(&items, &num_items, &item);
found_assigned_binding |= binding->handle == adt.binding_handle;
found_assigned_slot |= slot->handle == adt.slot_handle;
}
if (num_items > 0) {
@ -405,15 +405,15 @@ static const EnumPropertyItem *rna_AnimData_action_binding_itemf(bContext * /*C*
/* Only add the 'New' option when this is a Layered Action. */
const bool is_layered = anim.is_action_layered();
if (is_layered) {
RNA_enum_item_add(&items, &num_items, &rna_enum_action_binding_item_new);
RNA_enum_item_add(&items, &num_items, &rna_enum_action_slot_item_new);
}
if (!found_assigned_binding) {
/* The assigned binding was not found, so show an option that reflects that. */
if (!found_assigned_slot) {
/* The assigned slot was not found, so show an option that reflects that. */
RNA_enum_item_add(&items,
&num_items,
is_layered ? &rna_enum_action_binding_item_none :
&rna_enum_action_binding_item_legacy);
is_layered ? &rna_enum_action_slot_item_none :
&rna_enum_action_slot_item_legacy);
}
RNA_enum_item_end(&items, &num_items);
@ -1685,35 +1685,35 @@ static void rna_def_animdata(BlenderRNA *brna)
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, nullptr);
# ifdef WITH_ANIM_BAKLAVA
prop = RNA_def_property(srna, "action_binding_handle", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "binding_handle");
RNA_def_property_int_funcs(prop, nullptr, "rna_AnimData_action_binding_handle_set", nullptr);
prop = RNA_def_property(srna, "action_slot_handle", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "slot_handle");
RNA_def_property_int_funcs(prop, nullptr, "rna_AnimData_action_slot_handle_set", nullptr);
RNA_def_property_ui_text(prop,
"Action Binding Handle",
"Action Slot Handle",
"A number that identifies which sub-set of the Action is considered "
"to be for this data-block");
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_AnimData_dependency_update");
prop = RNA_def_property(srna, "action_binding_name", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "binding_name");
prop = RNA_def_property(srna, "action_slot_name", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "slot_name");
RNA_def_property_ui_text(
prop,
"Action Binding Name",
"The name of the action binding. The binding identifies which sub-set of the Action "
"is considered to be for this data-block, and its name is used to find the right binding "
"Action Slot Name",
"The name of the action slot. The slot identifies which sub-set of the Action "
"is considered to be for this data-block, and its name is used to find the right slot "
"when assigning an Action");
prop = RNA_def_property(srna, "action_binding", PROP_ENUM, PROP_NONE);
prop = RNA_def_property(srna, "action_slot", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_funcs(prop,
"rna_AnimData_action_binding_get",
"rna_AnimData_action_binding_set",
"rna_AnimData_action_binding_itemf");
RNA_def_property_enum_items(prop, rna_enum_action_binding_items);
"rna_AnimData_action_slot_get",
"rna_AnimData_action_slot_set",
"rna_AnimData_action_slot_itemf");
RNA_def_property_enum_items(prop, rna_enum_action_slot_items);
RNA_def_property_ui_text(
prop,
"Action Binding",
"The binding identifies which sub-set of the Action is considered to be for this "
"data-block, and its name is used to find the right binding when assigning an Action");
"Action Slot",
"The slot identifies which sub-set of the Action is considered to be for this "
"data-block, and its name is used to find the right slot when assigning an Action");
# endif

@ -12,7 +12,7 @@ blender -b --factory-startup --python tests/python/bl_animation_action.py
"""
class ActionBindingAssignmentTest(unittest.TestCase):
class ActionSlotAssignmentTest(unittest.TestCase):
"""Test assigning actions & check reference counts."""
def setUp(self) -> None:
@ -43,7 +43,7 @@ class ActionBindingAssignmentTest(unittest.TestCase):
bpy.data.objects.remove(camera)
self.assertEqual(0, anim.users)
def test_binding_assignment(self):
def test_slot_assignment(self):
# Create new Action.
anim = bpy.data.actions.new('TestAction')
self.assertEqual(0, anim.users)
@ -52,25 +52,25 @@ class ActionBindingAssignmentTest(unittest.TestCase):
cube = bpy.data.objects['Cube']
cube_adt = cube.animation_data_create()
cube_adt.action = anim
bind_cube = anim.bindings.new(for_id=cube)
cube_adt.action_binding_handle = bind_cube.handle
self.assertEqual(cube_adt.action_binding_handle, bind_cube.handle)
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)
# Assign the animation to the camera as well.
camera = bpy.data.objects['Camera']
bind_camera = anim.bindings.new(for_id=camera)
bind_camera = anim.slots.new(for_id=camera)
camera_adt = camera.animation_data_create()
camera_adt.action = anim
self.assertEqual(camera_adt.action_binding_handle, bind_camera.handle)
self.assertEqual(camera_adt.action_slot_handle, bind_camera.handle)
# Unassigning should keep the binding name.
# Unassigning should keep the slot name.
cube_adt.action = None
self.assertEqual(cube_adt.action_binding_name, bind_cube.name)
self.assertEqual(cube_adt.action_slot_name, bind_cube.name)
# It should not be possible to set the binding handle while the animation is unassigned.
bind_extra = anim.bindings.new()
cube_adt.action_binding_handle = bind_extra.handle
self.assertNotEqual(cube_adt.action_binding_handle, bind_extra.handle)
# 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)
class LimitationsTest(unittest.TestCase):
@ -139,10 +139,10 @@ class TestLegacyLayered(unittest.TestCase):
act.layers.new("laagje")
self.assertSequenceEqual([], act.layers)
# Adding a binding should be prevented.
# Adding a slot should be prevented.
with self.assertRaises(RuntimeError):
act.bindings.new()
self.assertSequenceEqual([], act.bindings)
act.slots.new()
self.assertSequenceEqual([], act.slots)
def test_layered_action(self) -> None:
"""Test legacy operations on a layered Action"""