diff --git a/scripts/startup/bl_ui/temp_anim_layers.py b/scripts/startup/bl_ui/temp_anim_layers.py index 732af5f6b0b..715b01a8cef 100644 --- a/scripts/startup/bl_ui/temp_anim_layers.py +++ b/scripts/startup/bl_ui/temp_anim_layers.py @@ -34,40 +34,40 @@ class VIEW3D_PT_animation_layers(Panel): # FIXME: this should be done in response to a message-bus callback, notifier, whatnot. adt = context.object.animation_data - with _wm_selected_animation_lock: + with _wm_selected_action_lock: if adt: - context.window_manager.selected_animation = adt.animation + context.window_manager.selected_action = adt.action else: - context.window_manager.selected_animation = None + context.window_manager.selected_action = None col = layout.column() # This has to go via an auxiliary property, as assigning an Animation # data-block should be possible even when `context.object.animation_data` # is `None`, and thus its `animation` property does not exist. - col.template_ID(context.window_manager, 'selected_animation') + col.template_ID(context.window_manager, 'selected_action') col = layout.column(align=False) - anim = adt and adt.animation + anim = adt and adt.action if anim: binding_sub = col.column(align=True) # Binding selector. row = binding_sub.row(align=True) - row.prop(adt, 'animation_binding', text="Binding") + row.prop(adt, 'action_binding', text="Binding") row.operator('anim.binding_unassign_object', text="", icon='X') - binding = anim.bindings.get(adt.animation_binding, None) + binding = anim.bindings.get(adt.action_binding, None) if binding: binding_sub.prop(binding, 'name_display', text="Name") internal_sub = binding_sub.box().column(align=True) internal_sub.active = False - internal_sub.prop(adt, 'animation_binding_handle', text="handle") + internal_sub.prop(adt, 'action_binding_handle', text="handle") if binding: internal_sub.prop(binding, 'name', text="Internal Name") if adt: - col.prop(adt, 'animation_binding_name', text="ADT Binding Name") + col.prop(adt, 'action_binding_name', text="ADT Binding Name") else: col.label(text="ADT Binding Name: -") @@ -89,42 +89,35 @@ classes = ( VIEW3D_PT_animation_layers, ) -_wm_selected_animation_lock = threading.Lock() +_wm_selected_action_lock = threading.Lock() -def _wm_selected_animation_update(wm, context): +def _wm_selected_action_update(wm, context): # Avoid responding to changes written by the panel above. - lock_ok = _wm_selected_animation_lock.acquire(blocking=False) + lock_ok = _wm_selected_action_lock.acquire(blocking=False) if not lock_ok: return try: - if wm.selected_animation is None and context.object.animation_data is None: + if wm.selected_action is None and context.object.animation_data is None: return adt = context.object.animation_data_create() - if adt.animation == wm.selected_animation: + if adt.action == wm.selected_action: # Avoid writing to the property when the new value hasn't changed. return - adt.animation = wm.selected_animation + adt.action = wm.selected_action finally: - _wm_selected_animation_lock.release() + _wm_selected_action_lock.release() def register_props(): - # Put behind a `try` because it won't exist when Blender is built without - # experimental features. - try: - from bpy.types import Animation - except ImportError: - return - # Due to this hackyness, the WindowManager will increase the user count of # the pointed-to Animation data-block. - WindowManager.selected_animation = PointerProperty( - type=Animation, - name="Animation", - description="Animation assigned to the active Object", - update=_wm_selected_animation_update, + WindowManager.selected_action = PointerProperty( + type=bpy.types.Action, + name="Action", + description="Action assigned to the active Object", + update=_wm_selected_action_update, ) diff --git a/source/blender/animrig/ANIM_action.hh b/source/blender/animrig/ANIM_action.hh index fbbd93ec1aa..872d912a3d7 100644 --- a/source/blender/animrig/ANIM_action.hh +++ b/source/blender/animrig/ANIM_action.hh @@ -5,15 +5,587 @@ /** \file * \ingroup animrig * - * \brief Functions to work with Actions. + * \brief Functions and classes to work with Actions. */ +#pragma once + +#include "ANIM_fcurve.hh" +#include "ANIM_keyframing.hh" + +#include "DNA_action_types.h" +#include "DNA_anim_types.h" + +#include "BLI_math_vector.hh" +#include "BLI_set.hh" +#include "BLI_string_ref.hh" #include "RNA_types.hh" +struct AnimationEvalContext; struct FCurve; -struct bAction; +struct FCurve; +struct ID; +struct Main; +struct PointerRNA; namespace blender::animrig { + +/* Forward declarations for the types defined later in this file. */ +class Layer; +class Strip; +class Binding; + +/* Use an alias for the Binding handle type to help disambiguate function parameters. */ +using binding_handle_t = decltype(::ActionBinding::handle); + +/** + * Container of animation data for one or more animated IDs. + * + * Broadly an Action consists of Layers, each Layer has Strips, and it's the + * Strips that eventually contain the animation data. + * + * 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. + * + * \note This wrapper class for the `bAction` DNA struct only has functionality + * for the layered animation data. The legacy F-Curves (in `bAction::curves`) + * and their groups (in `bAction::groups`) are not managed here. To see whether + * an Action uses this legacy data, or has been converted to the current layered + * structure, use `Action::is_action_legacy()` and + * `Action::is_action_layered()`. Note that an empty Action is considered valid + * for both. + * + * \see AnimData::action + * \see AnimData::binding_handle + */ +class Action : public ::bAction { + public: + Action() = default; + /** + * Copy constructor is deleted, as code should use regular ID library + * management functions to duplicate this data-block. + */ + Action(const Action &other) = delete; + + /* Discriminators for 'legacy' and 'layered' Actions. */ + /** + * Return whether this Action has any data at all. + * + * \return true when `bAction::layer_array` and `bAction::binding_array`, as well as + * the legacy `curves` list, are empty. + */ + bool is_empty() const; + /** + * Return whether this is a legacy Action. + * + * - Animation data is stored in `bAction::curves`. + * - Evaluated equally for all data-blocks that reference this Action. + * - Binding 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()`. + */ + bool is_action_legacy() const; + /** + * Return whether this is a layered Action. + * + * - Animation data is stored in `bAction::layer_array`. + * - Evaluated for data-blocks based on their binding handle. + * + * \note An empty Action is valid as both a legacy and layered Action. + */ + bool is_action_layered() const; + + /* Animation Layers access. */ + blender::Span layers() const; + blender::MutableSpan layers(); + const Layer *layer(int64_t index) const; + Layer *layer(int64_t index); + + Layer &layer_add(StringRefNull name); + + /** + * Remove the layer from this animation. + * + * After this call, the passed reference is no longer valid, as the memory + * will have been freed. Any strips on the layer will be freed too. + * + * \return true when the layer was found & removed, false if it wasn't found. + */ + bool layer_remove(Layer &layer_to_remove); + + /* Animation Binding access. */ + blender::Span bindings() const; + blender::MutableSpan bindings(); + const Binding *binding(int64_t index) const; + Binding *binding(int64_t index); + + Binding *binding_for_handle(binding_handle_t handle); + const Binding *binding_for_handle(binding_handle_t handle) const; + + /** + * Set the binding 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 + * 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. + * + * \see Action::binding_name_define + * \see Action::binding_name_propagate + */ + void binding_name_set(Main &bmain, Binding &binding, StringRefNull new_name); + + /** + * Set the binding 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. + * + * \see Action::binding_name_set + * \see Action::binding_name_propagate + */ + void binding_name_define(Binding &binding, StringRefNull new_name); + + /** + * Update the `AnimData::action_binding_name` field of any ID that is animated by + * this Binding. + * + * Should be called after `binding_name_define(binding)`. 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); + + Binding *binding_find_by_name(StringRefNull binding_name); + + Binding *binding_for_id(const ID &animated_id); + const Binding *binding_for_id(const ID &animated_id) const; + + /** + * Create a new, unused Binding. + * + * The returned binding will be suitable for any ID type. After binding to an + * ID, it be limited to that ID's type. + */ + Binding &binding_add(); + + /** + * Create a new binding, 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. + */ + Binding &binding_add_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 + * 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); + + /** + * Unassign this Animation from the animated ID. + * + * \param animated_id: ID that is animated by this Animation. Calling this + * function when this ID is _not_ animated by this Animation is not allowed, + * and considered a bug. + */ + void unassign_id(ID &animated_id); + + /** + * Find the binding that best matches the animated ID. + * + * If the ID is already animated by this Animation, by matching this + * Animation's bindings with (in order): + * + * - `animated_id.adt->binding_handle`, + * - `animated_id.adt->binding_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); + + /** + * Return whether this Animation actually has any animation data for the given binding. + */ + bool is_binding_animated(binding_handle_t binding_handle) const; + + protected: + /** Return the layer's index, or -1 if not found in this animation. */ + int64_t find_layer_index(const Layer &layer) const; + + private: + Binding &binding_allocate(); + + /** + * Ensure the binding name prefix matches its ID type. + * + * This ensures that the first two characters match the ID type of + * this binding. + * + * \see Action::binding_name_propagate + */ + void binding_name_ensure_prefix(Binding &binding); + + /** + * Set the binding'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 + * is considered a bug to call this function. + */ + void binding_setup_for_id(Binding &binding, const ID &animated_id); +}; +static_assert(sizeof(Action) == sizeof(::bAction), + "DNA struct and its C++ wrapper must have the same size"); + +/** + * Strips contain the actual animation data. + * + * Although the data model allows for different strip types, currently only a + * single type is implemented: keyframe strips. + */ +class Strip : public ::ActionStrip { + public: + /** + * Strip instances should not be created via this constructor. Create a sub-class like + * #KeyframeStrip instead. + * + * The reason is that various functions will assume that the `Strip` is actually a down-cast + * instance of another strip class, and that `Strip::type()` will say which type. To avoid having + * to explicitly deal with an 'invalid' type everywhere, creating a `Strip` directly is simply + * not allowed. + */ + Strip() = delete; + + /** + * Strip cannot be duplicated via the copy constructor. Either use a concrete + * strip type's copy constructor, or use Strip::duplicate(). + * + * The reason why the copy constructor won't work is due to the double nature + * of the inheritance at play here: + * + * C-style inheritance: `KeyframeActionStrip` "inherits" `ActionStrip" + * by embedding the latter. This means that any `KeyframeActionStrip *` + * can be reinterpreted as `ActionStrip *`. + * + * C++-style inheritance: the C++ wrappers inherit the DNA structs, so + * `animrig::Strip` inherits `::ActionStrip`, and + * `animrig::KeyframeStrip` inherits `::KeyframeActionStrip`. + */ + Strip(const Strip &other) = delete; + ~Strip(); + + Strip *duplicate(StringRefNull allocation_name) const; + + enum class Type : int8_t { Keyframe = 0 }; + + /** + * Strip type, so it's known which subclass this can be wrapped in without + * having to rely on C++ RTTI. + */ + Type type() const + { + return Type(this->strip_type); + } + + template bool is() const; + template T &as(); + template const T &as() const; + + bool contains_frame(float frame_time) const; + bool is_last_frame(float frame_time) const; + + /** + * Set the start and end frame. + * + * Note that this does not do anything else. There is no check whether the + * frame numbers are valid (i.e. frame_start <= frame_end). Infinite values + * (negative for frame_start, positive for frame_end) are supported. + */ + void resize(float frame_start, float frame_end); +}; +static_assert(sizeof(Strip) == sizeof(::ActionStrip), + "DNA struct and its C++ wrapper must have the same size"); + +/** + * Layers can be stacked on top of each other to define the animation. Each + * layer has a mix mode and an influence (0-1), which define how it is mixed + * with the layers below it. + * + * Layers contain one or more Strips, which in turn contain the animation data + * itself. + * + * Temporary limitation: at most one strip may exist on a layer, and it extends + * from negative to positive infinity. + */ +class Layer : public ::ActionLayer { + public: + Layer() = default; + Layer(const Layer &other); + ~Layer(); + + enum class Flags : uint8_t { + /* Set by default, cleared to mute. */ + Enabled = (1 << 0), + /* When adding/removing a flag, also update the ENUM_OPERATORS() invocation below. */ + }; + + Flags flags() const + { + return static_cast(this->layer_flags); + } + + enum class MixMode : int8_t { + /** Channels in this layer override the same channels from underlying layers. */ + Replace = 0, + /** Channels in this layer are added to underlying layers as sequential operations. */ + Offset = 1, + /** Channels in this layer are added to underlying layers on a per-channel basis. */ + Add = 2, + /** Channels in this layer are subtracted to underlying layers on a per-channel basis. */ + Subtract = 3, + /** Channels in this layer are multiplied with underlying layers on a per-channel basis. */ + Multiply = 4, + }; + + MixMode mix_mode() const + { + return static_cast(this->layer_mix_mode); + } + + /* Strip access. */ + blender::Span strips() const; + blender::MutableSpan strips(); + const Strip *strip(int64_t index) const; + Strip *strip(int64_t index); + Strip &strip_add(Strip::Type strip_type); + + /** + * Remove the strip from this layer. + * + * After this call, the passed reference is no longer valid, as the memory + * will have been freed. + * + * \return true when the strip was found & removed, false if it wasn't found. + */ + bool strip_remove(Strip &strip); + + protected: + /** Return the strip's index, or -1 if not found in this layer. */ + int64_t find_strip_index(const Strip &strip) const; +}; +static_assert(sizeof(Layer) == sizeof(::ActionLayer), + "DNA struct and its C++ wrapper must have the same size"); + +ENUM_OPERATORS(Layer::Flags, Layer::Flags::Enabled); + +/** + * Identifier for a sub-set of the animation data inside an Animation data-block. + * + * An animatable ID specifies both an `Animation*` and an `ActionBinding::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 + * of animation data that should animate it. + * + * \see AnimData::binding_handle + */ +class Binding : public ::ActionBinding { + public: + Binding() = default; + Binding(const Binding &other) = default; + ~Binding() = default; + + /** + * Binding handle value indicating that there is no binding assigned. + */ + constexpr static binding_handle_t unassigned = 0; + + /** + * Binding 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. + * + * This is the ID name prefix, so "OB" for objects, "CA" for cameras, etc. + */ + std::string name_prefix_for_idtype() const; + + /** + * Return the name without the prefix. + * + * \see name_prefix_for_idtype + */ + StringRefNull name_without_prefix() const; + + /** Return whether this Binding is usable by this ID type. */ + bool is_suitable_for(const ID &animated_id) const; + + /** Return whether this Binding has an `idtype` set. */ + bool has_idtype() const; + + protected: + friend Action; + + /** + * Ensure the first two characters of the name match the ID type. + * + * \note This does NOT ensure name uniqueness within the Animation. That is + * the responsibility of the caller. + */ + void name_ensure_prefix(); +}; +static_assert(sizeof(Binding) == sizeof(::ActionBinding), + "DNA struct and its C++ wrapper must have the same size"); + +/** + * KeyframeStrips effectively contain a bag of F-Curves for each Binding. + */ +class KeyframeStrip : public ::KeyframeActionStrip { + public: + KeyframeStrip() = default; + KeyframeStrip(const KeyframeStrip &other); + ~KeyframeStrip(); + + /* ChannelBag array access. */ + blender::Span channelbags() const; + blender::MutableSpan channelbags(); + const ChannelBag *channelbag(int64_t index) const; + ChannelBag *channelbag(int64_t index); + + /** + * Find the animation channels for this binding. + * + * \return nullptr if there is none yet for this binding. + */ + 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); + + /** + * Add the animation channels for this binding. + * + * Should only be called when there is no `ChannelBag` for this binding yet. + */ + ChannelBag &channelbag_for_binding_add(const Binding &binding); + /** + * Find an FCurve for this binding + RNA path + array index combination. + * + * If it cannot be found, `nullptr` is returned. + */ + FCurve *fcurve_find(const Binding &binding, StringRefNull rna_path, int array_index); + + /** + * Find an FCurve for this binding + RNA path + array index combination. + * + * If it cannot be found, a new one is created. + */ + FCurve &fcurve_find_or_create(const Binding &binding, StringRefNull rna_path, int array_index); + + SingleKeyingResult keyframe_insert(const Binding &binding, + StringRefNull rna_path, + int array_index, + float2 time_value, + const KeyframeSettings &settings); +}; +static_assert(sizeof(KeyframeStrip) == sizeof(::KeyframeActionStrip), + "DNA struct and its C++ wrapper must have the same size"); + +template<> KeyframeStrip &Strip::as(); +template<> const KeyframeStrip &Strip::as() const; + +/** + * Collection of F-Curves, intended for a specific Binding handle. + */ +class ChannelBag : public ::ActionChannelBag { + public: + ChannelBag() = default; + ChannelBag(const ChannelBag &other); + ~ChannelBag(); + + /* FCurves access. */ + blender::Span fcurves() const; + blender::MutableSpan fcurves(); + const FCurve *fcurve(int64_t index) const; + FCurve *fcurve(int64_t index); + + const FCurve *fcurve_find(StringRefNull rna_path, int array_index) const; +}; +static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag), + "DNA struct and its C++ wrapper must have the same size"); + +/** + * Assign the animation to the ID. + * + * This will will make a best-effort guess as to which binding to use, in this + * order; + * + * - By binding 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 + * 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 + * will still return `true` as the Animation was successfully assigned. + */ +bool assign_animation(Action &anim, ID &animated_id); + +/** + * Ensure that this ID is no longer animated. + */ +void unassign_animation(ID &animated_id); + +/** + * Clear the animation binding 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. + * + * \param adt the AnimData of the animated ID. + * + * \note this does not clear the Animation pointer, just the binding handle. + */ +void unassign_binding(AnimData &adt); + +/** + * Return the Animation of this ID, or nullptr if it has none. + */ +Action *get_animation(ID &animated_id); + +/** + * Return the F-Curves for this specific binding 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 + * strip, of a single type, this function can be used. + * + * The use of this function is also an indicator for code that will have to be altered when + * multi-layered animation is getting implemented. + */ +Span fcurves_for_animation(Action &anim, binding_handle_t binding_handle); +Span fcurves_for_animation(const Action &anim, binding_handle_t binding_handle); + /** * Get (or add relevant data to be able to do so) F-Curve from the given Action, * for the given Animation Data block. This assumes that all the destinations are valid. @@ -32,3 +604,59 @@ FCurve *action_fcurve_ensure(Main *bmain, FCurve *action_fcurve_find(bAction *act, const char rna_path[], int array_index); } // namespace blender::animrig + +/* Wrap functions for the DNA structs. */ + +inline blender::animrig::Action &bAction::wrap() +{ + return *reinterpret_cast(this); +} +inline const blender::animrig::Action &bAction::wrap() const +{ + return *reinterpret_cast(this); +} + +inline blender::animrig::Layer &ActionLayer::wrap() +{ + return *reinterpret_cast(this); +} +inline const blender::animrig::Layer &ActionLayer::wrap() const +{ + return *reinterpret_cast(this); +} + +inline blender::animrig::Binding &ActionBinding::wrap() +{ + return *reinterpret_cast(this); +} +inline const blender::animrig::Binding &ActionBinding::wrap() const +{ + return *reinterpret_cast(this); +} + +inline blender::animrig::Strip &ActionStrip::wrap() +{ + return *reinterpret_cast(this); +} +inline const blender::animrig::Strip &ActionStrip::wrap() const +{ + return *reinterpret_cast(this); +} + +inline blender::animrig::KeyframeStrip &KeyframeActionStrip::wrap() +{ + return *reinterpret_cast(this); +} +inline const blender::animrig::KeyframeStrip &KeyframeActionStrip::wrap() const +{ + return *reinterpret_cast(this); +} + +inline blender::animrig::ChannelBag &ActionChannelBag::wrap() +{ + return *reinterpret_cast(this); +} +inline const blender::animrig::ChannelBag &ActionChannelBag::wrap() const +{ + return *reinterpret_cast(this); +} diff --git a/source/blender/animrig/ANIM_animation.hh b/source/blender/animrig/ANIM_animation.hh deleted file mode 100644 index 36b7396c861..00000000000 --- a/source/blender/animrig/ANIM_animation.hh +++ /dev/null @@ -1,606 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup animrig - * - * \brief Animation data-block functionality. - */ -#pragma once - -#include "ANIM_fcurve.hh" - -#include "DNA_anim_types.h" - -#include "BLI_math_vector.hh" -#include "BLI_set.hh" -#include "BLI_string_ref.hh" - -struct AnimationEvalContext; -struct FCurve; -struct ID; -struct Main; -struct PointerRNA; - -namespace blender::animrig { - -/* Forward declarations for the types defined later in this file. */ -class Layer; -class Strip; -class Binding; - -/* Use an alias for the Binding handle type to help disambiguate function parameters. */ -using binding_handle_t = decltype(::AnimationBinding::handle); - -/** - * Container of animation data for one or more animated IDs. - * - * Broadly an Animation consists of Layers, each Layer has Strips, and it's the - * Strips that eventually contain the animation data. - * - * Temporary limitation: each Animation 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. - * - * \see AnimData::animation - * \see AnimData::binding_handle - */ -class Animation : public ::Animation { - public: - Animation() = default; - /** - * Copy constructor is deleted, as code should use regular ID library - * management functions to duplicate this data-block. - */ - Animation(const Animation &other) = delete; - - /* Animation Layers access. */ - blender::Span layers() const; - blender::MutableSpan layers(); - const Layer *layer(int64_t index) const; - Layer *layer(int64_t index); - - Layer &layer_add(StringRefNull name); - - /** - * Remove the layer from this animation. - * - * After this call, the passed reference is no longer valid, as the memory - * will have been freed. Any strips on the layer will be freed too. - * - * \return true when the layer was found & removed, false if it wasn't found. - */ - bool layer_remove(Layer &layer_to_remove); - - /* Animation Binding access. */ - blender::Span bindings() const; - blender::MutableSpan bindings(); - const Binding *binding(int64_t index) const; - Binding *binding(int64_t index); - - Binding *binding_for_handle(binding_handle_t handle); - const Binding *binding_for_handle(binding_handle_t handle) const; - - /** - * Set the binding 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 - * 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. - * - * \see Animation::binding_name_define - * \see Animation::binding_name_propagate - */ - void binding_name_set(Main &bmain, Binding &binding, StringRefNull new_name); - - /** - * Set the binding 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. - * - * \see Animation::binding_name_set - * \see Animation::binding_name_propagate - */ - void binding_name_define(Binding &binding, StringRefNull new_name); - - /** - * Update the `AnimData::animation_binding_name` field of any ID that is animated by - * this Binding. - * - * Should be called after `binding_name_define(binding)`. 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); - - Binding *binding_find_by_name(StringRefNull binding_name); - - Binding *binding_for_id(const ID &animated_id); - const Binding *binding_for_id(const ID &animated_id) const; - - /** - * Create a new, unused Binding. - * - * The returned binding will be suitable for any ID type. After binding to an - * ID, it be limited to that ID's type. - */ - Binding &binding_add(); - - /** - * Create a new binding, 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. - */ - Binding &binding_add_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 - * 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); - - /** - * Unassign this Animation from the animated ID. - * - * \param animated_id: ID that is animated by this Animation. Calling this - * function when this ID is _not_ animated by this Animation is not allowed, - * and considered a bug. - */ - void unassign_id(ID &animated_id); - - /** - * Find the binding that best matches the animated ID. - * - * If the ID is already animated by this Animation, by matching this - * Animation's bindings with (in order): - * - * - `animated_id.adt->binding_handle`, - * - `animated_id.adt->binding_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); - - /** - * Return whether this Animation actually has any animation data for the given binding. - */ - bool is_binding_animated(binding_handle_t binding_handle) const; - - /** Free all data in the `Animation`. Doesn't delete the `Animation` itself. */ - void free_data(); - - protected: - /** Return the layer's index, or -1 if not found in this animation. */ - int64_t find_layer_index(const Layer &layer) const; - - private: - Binding &binding_allocate(); - - /** - * Ensure the binding name prefix matches its ID type. - * - * This ensures that the first two characters match the ID type of - * this binding. - * - * \see Animation::binding_name_propagate - */ - void binding_name_ensure_prefix(Binding &binding); - - /** - * Set the binding'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 - * is considered a bug to call this function. - */ - void binding_setup_for_id(Binding &binding, const ID &animated_id); -}; -static_assert(sizeof(Animation) == sizeof(::Animation), - "DNA struct and its C++ wrapper must have the same size"); - -/** - * Strips contain the actual animation data. - * - * Although the data model allows for different strip types, currently only a - * single type is implemented: keyframe strips. - */ -class Strip : public ::AnimationStrip { - public: - /** - * Strip instances should not be created via this constructor. Create a sub-class like - * #KeyframeStrip instead. - * - * The reason is that various functions will assume that the `Strip` is actually a down-cast - * instance of another strip class, and that `Strip::type()` will say which type. To avoid having - * to explicitly deal with an 'invalid' type everywhere, creating a `Strip` directly is simply - * not allowed. - */ - Strip() = delete; - - /** - * Strip cannot be duplicated via the copy constructor. Either use a concrete - * strip type's copy constructor, or use Strip::duplicate(). - * - * The reason why the copy constructor won't work is due to the double nature - * of the inheritance at play here: - * - * C-style inheritance: `KeyframeAnimationStrip` "inherits" `AnimationStrip" - * by embedding the latter. This means that any `KeyframeAnimationStrip *` - * can be reinterpreted as `AnimationStrip *`. - * - * C++-style inheritance: the C++ wrappers inherit the DNA structs, so - * `animrig::Strip` inherits `::AnimationStrip`, and - * `animrig::KeyframeStrip` inherits `::KeyframeAnimationStrip`. - */ - Strip(const Strip &other) = delete; - ~Strip(); - - Strip *duplicate(StringRefNull allocation_name) const; - - enum class Type : int8_t { Keyframe = 0 }; - - /** - * Strip type, so it's known which subclass this can be wrapped in without - * having to rely on C++ RTTI. - */ - Type type() const - { - return Type(this->strip_type); - } - - template bool is() const; - template T &as(); - template const T &as() const; - - bool contains_frame(float frame_time) const; - bool is_last_frame(float frame_time) const; - - /** - * Set the start and end frame. - * - * Note that this does not do anything else. There is no check whether the - * frame numbers are valid (i.e. frame_start <= frame_end). Infinite values - * (negative for frame_start, positive for frame_end) are supported. - */ - void resize(float frame_start, float frame_end); -}; -static_assert(sizeof(Strip) == sizeof(::AnimationStrip), - "DNA struct and its C++ wrapper must have the same size"); - -/** - * Layers can be stacked on top of each other to define the animation. Each - * layer has a mix mode and an influence (0-1), which define how it is mixed - * with the layers below it. - * - * Layers contain one or more Strips, which in turn contain the animation data - * itself. - * - * Temporary limitation: at most one strip may exist on a layer, and it extends - * from negative to positive infinity. - */ -class Layer : public ::AnimationLayer { - public: - Layer() = default; - Layer(const Layer &other); - ~Layer(); - - enum class Flags : uint8_t { - /* Set by default, cleared to mute. */ - Enabled = (1 << 0), - /* When adding/removing a flag, also update the ENUM_OPERATORS() invocation below. */ - }; - - Flags flags() const - { - return static_cast(this->layer_flags); - } - - enum class MixMode : int8_t { - /** Channels in this layer override the same channels from underlying layers. */ - Replace = 0, - /** Channels in this layer are added to underlying layers as sequential operations. */ - Offset = 1, - /** Channels in this layer are added to underlying layers on a per-channel basis. */ - Add = 2, - /** Channels in this layer are subtracted to underlying layers on a per-channel basis. */ - Subtract = 3, - /** Channels in this layer are multiplied with underlying layers on a per-channel basis. */ - Multiply = 4, - }; - - MixMode mix_mode() const - { - return static_cast(this->layer_mix_mode); - } - - /* Strip access. */ - blender::Span strips() const; - blender::MutableSpan strips(); - const Strip *strip(int64_t index) const; - Strip *strip(int64_t index); - Strip &strip_add(Strip::Type strip_type); - - /** - * Remove the strip from this layer. - * - * After this call, the passed reference is no longer valid, as the memory - * will have been freed. - * - * \return true when the strip was found & removed, false if it wasn't found. - */ - bool strip_remove(Strip &strip); - - protected: - /** Return the strip's index, or -1 if not found in this layer. */ - int64_t find_strip_index(const Strip &strip) const; -}; -static_assert(sizeof(Layer) == sizeof(::AnimationLayer), - "DNA struct and its C++ wrapper must have the same size"); - -ENUM_OPERATORS(Layer::Flags, Layer::Flags::Enabled); - -/** - * Identifier for a sub-set of the animation data inside an Animation data-block. - * - * An animatable ID specifies both an `Animation*` and an `AnimationBinding::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 - * of animation data that should animate it. - * - * \see AnimData::binding_handle - */ -class Binding : public ::AnimationBinding { - public: - Binding() = default; - Binding(const Binding &other) = default; - ~Binding() = default; - - /** - * Binding handle value indicating that there is no binding assigned. - */ - constexpr static binding_handle_t unassigned = 0; - - /** - * Binding 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. - * - * This is the ID name prefix, so "OB" for objects, "CA" for cameras, etc. - */ - std::string name_prefix_for_idtype() const; - - /** - * Return the name without the prefix. - * - * \see name_prefix_for_idtype - */ - StringRefNull name_without_prefix() const; - - /** Return whether this Binding is usable by this ID type. */ - bool is_suitable_for(const ID &animated_id) const; - - /** Return whether this Binding has an `idtype` set. */ - bool has_idtype() const; - - protected: - friend Animation; - - /** - * Ensure the first two characters of the name match the ID type. - * - * \note This does NOT ensure name uniqueness within the Animation. That is - * the responsibility of the caller. - */ - void name_ensure_prefix(); -}; -static_assert(sizeof(Binding) == sizeof(::AnimationBinding), - "DNA struct and its C++ wrapper must have the same size"); - -/** - * KeyframeStrips effectively contain a bag of F-Curves for each Binding. - */ -class KeyframeStrip : public ::KeyframeAnimationStrip { - public: - KeyframeStrip() = default; - KeyframeStrip(const KeyframeStrip &other); - ~KeyframeStrip(); - - /* ChannelBag array access. */ - blender::Span channelbags() const; - blender::MutableSpan channelbags(); - const ChannelBag *channelbag(int64_t index) const; - ChannelBag *channelbag(int64_t index); - - /** - * Find the animation channels for this binding. - * - * \return nullptr if there is none yet for this binding. - */ - 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); - - /** - * Add the animation channels for this binding. - * - * Should only be called when there is no `ChannelBag` for this binding yet. - */ - ChannelBag &channelbag_for_binding_add(const Binding &binding); - /** - * Find an FCurve for this binding + RNA path + array index combination. - * - * If it cannot be found, `nullptr` is returned. - */ - FCurve *fcurve_find(const Binding &binding, StringRefNull rna_path, int array_index); - - /** - * Find an FCurve for this binding + RNA path + array index combination. - * - * If it cannot be found, a new one is created. - */ - FCurve &fcurve_find_or_create(const Binding &binding, StringRefNull rna_path, int array_index); - - SingleKeyingResult keyframe_insert(const Binding &binding, - StringRefNull rna_path, - int array_index, - float2 time_value, - const KeyframeSettings &settings); -}; -static_assert(sizeof(KeyframeStrip) == sizeof(::KeyframeAnimationStrip), - "DNA struct and its C++ wrapper must have the same size"); - -template<> KeyframeStrip &Strip::as(); -template<> const KeyframeStrip &Strip::as() const; - -/** - * Collection of F-Curves, intended for a specific Binding handle. - */ -class ChannelBag : public ::AnimationChannelBag { - public: - ChannelBag() = default; - ChannelBag(const ChannelBag &other); - ~ChannelBag(); - - /* FCurves access. */ - blender::Span fcurves() const; - blender::MutableSpan fcurves(); - const FCurve *fcurve(int64_t index) const; - FCurve *fcurve(int64_t index); - - const FCurve *fcurve_find(StringRefNull rna_path, int array_index) const; -}; -static_assert(sizeof(ChannelBag) == sizeof(::AnimationChannelBag), - "DNA struct and its C++ wrapper must have the same size"); - -/** - * Assign the animation to the ID. - * - * This will will make a best-effort guess as to which binding to use, in this - * order; - * - * - By binding 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 - * 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 - * will still return `true` as the Animation was successfully assigned. - */ -bool assign_animation(Animation &anim, ID &animated_id); - -/** - * Ensure that this ID is no longer animated. - */ -void unassign_animation(ID &animated_id); - -/** - * Clear the animation binding 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. - * - * \param adt the AnimData of the animated ID. - * - * \note this does not clear the Animation pointer, just the binding handle. - */ -void unassign_binding(AnimData &adt); - -/** - * Return the Animation of this ID, or nullptr if it has none. - */ -Animation *get_animation(ID &animated_id); - -/** - * Return the F-Curves for this specific binding 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 - * strip, of a single type, this function can be used. - * - * The use of this function is also an indicator for code that will have to be altered when - * multi-layered animation is getting implemented. - */ -Span fcurves_for_animation(Animation &anim, binding_handle_t binding_handle); -Span fcurves_for_animation(const Animation &anim, binding_handle_t binding_handle); - -} // namespace blender::animrig - -/* Wrap functions for the DNA structs. */ - -inline blender::animrig::Animation &Animation::wrap() -{ - return *reinterpret_cast(this); -} -inline const blender::animrig::Animation &Animation::wrap() const -{ - return *reinterpret_cast(this); -} - -inline blender::animrig::Layer &AnimationLayer::wrap() -{ - return *reinterpret_cast(this); -} -inline const blender::animrig::Layer &AnimationLayer::wrap() const -{ - return *reinterpret_cast(this); -} - -inline blender::animrig::Binding &AnimationBinding::wrap() -{ - return *reinterpret_cast(this); -} -inline const blender::animrig::Binding &AnimationBinding::wrap() const -{ - return *reinterpret_cast(this); -} - -inline blender::animrig::Strip &AnimationStrip::wrap() -{ - return *reinterpret_cast(this); -} -inline const blender::animrig::Strip &AnimationStrip::wrap() const -{ - return *reinterpret_cast(this); -} - -inline blender::animrig::KeyframeStrip &KeyframeAnimationStrip::wrap() -{ - return *reinterpret_cast(this); -} -inline const blender::animrig::KeyframeStrip &KeyframeAnimationStrip::wrap() const -{ - return *reinterpret_cast(this); -} - -inline blender::animrig::ChannelBag &AnimationChannelBag::wrap() -{ - return *reinterpret_cast(this); -} -inline const blender::animrig::ChannelBag &AnimationChannelBag::wrap() const -{ - return *reinterpret_cast(this); -} diff --git a/source/blender/animrig/ANIM_animdata.hh b/source/blender/animrig/ANIM_animdata.hh index 2b019f6b647..691b45db32b 100644 --- a/source/blender/animrig/ANIM_animdata.hh +++ b/source/blender/animrig/ANIM_animdata.hh @@ -22,7 +22,7 @@ struct bAction; namespace blender::animrig { -class Animation; +class Action; /** * Get (or add relevant data to be able to do so) the Active Action for the given @@ -66,7 +66,7 @@ bool animdata_remove_empty_action(AnimData *adt); * Again, this is just to hook up the new Animation data-block to the old * Blender UI code. */ -const FCurve *fcurve_find_by_rna_path(const Animation &anim, +const FCurve *fcurve_find_by_rna_path(const Action &anim, const ID &animated_id, StringRefNull rna_path, int array_index); diff --git a/source/blender/animrig/ANIM_evaluation.hh b/source/blender/animrig/ANIM_evaluation.hh index dc193929baf..cee217bf5b5 100644 --- a/source/blender/animrig/ANIM_evaluation.hh +++ b/source/blender/animrig/ANIM_evaluation.hh @@ -11,7 +11,7 @@ #include "DNA_anim_types.h" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" struct AnimationEvalContext; struct PointerRNA; @@ -27,7 +27,7 @@ namespace blender::animrig { * the given one is an evaluated copy) and update that too. */ void evaluate_and_apply_animation(PointerRNA &animated_id_ptr, - Animation &animation, + Action &animation, binding_handle_t binding_handle, const AnimationEvalContext &anim_eval_context, bool flush_to_original); diff --git a/source/blender/animrig/CMakeLists.txt b/source/blender/animrig/CMakeLists.txt index dfaf9e7f34c..d8a1c31c7d5 100644 --- a/source/blender/animrig/CMakeLists.txt +++ b/source/blender/animrig/CMakeLists.txt @@ -34,7 +34,6 @@ set(SRC intern/visualkey.cc ANIM_action.hh - ANIM_animation.hh ANIM_animdata.hh ANIM_armature_iter.hh ANIM_bone_collections.hh diff --git a/source/blender/animrig/intern/animation.cc b/source/blender/animrig/intern/animation.cc index 023962f7154..c076f9beb6b 100644 --- a/source/blender/animrig/intern/animation.cc +++ b/source/blender/animrig/intern/animation.cc @@ -2,7 +2,8 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "DNA_anim_defaults.h" +#include "DNA_action_defaults.h" +#include "DNA_action_types.h" #include "DNA_anim_types.h" #include "DNA_array_utils.hh" #include "DNA_defaults.h" @@ -14,11 +15,12 @@ #include "BLI_string_utf8.h" #include "BLI_string_utils.hh" +#include "BKE_action.hh" #include "BKE_anim_data.hh" -#include "BKE_animation.hh" #include "BKE_fcurve.hh" #include "BKE_lib_id.hh" #include "BKE_main.hh" +#include "BKE_preview_image.hh" #include "ED_keyframing.hh" @@ -28,7 +30,7 @@ #include "atomic_ops.h" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "ANIM_fcurve.hh" #include @@ -49,17 +51,17 @@ constexpr const char *binding_unbound_prefix = "XX"; } // namespace -static animrig::Layer &animationlayer_alloc() +static animrig::Layer &ActionLayer_alloc() { - AnimationLayer *layer = DNA_struct_default_alloc(AnimationLayer); + ActionLayer *layer = DNA_struct_default_alloc(ActionLayer); return layer->wrap(); } -static animrig::Strip &animationstrip_alloc_infinite(const Strip::Type type) +static animrig::Strip &ActionStrip_alloc_infinite(const Strip::Type type) { - AnimationStrip *strip = nullptr; + ActionStrip *strip = nullptr; switch (type) { case Strip::Type::Keyframe: { - KeyframeAnimationStrip *key_strip = MEM_new(__func__); + KeyframeActionStrip *key_strip = MEM_new(__func__); strip = &key_strip->strip; break; } @@ -67,8 +69,8 @@ static animrig::Strip &animationstrip_alloc_infinite(const Strip::Type type) BLI_assert_msg(strip, "unsupported strip type"); - /* Copy the default AnimationStrip fields into the allocated data-block. */ - memcpy(strip, DNA_struct_default_get(AnimationStrip), sizeof(*strip)); + /* Copy the default ActionStrip fields into the allocated data-block. */ + memcpy(strip, DNA_struct_default_get(ActionStrip), sizeof(*strip)); return strip->wrap(); } @@ -109,46 +111,63 @@ template static void shrink_array(T **array, int *num, const int shr /* ----- Animation implementation ----------- */ -blender::Span Animation::layers() const +bool Action::is_empty() const +{ + return this->layer_array_num == 0 && this->binding_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; +} +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 || + BLI_listbase_is_empty(&this->curves); +} + +blender::Span Action::layers() const { return blender::Span{reinterpret_cast(this->layer_array), this->layer_array_num}; } -blender::MutableSpan Animation::layers() +blender::MutableSpan Action::layers() { return blender::MutableSpan{reinterpret_cast(this->layer_array), this->layer_array_num}; } -const Layer *Animation::layer(const int64_t index) const +const Layer *Action::layer(const int64_t index) const { return &this->layer_array[index]->wrap(); } -Layer *Animation::layer(const int64_t index) +Layer *Action::layer(const int64_t index) { return &this->layer_array[index]->wrap(); } -Layer &Animation::layer_add(const StringRefNull name) +Layer &Action::layer_add(const StringRefNull name) { using namespace blender::animrig; - Layer &new_layer = animationlayer_alloc(); + Layer &new_layer = ActionLayer_alloc(); STRNCPY_UTF8(new_layer.name, name.c_str()); - grow_array_and_append<::AnimationLayer *>( - &this->layer_array, &this->layer_array_num, &new_layer); + grow_array_and_append<::ActionLayer *>(&this->layer_array, &this->layer_array_num, &new_layer); this->layer_active_index = this->layer_array_num - 1; return new_layer; } -static void layer_ptr_destructor(AnimationLayer **dna_layer_ptr) +static void layer_ptr_destructor(ActionLayer **dna_layer_ptr) { Layer &layer = (*dna_layer_ptr)->wrap(); MEM_delete(&layer); }; -bool Animation::layer_remove(Layer &layer_to_remove) +bool Action::layer_remove(Layer &layer_to_remove) { const int64_t layer_index = this->find_layer_index(layer_to_remove); if (layer_index < 0) { @@ -163,7 +182,7 @@ bool Animation::layer_remove(Layer &layer_to_remove) return true; } -int64_t Animation::find_layer_index(const Layer &layer) const +int64_t Action::find_layer_index(const Layer &layer) const { for (const int64_t layer_index : this->layers().index_range()) { const Layer *visit_layer = this->layer(layer_index); @@ -174,32 +193,32 @@ int64_t Animation::find_layer_index(const Layer &layer) const return -1; } -blender::Span Animation::bindings() const +blender::Span Action::bindings() const { return blender::Span{reinterpret_cast(this->binding_array), this->binding_array_num}; } -blender::MutableSpan Animation::bindings() +blender::MutableSpan Action::bindings() { return blender::MutableSpan{reinterpret_cast(this->binding_array), this->binding_array_num}; } -const Binding *Animation::binding(const int64_t index) const +const Binding *Action::binding(const int64_t index) const { return &this->binding_array[index]->wrap(); } -Binding *Animation::binding(const int64_t index) +Binding *Action::binding(const int64_t index) { return &this->binding_array[index]->wrap(); } -Binding *Animation::binding_for_handle(const binding_handle_t handle) +Binding *Action::binding_for_handle(const binding_handle_t handle) { - const Binding *binding = const_cast(this)->binding_for_handle(handle); + const Binding *binding = const_cast(this)->binding_for_handle(handle); return const_cast(binding); } -const Binding *Animation::binding_for_handle(const binding_handle_t handle) const +const Binding *Action::binding_for_handle(const binding_handle_t handle) const { /* TODO: implement hash-map lookup. */ for (const Binding *binding : bindings()) { @@ -210,12 +229,12 @@ const Binding *Animation::binding_for_handle(const binding_handle_t handle) cons return nullptr; } -static void anim_binding_name_ensure_unique(Animation &animation, Binding &binding) +static void anim_binding_name_ensure_unique(Action &animation, Binding &binding) { /* 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 { - Animation &anim; + Action &anim; Binding &binding; }; DupNameCheckData check_data = {animation, binding}; @@ -241,13 +260,13 @@ static void anim_binding_name_ensure_unique(Animation &animation, Binding &bindi * 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 Animation::binding_name_set(Main &bmain, Binding &binding, const StringRefNull new_name) +void Action::binding_name_set(Main &bmain, Binding &binding, const StringRefNull new_name) { this->binding_name_define(binding, new_name); this->binding_name_propagate(bmain, binding); } -void Animation::binding_name_define(Binding &binding, const StringRefNull new_name) +void Action::binding_name_define(Binding &binding, const StringRefNull new_name) { BLI_assert_msg( StringRef(new_name).size() >= Binding::name_length_min, @@ -256,7 +275,7 @@ void Animation::binding_name_define(Binding &binding, const StringRefNull new_na anim_binding_name_ensure_unique(*this, binding); } -void Animation::binding_name_propagate(Main &bmain, const Binding &binding) +void Action::binding_name_propagate(Main &bmain, const Binding &binding) { /* Just loop over all animatable IDs in the main database. */ ListBase *lb; @@ -270,7 +289,7 @@ void Animation::binding_name_propagate(Main &bmain, const Binding &binding) } AnimData *adt = BKE_animdata_from_id(id); - if (!adt || adt->animation != this) { + if (!adt || adt->action != this) { /* Not animated by this Animation. */ continue; } @@ -287,7 +306,7 @@ void Animation::binding_name_propagate(Main &bmain, const Binding &binding) FOREACH_MAIN_LISTBASE_END; } -Binding *Animation::binding_find_by_name(const StringRefNull binding_name) +Binding *Action::binding_find_by_name(const StringRefNull binding_name) { for (Binding *binding : bindings()) { if (STREQ(binding->name, binding_name.c_str())) { @@ -297,17 +316,17 @@ Binding *Animation::binding_find_by_name(const StringRefNull binding_name) return nullptr; } -Binding *Animation::binding_for_id(const ID &animated_id) +Binding *Action::binding_for_id(const ID &animated_id) { - const Binding *binding = const_cast(this)->binding_for_id(animated_id); + const Binding *binding = const_cast(this)->binding_for_id(animated_id); return const_cast(binding); } -const Binding *Animation::binding_for_id(const ID &animated_id) const +const Binding *Action::binding_for_id(const ID &animated_id) const { const AnimData *adt = BKE_animdata_from_id(&animated_id); - /* Note that there is no check that `adt->animation` is actually `this`. */ + /* Note that there is no check that `adt->action` is actually `this`. */ const Binding *binding = this->binding_for_handle(adt->binding_handle); if (!binding) { @@ -319,16 +338,16 @@ const Binding *Animation::binding_for_id(const ID &animated_id) const return binding; } -Binding &Animation::binding_allocate() +Binding &Action::binding_allocate() { - Binding &binding = MEM_new(__func__)->wrap(); + Binding &binding = MEM_new(__func__)->wrap(); this->last_binding_handle++; BLI_assert_msg(this->last_binding_handle > 0, "Animation Binding handle overflow"); binding.handle = this->last_binding_handle; return binding; } -Binding &Animation::binding_add() +Binding &Action::binding_add() { Binding &binding = this->binding_allocate(); @@ -337,14 +356,14 @@ Binding &Animation::binding_add() BLI_strncpy_utf8(binding.name + 2, DATA_(binding_default_name), ARRAY_SIZE(binding.name) - 2); /* Append the Binding to the animation data-block. */ - grow_array_and_append<::AnimationBinding *>( + grow_array_and_append<::ActionBinding *>( &this->binding_array, &this->binding_array_num, &binding); anim_binding_name_ensure_unique(*this, binding); return binding; } -Binding &Animation::binding_add_for_id(const ID &animated_id) +Binding &Action::binding_add_for_id(const ID &animated_id) { Binding &binding = this->binding_add(); @@ -357,13 +376,13 @@ Binding &Animation::binding_add_for_id(const ID &animated_id) return binding; } -Binding *Animation::find_suitable_binding_for(const ID &animated_id) +Binding *Action::find_suitable_binding_for(const ID &animated_id) { AnimData *adt = BKE_animdata_from_id(&animated_id); - /* The binding handle is only valid when this animation has already been + /* The binding handle is only valid when this action has already been * assigned. Otherwise it's meaningless. */ - if (adt && adt->animation == this) { + if (adt && adt->action == this) { Binding *binding = this->binding_for_handle(adt->binding_handle); if (binding && binding->is_suitable_for(animated_id)) { return binding; @@ -387,7 +406,7 @@ Binding *Animation::find_suitable_binding_for(const ID &animated_id) return nullptr; } -bool Animation::is_binding_animated(const binding_handle_t binding_handle) const +bool Action::is_binding_animated(const binding_handle_t binding_handle) const { if (binding_handle == Binding::unassigned) { return false; @@ -397,31 +416,14 @@ bool Animation::is_binding_animated(const binding_handle_t binding_handle) const return !fcurves.is_empty(); } -void Animation::free_data() -{ - /* Free layers. */ - for (Layer *layer : this->layers()) { - MEM_delete(layer); - } - MEM_SAFE_FREE(this->layer_array); - this->layer_array_num = 0; - - /* Free bindings. */ - for (Binding *binding : this->bindings()) { - MEM_delete(binding); - } - MEM_SAFE_FREE(this->binding_array); - this->binding_array_num = 0; -} - -bool Animation::assign_id(Binding *binding, ID &animated_id) +bool Action::assign_id(Binding *binding, ID &animated_id) { AnimData *adt = BKE_animdata_ensure_id(&animated_id); if (!adt) { return false; } - if (adt->animation && adt->animation != this) { + if (adt->action && adt->action != this) { /* The caller should unassign the ID from its existing animation first, or * use the top-level function `assign_animation(anim, ID)`. */ return false; @@ -441,24 +443,24 @@ bool Animation::assign_id(Binding *binding, ID &animated_id) unassign_binding(*adt); } - if (!adt->animation) { - /* Due to the precondition check above, we know that adt->animation is either 'this' (in which + if (!adt->action) { + /* Due to the precondition check above, we know that adt->action is either 'this' (in which * case the user count is already correct) or `nullptr` (in which case this is a new reference, * and the user count should be increased). */ id_us_plus(&this->id); - adt->animation = this; + adt->action = this; } return true; } -void Animation::binding_name_ensure_prefix(Binding &binding) +void Action::binding_name_ensure_prefix(Binding &binding) { binding.name_ensure_prefix(); anim_binding_name_ensure_unique(*this, binding); } -void Animation::binding_setup_for_id(Binding &binding, const ID &animated_id) +void Action::binding_setup_for_id(Binding &binding, const ID &animated_id) { if (binding.has_idtype()) { BLI_assert(binding.idtype == GS(animated_id.name)); @@ -469,26 +471,26 @@ void Animation::binding_setup_for_id(Binding &binding, const ID &animated_id) this->binding_name_ensure_prefix(binding); } -void Animation::unassign_id(ID &animated_id) +void Action::unassign_id(ID &animated_id) { AnimData *adt = BKE_animdata_from_id(&animated_id); BLI_assert_msg(adt, "ID is not animated at all"); - BLI_assert_msg(adt->animation == this, "ID is not assigned to this Animation"); + BLI_assert_msg(adt->action == this, "ID is not assigned to this Animation"); unassign_binding(*adt); id_us_min(&this->id); - adt->animation = nullptr; + adt->action = nullptr; } -/* ----- AnimationLayer implementation ----------- */ +/* ----- ActionLayer implementation ----------- */ Layer::Layer(const Layer &other) { memcpy(this, &other, sizeof(*this)); /* Strips. */ - this->strip_array = MEM_cnew_array(other.strip_array_num, __func__); + this->strip_array = MEM_cnew_array(other.strip_array_num, __func__); for (int i : other.strips().index_range()) { this->strip_array[i] = other.strip(i)->duplicate(__func__); } @@ -524,15 +526,15 @@ Strip *Layer::strip(const int64_t index) Strip &Layer::strip_add(const Strip::Type strip_type) { - Strip &strip = animationstrip_alloc_infinite(strip_type); + Strip &strip = ActionStrip_alloc_infinite(strip_type); /* Add the new strip to the strip array. */ - grow_array_and_append<::AnimationStrip *>(&this->strip_array, &this->strip_array_num, &strip); + grow_array_and_append<::ActionStrip *>(&this->strip_array, &this->strip_array_num, &strip); return strip; } -static void strip_ptr_destructor(AnimationStrip **dna_strip_ptr) +static void strip_ptr_destructor(ActionStrip **dna_strip_ptr) { Strip &strip = (*dna_strip_ptr)->wrap(); MEM_delete(&strip); @@ -562,7 +564,7 @@ int64_t Layer::find_strip_index(const Strip &strip) const return -1; } -/* ----- AnimationBinding implementation ----------- */ +/* ----- ActionBinding implementation ----------- */ bool Binding::is_suitable_for(const ID &animated_id) const { @@ -581,7 +583,7 @@ bool Binding::has_idtype() const return this->idtype != 0; } -bool assign_animation(Animation &anim, ID &animated_id) +bool assign_animation(Action &anim, ID &animated_id) { unassign_animation(animated_id); @@ -591,7 +593,7 @@ bool assign_animation(Animation &anim, ID &animated_id) void unassign_animation(ID &animated_id) { - Animation *anim = get_animation(animated_id); + Action *anim = get_animation(animated_id); if (!anim) { return; } @@ -607,8 +609,8 @@ void unassign_binding(AnimData &adt) * * TODO: Replace this with a BLI_assert() that the name is as expected, and "simply" ensure this * name is always correct. */ - if (adt.animation) { - const Animation &anim = adt.animation->wrap(); + if (adt.action) { + const Action &anim = adt.action->wrap(); const Binding *binding = anim.binding_for_handle(adt.binding_handle); if (binding) { STRNCPY_UTF8(adt.binding_name, binding->name); @@ -618,16 +620,17 @@ void unassign_binding(AnimData &adt) adt.binding_handle = Binding::unassigned; } -Animation *get_animation(ID &animated_id) +/* TODO: rename to get_action(). */ +Action *get_animation(ID &animated_id) { AnimData *adt = BKE_animdata_from_id(&animated_id); if (!adt) { return nullptr; } - if (!adt->animation) { + if (!adt->action) { return nullptr; } - return &adt->animation->wrap(); + return &adt->action->wrap(); } std::string Binding::name_prefix_for_idtype() const @@ -672,7 +675,7 @@ void Binding::name_ensure_prefix() *reinterpret_cast(this->name) = this->idtype; } -/* ----- AnimationStrip implementation ----------- */ +/* ----- ActionStrip implementation ----------- */ Strip *Strip::duplicate(const StringRefNull allocation_name) const { @@ -720,14 +723,14 @@ void Strip::resize(const float frame_start, const float frame_end) this->frame_end = frame_end; } -/* ----- KeyframeAnimationStrip implementation ----------- */ +/* ----- KeyframeActionStrip implementation ----------- */ KeyframeStrip::KeyframeStrip(const KeyframeStrip &other) { memcpy(this, &other, sizeof(*this)); - this->channelbags_array = MEM_cnew_array(other.channelbags_array_num, - __func__); + this->channelbags_array = MEM_cnew_array(other.channelbags_array_num, + __func__); Span channelbags_src = other.channelbags(); for (int i : channelbags_src.index_range()) { this->channelbags_array[i] = MEM_new(__func__, *other.channelbag(i)); @@ -808,10 +811,10 @@ ChannelBag &KeyframeStrip::channelbag_for_binding_add(const Binding &binding) BLI_assert_msg(channelbag_for_binding(binding) == nullptr, "Cannot add chans-for-binding for already-registered binding"); - ChannelBag &channels = MEM_new(__func__)->wrap(); + ChannelBag &channels = MEM_new(__func__)->wrap(); channels.binding_handle = binding.handle; - grow_array_and_append( + grow_array_and_append( &this->channelbags_array, &this->channelbags_array_num, &channels); return channels; @@ -896,7 +899,7 @@ SingleKeyingResult KeyframeStrip::keyframe_insert(const Binding &binding, return SingleKeyingResult::SUCCESS; } -/* AnimationChannelBag implementation. */ +/* ActionChannelBag implementation. */ ChannelBag::ChannelBag(const ChannelBag &other) { @@ -949,7 +952,7 @@ const FCurve *ChannelBag::fcurve_find(const StringRefNull rna_path, const int ar /* Utility function implementations. */ -static const animrig::ChannelBag *channelbag_for_animation(const Animation &anim, +static const animrig::ChannelBag *channelbag_for_animation(const Action &anim, const binding_handle_t binding_handle) { if (binding_handle == Binding::unassigned) { @@ -973,15 +976,15 @@ static const animrig::ChannelBag *channelbag_for_animation(const Animation &anim return nullptr; } -static animrig::ChannelBag *channelbag_for_animation(Animation &anim, +static animrig::ChannelBag *channelbag_for_animation(Action &anim, const binding_handle_t binding_handle) { - const animrig::ChannelBag *const_bag = channelbag_for_animation( - const_cast(anim), binding_handle); + const animrig::ChannelBag *const_bag = channelbag_for_animation(const_cast(anim), + binding_handle); return const_cast(const_bag); } -Span fcurves_for_animation(Animation &anim, const binding_handle_t binding_handle) +Span fcurves_for_animation(Action &anim, const binding_handle_t binding_handle) { animrig::ChannelBag *bag = channelbag_for_animation(anim, binding_handle); if (!bag) { @@ -990,7 +993,7 @@ Span fcurves_for_animation(Animation &anim, const binding_handle_t bin return bag->fcurves(); } -Span fcurves_for_animation(const Animation &anim, +Span fcurves_for_animation(const Action &anim, const binding_handle_t binding_handle) { const animrig::ChannelBag *bag = channelbag_for_animation(anim, binding_handle); diff --git a/source/blender/animrig/intern/animation_test.cc b/source/blender/animrig/intern/animation_test.cc index 43e07b14d29..a0b18e0e58c 100644 --- a/source/blender/animrig/intern/animation_test.cc +++ b/source/blender/animrig/intern/animation_test.cc @@ -2,10 +2,10 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "ANIM_animation.hh" +#include "ANIM_action.hh" +#include "BKE_action.hh" #include "BKE_anim_data.hh" -#include "BKE_animation.hh" #include "BKE_fcurve.hh" #include "BKE_idtype.hh" #include "BKE_lib_id.hh" @@ -24,10 +24,10 @@ #include "testing/testing.h" namespace blender::animrig::tests { -class AnimationLayersTest : public testing::Test { +class ActionLayersTest : public testing::Test { public: Main *bmain; - Animation *anim; + Action *anim; Object *cube; Object *suzanne; @@ -48,7 +48,7 @@ class AnimationLayersTest : public testing::Test { void SetUp() override { bmain = BKE_main_new(); - anim = static_cast(BKE_id_new(bmain, ID_AN, "ANÄnimåtië")); + anim = static_cast(BKE_id_new(bmain, ID_AC, "ACÄnimåtië")); cube = BKE_object_add_only_object(bmain, OB_EMPTY, "Küüübus"); suzanne = BKE_object_add_only_object(bmain, OB_EMPTY, "OBSuzanne"); } @@ -59,7 +59,7 @@ class AnimationLayersTest : public testing::Test { } }; -TEST_F(AnimationLayersTest, add_layer) +TEST_F(ActionLayersTest, add_layer) { Layer &layer = anim->layer_add("layer name"); @@ -71,7 +71,7 @@ TEST_F(AnimationLayersTest, add_layer) ASSERT_EQ(0, layer.strips().size()) << "Expected newly added layer to have no strip."; } -TEST_F(AnimationLayersTest, remove_layer) +TEST_F(ActionLayersTest, remove_layer) { Layer &layer0 = anim->layer_add("Test Læür nul"); Layer &layer1 = anim->layer_add("Test Læür één"); @@ -84,7 +84,7 @@ TEST_F(AnimationLayersTest, remove_layer) layer2.strip_add(Strip::Type::Keyframe); { /* Test removing a layer that is not owned. */ - Animation *other_anim = static_cast(BKE_id_new(bmain, ID_AN, "ANOtherAnim")); + Action *other_anim = static_cast(BKE_id_new(bmain, ID_AC, "ACOtherAnim")); Layer &other_layer = other_anim->layer_add("Another Layer"); EXPECT_FALSE(anim->layer_remove(other_layer)) << "Removing a layer not owned by the animation should be gracefully rejected"; @@ -104,7 +104,7 @@ TEST_F(AnimationLayersTest, remove_layer) EXPECT_EQ(0, anim->layers().size()); } -TEST_F(AnimationLayersTest, add_strip) +TEST_F(ActionLayersTest, add_strip) { Layer &layer = anim->layer_add("Test Læür"); @@ -133,7 +133,7 @@ TEST_F(AnimationLayersTest, add_strip) binding, "location", 0, {1.0f, 47.0f}, settings); } -TEST_F(AnimationLayersTest, remove_strip) +TEST_F(ActionLayersTest, remove_strip) { Layer &layer = anim->layer_add("Test Læür"); Strip &strip0 = layer.strip_add(Strip::Type::Keyframe); @@ -168,7 +168,7 @@ TEST_F(AnimationLayersTest, remove_strip) } } -TEST_F(AnimationLayersTest, add_binding) +TEST_F(ActionLayersTest, add_binding) { { /* Creating an 'unused' Binding should just be called 'Binding'. */ Binding &binding = anim->binding_add(); @@ -189,7 +189,7 @@ TEST_F(AnimationLayersTest, add_binding) } } -TEST_F(AnimationLayersTest, add_binding_multiple) +TEST_F(ActionLayersTest, add_binding_multiple) { Binding &bind_cube = anim->binding_add(); Binding &bind_suzanne = anim->binding_add(); @@ -201,7 +201,7 @@ TEST_F(AnimationLayersTest, add_binding_multiple) EXPECT_EQ(2, bind_suzanne.handle); } -TEST_F(AnimationLayersTest, anim_assign_id) +TEST_F(ActionLayersTest, anim_assign_id) { /* Assign to the only, 'virgin' Binding, should always work. */ Binding &binding_cube = anim->binding_add(); @@ -219,7 +219,7 @@ TEST_F(AnimationLayersTest, anim_assign_id) << "The binding name should be copied to the adt"; { /* Assign Cube to another animation+binding without unassigning first. */ - Animation *another_anim = static_cast(BKE_id_new(bmain, ID_AN, "ANOtherAnim")); + Action *another_anim = static_cast(BKE_id_new(bmain, ID_AC, "ACOtherAnim")); Binding &another_binding = another_anim->binding_add(); ASSERT_FALSE(another_anim->assign_id(&another_binding, cube->id)) << "Assigning animation (with this function) when already assigned should fail."; @@ -258,7 +258,7 @@ TEST_F(AnimationLayersTest, anim_assign_id) BKE_id_free(nullptr, mesh); } -TEST_F(AnimationLayersTest, rename_binding) +TEST_F(ActionLayersTest, rename_binding) { Binding &binding_cube = anim->binding_add(); ASSERT_TRUE(anim->assign_id(&binding_cube, cube->id)); @@ -285,7 +285,7 @@ TEST_F(AnimationLayersTest, rename_binding) EXPECT_STREQ("Even Newer Name", cube->adt->binding_name); } -TEST_F(AnimationLayersTest, binding_name_ensure_prefix) +TEST_F(ActionLayersTest, binding_name_ensure_prefix) { class AccessibleBinding : public Binding { public: @@ -321,7 +321,7 @@ TEST_F(AnimationLayersTest, binding_name_ensure_prefix) EXPECT_STREQ("XXNewName", binding.name); } -TEST_F(AnimationLayersTest, binding_name_prefix) +TEST_F(ActionLayersTest, binding_name_prefix) { Binding &binding = anim->binding_add(); EXPECT_EQ("XX", binding.name_prefix_for_idtype()); @@ -330,7 +330,7 @@ TEST_F(AnimationLayersTest, binding_name_prefix) EXPECT_EQ("CA", binding.name_prefix_for_idtype()); } -TEST_F(AnimationLayersTest, rename_binding_name_collision) +TEST_F(ActionLayersTest, rename_binding_name_collision) { Binding &binding1 = anim->binding_add(); Binding &binding2 = anim->binding_add(); @@ -341,7 +341,7 @@ TEST_F(AnimationLayersTest, rename_binding_name_collision) EXPECT_STREQ("New Binding Name.001", binding2.name); } -TEST_F(AnimationLayersTest, find_suitable_binding) +TEST_F(ActionLayersTest, find_suitable_binding) { /* === * Empty case, no bindings exist yet and the ID doesn't even have an AnimData. */ @@ -367,7 +367,7 @@ TEST_F(AnimationLayersTest, find_suitable_binding) other_binding.handle = 47; AnimData *adt = BKE_animdata_ensure_id(&cube->id); - adt->animation = nullptr; + 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); @@ -377,7 +377,7 @@ TEST_F(AnimationLayersTest, find_suitable_binding) * Same situation as above (AnimData has name of one binding, but the handle of another), * except that the animation data-block has already been assigned. In this case the handle * should take precedence. */ - adt->animation = anim; + adt->action = anim; id_us_plus(&anim->id); EXPECT_EQ(&other_binding, anim->find_suitable_binding_for(cube->id)); @@ -389,7 +389,7 @@ TEST_F(AnimationLayersTest, find_suitable_binding) EXPECT_EQ(&binding, anim->find_suitable_binding_for(cube->id)); } -TEST_F(AnimationLayersTest, strip) +TEST_F(ActionLayersTest, strip) { constexpr float inf = std::numeric_limits::infinity(); Layer &layer0 = anim->layer_add("Test Læür nul"); @@ -427,7 +427,7 @@ TEST_F(AnimationLayersTest, strip) EXPECT_FALSE(strip.is_last_frame(172800.075f)); } -TEST_F(AnimationLayersTest, KeyframeStrip__keyframe_insert) +TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert) { Binding &binding = anim->binding_add(); EXPECT_TRUE(anim->assign_id(&binding, cube->id)); diff --git a/source/blender/animrig/intern/animdata.cc b/source/blender/animrig/intern/animdata.cc index aa2b215526a..e8329b2ab96 100644 --- a/source/blender/animrig/intern/animdata.cc +++ b/source/blender/animrig/intern/animdata.cc @@ -6,7 +6,7 @@ * \ingroup animrig */ -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "ANIM_animdata.hh" #include "BKE_action.h" @@ -94,38 +94,40 @@ void animdata_fcurve_delete(bAnimContext *ac, AnimData *adt, FCurve *fcu) BLI_remlink(&adt->drivers, fcu); } else if (adt->action) { - bAction *act = adt->action; + Action &action = adt->action->wrap(); - /* Remove from group or action, whichever one "owns" the F-Curve. */ - if (fcu->grp) { - bActionGroup *agrp = fcu->grp; + if (action.is_action_legacy()) { + /* Remove from group or action, whichever one "owns" the F-Curve. */ + if (fcu->grp) { + bActionGroup *agrp = fcu->grp; - /* Remove F-Curve from group+action. */ - action_groups_remove_channel(act, fcu); + /* Remove F-Curve from group+action. */ + action_groups_remove_channel(&action, fcu); - /* If group has no more channels, remove it too, - * otherwise can have many dangling groups #33541. - */ - if (BLI_listbase_is_empty(&agrp->channels)) { - BLI_freelinkN(&act->groups, agrp); + /* If group has no more channels, remove it too, + * otherwise can have many dangling groups #33541. + */ + if (BLI_listbase_is_empty(&agrp->channels)) { + BLI_freelinkN(&action.groups, agrp); + } } + else { + BLI_remlink(&action.curves, fcu); + } + + /* If action has no more F-Curves as a result of this, unlink it from + * AnimData if it did not come from a NLA Strip being tweaked. + * + * This is done so that we don't have dangling Object+Action entries in + * channel list that are empty, and linger around long after the data they + * are for has disappeared (and probably won't come back). + */ + animdata_remove_empty_action(adt); } else { - BLI_remlink(&act->curves, fcu); + /* TODO: support deleting FCurves from Animation data-blocks. */ + return; } - - /* If action has no more F-Curves as a result of this, unlink it from - * AnimData if it did not come from a NLA Strip being tweaked. - * - * This is done so that we don't have dangling Object+Action entries in - * channel list that are empty, and linger around long after the data they - * are for has disappeared (and probably won't come back). - */ - animdata_remove_empty_action(adt); - } - else if (adt->animation) { - /* TODO: support deleting FCurves from Animation data-blocks. */ - return; } else { BLI_assert_unreachable(); @@ -182,7 +184,7 @@ void reevaluate_fcurve_errors(bAnimContext *ac) } } -const FCurve *fcurve_find_by_rna_path(const Animation &anim, +const FCurve *fcurve_find_by_rna_path(const Action &anim, const ID &animated_id, const StringRefNull rna_path, const int array_index) diff --git a/source/blender/animrig/intern/evaluation.cc b/source/blender/animrig/intern/evaluation.cc index 7a46876d7b0..96907f4c185 100644 --- a/source/blender/animrig/intern/evaluation.cc +++ b/source/blender/animrig/intern/evaluation.cc @@ -36,7 +36,7 @@ void apply_evaluation_result(const EvaluationResult &evaluation_result, bool flush_to_original); static EvaluationResult evaluate_animation(PointerRNA &animated_id_ptr, - Animation &animation, + Action &animation, const binding_handle_t binding_handle, const AnimationEvalContext &anim_eval_context) { @@ -70,7 +70,7 @@ static EvaluationResult evaluate_animation(PointerRNA &animated_id_ptr, } void evaluate_and_apply_animation(PointerRNA &animated_id_ptr, - Animation &animation, + Action &animation, const binding_handle_t binding_handle, const AnimationEvalContext &anim_eval_context, const bool flush_to_original) diff --git a/source/blender/animrig/intern/evaluation_test.cc b/source/blender/animrig/intern/evaluation_test.cc index 670780bace4..ed7af1afd46 100644 --- a/source/blender/animrig/intern/evaluation_test.cc +++ b/source/blender/animrig/intern/evaluation_test.cc @@ -2,14 +2,15 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "ANIM_evaluation.hh" #include "evaluation_internal.hh" -#include "BKE_animation.hh" +#include "BKE_action.hh" #include "BKE_animsys.h" #include "BKE_idtype.hh" #include "BKE_lib_id.hh" +#include "BKE_main.hh" #include "BKE_object.hh" #include "DNA_object_types.h" @@ -22,6 +23,7 @@ #include +#include "CLG_log.h" #include "testing/testing.h" namespace blender::animrig::tests { @@ -30,7 +32,8 @@ using namespace blender::animrig::internal; class AnimationEvaluationTest : public testing::Test { protected: - Animation anim = {}; + Main *bmain; + Action *anim; Object *cube; Binding *binding; Layer *layer; @@ -42,20 +45,28 @@ class AnimationEvaluationTest : public testing::Test { public: static void SetUpTestSuite() { + /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */ + CLG_init(); + /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */ BKE_idtype_init(); } + static void TearDownTestSuite() + { + CLG_exit(); + } + void SetUp() override { - anim = {}; - STRNCPY_UTF8(anim.id.name, "ANÄnimåtië"); + bmain = BKE_main_new(); + anim = static_cast(BKE_id_new(bmain, ID_AC, "ACÄnimåtië")); - cube = BKE_object_add_only_object(nullptr, OB_EMPTY, "Küüübus"); + cube = BKE_object_add_only_object(bmain, OB_EMPTY, "Küüübus"); - binding = &anim.binding_add(); - anim.assign_id(binding, cube->id); - layer = &anim.layer_add("Kübus layer"); + binding = &anim->binding_add(); + anim->assign_id(binding, cube->id); + layer = &anim->layer_add("Kübus layer"); /* Make it easier to predict test values. */ settings.interpolation = BEZT_IPO_LIN; @@ -65,9 +76,7 @@ class AnimationEvaluationTest : public testing::Test { void TearDown() override { - BKE_id_free(nullptr, &cube->id); - - anim.wrap().free_data(); + BKE_main_free(bmain); } /** Evaluate the layer, and return result for the given property. */ diff --git a/source/blender/blenkernel/BKE_animation.hh b/source/blender/blenkernel/BKE_animation.hh deleted file mode 100644 index cc245765f36..00000000000 --- a/source/blender/blenkernel/BKE_animation.hh +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - * - * This file only contains the memory management functions for the Animation - * data-block. For all other functionality, see `source/blender/animrig`. - */ - -#pragma once - -struct Animation; -struct Main; - -Animation *BKE_animation_add(Main *bmain, const char name[]); diff --git a/source/blender/blenkernel/BKE_idtype.hh b/source/blender/blenkernel/BKE_idtype.hh index 3b751278ac2..4147204d24c 100644 --- a/source/blender/blenkernel/BKE_idtype.hh +++ b/source/blender/blenkernel/BKE_idtype.hh @@ -281,7 +281,6 @@ extern IDTypeInfo IDType_ID_CV; extern IDTypeInfo IDType_ID_PT; extern IDTypeInfo IDType_ID_VO; extern IDTypeInfo IDType_ID_GP; -extern IDTypeInfo IDType_ID_AN; /** Empty shell mostly, but needed for read code. */ extern IDTypeInfo IDType_ID_LINK_PLACEHOLDER; diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index e83867a3d6d..392eb43784a 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -210,7 +210,6 @@ struct Main { ListBase collections; ListBase armatures; ListBase actions; - ListBase animations; ListBase nodetrees; ListBase brushes; ListBase particles; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 1b019bfee03..81f97bbfbc4 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -56,7 +56,6 @@ set(SRC intern/anim_path.cc intern/anim_sys.cc intern/anim_visualization.cc - intern/animation.cc intern/anonymous_attribute_id.cc intern/appdir.cc intern/armature.cc @@ -329,7 +328,6 @@ set(SRC BKE_anim_data.hh BKE_anim_path.h BKE_anim_visualization.h - BKE_animation.hh BKE_animsys.h BKE_anonymous_attribute_id.hh BKE_appdir.hh diff --git a/source/blender/blenkernel/intern/action.cc b/source/blender/blenkernel/intern/action.cc index 463b15edf8c..8b5264ee319 100644 --- a/source/blender/blenkernel/intern/action.cc +++ b/source/blender/blenkernel/intern/action.cc @@ -64,6 +64,7 @@ #include "BLO_read_write.hh" +#include "ANIM_action.hh" #include "ANIM_bone_collections.hh" #include "ANIM_bonecolor.hh" @@ -87,6 +88,7 @@ static CLG_LogRef LOG = {"bke.action"}; /**************************** Action Datablock ******************************/ /*********************** Armature Datablock ***********************/ +namespace blender::bke { /** * Only copy internal data of Action ID from source @@ -104,20 +106,23 @@ static void action_copy_data(Main * /*bmain*/, const ID *id_src, const int flag) { - bAction *action_dst = (bAction *)id_dst; - const bAction *action_src = (const bAction *)id_src; + bAction *dna_action_dst = reinterpret_cast(id_dst); + animrig::Action &action_dst = dna_action_dst->wrap(); + + const bAction *dna_action_src = reinterpret_cast(id_src); + const animrig::Action &action_src = dna_action_src->wrap(); bActionGroup *group_dst, *group_src; FCurve *fcurve_dst, *fcurve_src; /* Duplicate the lists of groups and markers. */ - BLI_duplicatelist(&action_dst->groups, &action_src->groups); - BLI_duplicatelist(&action_dst->markers, &action_src->markers); + BLI_duplicatelist(&action_dst.groups, &action_src.groups); + BLI_duplicatelist(&action_dst.markers, &action_src.markers); /* Copy F-Curves, fixing up the links as we go. */ - BLI_listbase_clear(&action_dst->curves); + BLI_listbase_clear(&action_dst.curves); - for (fcurve_src = static_cast(action_src->curves.first); fcurve_src; + for (fcurve_src = static_cast(action_src.curves.first); fcurve_src; fcurve_src = fcurve_src->next) { /* Duplicate F-Curve. */ @@ -126,11 +131,11 @@ static void action_copy_data(Main * /*bmain*/, * But surprisingly does not seem to be doing any ID reference-counting. */ fcurve_dst = BKE_fcurve_copy(fcurve_src); - BLI_addtail(&action_dst->curves, fcurve_dst); + BLI_addtail(&action_dst.curves, fcurve_dst); /* Fix group links (kind of bad list-in-list search, but this is the most reliable way). */ - for (group_dst = static_cast(action_dst->groups.first), - group_src = static_cast(action_src->groups.first); + for (group_dst = static_cast(action_dst.groups.first), + group_src = static_cast(action_src.groups.first); group_dst && group_src; group_dst = group_dst->next, group_src = group_src->next) { @@ -148,47 +153,97 @@ 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; + + /* Layers. */ + action_dst.layer_array = MEM_cnew_array(action_src.layer_array_num, __func__); + for (int i : action_src.layers().index_range()) { + action_dst.layer_array[i] = MEM_new(__func__, *action_src.layer(i)); + } + + /* Bindings. */ + action_dst.binding_array = MEM_cnew_array(action_src.binding_array_num, + __func__); + for (int i : action_src.bindings().index_range()) { + action_dst.binding_array[i] = MEM_new(__func__, *action_src.binding(i)); + } + if (flag & LIB_ID_COPY_NO_PREVIEW) { - action_dst->preview = nullptr; + action_dst.preview = nullptr; } else { - BKE_previewimg_id_copy(&action_dst->id, &action_src->id); + BKE_previewimg_id_copy(&action_dst.id, &action_src.id); } } /** Free (or release) any data used by this action (does not free the action itself). */ static void action_free_data(ID *id) { - bAction *action = (bAction *)id; - /* No animdata here. */ + animrig::Action &action = reinterpret_cast(id)->wrap(); - /* Free F-Curves. */ - BKE_fcurves_free(&action->curves); + /* Free layers. */ + for (animrig::Layer *layer : action.layers()) { + MEM_delete(layer); + } + MEM_SAFE_FREE(action.layer_array); + action.layer_array_num = 0; - /* Free groups. */ - BLI_freelistN(&action->groups); + /* Free bindings. */ + for (animrig::Binding *binding : action.bindings()) { + MEM_delete(binding); + } + MEM_SAFE_FREE(action.binding_array); + action.binding_array_num = 0; - /* Free pose-references (aka local markers). */ - BLI_freelistN(&action->markers); + /* Free legacy F-Curves & groups. */ + BKE_fcurves_free(&action.curves); + BLI_freelistN(&action.groups); - BKE_previewimg_free(&action->preview); + /* Free markers & preview. */ + BLI_freelistN(&action.markers); + BKE_previewimg_free(&action.preview); + + BLI_assert(action.is_empty()); } static void action_foreach_id(ID *id, LibraryForeachIDData *data) { - bAction *act = reinterpret_cast(id); + animrig::Action &action = reinterpret_cast(id)->wrap(); const int flag = BKE_lib_query_foreachid_process_flags_get(data); - LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { + /* TODO: it might be nice to have some iterator that just visits all animation channels + * in the layered Action data, and use that to replace this nested for-loop. */ + for (animrig::Layer *layer : action.layers()) { + for (animrig::Strip *strip : layer->strips()) { + switch (strip->type()) { + case animrig::Strip::Type::Keyframe: { + auto &key_strip = strip->as(); + for (animrig::ChannelBag *channelbag_for_binding : key_strip.channelbags()) { + for (FCurve *fcurve : channelbag_for_binding->fcurves()) { + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcurve, data)); + } + } + } + } + } + } + + /* Legacy F-Curves. */ + LISTBASE_FOREACH (FCurve *, fcu, &action.curves) { BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } - LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { + LISTBASE_FOREACH (TimeMarker *, marker, &action.markers) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); } + /* Even more legacy IPO curves. */ if (flag & IDWALK_DO_DEPRECATED_POINTERS) { - LISTBASE_FOREACH (bActionChannel *, chan, &act->chanbase) { + LISTBASE_FOREACH (bActionChannel *, chan, &action.chanbase) { BKE_LIB_FOREACHID_PROCESS_ID_NOCHECK(data, chan->ipo, IDWALK_CB_USER); LISTBASE_FOREACH (bConstraintChannel *, chan_constraint, &chan->constraintChannels) { BKE_LIB_FOREACHID_PROCESS_ID_NOCHECK(data, chan_constraint->ipo, IDWALK_CB_USER); @@ -197,53 +252,168 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data) } } +static void write_channelbag(BlendWriter *writer, animrig::ChannelBag &channelbag) +{ + BLO_write_struct(writer, ActionChannelBag, &channelbag); + + Span fcurves = channelbag.fcurves(); + BLO_write_pointer_array(writer, fcurves.size(), fcurves.data()); + + for (FCurve *fcurve : fcurves) { + BLO_write_struct(writer, FCurve, fcurve); + BKE_fcurve_blend_write_data(writer, fcurve); + } +} + +static void write_keyframe_strip(BlendWriter *writer, animrig::KeyframeStrip &key_strip) +{ + BLO_write_struct(writer, KeyframeActionStrip, &key_strip); + + auto channelbags = key_strip.channelbags(); + BLO_write_pointer_array(writer, channelbags.size(), channelbags.data()); + + for (animrig::ChannelBag *channelbag : channelbags) { + write_channelbag(writer, *channelbag); + } +} + +static void write_strips(BlendWriter *writer, Span strips) +{ + BLO_write_pointer_array(writer, strips.size(), strips.data()); + + for (animrig::Strip *strip : strips) { + switch (strip->type()) { + case animrig::Strip::Type::Keyframe: { + auto &key_strip = strip->as(); + write_keyframe_strip(writer, key_strip); + } + } + } +} + +static void write_layers(BlendWriter *writer, Span layers) +{ + BLO_write_pointer_array(writer, layers.size(), layers.data()); + + for (animrig::Layer *layer : layers) { + BLO_write_struct(writer, ActionLayer, layer); + write_strips(writer, layer->strips()); + } +} + +static void write_bindings(BlendWriter *writer, Span bindings) +{ + BLO_write_pointer_array(writer, bindings.size(), bindings.data()); + for (animrig::Binding *binding : bindings) { + BLO_write_struct(writer, ActionBinding, binding); + } +} + static void action_blend_write(BlendWriter *writer, ID *id, const void *id_address) { - bAction *act = (bAction *)id; + animrig::Action &action = reinterpret_cast(id)->wrap(); - BLO_write_id_struct(writer, bAction, id_address, &act->id); - BKE_id_blend_write(writer, &act->id); + BLO_write_id_struct(writer, bAction, id_address, &action.id); + BKE_id_blend_write(writer, &action.id); - BKE_fcurve_blend_write_listbase(writer, &act->curves); + /* Write layered Action data. */ + write_layers(writer, action.layers()); + write_bindings(writer, action.bindings()); - LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { + /* Write legacy F-Curves & groups. */ + BKE_fcurve_blend_write_listbase(writer, &action.curves); + LISTBASE_FOREACH (bActionGroup *, grp, &action.groups) { BLO_write_struct(writer, bActionGroup, grp); } - LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { + LISTBASE_FOREACH (TimeMarker *, marker, &action.markers) { BLO_write_struct(writer, TimeMarker, marker); } - BKE_previewimg_blend_write(writer, act->preview); + BKE_previewimg_blend_write(writer, action.preview); +} + +static void read_channelbag(BlendDataReader *reader, animrig::ChannelBag &channelbag) +{ + BLO_read_pointer_array(reader, reinterpret_cast(&channelbag.fcurve_array)); + + for (int i = 0; i < channelbag.fcurve_array_num; i++) { + BLO_read_struct(reader, FCurve, &channelbag.fcurve_array[i]); + BKE_fcurve_blend_read_data(reader, channelbag.fcurve_array[i]); + } +} + +static void read_keyframe_strip(BlendDataReader *reader, animrig::KeyframeStrip &strip) +{ + BLO_read_pointer_array(reader, reinterpret_cast(&strip.channelbags_array)); + + for (int i = 0; i < strip.channelbags_array_num; i++) { + BLO_read_struct(reader, ActionChannelBag, &strip.channelbags_array[i]); + ActionChannelBag *channelbag = strip.channelbags_array[i]; + read_channelbag(reader, channelbag->wrap()); + } +} + +static void read_layers(BlendDataReader *reader, animrig::Action &anim) +{ + BLO_read_pointer_array(reader, reinterpret_cast(&anim.layer_array)); + + for (int layer_idx = 0; layer_idx < anim.layer_array_num; layer_idx++) { + BLO_read_struct(reader, ActionLayer, &anim.layer_array[layer_idx]); + ActionLayer *layer = anim.layer_array[layer_idx]; + + BLO_read_pointer_array(reader, reinterpret_cast(&layer->strip_array)); + for (int strip_idx = 0; strip_idx < layer->strip_array_num; strip_idx++) { + BLO_read_struct(reader, ActionStrip, &layer->strip_array[strip_idx]); + ActionStrip *dna_strip = layer->strip_array[strip_idx]; + animrig::Strip &strip = dna_strip->wrap(); + + switch (strip.type()) { + case animrig::Strip::Type::Keyframe: { + read_keyframe_strip(reader, strip.as()); + } + } + } + } +} + +static void read_bindings(BlendDataReader *reader, animrig::Action &anim) +{ + BLO_read_pointer_array(reader, reinterpret_cast(&anim.binding_array)); + + for (int i = 0; i < anim.binding_array_num; i++) { + BLO_read_struct(reader, ActionBinding, &anim.binding_array[i]); + } } static void action_blend_read_data(BlendDataReader *reader, ID *id) { - bAction *act = (bAction *)id; + animrig::Action &action = reinterpret_cast(id)->wrap(); - BLO_read_struct_list(reader, FCurve, &act->curves); - BLO_read_struct_list( - reader, bActionChannel, &act->chanbase); /* XXX deprecated - old animation system */ - BLO_read_struct_list(reader, bActionGroup, &act->groups); - BLO_read_struct_list(reader, TimeMarker, &act->markers); + read_layers(reader, action); + read_bindings(reader, action); - /* XXX deprecated - old animation system <<< */ - LISTBASE_FOREACH (bActionChannel *, achan, &act->chanbase) { + /* Read legacy data. */ + BLO_read_struct_list(reader, FCurve, &action.curves); + BLO_read_struct_list(reader, bActionChannel, &action.chanbase); + BLO_read_struct_list(reader, bActionGroup, &action.groups); + BLO_read_struct_list(reader, TimeMarker, &action.markers); + + LISTBASE_FOREACH (bActionChannel *, achan, &action.chanbase) { BLO_read_struct(reader, bActionGroup, &achan->grp); - BLO_read_struct_list(reader, bConstraintChannel, &achan->constraintChannels); } - /* >>> XXX deprecated - old animation system */ - BKE_fcurve_blend_read_data_listbase(reader, &act->curves); + BKE_fcurve_blend_read_data_listbase(reader, &action.curves); - LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) { + LISTBASE_FOREACH (bActionGroup *, agrp, &action.groups) { BLO_read_struct(reader, FCurve, &agrp->channels.first); BLO_read_struct(reader, FCurve, &agrp->channels.last); } + /* End of reading legacy data. */ - BLO_read_struct(reader, PreviewImage, &act->preview); - BKE_previewimg_blend_read(reader, act->preview); + BLO_read_struct(reader, PreviewImage, &action.preview); + BKE_previewimg_blend_read(reader, action.preview); } static IDProperty *action_asset_type_property(const bAction *action) @@ -268,6 +438,8 @@ static AssetTypeInfo AssetType_AC = { /*on_clear_asset_fn*/ nullptr, }; +} // namespace blender::bke + IDTypeInfo IDType_ID_AC = { /*id_code*/ ID_AC, /*id_filter*/ FILTER_ID_AC, @@ -278,19 +450,19 @@ IDTypeInfo IDType_ID_AC = { /*name_plural*/ "actions", /*translation_context*/ BLT_I18NCONTEXT_ID_ACTION, /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA, - /*asset_type_info*/ &AssetType_AC, + /*asset_type_info*/ &blender::bke::AssetType_AC, /*init_data*/ nullptr, - /*copy_data*/ action_copy_data, - /*free_data*/ action_free_data, + /*copy_data*/ blender::bke::action_copy_data, + /*free_data*/ blender::bke::action_free_data, /*make_local*/ nullptr, - /*foreach_id*/ action_foreach_id, + /*foreach_id*/ blender::bke::action_foreach_id, /*foreach_cache*/ nullptr, /*foreach_path*/ nullptr, /*owner_pointer_get*/ nullptr, - /*blend_write*/ action_blend_write, - /*blend_read_data*/ action_blend_read_data, + /*blend_write*/ blender::bke::action_blend_write, + /*blend_read_data*/ blender::bke::action_blend_read_data, /*blend_read_after_liblink*/ nullptr, /*blend_read_undo_preserve*/ nullptr, diff --git a/source/blender/blenkernel/intern/anim_data.cc b/source/blender/blenkernel/intern/anim_data.cc index 091a9241f83..33f2ac1aad1 100644 --- a/source/blender/blenkernel/intern/anim_data.cc +++ b/source/blender/blenkernel/intern/anim_data.cc @@ -10,7 +10,7 @@ #include #include -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "BKE_action.h" #include "BKE_anim_data.hh" @@ -280,16 +280,14 @@ bool BKE_animdata_id_is_animated(const ID *id) return false; } - /* If an Animation is assigned, it takes precedence over the Action, even when - * this Animation has no F-Curves and the Action does. */ - if (adt->animation) { - const blender::animrig::Animation &anim = adt->animation->wrap(); - if (anim.is_binding_animated(adt->binding_handle)) { + if (adt->action) { + const blender::animrig::Action &action = adt->action->wrap(); + if (action.is_action_layered() && action.is_binding_animated(adt->binding_handle)) { + return true; + } + if (action.is_action_legacy() && !BLI_listbase_is_empty(&action.curves)) { return true; } - } - else if (adt->action != nullptr && !BLI_listbase_is_empty(&adt->action->curves)) { - return true; } return !BLI_listbase_is_empty(&adt->drivers) || !BLI_listbase_is_empty(&adt->nla_tracks) || @@ -302,7 +300,6 @@ void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->animation, IDWALK_CB_USER); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->action, IDWALK_CB_USER); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->tmpact, IDWALK_CB_USER); diff --git a/source/blender/blenkernel/intern/anim_sys.cc b/source/blender/blenkernel/intern/anim_sys.cc index 27d07098024..7d029a01c63 100644 --- a/source/blender/blenkernel/intern/anim_sys.cc +++ b/source/blender/blenkernel/intern/anim_sys.cc @@ -3927,24 +3927,21 @@ void BKE_animsys_evaluate_animdata(ID *id, */ /* TODO: need to double check that this all works correctly */ if (recalc & ADT_RECALC_ANIM) { - if (adt->animation && adt->binding_handle) { - /* Animation data-blocks take precedence over the old Action + NLA system. */ - blender::animrig::evaluate_and_apply_animation(id_ptr, - adt->animation->wrap(), - adt->binding_handle, - *anim_eval_context, - flush_to_original); + /* evaluate NLA data */ + if ((adt->nla_tracks.first) && !(adt->flag & ADT_NLA_EVAL_OFF)) { + /* evaluate NLA-stack + * - active action is evaluated as part of the NLA stack as the last item + */ + animsys_calculate_nla(&id_ptr, adt, anim_eval_context, flush_to_original); } - else { - /* evaluate NLA data */ - if ((adt->nla_tracks.first) && !(adt->flag & ADT_NLA_EVAL_OFF)) { - /* evaluate NLA-stack - * - active action is evaluated as part of the NLA stack as the last item - */ - animsys_calculate_nla(&id_ptr, adt, anim_eval_context, flush_to_original); + /* evaluate Active Action only */ + else if (adt->action) { + 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); } - /* evaluate Active Action only */ - else if (adt->action) { + else { animsys_evaluate_action(&id_ptr, adt->action, anim_eval_context, flush_to_original); } } diff --git a/source/blender/blenkernel/intern/animation.cc b/source/blender/blenkernel/intern/animation.cc deleted file mode 100644 index 35378b87e41..00000000000 --- a/source/blender/blenkernel/intern/animation.cc +++ /dev/null @@ -1,255 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file Animation data-block. - * \ingroup bke - */ - -#include "BLI_map.hh" -#include "BLI_string_utf8.h" - -#include "BLO_read_write.hh" - -#include "BKE_animation.hh" -#include "BKE_fcurve.hh" -#include "BKE_idtype.hh" -#include "BKE_lib_id.hh" -#include "BKE_lib_query.hh" -#include "BKE_main.hh" - -#include "ANIM_animation.hh" - -#include "DNA_anim_types.h" -#include "DNA_defaults.h" - -#include "BLT_translation.hh" - -struct BlendWriter; -struct BlendDataReader; - -namespace blender::bke { - -static void animation_copy_data(Main * /*bmain*/, - std::optional /*owner_library*/, - ID *id_dst, - const ID *id_src, - const int /*flag*/) -{ - Animation *dna_anim_dst = reinterpret_cast(id_dst); - animrig::Animation &anim_dst = dna_anim_dst->wrap(); - - const Animation *dna_anim_src = reinterpret_cast(id_src); - const animrig::Animation &anim_src = dna_anim_src->wrap(); - - /* Copy all simple properties. */ - anim_dst.layer_array_num = anim_src.layer_array_num; - anim_dst.layer_active_index = anim_src.layer_active_index; - anim_dst.binding_array_num = anim_src.binding_array_num; - anim_dst.last_binding_handle = anim_src.last_binding_handle; - - /* Layers. */ - anim_dst.layer_array = MEM_cnew_array(anim_src.layer_array_num, __func__); - for (int i : anim_src.layers().index_range()) { - anim_dst.layer_array[i] = MEM_new(__func__, *anim_src.layer(i)); - } - - /* Bindings. */ - anim_dst.binding_array = MEM_cnew_array(anim_src.binding_array_num, - __func__); - for (int i : anim_src.bindings().index_range()) { - anim_dst.binding_array[i] = MEM_new(__func__, *anim_src.binding(i)); - } -} - -/** Free (or release) any data used by this animation (does not free the animation itself). */ -static void animation_free_data(ID *id) -{ - reinterpret_cast(id)->wrap().free_data(); -} - -static void animation_foreach_id(ID *id, LibraryForeachIDData *data) -{ - animrig::Animation &anim = reinterpret_cast(id)->wrap(); - - for (animrig::Layer *layer : anim.layers()) { - for (animrig::Strip *strip : layer->strips()) { - switch (strip->type()) { - case animrig::Strip::Type::Keyframe: { - auto &key_strip = strip->as(); - for (animrig::ChannelBag *channelbag_for_binding : key_strip.channelbags()) { - for (FCurve *fcurve : channelbag_for_binding->fcurves()) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcurve, data)); - } - } - } - } - } - } -} - -static void write_channelbag(BlendWriter *writer, animrig::ChannelBag &channelbag) -{ - BLO_write_struct(writer, AnimationChannelBag, &channelbag); - - Span fcurves = channelbag.fcurves(); - BLO_write_pointer_array(writer, fcurves.size(), fcurves.data()); - - for (FCurve *fcurve : fcurves) { - BLO_write_struct(writer, FCurve, fcurve); - BKE_fcurve_blend_write_data(writer, fcurve); - } -} - -static void write_keyframe_strip(BlendWriter *writer, animrig::KeyframeStrip &key_strip) -{ - BLO_write_struct(writer, KeyframeAnimationStrip, &key_strip); - - auto channelbags = key_strip.channelbags(); - BLO_write_pointer_array(writer, channelbags.size(), channelbags.data()); - - for (animrig::ChannelBag *channelbag : channelbags) { - write_channelbag(writer, *channelbag); - } -} - -static void write_strips(BlendWriter *writer, Span strips) -{ - BLO_write_pointer_array(writer, strips.size(), strips.data()); - - for (animrig::Strip *strip : strips) { - switch (strip->type()) { - case animrig::Strip::Type::Keyframe: { - auto &key_strip = strip->as(); - write_keyframe_strip(writer, key_strip); - } - } - } -} - -static void write_layers(BlendWriter *writer, Span layers) -{ - BLO_write_pointer_array(writer, layers.size(), layers.data()); - - for (animrig::Layer *layer : layers) { - BLO_write_struct(writer, AnimationLayer, layer); - write_strips(writer, layer->strips()); - } -} - -static void write_bindings(BlendWriter *writer, Span bindings) -{ - BLO_write_pointer_array(writer, bindings.size(), bindings.data()); - for (animrig::Binding *binding : bindings) { - BLO_write_struct(writer, AnimationBinding, binding); - } -} - -static void animation_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - animrig::Animation &anim = reinterpret_cast(id)->wrap(); - - BLO_write_id_struct(writer, Animation, id_address, &anim.id); - BKE_id_blend_write(writer, &anim.id); - - write_layers(writer, anim.layers()); - write_bindings(writer, anim.bindings()); -} - -static void read_channelbag(BlendDataReader *reader, animrig::ChannelBag &channelbag) -{ - BLO_read_pointer_array(reader, reinterpret_cast(&channelbag.fcurve_array)); - - for (int i = 0; i < channelbag.fcurve_array_num; i++) { - BLO_read_struct(reader, FCurve, &channelbag.fcurve_array[i]); - BKE_fcurve_blend_read_data(reader, channelbag.fcurve_array[i]); - } -} - -static void read_keyframe_strip(BlendDataReader *reader, animrig::KeyframeStrip &strip) -{ - BLO_read_pointer_array(reader, reinterpret_cast(&strip.channelbags_array)); - - for (int i = 0; i < strip.channelbags_array_num; i++) { - BLO_read_struct(reader, AnimationChannelBag, &strip.channelbags_array[i]); - AnimationChannelBag *channelbag = strip.channelbags_array[i]; - read_channelbag(reader, channelbag->wrap()); - } -} - -static void read_animation_layers(BlendDataReader *reader, animrig::Animation &anim) -{ - BLO_read_pointer_array(reader, reinterpret_cast(&anim.layer_array)); - - for (int layer_idx = 0; layer_idx < anim.layer_array_num; layer_idx++) { - BLO_read_struct(reader, AnimationLayer, &anim.layer_array[layer_idx]); - AnimationLayer *layer = anim.layer_array[layer_idx]; - - BLO_read_pointer_array(reader, reinterpret_cast(&layer->strip_array)); - for (int strip_idx = 0; strip_idx < layer->strip_array_num; strip_idx++) { - BLO_read_struct(reader, AnimationStrip, &layer->strip_array[strip_idx]); - AnimationStrip *dna_strip = layer->strip_array[strip_idx]; - animrig::Strip &strip = dna_strip->wrap(); - - switch (strip.type()) { - case animrig::Strip::Type::Keyframe: { - read_keyframe_strip(reader, strip.as()); - } - } - } - } -} - -static void read_animation_bindings(BlendDataReader *reader, animrig::Animation &anim) -{ - BLO_read_pointer_array(reader, reinterpret_cast(&anim.binding_array)); - - for (int i = 0; i < anim.binding_array_num; i++) { - BLO_read_struct(reader, AnimationBinding, &anim.binding_array[i]); - } -} - -static void animation_blend_read_data(BlendDataReader *reader, ID *id) -{ - animrig::Animation &animation = reinterpret_cast(id)->wrap(); - read_animation_layers(reader, animation); - read_animation_bindings(reader, animation); -} - -} // namespace blender::bke - -IDTypeInfo IDType_ID_AN = { - /*id_code*/ ID_AN, - /*id_filter*/ FILTER_ID_AN, - /*dependencies_id_types*/ 0, - /*main_listbase_index*/ INDEX_ID_AN, - /*struct_size*/ sizeof(Animation), - /*name*/ "Animation", - /*name_plural*/ N_("animations"), - /*translation_context*/ BLT_I18NCONTEXT_ID_ANIMATION, - /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA, - /*asset_type_info*/ nullptr, - - /*init_data*/ nullptr, - /*copy_data*/ blender::bke::animation_copy_data, - /*free_data*/ blender::bke::animation_free_data, - /*make_local*/ nullptr, - /*foreach_id*/ blender::bke::animation_foreach_id, - /*foreach_cache*/ nullptr, - /*foreach_path*/ nullptr, - /*owner_pointer_get*/ nullptr, - - /*blend_write*/ blender::bke::animation_blend_write, - /*blend_read_data*/ blender::bke::animation_blend_read_data, - /*blend_read_after_liblink*/ nullptr, - - /*blend_read_undo_preserve*/ nullptr, - - /*lib_override_apply_post*/ nullptr, -}; - -Animation *BKE_animation_add(Main *bmain, const char name[]) -{ - Animation *anim = static_cast(BKE_id_new(bmain, ID_AN, name)); - return anim; -} diff --git a/source/blender/blenkernel/intern/fcurve.cc b/source/blender/blenkernel/intern/fcurve.cc index a277cbee604..d667c787196 100644 --- a/source/blender/blenkernel/intern/fcurve.cc +++ b/source/blender/blenkernel/intern/fcurve.cc @@ -14,7 +14,7 @@ #include "MEM_guardedalloc.h" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "ANIM_animdata.hh" #include "DNA_anim_types.h" @@ -355,6 +355,28 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con return matches; } +FCurve *action_fcurve_find_by_rna_path(const ID *id, + blender::animrig::Action &action, + const char *rna_path, + const int rna_index) +{ + if (action.is_empty()) { + return nullptr; + } + + if (action.is_action_layered()) { + BLI_assert(id); + const FCurve *fcu = blender::animrig::fcurve_find_by_rna_path( + action, *id, rna_path, rna_index); + + /* The new Animation data-block is stricter with const-ness than older code, hence the + * const_cast. */ + return const_cast(fcu); + } + + return BKE_fcurve_find(&action.curves, rna_path, rna_index); +} + FCurve *BKE_animadata_fcurve_find_by_rna_path(const ID *id, AnimData *animdata, const char *rna_path, @@ -369,32 +391,16 @@ FCurve *BKE_animadata_fcurve_find_by_rna_path(const ID *id, *r_action = nullptr; } - /* Animation data-block takes priority over Action data-block. */ - if (animdata->animation) { - /* TODO: this branch probably also needs a `Animation *r_anim` parameter for full - * compatibility with the Action-based uses. Even better: change to return a - * result struct with all the relevant information/data. */ - BLI_assert(id); - const FCurve *fcu = blender::animrig::fcurve_find_by_rna_path( - animdata->animation->wrap(), *id, rna_path, rna_index); - if (fcu) { - /* The new Animation data-block is stricter with const-ness than older code, hence the - * const_cast. */ - return const_cast(fcu); - } - } - /* Action takes priority over drivers. */ - const bool has_action_fcurves = animdata->action != nullptr && - !BLI_listbase_is_empty(&animdata->action->curves); - if (has_action_fcurves) { - FCurve *fcu = BKE_fcurve_find(&animdata->action->curves, rna_path, rna_index); - - if (fcu != nullptr) { - if (r_action != nullptr) { - *r_action = animdata->action; + if (animdata->action) { + blender::animrig::Action &action = animdata->action->wrap(); + /* TODO: pass the binding handle instead of the id. */ + FCurve *fcurve = action_fcurve_find_by_rna_path(id, action, rna_path, rna_index); + if (fcurve) { + if (r_action) { + *r_action = &action; } - return fcu; + return fcurve; } } diff --git a/source/blender/blenkernel/intern/idtype.cc b/source/blender/blenkernel/intern/idtype.cc index 13d0d599553..1546156f33b 100644 --- a/source/blender/blenkernel/intern/idtype.cc +++ b/source/blender/blenkernel/intern/idtype.cc @@ -83,7 +83,6 @@ static void id_type_init() INIT_TYPE(ID_GR); INIT_TYPE(ID_AR); INIT_TYPE(ID_AC); - INIT_TYPE(ID_AN); INIT_TYPE(ID_NT); INIT_TYPE(ID_BR); INIT_TYPE(ID_PA); @@ -226,7 +225,6 @@ int BKE_idtype_idcode_to_index(const short idcode) switch ((ID_Type)idcode) { CASE_IDINDEX(AC); - CASE_IDINDEX(AN); CASE_IDINDEX(AR); CASE_IDINDEX(BR); CASE_IDINDEX(CA); @@ -286,7 +284,6 @@ int BKE_idtype_idfilter_to_index(const uint64_t id_filter) switch (id_filter) { CASE_IDINDEX(AC); - CASE_IDINDEX(AN); CASE_IDINDEX(AR); CASE_IDINDEX(BR); CASE_IDINDEX(CA); diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index e823b11f745..ec0b068a197 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -850,8 +850,6 @@ ListBase *which_libbase(Main *bmain, short type) return &(bmain->armatures); case ID_AC: return &(bmain->actions); - case ID_AN: - return &(bmain->animations); case ID_NT: return &(bmain->nodetrees); case ID_BR: @@ -897,7 +895,6 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) /* Moved here to avoid problems when freeing with animato (aligorith). */ lb[INDEX_ID_AC] = &(bmain->actions); - lb[INDEX_ID_AN] = &(bmain->animations); lb[INDEX_ID_KE] = &(bmain->shapekeys); diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 440d60f2229..dbd1df234f0 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -168,6 +168,10 @@ if(WITH_PYTHON) ) endif() +if(WITH_EXPERIMENTAL_FEATURES) + add_definitions(-DWITH_ANIM_BAKLAVA) +endif() + blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") add_library(bf::depsgraph ALIAS bf_depsgraph) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 06002bc8b3a..59e59a242f7 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -573,9 +573,6 @@ void DepsgraphNodeBuilder::build_id(ID *id, const bool force_be_visible) case ID_AC: build_action((bAction *)id); break; - case ID_AN: - build_animation((Animation *)id); - break; case ID_AR: build_armature((bArmature *)id); break; @@ -1238,15 +1235,10 @@ void DepsgraphNodeBuilder::build_animdata(ID *id) if (adt->action != nullptr) { build_action(adt->action); } - if (adt->animation != nullptr) { - build_animation(adt->animation); - } /* Make sure ID node exists. */ (void)add_id_node(id); ID *id_cow = get_cow_id(id); - if (adt->action != nullptr || adt->animation != nullptr || - !BLI_listbase_is_empty(&adt->nla_tracks)) - { + if (adt->action != nullptr || !BLI_listbase_is_empty(&adt->nla_tracks)) { OperationNode *operation_node; /* Explicit entry operation. */ operation_node = add_operation_node(id, NodeType::ANIMATION, OperationCode::ANIMATION_ENTRY); @@ -1316,15 +1308,6 @@ void DepsgraphNodeBuilder::build_action(bAction *action) add_operation_node(&action->id, NodeType::ANIMATION, OperationCode::ANIMATION_EVAL); } -void DepsgraphNodeBuilder::build_animation(Animation *animation) -{ - if (built_map_.checkIsBuiltAndTag(animation)) { - return; - } - build_idproperties(animation->id.properties); - add_operation_node(&animation->id, NodeType::ANIMATION, OperationCode::ANIMATION_EVAL); -} - void DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcurve, int driver_index) { /* Create data node for this driver */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 44c6f5dec4e..3779f53af51 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -19,7 +19,6 @@ #include "DEG_depsgraph.hh" -struct Animation; struct CacheFile; struct Camera; struct Collection; @@ -219,7 +218,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { */ virtual void build_animation_images(ID *id); virtual void build_action(bAction *action); - virtual void build_animation(Animation *animation); /** * Build graph node(s) for Driver diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index bcbb82e5d29..87c6dcfc9a4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -90,7 +90,7 @@ #include "RNA_prototypes.h" #include "RNA_types.hh" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "SEQ_iterator.hh" #include "DEG_depsgraph.hh" @@ -526,9 +526,6 @@ void DepsgraphRelationBuilder::build_id(ID *id) case ID_AC: build_action((bAction *)id); break; - case ID_AN: - build_animation((Animation *)id); - break; case ID_AR: build_armature((bArmature *)id); break; @@ -1568,12 +1565,7 @@ void DepsgraphRelationBuilder::build_animdata_curves(ID *id) if (adt->action != nullptr) { build_action(adt->action); } - if (adt->animation != nullptr) { - build_animation(adt->animation); - } - if (adt->action == nullptr && adt->animation == nullptr && - BLI_listbase_is_empty(&adt->nla_tracks)) - { + if (adt->action == nullptr && BLI_listbase_is_empty(&adt->nla_tracks)) { return; } /* Ensure evaluation order from entry to exit. */ @@ -1589,11 +1581,6 @@ void DepsgraphRelationBuilder::build_animdata_curves(ID *id) ComponentKey action_key(&adt->action->id, NodeType::ANIMATION); add_relation(action_key, adt_key, "Action -> Animation"); } - /* Relation from Animation datablock itself. */ - if (adt->animation != nullptr) { - ComponentKey animation_key(&adt->animation->id, NodeType::ANIMATION); - add_relation(animation_key, adt_key, "Animation ID -> Animation"); - } /* Get source operations. */ Node *node_from = get_node(adt_key); BLI_assert(node_from != nullptr); @@ -1604,11 +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_curves_targets(id, adt_key, operation_from, &adt->action->curves); - } - if (adt->animation != nullptr) { - build_animdata_animation_targets( - id, adt->binding_handle, adt_key, operation_from, adt->animation); + build_animdata_action_targets(id, adt->binding_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); @@ -1664,26 +1647,35 @@ void DepsgraphRelationBuilder::build_animdata_curves_targets(ID *id, } } -void DepsgraphRelationBuilder::build_animdata_animation_targets(ID *id, - const int32_t binding_handle, - ComponentKey &adt_key, - OperationNode *operation_from, - Animation *dna_animation) +void DepsgraphRelationBuilder::build_animdata_action_targets(ID *id, + const int32_t binding_handle, + ComponentKey &adt_key, + OperationNode *operation_from, + bAction *dna_action) { BLI_assert(id != nullptr); BLI_assert(operation_from != nullptr); - BLI_assert(dna_animation != nullptr); + BLI_assert(dna_action != nullptr); + animrig::Action &action = dna_action->wrap(); - PointerRNA id_ptr = RNA_id_pointer_create(id); - animrig::Animation &animation = dna_animation->wrap(); - - const animrig::Binding *binding = animation.binding_for_handle(binding_handle); - if (binding == nullptr) { - /* If there's no matching binding, there's no animation dependency. */ + if (action.is_empty()) { + return; + } + if (action.is_action_legacy()) { + build_animdata_curves_targets(id, adt_key, operation_from, &action.curves); return; } - for (animrig::Layer *layer : animation.layers()) { +#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. */ + return; + } + + PointerRNA id_ptr = RNA_id_pointer_create(id); + + for (animrig::Layer *layer : action.layers()) { for (animrig::Strip *strip : layer->strips()) { switch (strip->type()) { case animrig::Strip::Type::Keyframe: { @@ -1701,6 +1693,9 @@ void DepsgraphRelationBuilder::build_animdata_animation_targets(ID *id, } } } +#else + UNUSED_VARS(binding_handle); +#endif } void DepsgraphRelationBuilder::build_animdata_nlastrip_targets(ID *id, @@ -1715,7 +1710,13 @@ void DepsgraphRelationBuilder::build_animdata_nlastrip_targets(ID *id, ComponentKey action_key(&strip->act->id, NodeType::ANIMATION); add_relation(action_key, adt_key, "Action -> Animation"); - build_animdata_curves_targets(id, adt_key, operation_from, &strip->act->curves); + if (!strip->act->wrap().is_action_legacy()) { + /* 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); } else if (strip->strips.first != nullptr) { build_animdata_nlastrip_targets(id, adt_key, operation_from, &strip->strips); @@ -1800,37 +1801,31 @@ void DepsgraphRelationBuilder::build_animdata_force(ID *id) add_relation(animation_key, rigidbody_key, "Animation -> Rigid Body"); } -void DepsgraphRelationBuilder::build_action(bAction *action) +void DepsgraphRelationBuilder::build_action(bAction *dna_action) { - if (built_map_.checkIsBuiltAndTag(action)) { + if (built_map_.checkIsBuiltAndTag(dna_action)) { return; } - const BuilderStack::ScopedEntry stack_entry = stack_.trace(action->id); + const BuilderStack::ScopedEntry stack_entry = stack_.trace(dna_action->id); - build_idproperties(action->id.properties); - if (!BLI_listbase_is_empty(&action->curves)) { + build_idproperties(dna_action->id.properties); + + blender::animrig::Action &action = dna_action->wrap(); +#ifndef WITH_ANIM_BAKLAVA + /* Prevent evaluation of data introduced by Project Baklava. */ + if (action.is_action_layered()) { + return; + } +#endif + + if (!action.is_empty()) { TimeSourceKey time_src_key; - ComponentKey animation_key(&action->id, NodeType::ANIMATION); + ComponentKey animation_key(&dna_action->id, NodeType::ANIMATION); add_relation(time_src_key, animation_key, "TimeSrc -> Animation"); } } -void DepsgraphRelationBuilder::build_animation(Animation *animation) -{ - if (built_map_.checkIsBuiltAndTag(animation)) { - return; - } - - const BuilderStack::ScopedEntry stack_entry = stack_.trace(animation->id); - - build_idproperties(animation->id.properties); - - TimeSourceKey time_src_key; - ComponentKey animation_key(&animation->id, NodeType::ANIMATION); - add_relation(time_src_key, animation_key, "TimeSrc -> Animation"); -} - void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) { ChannelDriver *driver = fcu->driver; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 35b782d9171..6c3e0ea487b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -32,7 +32,6 @@ #include "intern/node/deg_node_id.hh" #include "intern/node/deg_node_operation.hh" -struct Animation; struct CacheFile; struct Camera; struct Collection; @@ -175,11 +174,11 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { ComponentKey &adt_key, OperationNode *operation_from, ListBase *curves); - virtual void build_animdata_animation_targets(ID *id, - int32_t binding_handle, - ComponentKey &adt_key, - OperationNode *operation_from, - Animation *animation); + virtual void build_animdata_action_targets(ID *id, + int32_t binding_handle, + ComponentKey &adt_key, + OperationNode *operation_from, + bAction *action); virtual void build_animdata_nlastrip_targets(ID *id, ComponentKey &adt_key, OperationNode *operation_from, @@ -188,7 +187,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_animdata_force(ID *id); virtual void build_animation_images(ID *id); virtual void build_action(bAction *action); - virtual void build_animation(Animation *animation); virtual void build_driver(ID *id, FCurve *fcurve); virtual void build_driver_data(ID *id, FCurve *fcurve); virtual void build_driver_variables(ID *id, FCurve *fcurve); diff --git a/source/blender/editors/animation/anim_channels_defines.cc b/source/blender/editors/animation/anim_channels_defines.cc index f965ebf5f38..317df395354 100644 --- a/source/blender/editors/animation/anim_channels_defines.cc +++ b/source/blender/editors/animation/anim_channels_defines.cc @@ -9,7 +9,6 @@ #include #include "ANIM_action.hh" -#include "ANIM_animation.hh" #include "ANIM_animdata.hh" #include "ANIM_keyframing.hh" @@ -1315,9 +1314,10 @@ static void *acf_fillanim_setting_ptr(bAnimListElem *ale, } } -/** Object Animation expander type define. */ +/* TODO: merge this with the regular Action expander. */ +/** Object's Layered Action expander type define. */ static bAnimChannelType ACF_FILLANIM = { - /*channel_type_name*/ "Ob-Animation Filler", + /*channel_type_name*/ "Ob-Layered-Action Filler", /*channel_role*/ ACHANNEL_ROLE_EXPANDER, /*get_backdrop_color*/ acf_generic_dataexpand_color, @@ -4372,7 +4372,7 @@ static void ANIM_init_channel_typeinfo_data() animchannelTypeInfo[type++] = &ACF_NLACURVE; /* NLA Control FCurve Channel */ #ifdef WITH_ANIM_BAKLAVA - animchannelTypeInfo[type++] = &ACF_FILLANIM; /* Object Animation Expander */ + animchannelTypeInfo[type++] = &ACF_FILLANIM; /* Object's Layered Action Expander */ #else animchannelTypeInfo[type++] = nullptr; #endif @@ -4423,8 +4423,8 @@ static void ANIM_init_channel_typeinfo_data() animchannelTypeInfo[type++] = &ACF_NLAACTION; /* NLA Action */ #ifdef WITH_ANIM_BAKLAVA - BLI_assert_msg(animchannelTypeInfo[ANIMTYPE_FILLANIM] == &ACF_FILLANIM, - "ANIMTYPE_FILLANIM does not match ACF_FILLANIM"); + BLI_assert_msg(animchannelTypeInfo[ANIMTYPE_FILLACT_LAYERED] == &ACF_FILLANIM, + "ANIMTYPE_FILLACT_LAYERED does not match ACF_FILLANIM"); #endif } } diff --git a/source/blender/editors/animation/anim_channels_edit.cc b/source/blender/editors/animation/anim_channels_edit.cc index a4a2e25f01c..101ff5de56d 100644 --- a/source/blender/editors/animation/anim_channels_edit.cc +++ b/source/blender/editors/animation/anim_channels_edit.cc @@ -256,9 +256,9 @@ void ANIM_set_active_channel(bAnimContext *ac, ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE); break; } - case ANIMTYPE_FILLACTD: /* Action Expander */ - case ANIMTYPE_FILLANIM: /* Animation Expander */ - case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ + case ANIMTYPE_FILLACTD: /* Action Expander */ + case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */ + case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: case ANIMTYPE_DSCACHEFILE: @@ -313,9 +313,9 @@ void ANIM_set_active_channel(bAnimContext *ac, nlt->flag |= NLATRACK_ACTIVE; break; } - case ANIMTYPE_FILLACTD: /* Action Expander */ - case ANIMTYPE_FILLANIM: /* Animation Expander */ - case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ + case ANIMTYPE_FILLACTD: /* Action Expander */ + case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */ + case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: case ANIMTYPE_DSCACHEFILE: @@ -367,9 +367,9 @@ void ANIM_set_active_channel(bAnimContext *ac, bool ANIM_is_active_channel(bAnimListElem *ale) { switch (ale->type) { - case ANIMTYPE_FILLACTD: /* Action Expander */ - case ANIMTYPE_FILLANIM: /* Animation Expander */ - case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ + case ANIMTYPE_FILLACTD: /* Action Expander */ + case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */ + case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: case ANIMTYPE_DSCACHEFILE: @@ -504,9 +504,9 @@ static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListB } break; - case ANIMTYPE_FILLACTD: /* Action Expander */ - case ANIMTYPE_FILLANIM: /* Animation Expander */ - case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ + case ANIMTYPE_FILLACTD: /* Action Expander */ + case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */ + case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: case ANIMTYPE_DSCACHEFILE: @@ -619,9 +619,9 @@ static void anim_channels_select_set(bAnimContext *ac, nlt->flag &= ~NLATRACK_ACTIVE; break; } - case ANIMTYPE_FILLACTD: /* Action Expander */ - case ANIMTYPE_FILLANIM: /* Animation Expander */ - case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ + case ANIMTYPE_FILLACTD: /* Action Expander */ + case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */ + case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: case ANIMTYPE_DSCACHEFILE: @@ -3837,9 +3837,9 @@ static int mouse_anim_channels(bContext *C, case ANIMTYPE_OBJECT: notifierFlags |= click_select_channel_object(C, ac, ale, selectmode); break; - case ANIMTYPE_FILLACTD: /* Action Expander */ - case ANIMTYPE_FILLANIM: /* Animation Expander */ - case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ + case ANIMTYPE_FILLACTD: /* Action Expander */ + case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */ + case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: case ANIMTYPE_DSCACHEFILE: diff --git a/source/blender/editors/animation/anim_deps.cc b/source/blender/editors/animation/anim_deps.cc index 16adbc4032f..23a6fea7970 100644 --- a/source/blender/editors/animation/anim_deps.cc +++ b/source/blender/editors/animation/anim_deps.cc @@ -55,10 +55,7 @@ void ANIM_list_elem_update(Main *bmain, Scene *scene, bAnimListElem *ale) adt = BKE_animdata_from_id(id); if (adt) { DEG_id_tag_update(id, ID_RECALC_ANIMATION); - if (adt->animation != nullptr) { - DEG_id_tag_update(&adt->animation->id, ID_RECALC_ANIMATION); - } - else if (adt->action != nullptr) { + if (adt->action != nullptr) { DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION); } } diff --git a/source/blender/editors/animation/anim_filter.cc b/source/blender/editors/animation/anim_filter.cc index d9f6083d5b7..7d1d982b1c4 100644 --- a/source/blender/editors/animation/anim_filter.cc +++ b/source/blender/editors/animation/anim_filter.cc @@ -88,7 +88,7 @@ #include "SEQ_sequencer.hh" #include "SEQ_utils.hh" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "ANIM_bone_collections.hh" using namespace blender; @@ -450,13 +450,23 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type) /* ............................... */ /* Test whether AnimData has a usable Action. */ -#define ANIMDATA_HAS_ACTION(id) ((id)->adt && !(id)->adt->animation && (id)->adt->action) +#define ANIMDATA_HAS_ACTION_LEGACY(id) \ + ((id)->adt && (id)->adt->action && (id)->adt->action->wrap().is_action_legacy()) + +#ifdef WITH_ANIM_BAKLAVA +# define ANIMDATA_HAS_ACTION_LAYERED(id) \ + ((id)->adt && (id)->adt->action && (id)->adt->action->wrap().is_action_layered()) +#else +# define ANIMDATA_HAS_ACTION_LAYERED(id) false +#endif /* quick macro to test if AnimData is usable for drivers */ #define ANIMDATA_HAS_DRIVERS(id) ((id)->adt && (id)->adt->drivers.first) /* quick macro to test if AnimData is usable for NLA */ -#define ANIMDATA_HAS_NLA(id) ((id)->adt && !(id)->adt->animation && (id)->adt->nla_tracks.first) +#define ANIMDATA_HAS_NLA(id) \ + ((id)->adt && (id)->adt->nla_tracks.first && \ + (!(id)->adt->action || (id)->adt->action->wrap().is_action_legacy())) /** * Quick macro to test for all three above usability tests, performing the appropriate provided @@ -495,7 +505,8 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type) * 4B) normal keyframes: only when there is an active action * 4C) normal keyframes: only when there is an Animation assigned */ -#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, nlaKeysOk, keysOk, animOk) \ +#define ANIMDATA_FILTER_CASES( \ + id, adtOk, nlaOk, driversOk, nlaKeysOk, legacyActionOk, layeredActionOk) \ { \ if ((id)->adt) { \ if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || \ @@ -507,7 +518,7 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type) if (ANIMDATA_HAS_NLA(id)) { \ nlaOk \ } \ - else if (!(ads->filterflag & ADS_FILTER_NLA_NOACT) || ANIMDATA_HAS_ACTION(id)) { \ + else if (!(ads->filterflag & ADS_FILTER_NLA_NOACT) || ANIMDATA_HAS_ACTION_LEGACY(id)) { \ nlaOk \ } \ } \ @@ -516,15 +527,15 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type) driversOk \ } \ } \ - else if ((id)->adt->animation) { \ - animOk \ + else if (ANIMDATA_HAS_ACTION_LAYERED(id)) { \ + layeredActionOk \ } \ else { \ if (ANIMDATA_HAS_NLA(id)) { \ nlaKeysOk \ } \ - if (ANIMDATA_HAS_ACTION(id)) { \ - keysOk \ + if (ANIMDATA_HAS_ACTION_LEGACY(id)) { \ + legacyActionOk \ } \ } \ } \ @@ -596,26 +607,15 @@ static void key_data_from_adt(bAnimListElem &ale, AnimData *adt) { ale.adt = adt; - if (!adt) { + if (!adt || !adt->action) { ale.key_data = nullptr; ale.datatype = ALE_NONE; return; } - if (adt->animation) { - ale.key_data = adt->animation; - ale.datatype = ALE_ANIM; - return; - } - - if (adt->action) { - ale.key_data = adt->action; - ale.datatype = ALE_ACT; - return; - } - - ale.key_data = nullptr; - ale.datatype = ALE_NONE; + blender::animrig::Action &action = adt->action->wrap(); + ale.key_data = &action; + ale.datatype = action.is_action_layered() ? ALE_ACTION_LAYERED : ALE_ACT; } /* this function allocates memory for a new bAnimListElem struct for the @@ -672,13 +672,13 @@ static bAnimListElem *make_new_animlistelem(void *data, ale->adt = BKE_animdata_from_id(&ob->id); break; } - case ANIMTYPE_FILLANIM: { - Animation *anim = (Animation *)data; + case ANIMTYPE_FILLACT_LAYERED: { + bAction *action = (bAction *)data; - ale->flag = anim->flag; + ale->flag = action->flag; - ale->key_data = anim; - ale->datatype = ALE_ANIM; + ale->key_data = action; + ale->datatype = ALE_ACTION_LAYERED; break; } case ANIMTYPE_FILLACTD: { @@ -1423,67 +1423,52 @@ static size_t animfilter_act_group(bAnimContext *ac, static size_t animfilter_action(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, - bAction *act, - int filter_mode, + animrig::Action &action, + const animrig::binding_handle_t binding_handle, + const int filter_mode, ID *owner_id) { FCurve *lastchan = nullptr; size_t items = 0; + if (action.is_empty()) { + return 0; + } + /* don't include anything from this action if it is linked in from another file, * and we're getting stuff for editing... */ - if ((filter_mode & ANIMFILTER_FOREDIT) && (ID_IS_LINKED(act) || ID_IS_OVERRIDE_LIBRARY(act))) { - return 0; - } - - /* do groups */ - /* TODO: do nested groups? */ - LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) { - /* store reference to last channel of group */ - if (agrp->channels.last) { - lastchan = static_cast(agrp->channels.last); - } - - /* action group's channels */ - items += animfilter_act_group(ac, anim_data, ads, act, agrp, filter_mode, owner_id); - } - - /* un-grouped F-Curves (only if we're not only considering those channels in the active group) */ - if (!(filter_mode & ANIMFILTER_ACTGROUPED)) { - FCurve *firstfcu = (lastchan) ? (lastchan->next) : static_cast(act->curves.first); - items += animfilter_fcurves( - anim_data, ads, firstfcu, ANIMTYPE_FCURVE, filter_mode, nullptr, owner_id, &act->id); - } - - /* return the number of items added to the list */ - return items; -} - -/** - * Add animation list elements for FCurves in an Animation data-block. - */ -static size_t animfilter_animation_fcurves( - ListBase *anim_data, bDopeSheet *ads, AnimData *adt, const int filter_mode, ID *owner_id) -{ - /* If this ID is not bound, there is nothing to show. */ - if (adt->binding_handle == 0) { - return 0; - } - - BLI_assert(adt->animation); /* Otherwise this function wouldn't be called. */ - animrig::Animation &anim = adt->animation->wrap(); - - /* Don't include anything from this animation if it is linked in from another - * file, and we're getting stuff for editing... */ - if ((filter_mode & ANIMFILTER_FOREDIT) && (ID_IS_LINKED(&anim) || ID_IS_OVERRIDE_LIBRARY(&anim))) + if ((filter_mode & ANIMFILTER_FOREDIT) && + (ID_IS_LINKED(&action) || ID_IS_OVERRIDE_LIBRARY(&action))) { return 0; } + if (action.is_action_legacy()) { + LISTBASE_FOREACH (bActionGroup *, agrp, &action.groups) { + /* Store reference to last channel of group. */ + if (agrp->channels.last) { + lastchan = static_cast(agrp->channels.last); + } + + items += animfilter_act_group(ac, anim_data, ads, &action, agrp, filter_mode, owner_id); + } + + /* Un-grouped F-Curves (only if we're not only considering those channels in + * the active group) */ + if (!(filter_mode & ANIMFILTER_ACTGROUPED)) { + FCurve *firstfcu = (lastchan) ? (lastchan->next) : + static_cast(action.curves.first); + items += animfilter_fcurves( + anim_data, ads, firstfcu, ANIMTYPE_FCURVE, filter_mode, nullptr, owner_id, &action.id); + } + + return items; + } + /* For now we don't show layers anywhere, just the contained F-Curves. */ - Span fcurves = animrig::fcurves_for_animation(anim, adt->binding_handle); - return animfilter_fcurves_span(anim_data, ads, fcurves, filter_mode, owner_id, &anim.id); + Span fcurves = animrig::fcurves_for_animation(action, binding_handle); + return animfilter_fcurves_span(anim_data, ads, fcurves, filter_mode, owner_id, &action.id); } /* Include NLA-Data for NLA-Editor: @@ -1677,11 +1662,13 @@ static size_t animfilter_block_data( { /* NLA Control Keyframes */ items += animfilter_nla_controls(anim_data, ads, adt, filter_mode, id); }, - { /* Keyframes from Action. */ - items += animfilter_action(ac, anim_data, ads, adt->action, filter_mode, id); + { /* Keyframes from legacy Action. */ + items += animfilter_action( + ac, anim_data, ads, adt->action->wrap(), adt->binding_handle, filter_mode, id); }, - { /* Keyframes from Animation. */ - items += animfilter_animation_fcurves(anim_data, ads, adt, filter_mode, id); + { /* Keyframes from layered Action. */ + items += animfilter_action( + ac, anim_data, ads, adt->action->wrap(), adt->binding_handle, filter_mode, id); }); } @@ -1737,8 +1724,13 @@ static size_t animdata_filter_shapekey(bAnimContext *ac, } } else if (key->adt->action) { - items = animfilter_action( - ac, anim_data, nullptr, key->adt->action, filter_mode, (ID *)key); + items = animfilter_action(ac, + anim_data, + nullptr, + key->adt->action->wrap(), + key->adt->binding_handle, + filter_mode, + (ID *)key); } } } @@ -2956,14 +2948,14 @@ static size_t animdata_filter_ds_obanim( expanded = EXPANDED_DRVD(adt); }, {/* NLA Strip Controls - no dedicated channel for now (XXX) */}, - { /* Keyframes from Action. */ + { /* Keyframes from legacy Action. */ type = ANIMTYPE_FILLACTD; cdata = adt->action; expanded = EXPANDED_ACTC(adt->action); }, - { /* Keyframes from Animation. */ - type = ANIMTYPE_FILLANIM; - cdata = adt->animation; + { /* Keyframes from layered action. */ + type = ANIMTYPE_FILLACT_LAYERED; + cdata = adt->action; expanded = EXPANDED_ADT(adt); }); @@ -3140,14 +3132,14 @@ static size_t animdata_filter_ds_scene( expanded = EXPANDED_DRVD(adt); }, {/* NLA Strip Controls - no dedicated channel for now (XXX) */}, - { /* Keyframes from Action. */ + { /* Keyframes from legacy Action. */ type = ANIMTYPE_FILLACTD; cdata = adt->action; expanded = EXPANDED_ACTC(adt->action); }, - { /* Keyframes from Animation. */ - type = ANIMTYPE_FILLANIM; - cdata = adt->animation; + { /* Keyframes from layered Action. */ + type = ANIMTYPE_FILLACT_LAYERED; + cdata = adt->action; expanded = EXPANDED_ADT(adt); }); @@ -3696,8 +3688,14 @@ size_t ANIM_animdata_filter(bAnimContext *ac, /* The check for the DopeSheet summary is included here * since the summary works here too. */ if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) { + BLI_assert_msg( + obact->adt && data == obact->adt->action, + "This code assumes the Action editor shows the Action of the active object"); + + animrig::Action &action = static_cast(data)->wrap(); + const animrig::binding_handle_t binding_handle = obact->adt->binding_handle; items += animfilter_action( - ac, anim_data, ads, static_cast(data), filter_mode, (ID *)obact); + ac, anim_data, ads, action, binding_handle, filter_mode, (ID *)obact); } } diff --git a/source/blender/editors/animation/anim_ops.cc b/source/blender/editors/animation/anim_ops.cc index e5bda4b892b..cbb919ad376 100644 --- a/source/blender/editors/animation/anim_ops.cc +++ b/source/blender/editors/animation/anim_ops.cc @@ -41,7 +41,7 @@ #include "SEQ_sequencer.hh" #include "SEQ_time.hh" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" #include "anim_intern.hh" @@ -694,7 +694,7 @@ static void ANIM_OT_binding_unassign_object(wmOperatorType *ot) ot->name = "Unassign Binding"; ot->idname = "ANIM_OT_binding_unassign_object"; ot->description = - "Clear the assigned animation binding, effectively making this data-block non-animated"; + "Clear the assigned action binding, effectively making this data-block non-animated"; /* api callbacks */ ot->exec = binding_unassign_object_exec; diff --git a/source/blender/editors/animation/keyframes_draw.cc b/source/blender/editors/animation/keyframes_draw.cc index 82434200fd5..1e293d55961 100644 --- a/source/blender/editors/animation/keyframes_draw.cc +++ b/source/blender/editors/animation/keyframes_draw.cc @@ -33,6 +33,8 @@ #include "ED_keyframes_draw.hh" #include "ED_keyframes_keylist.hh" +#include "ANIM_action.hh" + /* *************************** Keyframe Drawing *************************** */ void draw_keyframe_shape(const float x, @@ -384,8 +386,8 @@ enum class ChannelType { SCENE, OBJECT, FCURVE, - ANIMATION, - ACTION, + ACTION_LAYERED, + ACTION_LEGACY, ACTION_GROUP, GREASE_PENCIL_CELS, GREASE_PENCIL_GROUP, @@ -410,7 +412,6 @@ struct ChannelListElement { Object *ob; AnimData *adt; FCurve *fcu; - Animation *anim; bAction *act; bActionGroup *agrp; bGPDlayer *gpl; @@ -439,11 +440,11 @@ static void build_channel_keylist(ChannelListElement *elem, blender::float2 rang fcurve_to_keylist(elem->adt, elem->fcu, elem->keylist, elem->saction_flag, range); break; } - case ChannelType::ANIMATION: { - animation_to_keylist(elem->adt, elem->anim, elem->keylist, elem->saction_flag, range); + case ChannelType::ACTION_LAYERED: { + action_to_keylist(elem->adt, elem->act, elem->keylist, elem->saction_flag, range); break; } - case ChannelType::ACTION: { + case ChannelType::ACTION_LEGACY: { action_to_keylist(elem->adt, elem->act, elem->keylist, elem->saction_flag, range); break; } @@ -711,22 +712,23 @@ void ED_add_action_group_channel(ChannelDrawList *channel_list, draw_elem->channel_locked = locked; } -void ED_add_animation_channel(ChannelDrawList *channel_list, - AnimData *adt, - Animation *anim, - const float ypos, - const float yscale_fac, - int saction_flag) +void ED_add_action_layered_channel(ChannelDrawList *channel_list, + AnimData *adt, + bAction *action, + const float ypos, + const float yscale_fac, + int saction_flag) { - BLI_assert(anim); + BLI_assert(action); + BLI_assert(action->wrap().is_action_layered()); - const bool locked = (anim && (ID_IS_LINKED(anim) || ID_IS_OVERRIDE_LIBRARY(anim))); + const bool locked = (ID_IS_LINKED(action) || ID_IS_OVERRIDE_LIBRARY(action)); saction_flag &= ~SACTION_SHOW_EXTREMES; ChannelListElement *draw_elem = channel_list_add_element( - channel_list, ChannelType::ANIMATION, ypos, yscale_fac, eSAction_Flag(saction_flag)); + channel_list, ChannelType::ACTION_LAYERED, ypos, yscale_fac, eSAction_Flag(saction_flag)); draw_elem->adt = adt; - draw_elem->anim = anim; + draw_elem->act = action; draw_elem->channel_locked = locked; } @@ -737,11 +739,15 @@ void ED_add_action_channel(ChannelDrawList *channel_list, float yscale_fac, int saction_flag) { +#ifdef WITH_ANIM_BAKLAVA + BLI_assert(!act || act->wrap().is_action_legacy()); +#endif + const bool locked = (act && (ID_IS_LINKED(act) || ID_IS_OVERRIDE_LIBRARY(act))); saction_flag &= ~SACTION_SHOW_EXTREMES; ChannelListElement *draw_elem = channel_list_add_element( - channel_list, ChannelType::ACTION, ypos, yscale_fac, eSAction_Flag(saction_flag)); + channel_list, ChannelType::ACTION_LEGACY, ypos, yscale_fac, eSAction_Flag(saction_flag)); draw_elem->adt = adt; draw_elem->act = act; draw_elem->channel_locked = locked; diff --git a/source/blender/editors/animation/keyframes_edit.cc b/source/blender/editors/animation/keyframes_edit.cc index be858ca124c..ff8ef603bea 100644 --- a/source/blender/editors/animation/keyframes_edit.cc +++ b/source/blender/editors/animation/keyframes_edit.cc @@ -30,7 +30,7 @@ #include "ED_keyframes_edit.hh" #include "ED_markers.hh" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" using namespace blender; @@ -170,7 +170,7 @@ static short agrp_keyframes_loop(KeyframeEditData *ked, /* Loop over all keyframes in the Animation. */ static short anim_keyframes_loop(KeyframeEditData *ked, - animrig::Animation &anim, + animrig::Action &anim, animrig::Binding *binding, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, @@ -415,12 +415,12 @@ short ANIM_animchannel_keyframes_loop(KeyframeEditData *ked, */ case ALE_GROUP: /* action group */ return agrp_keyframes_loop(ked, (bActionGroup *)ale->data, key_ok, key_cb, fcu_cb); - case ALE_ANIM: { /* Animation data-block. */ + case ALE_ACTION_LAYERED: { /* Animation data-block. */ #ifdef WITH_ANIM_BAKLAVA - /* This assumes that the ALE_ANIM 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. */ - animrig::Animation &anim = static_cast(ale->key_data)->wrap(); + /* 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. */ + animrig::Action &anim = static_cast(ale->key_data)->wrap(); animrig::Binding *binding = anim.binding_for_handle(ale->adt->binding_handle); return anim_keyframes_loop(ked, anim, binding, key_ok, key_cb, fcu_cb); #else @@ -464,7 +464,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_ANIM: + case ALE_ACTION_LAYERED: /* This function is only used in nlaedit_apply_scale_exec(). Since the NLA has no support for * Animation data-blocks in strips, there is no need to implement this here. */ return 0; diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index be735e292f3..5c82c796de8 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -36,7 +36,7 @@ #include "ED_anim_api.hh" #include "ED_keyframes_keylist.hh" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" /* *************************** Keyframe Processing *************************** */ @@ -1167,37 +1167,32 @@ void action_group_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. - */ -void animation_to_keylist(AnimData *adt, - Animation *anim, - AnimKeylist *keylist, - const int saction_flag, - blender::float2 range) -{ - BLI_assert(adt); - BLI_assert(anim); - BLI_assert(GS(anim->id.name) == ID_AN); - - for (FCurve *fcurve : fcurves_for_animation(anim->wrap(), adt->binding_handle)) { - fcurve_to_keylist(adt, fcurve, keylist, saction_flag, range); - } -} - void action_to_keylist(AnimData *adt, - bAction *act, + bAction *dna_action, AnimKeylist *keylist, const int saction_flag, blender::float2 range) { - if (!act) { + if (!dna_action) { return; } - LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { - fcurve_to_keylist(adt, fcu, keylist, saction_flag, range); + blender::animrig::Action &action = dna_action->wrap(); + + /* TODO: move this into fcurves_for_animation(). */ + if (action.is_action_legacy()) { + LISTBASE_FOREACH (FCurve *, fcu, &action.curves) { + fcurve_to_keylist(adt, fcu, keylist, saction_flag, range); + } + return; + } + + /** + * 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. + */ + for (FCurve *fcurve : fcurves_for_animation(action, adt->binding_handle)) { + fcurve_to_keylist(adt, fcurve, keylist, saction_flag, range); } } diff --git a/source/blender/editors/include/ED_anim_api.hh b/source/blender/editors/include/ED_anim_api.hh index 48849684376..f5a8e4464eb 100644 --- a/source/blender/editors/include/ED_anim_api.hh +++ b/source/blender/editors/include/ED_anim_api.hh @@ -212,8 +212,8 @@ enum eAnim_ChannelType { ANIMTYPE_NLACONTROLS, ANIMTYPE_NLACURVE, - ANIMTYPE_FILLANIM, - ANIMTYPE_FILLACTD, + ANIMTYPE_FILLACT_LAYERED, /* Layered Actions. */ + ANIMTYPE_FILLACTD, /* Legacy Actions. */ ANIMTYPE_FILLDRIVERS, ANIMTYPE_DSMAT, @@ -267,12 +267,12 @@ enum eAnim_KeyType { ALE_MASKLAY, /* Mask */ ALE_NLASTRIP, /* NLA Strips */ - ALE_ALL, /* All channels summary */ - ALE_SCE, /* Scene summary */ - ALE_OB, /* Object summary */ - ALE_ACT, /* Action summary */ - ALE_GROUP, /* Action Group summary */ - ALE_ANIM, /* Animation data-block summary. */ + ALE_ALL, /* All channels summary */ + ALE_SCE, /* Scene summary */ + ALE_OB, /* Object summary */ + ALE_ACT, /* Action summary (legacy). */ + ALE_GROUP, /* Action Group summary (legacy). */ + ALE_ACTION_LAYERED, /* Action summary (layered). */ ALE_GREASE_PENCIL_CEL, /* Grease Pencil Cels. */ ALE_GREASE_PENCIL_DATA, /* Grease Pencil Cels summary. */ diff --git a/source/blender/editors/include/ED_keyframes_draw.hh b/source/blender/editors/include/ED_keyframes_draw.hh index 708342e0b4e..e98f1647430 100644 --- a/source/blender/editors/include/ED_keyframes_draw.hh +++ b/source/blender/editors/include/ED_keyframes_draw.hh @@ -14,7 +14,6 @@ #include "ED_keyframes_keylist.hh" -struct Animation; struct AnimData; struct ChannelDrawList; struct FCurve; @@ -73,14 +72,14 @@ void ED_add_action_group_channel(ChannelDrawList *draw_list, float ypos, float yscale_fac, int saction_flag); -/* Animation Summary.*/ -void ED_add_animation_channel(ChannelDrawList *channel_list, - AnimData *adt, - Animation *anim, - float ypos, - float yscale_fac, - int saction_flag); -/* Action Summary */ +/* Layered Action Summary.*/ +void ED_add_action_layered_channel(ChannelDrawList *channel_list, + AnimData *adt, + bAction *action, + const float ypos, + const float yscale_fac, + int saction_flag); +/* Legacy Action Summary */ void ED_add_action_channel(ChannelDrawList *draw_list, AnimData *adt, bAction *act, diff --git a/source/blender/editors/include/ED_keyframes_keylist.hh b/source/blender/editors/include/ED_keyframes_keylist.hh index 48e68dbf272..80ea0badfe7 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.hh +++ b/source/blender/editors/include/ED_keyframes_keylist.hh @@ -13,7 +13,6 @@ #include "DNA_curve_types.h" -struct Animation; struct AnimData; struct CacheFile; struct FCurve; @@ -162,9 +161,6 @@ void action_group_to_keylist(AnimData *adt, AnimKeylist *keylist, int saction_flag, blender::float2 range); -/* Animation */ -void animation_to_keylist( - AnimData *adt, Animation *anim, AnimKeylist *keylist, int saction_flag, blender::float2 range); /* Action */ void action_to_keylist( AnimData *adt, bAction *act, AnimKeylist *keylist, int saction_flag, blender::float2 range); diff --git a/source/blender/editors/interface/interface_icons.cc b/source/blender/editors/interface/interface_icons.cc index 89a3dc7c6fd..c5929546500 100644 --- a/source/blender/editors/interface/interface_icons.cc +++ b/source/blender/editors/interface/interface_icons.cc @@ -2519,8 +2519,6 @@ int UI_icon_from_idcode(const int idcode) switch ((ID_Type)idcode) { case ID_AC: return ICON_ACTION; - case ID_AN: - return ICON_ACTION; /* TODO: give Animation its own icon. */ case ID_AR: return ICON_ARMATURE_DATA; case ID_BR: diff --git a/source/blender/editors/interface/interface_ops_test.cc b/source/blender/editors/interface/interface_ops_test.cc index 8f16cf4baf2..e10fb0a07f8 100644 --- a/source/blender/editors/interface/interface_ops_test.cc +++ b/source/blender/editors/interface/interface_ops_test.cc @@ -4,8 +4,8 @@ #include +#include "BKE_action.hh" #include "BKE_anim_data.hh" -#include "BKE_animation.hh" #include "BKE_fcurve.hh" #include "BKE_idtype.hh" #include "BKE_lib_id.hh" diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index 1f0255618f8..325eac353c1 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -1121,8 +1121,6 @@ static const char *template_id_browse_tip(const StructRNA *type) return N_("Browse Armature data to be linked"); case ID_AC: return N_("Browse Action to be linked"); - case ID_AN: - return N_("Browse Animation to be linked"); case ID_NT: return N_("Browse Node Tree to be linked"); case ID_BR: diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index f6b77f6ac14..bcf864530c9 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -616,7 +616,6 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data) case ID_SCR: /* Screen */ case ID_GR: /* Group */ case ID_AC: /* bAction */ - case ID_AN: /* Animation */ case ID_BR: /* Brush */ case ID_WM: /* WindowManager */ case ID_LS: /* FreestyleLineStyle */ diff --git a/source/blender/editors/space_action/action_draw.cc b/source/blender/editors/space_action/action_draw.cc index 606e3091d8c..8bcf2c09dfb 100644 --- a/source/blender/editors/space_action/action_draw.cc +++ b/source/blender/editors/space_action/action_draw.cc @@ -222,7 +222,7 @@ static void draw_backdrops(bAnimContext *ac, ListBase &anim_data, View2D *v2d, u break; } case ANIMTYPE_FILLACTD: - case ANIMTYPE_FILLANIM: + case ANIMTYPE_FILLACT_LAYERED: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: { immUniformColor3ubvAlpha(col2b, sel ? col1[3] : col2b[3]); @@ -368,13 +368,13 @@ static void draw_keyframes(bAnimContext *ac, scale_factor, action_flag); break; - case ALE_ANIM: - ED_add_animation_channel(draw_list, - adt, - static_cast(ale->key_data), - ycenter, - scale_factor, - action_flag); + case ALE_ACTION_LAYERED: + ED_add_action_layered_channel(draw_list, + adt, + static_cast(ale->key_data), + ycenter, + scale_factor, + action_flag); break; case ALE_ACT: ED_add_action_channel(draw_list, diff --git a/source/blender/editors/space_action/action_select.cc b/source/blender/editors/space_action/action_select.cc index 764c8b4fbf8..3f5afc18537 100644 --- a/source/blender/editors/space_action/action_select.cc +++ b/source/blender/editors/space_action/action_select.cc @@ -112,9 +112,9 @@ static void actkeys_list_element_to_keylist(bAnimContext *ac, ob_to_keylist(ads, ob, keylist, 0, range); break; } - case ALE_ANIM: { - Animation *anim = (Animation *)ale->key_data; - animation_to_keylist(adt, anim, keylist, 0, range); + case ALE_ACTION_LAYERED: { + bAction *action = (bAction *)ale->key_data; + action_to_keylist(adt, action, keylist, 0, range); break; } case ALE_ACT: { diff --git a/source/blender/editors/space_nla/nla_tracks.cc b/source/blender/editors/space_nla/nla_tracks.cc index 4edb1396f0e..f06ee85992c 100644 --- a/source/blender/editors/space_nla/nla_tracks.cc +++ b/source/blender/editors/space_nla/nla_tracks.cc @@ -261,7 +261,7 @@ static int mouse_nla_tracks(bContext *C, bAnimContext *ac, int track_index, shor } break; } - case ANIMTYPE_FILLANIM: + case ANIMTYPE_FILLACT_LAYERED: /* The NLA doesn't support Animation data-blocks. */ break; default: diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 20c77f25b8f..96fce0e28a3 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -2485,8 +2485,6 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id) return ICON_WORLD_DATA; case ID_AC: return ICON_ACTION; - case ID_AN: - return ICON_ACTION; /* TODO: give Animation its own icon. */ case ID_NLA: return ICON_NLA; case ID_TXT: { diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index 681bb659bec..4d9df82bbed 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -139,7 +139,6 @@ struct TreeElementIcon { ID_GR, \ ID_AR, \ ID_AC, \ - ID_AN, \ ID_BR, \ ID_PA, \ ID_GD_LEGACY, \ diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 89082e3718d..f5e872e53cd 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -141,7 +141,6 @@ static void get_element_operation_type( case ID_KE: case ID_WO: case ID_AC: - case ID_AN: case ID_TXT: case ID_GR: case ID_LS: diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index 1c48221166a..0875b7836f1 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -88,7 +88,6 @@ std::unique_ptr TreeElementID::create_from_id(TreeElement &legacy case ID_TXT: case ID_SO: case ID_AC: - case ID_AN: case ID_PAL: case ID_PC: case ID_CF: diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c25223dcb66..6580f529062 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -1212,7 +1212,6 @@ typedef enum IDRecalcFlag { #define FILTER_ID_LI (1ULL << 39) #define FILTER_ID_GP (1ULL << 40) #define FILTER_ID_IP (1ULL << 41) -#define FILTER_ID_AN (1ULL << 42) #define FILTER_ID_ALL \ (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \ @@ -1222,7 +1221,7 @@ typedef enum IDRecalcFlag { FILTER_ID_SPK | FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | \ FILTER_ID_CF | FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | \ FILTER_ID_SIM | FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM | FILTER_ID_LI | FILTER_ID_GP | \ - FILTER_ID_IP | FILTER_ID_AN) + FILTER_ID_IP) /** * This enum defines the index assigned to each type of IDs in the array returned by @@ -1261,7 +1260,6 @@ typedef enum eID_Index { /* Animation types, might be used by almost all other types. */ INDEX_ID_IP, /* Deprecated. */ INDEX_ID_AC, - INDEX_ID_AN, /* Grease Pencil, special case, should be with the other obdata, but it can also be used by many * other ID types, including node trees e.g. diff --git a/source/blender/makesdna/DNA_ID_enums.h b/source/blender/makesdna/DNA_ID_enums.h index a4db7befa1c..7ce8785aed0 100644 --- a/source/blender/makesdna/DNA_ID_enums.h +++ b/source/blender/makesdna/DNA_ID_enums.h @@ -84,7 +84,6 @@ typedef enum ID_Type { ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */ ID_VO = MAKE_ID2('V', 'O'), /* Volume */ ID_GP = MAKE_ID2('G', 'P'), /* Grease Pencil */ - ID_AN = MAKE_ID2('A', 'N'), /* Animation */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked data-blocks. */ diff --git a/source/blender/makesdna/DNA_anim_defaults.h b/source/blender/makesdna/DNA_action_defaults.h similarity index 78% rename from source/blender/makesdna/DNA_anim_defaults.h rename to source/blender/makesdna/DNA_action_defaults.h index e6e6c3bf1da..ccb2b3cb21e 100644 --- a/source/blender/makesdna/DNA_anim_defaults.h +++ b/source/blender/makesdna/DNA_action_defaults.h @@ -13,10 +13,10 @@ /* clang-format off */ /* -------------------------------------------------------------------- */ -/** \name AnimationLayer Struct +/** \name ActionLayer Struct * \{ */ -#define _DNA_DEFAULT_AnimationLayer \ +#define _DNA_DEFAULT_ActionLayer \ { \ .influence = 1.0f, \ } @@ -24,10 +24,10 @@ /** \} */ /* -------------------------------------------------------------------- */ -/** \name AnimationStrip Struct +/** \name ActionStrip Struct * \{ */ -#define _DNA_DEFAULT_AnimationStrip \ +#define _DNA_DEFAULT_ActionStrip \ { \ .frame_start = -INFINITY, \ .frame_end = INFINITY, \ diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 4a25e23ac7e..042c45f6cf6 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -20,7 +20,13 @@ #include "DNA_vec_types.h" #include "DNA_view2d_types.h" +#ifdef __cplusplus +# include +#endif + +struct AnimData; struct Collection; +struct FCurve; struct GHash; struct Object; struct SpaceLink; @@ -36,6 +42,24 @@ typedef struct GPUBatchHandle GPUBatchHandle; typedef struct GPUVertBufHandle GPUVertBufHandle; #endif +/* Forward declarations so the actual declarations can happen top-down. */ +struct ActionLayer; +struct ActionBinding; +struct ActionStrip; +struct ActionChannelBag; + +/* Declarations of the C++ wrappers. */ +#ifdef __cplusplus +namespace blender::animrig { +class Action; +class Binding; +class ChannelBag; +class KeyframeStrip; +class Layer; +class Strip; +} // namespace blender::animrig +#endif + /* ************************************************ */ /* Visualization */ @@ -707,16 +731,37 @@ typedef struct bAction { /** ID-serialization for relinking. */ ID id; - /** Function-curves (FCurve). */ + struct ActionLayer **layer_array; /* Array of 'layer_array_num' layers. */ + 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; + + /* Note about legacy animation data: + * + * Blender 2.5 introduced a new animation system 'Animato'. This replaced the + * IPO ('interpolation') curves with F-Curves. Both are considered 'legacy' at + * different levels: + * + * - Actions with F-Curves in `curves`, as introduced in Blender 2.5, are + * considered 'legacy' but still functional in current Blender. + * - Pre-2.5 data are deprecated and old files are automatically converted to + * the post-2.5 data model. + */ + + /** Legacy F-Curves (FCurve), introduced in Blender 2.5. */ ListBase curves; - /** Legacy data - Action Channels (bActionChannel) in pre-2.5 animation system. */ + /** Legacy Action Channels (bActionChannel) from pre-2.5 animation system. */ ListBase chanbase DNA_DEPRECATED; - /** Groups of function-curves (bActionGroup). */ + /** Legacy Groups of function-curves (bActionGroup), introduced in Blender 2.5. */ ListBase groups; + /** Markers local to the Action (used to provide Pose-Libraries). */ ListBase markers; - /** Settings for this action. */ + /** Settings for this action. \see eAction_Flags */ int flag; /** Index of the active marker. */ int active_marker; @@ -735,6 +780,11 @@ typedef struct bAction { float frame_start, frame_end; PreviewImage *preview; + +#ifdef __cplusplus + blender::animrig::Action &wrap(); + const blender::animrig::Action &wrap() const; +#endif } bAction; /** Flags for the action. */ @@ -1018,3 +1068,148 @@ typedef struct bActionChannel { /** Temporary setting - may be used to indicate group that channel belongs to during syncing. */ int temp; } bActionChannel; + +/* ************************************************ */ +/* Layered Animation data-types. */ + +/** + * \see #blender::animrig::Layer + */ +typedef struct ActionLayer { + /** User-Visible identifier, unique within the Animation. */ + char name[64]; /* MAX_NAME. */ + + float influence; /* [0-1] */ + + /** \see #blender::animrig::Layer::flags() */ + uint8_t layer_flags; + + /** \see #blender::animrig::Layer::mixmode() */ + int8_t layer_mix_mode; + + uint8_t _pad0[2]; + + /** + * There is always at least one strip. + * If there is only one, it can be infinite. This is the default for new layers. + */ + struct ActionStrip **strip_array; /* Array of 'strip_array_num' strips. */ + int strip_array_num; + + uint8_t _pad1[4]; + +#ifdef __cplusplus + blender::animrig::Layer &wrap(); + const blender::animrig::Layer &wrap() const; +#endif +} ActionLayer; + +/** + * \see #blender::animrig::Binding + */ +typedef struct ActionBinding { + /** + * Typically the ID name this binding was created for, including the two + * letters indicating the ID type. + * + * \see #AnimData::binding_name + */ + char name[66]; /* MAX_ID_NAME */ + uint8_t _pad0[2]; + + /** + * Type of ID-blocks that this binding 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. + * + * This number allows reorganization of the #bAction::binding_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. + * + * \see #blender::animrig::Action::binding_for_handle() + */ + int32_t handle; + +#ifdef __cplusplus + blender::animrig::Binding &wrap(); + const blender::animrig::Binding &wrap() const; +#endif +} ActionBinding; + +/** + * \see #blender::animrig::Strip + */ +typedef struct ActionStrip { + /** + * \see #blender::animrig::Strip::type() + */ + int8_t strip_type; + uint8_t _pad0[3]; + + float frame_start; /** Start frame of the strip, in Animation time. */ + float frame_end; /** End frame of the strip, in Animation time. */ + + /** + * Offset applied to the contents of the strip, in frames. + * + * This offset determines the difference between "Animation time" (which would + * typically be the same as the scene time, until the animation system + * supports strips referencing other Animation data-blocks). + */ + float frame_offset; + +#ifdef __cplusplus + blender::animrig::Strip &wrap(); + const blender::animrig::Strip &wrap() const; +#endif +} ActionStrip; + +/** + * #ActionStrip::type = #Strip::Type::Keyframe. + * + * \see #blender::animrig::KeyframeStrip + */ +typedef struct KeyframeActionStrip { + ActionStrip strip; + + struct ActionChannelBag **channelbags_array; + int channelbags_array_num; + + uint8_t _pad[4]; + +#ifdef __cplusplus + blender::animrig::KeyframeStrip &wrap(); + const blender::animrig::KeyframeStrip &wrap() const; +#endif +} KeyframeActionStrip; + +/** + * \see #blender::animrig::ChannelBag + */ +typedef struct ActionChannelBag { + int32_t binding_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 */ +#ifdef __cplusplus + blender::animrig::ChannelBag &wrap(); + const blender::animrig::ChannelBag &wrap() const; +#endif +} ChannelBag; + +#ifdef __cplusplus +/* Some static assertions that things that should have the same type actually do. */ +static_assert( + std::is_same_v); +static_assert( + std::is_same_v); +#endif diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 407b59f8631..72928668ea1 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -21,13 +21,6 @@ # include #endif -/* Forward declarations so the actual declarations can happen top-down. */ -struct Animation; -struct AnimationLayer; -struct AnimationBinding; -struct AnimationStrip; -struct AnimationChannelBag; - /* ************************************************ */ /* F-Curve DataTypes */ @@ -1127,6 +1120,22 @@ typedef struct AnimData { */ bAction *action; + /** + * Identifier for which ActionBinding 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; + /** + * Binding name, primarily used for mapping to the right binding when assigning + * another Animation data-block. Should be the same type as #ActionBinding::name. + * + * \see #ActionBinding::name + */ + char binding_name[66]; /* MAX_ID_NAME */ + uint8_t _pad0[2]; + /** * Temp-storage for the 'real' active action (i.e. the one used before the tweaking-action * took over to be edited in the Animation Editors) @@ -1158,29 +1167,6 @@ typedef struct AnimData { /** Runtime data, for depsgraph evaluation. */ FCurve **driver_array; - /** - * Active Animation data-block. If this is set, `action` and NLA-related - * properties should be ignored. Note that there is plenty of code in Blender - * that doesn't check this pointer yet. - */ - struct Animation *animation; - - /** - * Identifier for which AnimationBinding of the above Animation is actually animating this - * data-block. - * - * Do not set this directly, use one of the assignment functions in ANIM_animation.hh instead. - */ - int32_t binding_handle; - /** - * Binding name, primarily used for mapping to the right binding when assigning - * another Animation data-block. Should be the same type as #AnimationBinding::name. - * - * \see #AnimationBinding::name - */ - char binding_name[66]; - uint8_t _pad0[6]; - /* settings for animation evaluation */ /** User-defined settings. */ int flag; @@ -1192,8 +1178,16 @@ typedef struct AnimData { short act_extendmode; /** Influence for active action. */ float act_influence; + + uint8_t _pad1[4]; } AnimData; +#ifdef __cplusplus +/* Some static assertions that things that should have the same type actually do. */ +static_assert(std::is_same_v); +static_assert(std::is_same_v); +#endif + /* Animation Data settings (mostly for NLA) */ typedef enum eAnimData_Flag { /** Only evaluate a single track in the NLA. */ @@ -1244,188 +1238,3 @@ typedef struct IdAdtTemplate { /* From: `DNA_object_types.h`, see its doc-string there. */ #define SELECT 1 - -/* ************************************************ */ -/* Layered Animation data-types. */ - -/* Declarations of C++ wrappers. See ANIM_animation.hh for the actual classes. */ -#ifdef __cplusplus -namespace blender::animrig { -class Animation; -class ChannelBag; -class KeyframeStrip; -class Layer; -class Binding; -class Strip; -} // namespace blender::animrig -#endif - -/** - * Container of layered animation data. - * - * \see #blender::animrig::Animation - */ -typedef struct Animation { - ID id; - - struct AnimationLayer **layer_array; /* Array of 'layer_array_num' layers. */ - int layer_array_num; - int layer_active_index; /* Index into layer_array, -1 means 'no active'. */ - - struct AnimationBinding **binding_array; /* Array of 'binding_array_num` bindings. */ - int binding_array_num; - int32_t last_binding_handle; - - uint8_t flag; - uint8_t _pad0[7]; - -#ifdef __cplusplus - blender::animrig::Animation &wrap(); - const blender::animrig::Animation &wrap() const; -#endif -} Animation; - -/** - * \see #blender::animrig::Layer - */ -typedef struct AnimationLayer { - /** User-Visible identifier, unique within the Animation. */ - char name[64]; /* MAX_NAME. */ - - float influence; /* [0-1] */ - - /** \see #blender::animrig::Layer::flags() */ - uint8_t layer_flags; - - /** \see #blender::animrig::Layer::mixmode() */ - int8_t layer_mix_mode; - - uint8_t _pad0[2]; - - /** - * There is always at least one strip. - * If there is only one, it can be infinite. This is the default for new layers. - */ - struct AnimationStrip **strip_array; /* Array of 'strip_array_num' strips. */ - int strip_array_num; - - uint8_t _pad1[4]; - -#ifdef __cplusplus - blender::animrig::Layer &wrap(); - const blender::animrig::Layer &wrap() const; -#endif -} AnimationLayer; - -/** - * \see #blender::animrig::Binding - */ -typedef struct AnimationBinding { - /** - * Typically the ID name this binding was created for, including the two - * letters indicating the ID type. - * - * \see #AnimData::binding_name - */ - char name[66]; /* MAX_ID_NAME */ - uint8_t _pad0[2]; - - /** - * Type of ID-blocks that this binding 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. - * - * This number allows reorganization of the #Animation::bindings_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. - * - * \see #blender::animrig::Animation::binding_for_handle() - */ - int32_t handle; - -#ifdef __cplusplus - blender::animrig::Binding &wrap(); - const blender::animrig::Binding &wrap() const; -#endif -} AnimationBinding; - -/** - * \see #blender::animrig::Strip - */ -typedef struct AnimationStrip { - /** - * \see #blender::animrig::Strip::type() - */ - int8_t strip_type; - uint8_t _pad0[3]; - - float frame_start; /** Start frame of the strip, in Animation time. */ - float frame_end; /** End frame of the strip, in Animation time. */ - - /** - * Offset applied to the contents of the strip, in frames. - * - * This offset determines the difference between "Animation time" (which would - * typically be the same as the scene time, until the animation system - * supports strips referencing other Animation data-blocks). - */ - float frame_offset; - -#ifdef __cplusplus - blender::animrig::Strip &wrap(); - const blender::animrig::Strip &wrap() const; -#endif -} AnimationStrip; - -/** - * #AnimationStrip::type = #Strip::Type::Keyframe. - * - * \see #blender::animrig::KeyframeStrip - */ -typedef struct KeyframeAnimationStrip { - AnimationStrip strip; - - struct AnimationChannelBag **channelbags_array; - int channelbags_array_num; - - uint8_t _pad[4]; - -#ifdef __cplusplus - blender::animrig::KeyframeStrip &wrap(); - const blender::animrig::KeyframeStrip &wrap() const; -#endif -} KeyframeAnimationStrip; - -/** - * \see #blender::animrig::ChannelBag - */ -typedef struct AnimationChannelBag { - int32_t binding_handle; - - int fcurve_array_num; - 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 */ -#ifdef __cplusplus - blender::animrig::ChannelBag &wrap(); - const blender::animrig::ChannelBag &wrap() const; -#endif -} ChannelBag; - -#ifdef __cplusplus -/* Some static assertions that things that should have the same type actually do. */ -static_assert( - std::is_same_v); -static_assert( - std::is_same_v); -static_assert(std::is_same_v); -static_assert(std::is_same_v); -#endif diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 39b002a1d24..e24ab8087a4 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -110,7 +110,7 @@ #include "DNA_volume_types.h" #include "DNA_world_types.h" -#include "DNA_anim_defaults.h" +#include "DNA_action_defaults.h" #include "DNA_armature_defaults.h" #include "DNA_asset_defaults.h" #include "DNA_brush_defaults.h" @@ -146,9 +146,9 @@ #define SDNA_DEFAULT_DECL_STRUCT(struct_name) \ static const struct_name DNA_DEFAULT_##struct_name = _DNA_DEFAULT_##struct_name -/* DNA_anim_defaults.h */ -SDNA_DEFAULT_DECL_STRUCT(AnimationLayer); -SDNA_DEFAULT_DECL_STRUCT(AnimationStrip); +/* DNA_action_defaults.h */ +SDNA_DEFAULT_DECL_STRUCT(ActionLayer); +SDNA_DEFAULT_DECL_STRUCT(ActionStrip); /* DNA_asset_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(AssetMetaData); @@ -397,8 +397,8 @@ extern const bTheme U_theme_default; const void *DNA_default_table[SDNA_TYPE_MAX] = { /* DNA_anim_defaults.h */ - SDNA_DEFAULT_DECL(AnimationLayer), - SDNA_DEFAULT_DECL(AnimationStrip), + SDNA_DEFAULT_DECL(ActionLayer), + SDNA_DEFAULT_DECL(ActionStrip), /* DNA_asset_defaults.h */ SDNA_DEFAULT_DECL(AssetMetaData), diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 7903368da56..991df82d0b4 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -112,7 +112,6 @@ if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_GREASE_PENCIL_V3) add_definitions(-DWITH_ANIM_BAKLAVA) list(APPEND DEFSRC - rna_animation_id.cc rna_grease_pencil.cc ) endif() diff --git a/source/blender/makesrna/intern/makesrna.cc b/source/blender/makesrna/intern/makesrna.cc index 10ae8b482fc..1c4fcd0beea 100644 --- a/source/blender/makesrna/intern/makesrna.cc +++ b/source/blender/makesrna/intern/makesrna.cc @@ -4769,9 +4769,6 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_texture.cc", "rna_texture_api.cc", RNA_def_texture}, {"rna_action.cc", "rna_action_api.cc", RNA_def_action}, {"rna_animation.cc", "rna_animation_api.cc", RNA_def_animation}, -#ifdef WITH_ANIM_BAKLAVA - {"rna_animation_id.cc", nullptr, RNA_def_animation_id}, -#endif {"rna_animviz.cc", nullptr, RNA_def_animviz}, {"rna_armature.cc", "rna_armature_api.cc", RNA_def_armature}, {"rna_attribute.cc", nullptr, RNA_def_attribute}, diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index 22e6a86fe00..e2df58ebe05 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -34,7 +34,6 @@ */ const EnumPropertyItem rna_enum_id_type_items[] = { {ID_AC, "ACTION", ICON_ACTION, "Action", ""}, - {ID_AN, "ANIMATION", ICON_ACTION, "Animation", ""}, /* TODO: give Animation its own icon. */ {ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armature", ""}, {ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""}, {ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""}, @@ -123,9 +122,6 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items const IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { /* Datablocks */ {FILTER_ID_AC, "filter_action", ICON_ACTION, "Actions", "Show Action data-blocks"}, -#ifdef WITH_ANIM_BAKLAVA - {FILTER_ID_AN, "filter_animation", ICON_ACTION, "Animations", "Show Animation data-blocks"}, -#endif {FILTER_ID_AR, "filter_armature", ICON_ARMATURE_DATA, @@ -378,11 +374,6 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_Action) { return ID_AC; } -# ifdef WITH_ANIM_BAKLAVA - if (base_type == &RNA_Animation) { - return ID_AN; - } -# endif if (base_type == &RNA_Armature) { return ID_AR; } @@ -505,11 +496,6 @@ StructRNA *ID_code_to_RNA_type(short idcode) switch ((ID_Type)idcode) { case ID_AC: return &RNA_Action; - case ID_AN: -# ifdef WITH_ANIM_BAKLAVA - return &RNA_Animation; -# endif - break; case ID_AR: return &RNA_Armature; case ID_BR: @@ -1069,8 +1055,7 @@ static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag allow_flag = OB_RECALC_ALL | PSYS_RECALC; break; # endif - case ID_AC: /* Fall-through. */ - case ID_AN: + case ID_AC: allow_flag = ID_RECALC_ANIMATION; break; default: diff --git a/source/blender/makesrna/intern/rna_action.cc b/source/blender/makesrna/intern/rna_action.cc index aa824e38a83..a620e61a686 100644 --- a/source/blender/makesrna/intern/rna_action.cc +++ b/source/blender/makesrna/intern/rna_action.cc @@ -26,8 +26,52 @@ #include "rna_internal.hh" +#include "ANIM_action.hh" + #include "WM_types.hh" +using namespace blender; + +#ifdef WITH_ANIM_BAKLAVA +const EnumPropertyItem rna_enum_layer_mix_mode_items[] = { + {int(animrig::Layer::MixMode::Replace), + "REPLACE", + 0, + "Replace", + "Channels in this layer override the same channels from underlying layers"}, + {int(animrig::Layer::MixMode::Offset), + "OFFSET", + 0, + "Offset", + "Channels in this layer are added to underlying layers as sequential operations"}, + {int(animrig::Layer::MixMode::Add), + "ADD", + 0, + "Add", + "Channels in this layer are added to underlying layers on a per-channel basis"}, + {int(animrig::Layer::MixMode::Subtract), + "SUBTRACT", + 0, + "Subtract", + "Channels in this layer are subtracted to underlying layers on a per-channel basis"}, + {int(animrig::Layer::MixMode::Multiply), + "MULTIPLY", + 0, + "Multiply", + "Channels in this layer are multiplied with underlying layers on a per-channel basis"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +const EnumPropertyItem rna_enum_strip_type_items[] = { + {int(animrig::Strip::Type::Keyframe), + "KEYFRAME", + 0, + "Keyframe", + "Strip containing keyframes on F-Curves"}, + {0, nullptr, 0, nullptr, nullptr}, +}; +#endif // WITH_ANIM_BAKLAVA + #ifdef RNA_RUNTIME # include @@ -44,6 +88,370 @@ # include "WM_api.hh" +# include "DEG_depsgraph.hh" + +# include "ANIM_keyframing.hh" + +# include + +# ifdef WITH_ANIM_BAKLAVA + +static animrig::Action &rna_action(const PointerRNA *ptr) +{ + return reinterpret_cast(ptr->owner_id)->wrap(); +} + +static animrig::Binding &rna_data_binding(const PointerRNA *ptr) +{ + return reinterpret_cast(ptr->data)->wrap(); +} + +static animrig::Layer &rna_data_layer(const PointerRNA *ptr) +{ + return reinterpret_cast(ptr->data)->wrap(); +} + +static animrig::Strip &rna_data_strip(const PointerRNA *ptr) +{ + return reinterpret_cast(ptr->data)->wrap(); +} + +static void rna_Action_tag_animupdate(Main *, Scene *, PointerRNA *ptr) +{ + animrig::Action &anim = rna_action(ptr); + DEG_id_tag_update(&anim.id, ID_RECALC_ANIMATION); +} + +static animrig::KeyframeStrip &rna_data_keyframe_strip(const PointerRNA *ptr) +{ + animrig::Strip &strip = reinterpret_cast(ptr->data)->wrap(); + return strip.as(); +} + +static animrig::ChannelBag &rna_data_channelbag(const PointerRNA *ptr) +{ + return reinterpret_cast(ptr->data)->wrap(); +} + +template +static void rna_iterator_array_begin(CollectionPropertyIterator *iter, Span items) +{ + rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr); +} + +template +static void rna_iterator_array_begin(CollectionPropertyIterator *iter, MutableSpan items) +{ + rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr); +} + +static ActionBinding *rna_Action_bindings_new(bAction *anim_id, bContext *C, ID *id_for_binding) +{ + animrig::Action &anim = anim_id->wrap(); + animrig::Binding *binding; + + if (id_for_binding) { + binding = &anim.binding_add_for_id(*id_for_binding); + } + else { + binding = &anim.binding_add(); + } + + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); + return binding; +} + +static void rna_iterator_action_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + animrig::Action &anim = rna_action(ptr); + rna_iterator_array_begin(iter, anim.layers()); +} + +static int rna_iterator_action_layers_length(PointerRNA *ptr) +{ + animrig::Action &anim = rna_action(ptr); + return anim.layers().size(); +} + +static ActionLayer *rna_Action_layers_new(bAction *dna_action, + bContext *C, + ReportList *reports, + const char *name) +{ + animrig::Action &anim = dna_action->wrap(); + + if (anim.layers().size() >= 1) { + /* Not allowed to have more than one layer, for now. This limitation is in + * place until working with multiple animated IDs is fleshed out better. */ + BKE_report(reports, RPT_ERROR, "An Animation may not have more than one layer"); + return nullptr; + } + + animrig::Layer &layer = anim.layer_add(name); + + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); + return &layer; +} + +void rna_Action_layers_remove(bAction *dna_action, + bContext *C, + ReportList *reports, + ActionLayer *dna_layer) +{ + animrig::Action &anim = dna_action->wrap(); + animrig::Layer &layer = dna_layer->wrap(); + if (!anim.layer_remove(layer)) { + BKE_report(reports, RPT_ERROR, "This layer does not belong to this animation"); + return; + } + + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); + DEG_id_tag_update(&anim.id, ID_RECALC_ANIMATION); +} + +static void rna_iterator_animation_bindings_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ + animrig::Action &anim = rna_action(ptr); + rna_iterator_array_begin(iter, anim.bindings()); +} + +static int rna_iterator_animation_bindings_length(PointerRNA *ptr) +{ + animrig::Action &anim = rna_action(ptr); + return anim.bindings().size(); +} + +static std::optional rna_ActionBinding_path(const PointerRNA *ptr) +{ + animrig::Binding &binding = rna_data_binding(ptr); + + char name_esc[sizeof(binding.name) * 2]; + BLI_str_escape(name_esc, binding.name, sizeof(name_esc)); + return fmt::format("bindings[\"{}\"]", name_esc); +} + +/* Name functions that ignore the first two ID characters */ +void rna_ActionBinding_name_display_get(PointerRNA *ptr, char *value) +{ + animrig::Binding &binding = rna_data_binding(ptr); + binding.name_without_prefix().unsafe_copy(value); +} + +int rna_ActionBinding_name_display_length(PointerRNA *ptr) +{ + animrig::Binding &binding = rna_data_binding(ptr); + return binding.name_without_prefix().size(); +} + +static void rna_ActionBinding_name_display_set(PointerRNA *ptr, const char *name) +{ + animrig::Action &anim = rna_action(ptr); + animrig::Binding &binding = rna_data_binding(ptr); + const StringRef name_ref(name); + + if (name_ref.is_empty()) { + WM_report(RPT_ERROR, "Animation binding 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); +} + +static void rna_ActionBinding_name_set(PointerRNA *ptr, const char *name) +{ + animrig::Action &anim = rna_action(ptr); + animrig::Binding &binding = rna_data_binding(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"); + return; + } + + if (binding.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(); + + 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", + new_prefix.c_str(), + expect_prefix.c_str()); + } + } + + anim.binding_name_define(binding, name); +} + +static void rna_ActionBinding_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); +} + +static std::optional rna_ActionLayer_path(const PointerRNA *ptr) +{ + animrig::Layer &layer = rna_data_layer(ptr); + + char name_esc[sizeof(layer.name) * 2]; + BLI_str_escape(name_esc, layer.name, sizeof(name_esc)); + return fmt::format("layers[\"{}\"]", name_esc); +} + +static void rna_iterator_ActionLayer_strips_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ + animrig::Layer &layer = rna_data_layer(ptr); + rna_iterator_array_begin(iter, layer.strips()); +} + +static int rna_iterator_ActionLayer_strips_length(PointerRNA *ptr) +{ + animrig::Layer &layer = rna_data_layer(ptr); + return layer.strips().size(); +} + +ActionStrip *rna_ActionStrips_new(ActionLayer *dna_layer, + bContext *C, + ReportList *reports, + const int type) +{ + const animrig::Strip::Type strip_type = animrig::Strip::Type(type); + + animrig::Layer &layer = dna_layer->wrap(); + + if (layer.strips().size() >= 1) { + /* Not allowed to have more than one strip, for now. This limitation is in + * place until working with layers is fleshed out better. */ + BKE_report(reports, RPT_ERROR, "A layer may not have more than one strip"); + return nullptr; + } + + animrig::Strip &strip = layer.strip_add(strip_type); + + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); + return &strip; +} + +void rna_ActionStrips_remove(ID *animation_id, + ActionLayer *dna_layer, + bContext *C, + ReportList *reports, + ActionStrip *dna_strip) +{ + animrig::Layer &layer = dna_layer->wrap(); + animrig::Strip &strip = dna_strip->wrap(); + if (!layer.strip_remove(strip)) { + BKE_report(reports, RPT_ERROR, "This strip does not belong to this layer"); + return; + } + + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); + DEG_id_tag_update(animation_id, ID_RECALC_ANIMATION); +} + +static StructRNA *rna_ActionStrip_refine(PointerRNA *ptr) +{ + animrig::Strip &strip = rna_data_strip(ptr); + switch (strip.type()) { + case animrig::Strip::Type::Keyframe: + return &RNA_KeyframeActionStrip; + } + return &RNA_UnknownType; +} + +static std::optional rna_ActionStrip_path(const PointerRNA *ptr) +{ + animrig::Action &anim = rna_action(ptr); + animrig::Strip &strip_to_find = rna_data_strip(ptr); + + for (animrig::Layer *layer : anim.layers()) { + Span strips = layer->strips(); + const int index = strips.first_index_try(&strip_to_find); + if (index < 0) { + continue; + } + + PointerRNA layer_ptr = RNA_pointer_create(&anim.id, &RNA_ActionLayer, layer); + const std::optional layer_path = rna_ActionLayer_path(&layer_ptr); + BLI_assert_msg(layer_path, "Every animation layer should have a valid RNA path."); + const std::string strip_path = fmt::format("{}.strips[{}]", *layer_path, index); + return strip_path; + } + + return std::nullopt; +} + +static void rna_iterator_keyframestrip_channelbags_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ + animrig::KeyframeStrip &key_strip = rna_data_keyframe_strip(ptr); + rna_iterator_array_begin(iter, key_strip.channelbags()); +} + +static int rna_iterator_keyframestrip_channelbags_length(PointerRNA *ptr) +{ + animrig::KeyframeStrip &key_strip = rna_data_keyframe_strip(ptr); + return key_strip.channelbags().size(); +} + +static bool rna_KeyframeActionStrip_key_insert(ID *id, + KeyframeActionStrip *dna_strip, + Main *bmain, + ReportList *reports, + ActionBinding *dna_binding, + 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"); + return false; + } + + animrig::KeyframeStrip &key_strip = dna_strip->wrap(); + const animrig::Binding &binding = dna_binding->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); + + const bool ok = result == animrig::SingleKeyingResult::SUCCESS; + if (ok) { + DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION); + } + + return ok; +} + +static void rna_iterator_ChannelBag_fcurves_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ + animrig::ChannelBag &bag = rna_data_channelbag(ptr); + rna_iterator_array_begin(iter, bag.fcurves()); +} + +static int rna_iterator_ChannelBag_fcurves_length(PointerRNA *ptr) +{ + animrig::ChannelBag &bag = rna_data_channelbag(ptr); + return bag.fcurves().size(); +} + +static ActionChannelBag *rna_KeyframeActionStrip_channels( + KeyframeActionStrip *self, const animrig::binding_handle_t binding_handle) +{ + animrig::KeyframeStrip &key_strip = self->wrap(); + return key_strip.channelbag_for_binding(binding_handle); +} + +# endif // WITH_ANIM_BAKLAVA + static void rna_ActionGroup_channels_next(CollectionPropertyIterator *iter) { ListBaseIterator *internal = &iter->internal.listbase; @@ -244,6 +652,14 @@ static void rna_Action_active_pose_marker_index_range( *max = max_ii(0, BLI_listbase_count(&act->markers) - 1); } +# ifdef WITH_ANIM_BAKLAVA +static bool rna_Action_is_empty_get(PointerRNA *ptr) +{ + animrig::Action &action = rna_action(ptr); + return action.is_empty(); +} +# endif // WITH_ANIM_BAKLAVA + static void rna_Action_frame_range_get(PointerRNA *ptr, float *r_values) { BKE_action_frame_range_get((bAction *)ptr->owner_id, &r_values[0], &r_values[1]); @@ -690,6 +1106,386 @@ static void rna_def_dopesheet(BlenderRNA *brna) RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, nullptr); } +/* =========================== Layered Action interface =========================== */ + +# ifdef WITH_ANIM_BAKLAVA + +static void rna_def_action_bindings(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_struct_sdna(srna, "bAction"); + RNA_def_struct_ui_text(srna, "Action Bindings", "Collection of animation bindings"); + + /* 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"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + 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 " + "data-blocks of its type. If ommitted, limiting the ID type will happen as soon as the " + "binding 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"); + RNA_def_function_return(func, parm); +} + +static void rna_def_action_layers(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "ActionLayers"); + srna = RNA_def_struct(brna, "ActionLayers", nullptr); + RNA_def_struct_sdna(srna, "bAction"); + RNA_def_struct_ui_text(srna, "Action Layers", "Collection of animation layers"); + + /* Animation.layers.new(...) */ + func = RNA_def_function(srna, "new", "rna_Action_layers_new"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description( + func, + "Add a layer to the Animation. Currently an Animation can only have at most one layer"); + parm = RNA_def_string(func, + "name", + nullptr, + sizeof(ActionLayer::name) - 1, + "Name", + "Name of the layer, will be made unique within the Animation data-block"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + parm = RNA_def_pointer(func, "layer", "ActionLayer", "", "Newly created animation layer"); + RNA_def_function_return(func, parm); + + /* Animation.layers.remove(layer) */ + func = RNA_def_function(srna, "remove", "rna_Action_layers_remove"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Remove the layer from the animation"); + parm = RNA_def_pointer( + func, "anim_layer", "ActionLayer", "Animation Layer", "The layer to remove"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); +} + +static void rna_def_action_binding(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ActionBinding", nullptr); + RNA_def_struct_path_func(srna, "rna_ActionBinding_path"); + RNA_def_struct_ui_text( + srna, + "Animation Binding", + "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_struct_ui_text( + srna, + "Binding Name", + "Used when connecting an Animation to a data-block, to find the correct binding 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_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 " + "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" + "This is used, for example, on a KeyframeActionStrip to look up the " + "ActionChannelBag for this Binding"); +} + +static void rna_def_ActionLayer_strips(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "ActionStrips"); + srna = RNA_def_struct(brna, "ActionStrips", nullptr); + RNA_def_struct_sdna(srna, "ActionLayer"); + RNA_def_struct_ui_text(srna, "Action Strips", "Collection of animation strips"); + + /* Layer.strips.new(type='...') */ + func = RNA_def_function(srna, "new", "rna_ActionStrips_new"); + RNA_def_function_ui_description(func, + "Add a new strip to the layer. Currently a layer can only have " + "one strip, with infinite boundaries"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + parm = RNA_def_enum(func, + "type", + rna_enum_strip_type_items, + int(animrig::Strip::Type::Keyframe), + "Type", + "The type of strip to create"); + /* Return value. */ + parm = RNA_def_pointer(func, "strip", "ActionStrip", "", "Newly created animation strip"); + RNA_def_function_return(func, parm); + + /* Layer.strips.remove(strip) */ + func = RNA_def_function(srna, "remove", "rna_ActionStrips_remove"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Remove the strip from the animation layer"); + parm = RNA_def_pointer( + func, "anim_strip", "ActionStrip", "Animation Strip", "The strip to remove"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); +} + +static void rna_def_action_layer(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ActionLayer", nullptr); + RNA_def_struct_ui_text(srna, "Action Layer", ""); + RNA_def_struct_path_func(srna, "rna_ActionLayer_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text( + prop, "Influence", "How much of this layer is used when blending into the lower layers"); + RNA_def_property_ui_range(prop, 0.0, 1.0, 3, 2); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_Action_tag_animupdate"); + + prop = RNA_def_property(srna, "mix_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "layer_mix_mode"); + RNA_def_property_ui_text( + prop, "Mix Mode", "How animation of this layer is blended into the lower layers"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_enum_items(prop, rna_enum_layer_mix_mode_items); + RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_Action_tag_animupdate"); + + /* Collection properties .*/ + prop = RNA_def_property(srna, "strips", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ActionStrip"); + RNA_def_property_collection_funcs(prop, + "rna_iterator_ActionLayer_strips_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_dereference_get", + "rna_iterator_ActionLayer_strips_length", + nullptr, + nullptr, + nullptr); + RNA_def_property_ui_text(prop, "Strips", "The list of strips that are on this animation layer"); + + rna_def_ActionLayer_strips(brna, prop); +} + +static void rna_def_keyframestrip_channelbags(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + RNA_def_property_srna(cprop, "ActionChannelBags"); + srna = RNA_def_struct(brna, "ActionChannelBags", nullptr); + 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"); +} + +static void rna_def_action_keyframe_strip(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + 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"); + + prop = RNA_def_property(srna, "channelbags", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ActionChannelBag"); + RNA_def_property_collection_funcs(prop, + "rna_iterator_keyframestrip_channelbags_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_dereference_get", + "rna_iterator_keyframestrip_channelbags_length", + nullptr, + nullptr, + nullptr); + rna_def_keyframestrip_channelbags(brna, prop); + + { + FunctionRNA *func; + PropertyRNA *parm; + + /* KeyframeStrip.channels(...). */ + func = RNA_def_function(srna, "channels", "rna_KeyframeActionStrip_channels"); + RNA_def_function_ui_description(func, "Find the ActionChannelBag for a specific Binding"); + parm = RNA_def_int(func, + "binding_handle", + 0, + 0, + INT_MAX, + "Binding Handle", + "Number that identifies a specific animation binding", + 0, + INT_MAX); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + parm = RNA_def_pointer(func, "channels", "ActionChannelBag", "Channels", ""); + RNA_def_function_return(func, parm); + + /* KeyframeStrip.key_insert(...). */ + + 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"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + parm = RNA_def_int( + func, + "array_index", + -1, + -INT_MAX, + INT_MAX, + "Array Index", + "Index of the animated array element, or -1 if the property is not an array", + -1, + 4); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + parm = RNA_def_float(func, + "value", + 0.0, + -FLT_MAX, + FLT_MAX, + "Value to key", + "Value of the animated property", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + parm = RNA_def_float(func, + "time", + 0.0, + -FLT_MAX, + FLT_MAX, + "Time of the key", + "Time, in frames, of the key", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + parm = RNA_def_boolean( + func, "success", true, "Success", "Whether the key was successfully inserted"); + + RNA_def_function_return(func, parm); + } +} + +static void rna_def_action_strip(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ActionStrip", nullptr); + RNA_def_struct_ui_text(srna, "Action Strip", ""); + RNA_def_struct_path_func(srna, "rna_ActionStrip_path"); + RNA_def_struct_refine_func(srna, "rna_ActionStrip_refine"); + + static const EnumPropertyItem prop_type_items[] = { + {int(animrig::Strip::Type::Keyframe), + "KEYFRAME", + 0, + "Keyframe", + "Strip with a set of F-Curves for each animation binding"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "strip_type"); + RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + /* Define Strip subclasses. */ + rna_def_action_keyframe_strip(brna); +} + +static void rna_def_channelbag_for_binding_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"); +} + +static void rna_def_action_channelbag(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ActionChannelBag", nullptr); + RNA_def_struct_ui_text( + srna, + "Animation Channel Bag", + "Collection of animation channels, typically associated with an animation binding"); + + prop = RNA_def_property(srna, "binding_handle", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_iterator_ChannelBag_fcurves_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_dereference_get", + "rna_iterator_ChannelBag_fcurves_length", + nullptr, + 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); +} +# endif // WITH_ANIM_BAKLAVA + +/* =========================== Legacy Action interface =========================== */ + static void rna_def_action_group(BlenderRNA *brna) { StructRNA *srna; @@ -895,16 +1691,12 @@ static void rna_def_action_pose_markers(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_ui_text(prop, "Active Pose Marker Index", "Index of active pose marker"); } -static void rna_def_action(BlenderRNA *brna) +/* Access to 'legacy' Action features, like the top-level F-Curves, the corresponding F-Curve + * groups, and the top-level id_root. */ +static void rna_def_action_legacy(BlenderRNA *brna, StructRNA *srna) { - StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "Action", "ID"); - RNA_def_struct_sdna(srna, "bAction"); - RNA_def_struct_ui_text(srna, "Action", "A collection of F-Curves for animation"); - RNA_def_struct_ui_icon(srna, ICON_ACTION); - /* collections */ prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "curves", nullptr); @@ -918,6 +1710,72 @@ static void rna_def_action(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Groups", "Convenient groupings of F-Curves"); rna_def_action_groups(brna, prop); + /* special "type" limiter - should not really be edited in general, + * but is still available/editable in 'emergencies' */ + prop = RNA_def_property(srna, "id_root", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "idroot"); + RNA_def_property_enum_items(prop, rna_enum_id_type_items); + RNA_def_property_ui_text(prop, + "ID Root Type", + "Type of ID block that action can be used on - " + "DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); +} + +static void rna_def_action(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Action", "ID"); + RNA_def_struct_sdna(srna, "bAction"); + RNA_def_struct_ui_text(srna, "Action", "A collection of F-Curves for animation"); + RNA_def_struct_ui_icon(srna, ICON_ACTION); + +# ifdef WITH_ANIM_BAKLAVA + /* Properties. */ + prop = RNA_def_property(srna, "last_binding_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"); + RNA_def_property_boolean_funcs(prop, "rna_Action_is_empty_get", nullptr); +# endif // WITH_ANIM_BAKLAVA + + /* Collection properties. */ + +# ifdef WITH_ANIM_BAKLAVA + prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ActionBinding"); + RNA_def_property_collection_funcs(prop, + "rna_iterator_animation_bindings_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_dereference_get", + "rna_iterator_animation_bindings_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); + + prop = RNA_def_property(srna, "layers", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ActionLayer"); + RNA_def_property_collection_funcs(prop, + "rna_iterator_action_layers_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_dereference_get", + "rna_iterator_action_layers_length", + nullptr, + nullptr, + nullptr); + RNA_def_property_ui_text(prop, "Layers", "The list of layers that make up this Animation"); + rna_def_action_layers(brna, prop); +# endif // WITH_ANIM_BAKLAVA + prop = RNA_def_property(srna, "pose_markers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "markers", nullptr); RNA_def_property_struct_type(prop, "TimelineMarker"); @@ -998,16 +1856,7 @@ static void rna_def_action(BlenderRNA *brna) RNA_def_property_float_funcs(prop, "rna_Action_curve_frame_range_get", nullptr, nullptr); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - /* special "type" limiter - should not really be edited in general, - * but is still available/editable in 'emergencies' */ - prop = RNA_def_property(srna, "id_root", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "idroot"); - RNA_def_property_enum_items(prop, rna_enum_id_type_items); - RNA_def_property_ui_text(prop, - "ID Root Type", - "Type of ID block that action can be used on - " - "DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); + rna_def_action_legacy(brna, srna); /* API calls */ RNA_api_action(srna); @@ -1020,6 +1869,13 @@ void RNA_def_action(BlenderRNA *brna) rna_def_action(brna); rna_def_action_group(brna); rna_def_dopesheet(brna); + +# ifdef WITH_ANIM_BAKLAVA + rna_def_action_binding(brna); + rna_def_action_layer(brna); + rna_def_action_strip(brna); + rna_def_action_channelbag(brna); +# endif } #endif diff --git a/source/blender/makesrna/intern/rna_animation.cc b/source/blender/makesrna/intern/rna_animation.cc index f910070002d..7063df9f992 100644 --- a/source/blender/makesrna/intern/rna_animation.cc +++ b/source/blender/makesrna/intern/rna_animation.cc @@ -32,7 +32,7 @@ #include "ED_keyframing.hh" -#include "ANIM_animation.hh" +#include "ANIM_action.hh" /* exported for use in API */ const EnumPropertyItem rna_enum_keyingset_path_grouping_items[] = { @@ -103,8 +103,9 @@ const EnumPropertyItem rna_enum_keying_flag_api_items[] = { {0, nullptr, 0, nullptr, nullptr}, }; +#ifdef WITH_ANIM_BAKLAVA constexpr int binding_items_value_create_new = -1; -const EnumPropertyItem rna_enum_animation_binding_items[] = { +const EnumPropertyItem rna_enum_action_binding_items[] = { {binding_items_value_create_new, "NEW", ICON_ADD, @@ -113,6 +114,7 @@ const EnumPropertyItem rna_enum_animation_binding_items[] = { {int(blender::animrig::Binding::unassigned), "UNASSIGNED", 0, "(none/legacy)", ""}, {0, nullptr, 0, nullptr, nullptr}, }; +#endif // WITH_ANIM_BAKLAVA #ifdef RNA_RUNTIME @@ -158,10 +160,23 @@ static int rna_AnimData_action_editable(const PointerRNA *ptr, const char ** /*r static void rna_AnimData_action_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/) { - ID *ownerId = ptr->owner_id; +# ifdef WITH_ANIM_BAKLAVA + BLI_assert(ptr->owner_id); + ID &animated_id = *ptr->owner_id; - /* set action */ + /* TODO: protect against altering action in NLA tweak mode, see BKE_animdata_action_editable() */ + + bAction *action = static_cast(value.data); + if (!action) { + blender::animrig::unassign_animation(animated_id); + return; + } + + blender::animrig::assign_animation(action->wrap(), animated_id); +# else + ID *ownerId = ptr->owner_id; BKE_animdata_set_action(nullptr, ownerId, static_cast(value.data)); +# endif } static void rna_AnimData_tmpact_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/) @@ -216,21 +231,7 @@ bool rna_AnimData_tweakmode_override_apply(Main * /*bmain*/, } # ifdef WITH_ANIM_BAKLAVA -static void rna_AnimData_animation_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/) -{ - BLI_assert(ptr->owner_id); - ID &animated_id = *ptr->owner_id; - - Animation *anim = static_cast(value.data); - if (!anim) { - blender::animrig::unassign_animation(animated_id); - return; - } - - blender::animrig::assign_animation(anim->wrap(), animated_id); -} - -static void rna_AnimData_animation_binding_handle_set( +static void rna_AnimData_action_binding_handle_set( PointerRNA *ptr, const blender::animrig::binding_handle_t new_binding_handle) { BLI_assert(ptr->owner_id); @@ -252,7 +253,7 @@ static void rna_AnimData_animation_binding_handle_set( return; } - blender::animrig::Animation *anim = blender::animrig::get_animation(animated_id); + 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 * will be completely ignored when re-assigning an Animation, better to @@ -287,15 +288,15 @@ static AnimData &rna_animdata(const PointerRNA *ptr) return *reinterpret_cast(ptr->data); } -static int rna_AnimData_animation_binding_get(PointerRNA *ptr) +static int rna_AnimData_action_binding_get(PointerRNA *ptr) { AnimData &adt = rna_animdata(ptr); return adt.binding_handle; } -static void rna_AnimData_animation_binding_set(PointerRNA *ptr, int value) +static void rna_AnimData_action_binding_set(PointerRNA *ptr, int value) { - using blender::animrig::Animation; + using blender::animrig::Action; using blender::animrig::Binding; using blender::animrig::binding_handle_t; @@ -309,19 +310,22 @@ static void rna_AnimData_animation_binding_set(PointerRNA *ptr, int value) return; } - if (!adt.animation) { - /* No animation to verify the binding handle is valid. As the binding handle + if (!adt.action) { + /* No Action to verify the binding handle is valid. As the binding 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 Action, cannot set binding handle", animated_id.name + 2); return; } - Animation &anim = adt.animation->wrap(); + Action &anim = adt.action->wrap(); Binding *binding = nullptr; + /* TODO: handle legacy Action. */ + BLI_assert(anim.is_action_layered()); + if (new_binding_handle == binding_items_value_create_new) { /* Special case for this enum item. */ binding = &anim.binding_add_for_id(animated_id); @@ -350,27 +354,27 @@ static void rna_AnimData_animation_binding_set(PointerRNA *ptr, int value) WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN, nullptr); } -static const EnumPropertyItem *rna_AnimData_animation_binding_itemf(bContext * /*C*/, - PointerRNA *ptr, - PropertyRNA * /*prop*/, - bool *r_free) +static const EnumPropertyItem *rna_AnimData_action_binding_itemf(bContext * /*C*/, + PointerRNA *ptr, + PropertyRNA * /*prop*/, + bool *r_free) { - using blender::animrig::Animation; + using blender::animrig::Action; using blender::animrig::Binding; AnimData &adt = rna_animdata(ptr); - if (!adt.animation) { + if (!adt.action) { // TODO: handle properly. *r_free = false; - return rna_enum_animation_binding_items; + return rna_enum_action_binding_items; } - const Animation &anim = adt.animation->wrap(); - EnumPropertyItem item = {0}; EnumPropertyItem *items = nullptr; int num_items = 0; + const Action &anim = adt.action->wrap(); + bool found_assigned_binding = false; for (const Binding *binding : anim.bindings()) { item.value = binding->handle; @@ -387,14 +391,16 @@ static const EnumPropertyItem *rna_AnimData_animation_binding_itemf(bContext * / RNA_enum_item_add_separator(&items, &num_items); } - /* Only add the 'New' option. Unassigning should be done with the 'X' button. */ - BLI_assert(rna_enum_animation_binding_items[0].value == binding_items_value_create_new); - RNA_enum_item_add(&items, &num_items, &rna_enum_animation_binding_items[0]); + /* Only add the 'New' option when this is a Layered Action. */ + if (anim.is_action_layered()) { + BLI_assert(rna_enum_action_binding_items[0].value == binding_items_value_create_new); + RNA_enum_item_add(&items, &num_items, &rna_enum_action_binding_items[0]); + } if (!found_assigned_binding) { /* The assigned binding was not found, so show an option that reflects that. */ - BLI_assert(rna_enum_animation_binding_items[1].value == Binding::unassigned); - RNA_enum_item_add(&items, &num_items, &rna_enum_animation_binding_items[1]); + BLI_assert(rna_enum_action_binding_items[1].value == Binding::unassigned); + RNA_enum_item_add(&items, &num_items, &rna_enum_action_binding_items[1]); } RNA_enum_item_end(&items, &num_items); @@ -1666,43 +1672,35 @@ static void rna_def_animdata(BlenderRNA *brna) RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, nullptr); # ifdef WITH_ANIM_BAKLAVA - /* Animation data-block */ - prop = RNA_def_property(srna, "animation", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "Animation"); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_pointer_funcs(prop, nullptr, "rna_AnimData_animation_set", nullptr, nullptr); - RNA_def_property_ui_text(prop, "Animation", "Active Animation for this data-block"); - RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_AnimData_dependency_update"); - - prop = RNA_def_property(srna, "animation_binding_handle", PROP_INT, PROP_NONE); + 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_animation_binding_handle_set", nullptr); + RNA_def_property_int_funcs(prop, nullptr, "rna_AnimData_action_binding_handle_set", nullptr); RNA_def_property_ui_text(prop, - "Animation Binding Handle", - "A number that identifies which sub-set of the Animation is considered " + "Action Binding 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, "animation_binding_name", PROP_STRING, PROP_NONE); + prop = RNA_def_property(srna, "action_binding_name", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, nullptr, "binding_name"); RNA_def_property_ui_text( prop, - "Animation Binding Name", - "The name of the animation binding. The binding identifies which sub-set of the Animation " + "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 " - "when assigning an Animation"); + "when assigning an Action"); - prop = RNA_def_property(srna, "animation_binding", PROP_ENUM, PROP_NONE); + prop = RNA_def_property(srna, "action_binding", PROP_ENUM, PROP_NONE); RNA_def_property_enum_funcs(prop, - "rna_AnimData_animation_binding_get", - "rna_AnimData_animation_binding_set", - "rna_AnimData_animation_binding_itemf"); - RNA_def_property_enum_items(prop, rna_enum_animation_binding_items); + "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_def_property_ui_text( prop, - "Animation Binding", - "The binding identifies which sub-set of the Animation is considered to be for this " - "data-block, and its name is used to find the right binding when assigning an Animation"); + "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"); # endif diff --git a/source/blender/makesrna/intern/rna_animation_id.cc b/source/blender/makesrna/intern/rna_animation_id.cc deleted file mode 100644 index 24368e66978..00000000000 --- a/source/blender/makesrna/intern/rna_animation_id.cc +++ /dev/null @@ -1,862 +0,0 @@ -/* SPDX-FileCopyrightText: 2023 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup RNA - */ - -#include -#include - -#include "DNA_anim_types.h" - -#include "ANIM_animation.hh" - -#include "BLI_utildefines.h" - -#include "BLT_translation.hh" - -#include "MEM_guardedalloc.h" - -#include "RNA_access.hh" -#include "RNA_define.hh" -#include "RNA_enum_types.hh" - -#include "rna_internal.hh" - -#include "WM_api.hh" -#include "WM_types.hh" - -using namespace blender; - -const EnumPropertyItem rna_enum_layer_mix_mode_items[] = { - {int(animrig::Layer::MixMode::Replace), - "REPLACE", - 0, - "Replace", - "Channels in this layer override the same channels from underlying layers"}, - {int(animrig::Layer::MixMode::Offset), - "OFFSET", - 0, - "Offset", - "Channels in this layer are added to underlying layers as sequential operations"}, - {int(animrig::Layer::MixMode::Add), - "ADD", - 0, - "Add", - "Channels in this layer are added to underlying layers on a per-channel basis"}, - {int(animrig::Layer::MixMode::Subtract), - "SUBTRACT", - 0, - "Subtract", - "Channels in this layer are subtracted to underlying layers on a per-channel basis"}, - {int(animrig::Layer::MixMode::Multiply), - "MULTIPLY", - 0, - "Multiply", - "Channels in this layer are multiplied with underlying layers on a per-channel basis"}, - {0, nullptr, 0, nullptr, nullptr}, -}; - -const EnumPropertyItem rna_enum_strip_type_items[] = { - {int(animrig::Strip::Type::Keyframe), - "KEYFRAME", - 0, - "Keyframe", - "Strip containing keyframes on F-Curves"}, - {0, nullptr, 0, nullptr, nullptr}, -}; - -#ifdef RNA_RUNTIME - -# include "ANIM_animation.hh" - -# include "DEG_depsgraph.hh" - -# include - -static animrig::Animation &rna_animation(const PointerRNA *ptr) -{ - return reinterpret_cast(ptr->owner_id)->wrap(); -} - -static animrig::Binding &rna_data_binding(const PointerRNA *ptr) -{ - return reinterpret_cast(ptr->data)->wrap(); -} - -static animrig::Layer &rna_data_layer(const PointerRNA *ptr) -{ - return reinterpret_cast(ptr->data)->wrap(); -} - -static animrig::Strip &rna_data_strip(const PointerRNA *ptr) -{ - return reinterpret_cast(ptr->data)->wrap(); -} - -static void rna_Animation_tag_animupdate(Main *, Scene *, PointerRNA *ptr) -{ - animrig::Animation &anim = rna_animation(ptr); - DEG_id_tag_update(&anim.id, ID_RECALC_ANIMATION); -} - -static animrig::KeyframeStrip &rna_data_keyframe_strip(const PointerRNA *ptr) -{ - animrig::Strip &strip = reinterpret_cast(ptr->data)->wrap(); - return strip.as(); -} - -static animrig::ChannelBag &rna_data_channelbag(const PointerRNA *ptr) -{ - return reinterpret_cast(ptr->data)->wrap(); -} - -template -static void rna_iterator_array_begin(CollectionPropertyIterator *iter, Span items) -{ - rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr); -} - -template -static void rna_iterator_array_begin(CollectionPropertyIterator *iter, MutableSpan items) -{ - rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr); -} - -static AnimationBinding *rna_Animation_bindings_new(Animation *anim_id, - bContext *C, - ID *id_for_binding) -{ - animrig::Animation &anim = anim_id->wrap(); - animrig::Binding *binding; - - if (id_for_binding) { - binding = &anim.binding_add_for_id(*id_for_binding); - } - else { - binding = &anim.binding_add(); - } - - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); - return binding; -} - -static void rna_iterator_animation_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - animrig::Animation &anim = rna_animation(ptr); - rna_iterator_array_begin(iter, anim.layers()); -} - -static int rna_iterator_animation_layers_length(PointerRNA *ptr) -{ - animrig::Animation &anim = rna_animation(ptr); - return anim.layers().size(); -} - -static AnimationLayer *rna_Animation_layers_new(Animation *dna_animation, - bContext *C, - ReportList *reports, - const char *name) -{ - animrig::Animation &anim = dna_animation->wrap(); - - if (anim.layers().size() >= 1) { - /* Not allowed to have more than one layer, for now. This limitation is in - * place until working with multiple animated IDs is fleshed out better. */ - BKE_report(reports, RPT_ERROR, "An Animation may not have more than one layer"); - return nullptr; - } - - animrig::Layer &layer = anim.layer_add(name); - - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); - return &layer; -} - -void rna_Animation_layers_remove(Animation *dna_animation, - bContext *C, - ReportList *reports, - AnimationLayer *dna_layer) -{ - animrig::Animation &anim = dna_animation->wrap(); - animrig::Layer &layer = dna_layer->wrap(); - if (!anim.layer_remove(layer)) { - BKE_report(reports, RPT_ERROR, "This layer does not belong to this animation"); - return; - } - - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); - DEG_id_tag_update(&anim.id, ID_RECALC_ANIMATION); -} - -static void rna_iterator_animation_bindings_begin(CollectionPropertyIterator *iter, - PointerRNA *ptr) -{ - animrig::Animation &anim = rna_animation(ptr); - rna_iterator_array_begin(iter, anim.bindings()); -} - -static int rna_iterator_animation_bindings_length(PointerRNA *ptr) -{ - animrig::Animation &anim = rna_animation(ptr); - return anim.bindings().size(); -} - -static std::optional rna_AnimationBinding_path(const PointerRNA *ptr) -{ - animrig::Binding &binding = rna_data_binding(ptr); - - char name_esc[sizeof(binding.name) * 2]; - BLI_str_escape(name_esc, binding.name, sizeof(name_esc)); - return fmt::format("bindings[\"{}\"]", name_esc); -} - -/* Name functions that ignore the first two ID characters */ -void rna_AnimationBinding_name_display_get(PointerRNA *ptr, char *value) -{ - animrig::Binding &binding = rna_data_binding(ptr); - binding.name_without_prefix().unsafe_copy(value); -} - -int rna_AnimationBinding_name_display_length(PointerRNA *ptr) -{ - animrig::Binding &binding = rna_data_binding(ptr); - return binding.name_without_prefix().size(); -} - -static void rna_AnimationBinding_name_display_set(PointerRNA *ptr, const char *name) -{ - animrig::Animation &anim = rna_animation(ptr); - animrig::Binding &binding = rna_data_binding(ptr); - const StringRef name_ref(name); - - if (name_ref.is_empty()) { - WM_report(RPT_ERROR, "Animation binding 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); -} - -static void rna_AnimationBinding_name_set(PointerRNA *ptr, const char *name) -{ - animrig::Animation &anim = rna_animation(ptr); - animrig::Binding &binding = rna_data_binding(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"); - return; - } - - if (binding.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(); - - 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", - new_prefix.c_str(), - expect_prefix.c_str()); - } - } - - anim.binding_name_define(binding, name); -} - -static void rna_AnimationBinding_name_update(Main *bmain, Scene *, PointerRNA *ptr) -{ - animrig::Animation &anim = rna_animation(ptr); - animrig::Binding &binding = rna_data_binding(ptr); - anim.binding_name_propagate(*bmain, binding); -} - -static std::optional rna_AnimationLayer_path(const PointerRNA *ptr) -{ - animrig::Layer &layer = rna_data_layer(ptr); - - char name_esc[sizeof(layer.name) * 2]; - BLI_str_escape(name_esc, layer.name, sizeof(name_esc)); - return fmt::format("layers[\"{}\"]", name_esc); -} - -static void rna_iterator_animationlayer_strips_begin(CollectionPropertyIterator *iter, - PointerRNA *ptr) -{ - animrig::Layer &layer = rna_data_layer(ptr); - rna_iterator_array_begin(iter, layer.strips()); -} - -static int rna_iterator_animationlayer_strips_length(PointerRNA *ptr) -{ - animrig::Layer &layer = rna_data_layer(ptr); - return layer.strips().size(); -} - -AnimationStrip *rna_AnimationStrips_new(AnimationLayer *dna_layer, - bContext *C, - ReportList *reports, - const int type) -{ - const animrig::Strip::Type strip_type = animrig::Strip::Type(type); - - animrig::Layer &layer = dna_layer->wrap(); - - if (layer.strips().size() >= 1) { - /* Not allowed to have more than one strip, for now. This limitation is in - * place until working with layers is fleshed out better. */ - BKE_report(reports, RPT_ERROR, "A layer may not have more than one strip"); - return nullptr; - } - - animrig::Strip &strip = layer.strip_add(strip_type); - - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); - return &strip; -} - -void rna_AnimationStrips_remove(ID *animation_id, - AnimationLayer *dna_layer, - bContext *C, - ReportList *reports, - AnimationStrip *dna_strip) -{ - animrig::Layer &layer = dna_layer->wrap(); - animrig::Strip &strip = dna_strip->wrap(); - if (!layer.strip_remove(strip)) { - BKE_report(reports, RPT_ERROR, "This strip does not belong to this layer"); - return; - } - - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); - DEG_id_tag_update(animation_id, ID_RECALC_ANIMATION); -} - -static StructRNA *rna_AnimationStrip_refine(PointerRNA *ptr) -{ - animrig::Strip &strip = rna_data_strip(ptr); - switch (strip.type()) { - case animrig::Strip::Type::Keyframe: - return &RNA_KeyframeAnimationStrip; - } - return &RNA_UnknownType; -} - -static std::optional rna_AnimationStrip_path(const PointerRNA *ptr) -{ - animrig::Animation &anim = rna_animation(ptr); - animrig::Strip &strip_to_find = rna_data_strip(ptr); - - for (animrig::Layer *layer : anim.layers()) { - Span strips = layer->strips(); - const int index = strips.first_index_try(&strip_to_find); - if (index < 0) { - continue; - } - - PointerRNA layer_ptr = RNA_pointer_create(&anim.id, &RNA_AnimationLayer, layer); - const std::optional layer_path = rna_AnimationLayer_path(&layer_ptr); - BLI_assert_msg(layer_path, "Every animation layer should have a valid RNA path."); - const std::string strip_path = fmt::format("{}.strips[{}]", *layer_path, index); - return strip_path; - } - - return std::nullopt; -} - -static void rna_iterator_keyframestrip_channelbags_begin(CollectionPropertyIterator *iter, - PointerRNA *ptr) -{ - animrig::KeyframeStrip &key_strip = rna_data_keyframe_strip(ptr); - rna_iterator_array_begin(iter, key_strip.channelbags()); -} - -static int rna_iterator_keyframestrip_channelbags_length(PointerRNA *ptr) -{ - animrig::KeyframeStrip &key_strip = rna_data_keyframe_strip(ptr); - return key_strip.channelbags().size(); -} - -static bool rna_KeyframeAnimationStrip_key_insert(ID *id, - KeyframeAnimationStrip *dna_strip, - Main *bmain, - ReportList *reports, - AnimationBinding *dna_binding, - 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"); - return false; - } - - animrig::KeyframeStrip &key_strip = dna_strip->wrap(); - const animrig::Binding &binding = dna_binding->wrap(); - const animrig::KeyframeSettings settings = animrig::get_keyframe_settings(true); - - animrig::SingleKeyingResult result = key_strip.keyframe_insert( - binding, rna_path, array_index, {time, value}, settings); - - if (result == animrig::SingleKeyingResult::SUCCESS) { - DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION); - } - - return result == animrig::SingleKeyingResult::SUCCESS; -} - -static void rna_iterator_ChannelBag_fcurves_begin(CollectionPropertyIterator *iter, - PointerRNA *ptr) -{ - animrig::ChannelBag &bag = rna_data_channelbag(ptr); - rna_iterator_array_begin(iter, bag.fcurves()); -} - -static int rna_iterator_ChannelBag_fcurves_length(PointerRNA *ptr) -{ - animrig::ChannelBag &bag = rna_data_channelbag(ptr); - return bag.fcurves().size(); -} - -static AnimationChannelBag *rna_KeyframeAnimationStrip_channels( - KeyframeAnimationStrip *self, const animrig::binding_handle_t binding_handle) -{ - animrig::KeyframeStrip &key_strip = self->wrap(); - return key_strip.channelbag_for_binding(binding_handle); -} - -#else - -static void rna_def_animation_bindings(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "AnimationBindings"); - srna = RNA_def_struct(brna, "AnimationBindings", nullptr); - RNA_def_struct_sdna(srna, "Animation"); - RNA_def_struct_ui_text(srna, "Animation Bindings", "Collection of animation bindings"); - - /* Animation.bindings.new(...) */ - func = RNA_def_function(srna, "new", "rna_Animation_bindings_new"); - RNA_def_function_ui_description(func, "Add a binding to the animation"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT); - 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 " - "data-blocks of its type. If ommitted, limiting the ID type will happen as soon as the " - "binding 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", "AnimationBinding", "", "Newly created animation binding"); - RNA_def_function_return(func, parm); -} - -static void rna_def_animation_layers(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "AnimationLayers"); - srna = RNA_def_struct(brna, "AnimationLayers", nullptr); - RNA_def_struct_sdna(srna, "Animation"); - RNA_def_struct_ui_text(srna, "Animation Layers", "Collection of animation layers"); - - /* Animation.layers.new(...) */ - func = RNA_def_function(srna, "new", "rna_Animation_layers_new"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); - RNA_def_function_ui_description( - func, - "Add a layer to the Animation. Currently an Animation can only have at most one layer"); - parm = RNA_def_string(func, - "name", - nullptr, - sizeof(AnimationLayer::name) - 1, - "Name", - "Name of the layer, will be made unique within the Animation data-block"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_pointer(func, "layer", "AnimationLayer", "", "Newly created animation layer"); - RNA_def_function_return(func, parm); - - /* Animation.layers.remove(layer) */ - func = RNA_def_function(srna, "remove", "rna_Animation_layers_remove"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); - RNA_def_function_ui_description(func, "Remove the layer from the animation"); - parm = RNA_def_pointer( - func, "anim_layer", "AnimationLayer", "Animation Layer", "The layer to remove"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); -} - -static void rna_def_animation(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "Animation", "ID"); - RNA_def_struct_sdna(srna, "Animation"); - RNA_def_struct_ui_text(srna, "Animation", "A collection of animation layers"); - RNA_def_struct_ui_icon(srna, ICON_ACTION); - - prop = RNA_def_property(srna, "last_binding_handle", PROP_INT, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - /* Collection properties .*/ - prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); - RNA_def_property_struct_type(prop, "AnimationBinding"); - RNA_def_property_collection_funcs(prop, - "rna_iterator_animation_bindings_begin", - "rna_iterator_array_next", - "rna_iterator_array_end", - "rna_iterator_array_dereference_get", - "rna_iterator_animation_bindings_length", - nullptr, - nullptr, - nullptr); - RNA_def_property_ui_text(prop, "Bindings", "The list of bindings in this animation data-block"); - rna_def_animation_bindings(brna, prop); - - prop = RNA_def_property(srna, "layers", PROP_COLLECTION, PROP_NONE); - RNA_def_property_struct_type(prop, "AnimationLayer"); - RNA_def_property_collection_funcs(prop, - "rna_iterator_animation_layers_begin", - "rna_iterator_array_next", - "rna_iterator_array_end", - "rna_iterator_array_dereference_get", - "rna_iterator_animation_layers_length", - nullptr, - nullptr, - nullptr); - RNA_def_property_ui_text(prop, "Layers", "The list of layers that make up this Animation"); - rna_def_animation_layers(brna, prop); -} - -static void rna_def_animation_binding(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "AnimationBinding", nullptr); - RNA_def_struct_path_func(srna, "rna_AnimationBinding_path"); - RNA_def_struct_ui_text( - srna, - "Animation Binding", - "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_AnimationBinding_name_set"); - RNA_def_property_string_maxlength(prop, sizeof(AnimationBinding::name) - 2); - RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_AnimationBinding_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"); - - prop = RNA_def_property(srna, "name_display", PROP_STRING, PROP_NONE); - RNA_def_property_string_funcs(prop, - "rna_AnimationBinding_name_display_get", - "rna_AnimationBinding_name_display_length", - "rna_AnimationBinding_name_display_set"); - RNA_def_property_string_maxlength(prop, sizeof(AnimationBinding::name) - 2); - RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_AnimationBinding_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 " - "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" - "This is used, for example, on a KeyframeAnimationStrip to look up the " - "AnimationChannelBag for this Binding"); -} - -static void rna_def_animationlayer_strips(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "AnimationStrips"); - srna = RNA_def_struct(brna, "AnimationStrips", nullptr); - RNA_def_struct_sdna(srna, "AnimationLayer"); - RNA_def_struct_ui_text(srna, "Animation Strips", "Collection of animation strips"); - - /* Layer.strips.new(type='...') */ - func = RNA_def_function(srna, "new", "rna_AnimationStrips_new"); - RNA_def_function_ui_description(func, - "Add a new strip to the layer. Currently a layer can only have " - "one strip, with infinite boundaries"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); - parm = RNA_def_enum(func, - "type", - rna_enum_strip_type_items, - int(animrig::Strip::Type::Keyframe), - "Type", - "The type of strip to create"); - /* Return value. */ - parm = RNA_def_pointer(func, "strip", "AnimationStrip", "", "Newly created animation strip"); - RNA_def_function_return(func, parm); - - /* Layer.strips.remove(strip) */ - func = RNA_def_function(srna, "remove", "rna_AnimationStrips_remove"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT | FUNC_USE_REPORTS); - RNA_def_function_ui_description(func, "Remove the strip from the animation layer"); - parm = RNA_def_pointer( - func, "anim_strip", "AnimationStrip", "Animation Strip", "The strip to remove"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); -} - -static void rna_def_animation_layer(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "AnimationLayer", nullptr); - RNA_def_struct_ui_text(srna, "Animation Layer", ""); - RNA_def_struct_path_func(srna, "rna_AnimationLayer_path"); - - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_struct_name_property(srna, prop); - - prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text( - prop, "Influence", "How much of this layer is used when blending into the lower layers"); - RNA_def_property_ui_range(prop, 0.0, 1.0, 3, 2); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_Animation_tag_animupdate"); - - prop = RNA_def_property(srna, "mix_mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "layer_mix_mode"); - RNA_def_property_ui_text( - prop, "Mix Mode", "How animation of this layer is blended into the lower layers"); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_enum_items(prop, rna_enum_layer_mix_mode_items); - RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_Animation_tag_animupdate"); - - /* Collection properties .*/ - prop = RNA_def_property(srna, "strips", PROP_COLLECTION, PROP_NONE); - RNA_def_property_struct_type(prop, "AnimationStrip"); - RNA_def_property_collection_funcs(prop, - "rna_iterator_animationlayer_strips_begin", - "rna_iterator_array_next", - "rna_iterator_array_end", - "rna_iterator_array_dereference_get", - "rna_iterator_animationlayer_strips_length", - nullptr, - nullptr, - nullptr); - RNA_def_property_ui_text(prop, "Strips", "The list of strips that are on this animation layer"); - - rna_def_animationlayer_strips(brna, prop); -} - -static void rna_def_keyframestrip_channelbags(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - - RNA_def_property_srna(cprop, "AnimationChannelBags"); - srna = RNA_def_struct(brna, "AnimationChannelBags", nullptr); - RNA_def_struct_sdna(srna, "KeyframeAnimationStrip"); - 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"); -} - -static void rna_def_animation_keyframe_strip(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "KeyframeAnimationStrip", "AnimationStrip"); - RNA_def_struct_ui_text( - srna, "Keyframe Animation Strip", "Strip with a set of F-Curves for each animation binding"); - - prop = RNA_def_property(srna, "channelbags", PROP_COLLECTION, PROP_NONE); - RNA_def_property_struct_type(prop, "AnimationChannelBag"); - RNA_def_property_collection_funcs(prop, - "rna_iterator_keyframestrip_channelbags_begin", - "rna_iterator_array_next", - "rna_iterator_array_end", - "rna_iterator_array_dereference_get", - "rna_iterator_keyframestrip_channelbags_length", - nullptr, - nullptr, - nullptr); - rna_def_keyframestrip_channelbags(brna, prop); - - { - FunctionRNA *func; - PropertyRNA *parm; - - /* KeyframeStrip.channels(...). */ - func = RNA_def_function(srna, "channels", "rna_KeyframeAnimationStrip_channels"); - RNA_def_function_ui_description(func, "Find the AnimationChannelBag for a specific Binding"); - parm = RNA_def_int(func, - "binding_handle", - 0, - 0, - INT_MAX, - "Binding Handle", - "Number that identifies a specific animation binding", - 0, - INT_MAX); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_pointer(func, "channels", "AnimationChannelBag", "Channels", ""); - RNA_def_function_return(func, parm); - - /* KeyframeStrip.key_insert(...). */ - - func = RNA_def_function(srna, "key_insert", "rna_KeyframeAnimationStrip_key_insert"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, - "binding", - "AnimationBinding", - "Binding", - "The binding 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"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - parm = RNA_def_int( - func, - "array_index", - -1, - -INT_MAX, - INT_MAX, - "Array Index", - "Index of the animated array element, or -1 if the property is not an array", - -1, - 4); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - parm = RNA_def_float(func, - "value", - 0.0, - -FLT_MAX, - FLT_MAX, - "Value to key", - "Value of the animated property", - -FLT_MAX, - FLT_MAX); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - parm = RNA_def_float(func, - "time", - 0.0, - -FLT_MAX, - FLT_MAX, - "Time of the key", - "Time, in frames, of the key", - -FLT_MAX, - FLT_MAX); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - parm = RNA_def_boolean( - func, "success", true, "Success", "Whether the key was successfully inserted"); - RNA_def_function_return(func, parm); - } -} - -static void rna_def_animation_strip(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "AnimationStrip", nullptr); - RNA_def_struct_ui_text(srna, "Animation Strip", ""); - RNA_def_struct_path_func(srna, "rna_AnimationStrip_path"); - RNA_def_struct_refine_func(srna, "rna_AnimationStrip_refine"); - - static const EnumPropertyItem prop_type_items[] = { - {int(animrig::Strip::Type::Keyframe), - "KEYFRAME", - 0, - "Keyframe", - "Strip with a set of F-Curves for each animation binding"}, - {0, nullptr, 0, nullptr, nullptr}, - }; - - prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "strip_type"); - RNA_def_property_enum_items(prop, prop_type_items); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - /* Define Strip subclasses. */ - rna_def_animation_keyframe_strip(brna); -} - -static void rna_def_channelbag_for_binding_fcurves(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - - RNA_def_property_srna(cprop, "AnimationChannelBagFCurves"); - srna = RNA_def_struct(brna, "AnimationChannelBagFCurves", nullptr); - RNA_def_struct_sdna(srna, "bAnimationChannelBag"); - RNA_def_struct_ui_text( - srna, "F-Curves", "Collection of F-Curves for a specific animation binding"); -} - -static void rna_def_animation_channelbag(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "AnimationChannelBag", nullptr); - RNA_def_struct_ui_text( - srna, - "Animation Channel Bag", - "Collection of animation channels, typically associated with an animation binding"); - - prop = RNA_def_property(srna, "binding_handle", PROP_INT, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_funcs(prop, - "rna_iterator_ChannelBag_fcurves_begin", - "rna_iterator_array_next", - "rna_iterator_array_end", - "rna_iterator_array_dereference_get", - "rna_iterator_ChannelBag_fcurves_length", - nullptr, - 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); -} - -void RNA_def_animation_id(BlenderRNA *brna) -{ - rna_def_animation(brna); - rna_def_animation_binding(brna); - rna_def_animation_layer(brna); - rna_def_animation_strip(brna); - rna_def_animation_channelbag(brna); -} - -#endif diff --git a/source/blender/makesrna/intern/rna_internal.hh b/source/blender/makesrna/intern/rna_internal.hh index a20b34e70ad..f67ffbf8917 100644 --- a/source/blender/makesrna/intern/rna_internal.hh +++ b/source/blender/makesrna/intern/rna_internal.hh @@ -139,9 +139,6 @@ extern BlenderRNA BLENDER_RNA; void RNA_def_ID(BlenderRNA *brna); void RNA_def_action(BlenderRNA *brna); void RNA_def_animation(BlenderRNA *brna); -#ifdef WITH_ANIM_BAKLAVA -void RNA_def_animation_id(BlenderRNA *brna); -#endif void RNA_def_animviz(BlenderRNA *brna); void RNA_def_armature(BlenderRNA *brna); void RNA_def_attribute(BlenderRNA *brna); @@ -489,9 +486,6 @@ void RNA_def_main_speakers(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_sounds(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_armatures(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop); -#ifdef WITH_ANIM_BAKLAVA -void RNA_def_main_animations(BlenderRNA *brna, PropertyRNA *cprop); -#endif void RNA_def_main_particles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_gpencil_legacy(BlenderRNA *brna, PropertyRNA *cprop); diff --git a/source/blender/makesrna/intern/rna_main.cc b/source/blender/makesrna/intern/rna_main.cc index d7e5d17375a..e02bc64bab1 100644 --- a/source/blender/makesrna/intern/rna_main.cc +++ b/source/blender/makesrna/intern/rna_main.cc @@ -90,9 +90,6 @@ static void rna_Main_filepath_set(PointerRNA *ptr, const char *value) } RNA_MAIN_LISTBASE_FUNCS_DEF(actions) -# ifdef WITH_ANIM_BAKLAVA -RNA_MAIN_LISTBASE_FUNCS_DEF(animations) -# endif RNA_MAIN_LISTBASE_FUNCS_DEF(armatures) RNA_MAIN_LISTBASE_FUNCS_DEF(brushes) RNA_MAIN_LISTBASE_FUNCS_DEF(cachefiles) @@ -322,14 +319,6 @@ void RNA_def_main(BlenderRNA *brna) "Actions", "Action data-blocks", RNA_def_main_actions}, -# ifdef WITH_ANIM_BAKLAVA - {"animations", - "Animation", - "rna_Main_animations_begin", - "animations", - "Animation data-blocks", - RNA_def_main_animations}, -# endif {"particles", "ParticleSettings", "rna_Main_particles_begin", diff --git a/source/blender/makesrna/intern/rna_main_api.cc b/source/blender/makesrna/intern/rna_main_api.cc index 37ea486d456..49332f08763 100644 --- a/source/blender/makesrna/intern/rna_main_api.cc +++ b/source/blender/makesrna/intern/rna_main_api.cc @@ -26,7 +26,7 @@ #ifdef RNA_RUNTIME # include "BKE_action.h" -# include "BKE_animation.hh" +# include "BKE_action.hh" # include "BKE_armature.hh" # include "BKE_brush.hh" # include "BKE_camera.h" @@ -637,22 +637,6 @@ static bAction *rna_Main_actions_new(Main *bmain, const char *name) return act; } -# ifdef WITH_ANIM_BAKLAVA -static Animation *rna_Main_animations_new(Main *bmain, const char *name) -{ - char safe_name[MAX_ID_NAME - 2]; - rna_idname_validate(name, safe_name); - - Animation *anim = BKE_animation_add(bmain, safe_name); - id_fake_user_clear(&anim->id); - id_us_min(&anim->id); - - WM_main_add_notifier(NC_ID | NA_ADDED, nullptr); - - return anim; -} -# endif - static ParticleSettings *rna_Main_particles_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -839,9 +823,6 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(speakers, speakers, ID_SPK) RNA_MAIN_ID_TAG_FUNCS_DEF(sounds, sounds, ID_SO) RNA_MAIN_ID_TAG_FUNCS_DEF(armatures, armatures, ID_AR) RNA_MAIN_ID_TAG_FUNCS_DEF(actions, actions, ID_AC) -# ifdef WITH_ANIM_BAKLAVA -RNA_MAIN_ID_TAG_FUNCS_DEF(animations, animations, ID_AN) -# endif RNA_MAIN_ID_TAG_FUNCS_DEF(particles, particles, ID_PA) RNA_MAIN_ID_TAG_FUNCS_DEF(palettes, palettes, ID_PAL) RNA_MAIN_ID_TAG_FUNCS_DEF(gpencils, gpencils, ID_GD_LEGACY) @@ -1902,49 +1883,7 @@ void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", false, "Value", ""); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); } -# ifdef WITH_ANIM_BAKLAVA -void RNA_def_main_animations(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - FunctionRNA *func; - PropertyRNA *parm; - RNA_def_property_srna(cprop, "BlendDataAnimations"); - srna = RNA_def_struct(brna, "BlendDataAnimations", nullptr); - RNA_def_struct_sdna(srna, "Main"); - RNA_def_struct_ui_text(srna, "Main Animations", "Collection of animation data-blocks"); - - func = RNA_def_function(srna, "new", "rna_Main_animations_new"); - RNA_def_function_ui_description(func, "Add a new animation data-block to the main database"); - parm = RNA_def_string(func, "name", "Animation", 0, "", "Name for the new data-block"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - /* return type */ - parm = RNA_def_pointer(func, "animation", "Animation", "", "New animation data-block"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_Main_ID_remove"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); - RNA_def_function_ui_description(func, - "Remove an animation data-block from the current blendfile"); - parm = RNA_def_pointer(func, "animation", "Animation", "", "Animation to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); - RNA_def_boolean( - func, "do_unlink", true, "", "Unlink all usages of this animation before deleting it"); - RNA_def_boolean(func, - "do_id_user", - true, - "", - "Decrement user counter of all datablocks used by this animation"); - RNA_def_boolean( - func, "do_ui_user", true, "", "Make sure interface does not reference this animation"); - - /* Defined via RNA_MAIN_LISTBASE_FUNCS_DEF. */ - func = RNA_def_function(srna, "tag", "rna_Main_animations_tag"); - parm = RNA_def_boolean(func, "value", false, "Value", ""); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); -} -# endif void RNA_def_main_particles(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index 66d36991bc1..f031c212666 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -3505,11 +3505,7 @@ static const EnumPropertyItem dt_uv_items[] = { static IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[] = { /* Categories */ {FILTER_ID_SCE, "category_scene", ICON_SCENE_DATA, "Scenes", "Show scenes"}, - {FILTER_ID_AC | FILTER_ID_AN, - "category_animation", - ICON_ANIM_DATA, - "Animations", - "Show animation data"}, + {FILTER_ID_AC, "category_animation", ICON_ANIM_DATA, "Animations", "Show animation data"}, {FILTER_ID_OB | FILTER_ID_GR, "category_object", ICON_OUTLINER_COLLECTION, diff --git a/tests/python/bl_animation_id.py b/tests/python/bl_animation_id.py index cb136800b87..7900ee4c8a8 100644 --- a/tests/python/bl_animation_id.py +++ b/tests/python/bl_animation_id.py @@ -12,62 +12,65 @@ blender -b --factory-startup --python tests/python/bl_animation_id.py """ -class AnimationIDAssignmentTest(unittest.TestCase): - """Test assigning animations & check reference counts.""" +class ActionBindingAssignmentTest(unittest.TestCase): + """Test assigning actions & check reference counts.""" - def test_animation_id_assignment(self): - # Create new animation datablock. - anim = bpy.data.animations.new('TestAnim') + def setUp(self) -> None: + bpy.ops.wm.read_homefile(use_factory_startup=True) + + def test_action_assignment(self): + # Create new Action. + anim = bpy.data.actions.new('TestAction') self.assertEqual(0, anim.users) # Assign the animation to the cube, cube = bpy.data.objects['Cube'] cube_adt = cube.animation_data_create() - cube_adt.animation = anim + cube_adt.action = anim self.assertEqual(1, anim.users) # Assign the animation to the camera as well. camera = bpy.data.objects['Camera'] camera_adt = camera.animation_data_create() - camera_adt.animation = anim + camera_adt.action = anim self.assertEqual(2, anim.users) # Unassigning should decrement the user count. - cube_adt.animation = None + cube_adt.action = None self.assertEqual(1, anim.users) # Deleting the camera should also decrement the user count. bpy.data.objects.remove(camera) self.assertEqual(0, anim.users) - def test_animation_binding_assignment(self): - # Create new animation datablock. - anim = bpy.data.animations.new('TestAnim') + def test_binding_assignment(self): + # Create new Action. + anim = bpy.data.actions.new('TestAction') self.assertEqual(0, anim.users) # Assign the animation to the cube, cube = bpy.data.objects['Cube'] cube_adt = cube.animation_data_create() - cube_adt.animation = anim + cube_adt.action = anim bind_cube = anim.bindings.new(for_id=cube) - cube_adt.animation_binding_handle = bind_cube.handle - self.assertEqual(cube_adt.animation_binding_handle, bind_cube.handle) + cube_adt.action_binding_handle = bind_cube.handle + self.assertEqual(cube_adt.action_binding_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) camera_adt = camera.animation_data_create() - camera_adt.animation = anim - self.assertEqual(camera_adt.animation_binding_handle, bind_camera.handle) + camera_adt.action = anim + self.assertEqual(camera_adt.action_binding_handle, bind_camera.handle) # Unassigning should keep the binding name. - cube_adt.animation = None - self.assertEqual(cube_adt.animation_binding_name, bind_cube.name) + cube_adt.action = None + self.assertEqual(cube_adt.action_binding_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.animation_binding_handle = bind_extra.handle - self.assertNotEqual(cube_adt.animation_binding_handle, bind_extra.handle) + cube_adt.action_binding_handle = bind_extra.handle + self.assertNotEqual(cube_adt.action_binding_handle, bind_extra.handle) class LimitationsTest(unittest.TestCase): @@ -77,19 +80,19 @@ class LimitationsTest(unittest.TestCase): """ def setUp(self): - anims = bpy.data.animations + anims = bpy.data.actions while anims: anims.remove(anims[0]) def test_initial_layers(self): """Test that upon creation an Animation has no layers/strips.""" - anim = bpy.data.animations.new('TestAnim') + anim = bpy.data.actions.new('TestAction') self.assertEqual([], anim.layers[:]) def test_limited_layers_strips(self): """Test that there can only be one layer with one strip.""" - anim = bpy.data.animations.new('TestAnim') + anim = bpy.data.actions.new('TestAction') layer = anim.layers.new(name="Layer") self.assertEqual([], layer.strips[:]) strip = layer.strips.new(type='KEYFRAME') @@ -107,7 +110,7 @@ class LimitationsTest(unittest.TestCase): def test_limited_strip_api(self): """Test that strips have no frame start/end/offset properties.""" - anim = bpy.data.animations.new('TestAnim') + anim = bpy.data.actions.new('TestAction') layer = anim.layers.new(name="Layer") strip = layer.strips.new(type='KEYFRAME') @@ -118,18 +121,18 @@ class LimitationsTest(unittest.TestCase): class DataPathTest(unittest.TestCase): def setUp(self): - anims = bpy.data.animations + anims = bpy.data.actions while anims: anims.remove(anims[0]) def test_repr(self): - anim = bpy.data.animations.new('TestAnim') + anim = bpy.data.actions.new('TestAction') layer = anim.layers.new(name="Layer") - self.assertEqual("bpy.data.animations['TestAnim'].layers[\"Layer\"]", repr(layer)) + self.assertEqual("bpy.data.actions['TestAction'].layers[\"Layer\"]", repr(layer)) strip = layer.strips.new(type='KEYFRAME') - self.assertEqual("bpy.data.animations['TestAnim'].layers[\"Layer\"].strips[0]", repr(strip)) + self.assertEqual("bpy.data.actions['TestAction'].layers[\"Layer\"].strips[0]", repr(strip)) def main():