From d94a56bdadf3b7e12377412ad088fd0a6914bc46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 13 May 2024 15:51:26 +0200 Subject: [PATCH] Anim: merge Animation data-block into bAction The new/experimental, layered `Animation` data-block is merged with the existing `bAction` data-block. The `Animation` data-block is considerably newer than `bAction`, so the supporting code that was written for it is also more modern. When moving that code into `bAction`, I chose to keep the modernity where possible, and thus some of the old code has been updated as well. Things like preferring references over pointers. The `Animation` data-block is now gone from DNA, the main database, etc. As this was still an experimental feature, there is no versioning code to convert any of that to Actions. The DNA struct `bAction` now has a C++ wrapper `animrig::Action`, that can be obtained via `some_action->wrap()`. `animrig::Action` has functions `is_empty()`, `is_action_legacy()`, and `is_action_layered()`. They **all** return `true` when the Action is empty, as in that case none of the data that makes an action either 'legacy' or 'layered' is there. The 'animation filtering' code (for showing things in the dope sheet, graph editor, etc) that I wrote for `Animation` is intentionally kept around. These types now target 'layered actions' and the already-existing ones 'legacy actions'. A future PR may merge these two together, but given how much work it was to add something new there, I'd rather wait until the dust has settled on this commit. There are plenty of variables (and some comments) named `anim` or `animation` that now are of type `animrig::Action`. I haven't renamed them all, to keep the noise level low in this commit (it's already big enough). This can be done in a followup, non-functional PR. Related task: #121355 Pull Request: https://projects.blender.org/blender/blender/pulls/121357 --- scripts/startup/bl_ui/temp_anim_layers.py | 49 +- source/blender/animrig/ANIM_action.hh | 632 ++++++++++++- source/blender/animrig/ANIM_animation.hh | 606 ------------ source/blender/animrig/ANIM_animdata.hh | 4 +- source/blender/animrig/ANIM_evaluation.hh | 4 +- source/blender/animrig/CMakeLists.txt | 1 - source/blender/animrig/intern/animation.cc | 205 ++-- .../blender/animrig/intern/animation_test.cc | 46 +- source/blender/animrig/intern/animdata.cc | 56 +- source/blender/animrig/intern/evaluation.cc | 4 +- .../blender/animrig/intern/evaluation_test.cc | 33 +- source/blender/blenkernel/BKE_animation.hh | 17 - source/blender/blenkernel/BKE_idtype.hh | 1 - source/blender/blenkernel/BKE_main.hh | 1 - source/blender/blenkernel/CMakeLists.txt | 2 - source/blender/blenkernel/intern/action.cc | 274 +++++- source/blender/blenkernel/intern/anim_data.cc | 17 +- source/blender/blenkernel/intern/anim_sys.cc | 29 +- source/blender/blenkernel/intern/animation.cc | 255 ----- source/blender/blenkernel/intern/fcurve.cc | 56 +- source/blender/blenkernel/intern/idtype.cc | 3 - source/blender/blenkernel/intern/main.cc | 3 - source/blender/depsgraph/CMakeLists.txt | 4 + .../intern/builder/deg_builder_nodes.cc | 19 +- .../intern/builder/deg_builder_nodes.h | 2 - .../intern/builder/deg_builder_relations.cc | 105 +-- .../intern/builder/deg_builder_relations.h | 12 +- .../animation/anim_channels_defines.cc | 12 +- .../editors/animation/anim_channels_edit.cc | 36 +- source/blender/editors/animation/anim_deps.cc | 5 +- .../blender/editors/animation/anim_filter.cc | 182 ++-- source/blender/editors/animation/anim_ops.cc | 4 +- .../editors/animation/keyframes_draw.cc | 40 +- .../editors/animation/keyframes_edit.cc | 16 +- .../editors/animation/keyframes_keylist.cc | 43 +- source/blender/editors/include/ED_anim_api.hh | 16 +- .../editors/include/ED_keyframes_draw.hh | 17 +- .../editors/include/ED_keyframes_keylist.hh | 4 - .../editors/interface/interface_icons.cc | 2 - .../editors/interface/interface_ops_test.cc | 2 +- .../templates/interface_templates.cc | 2 - .../blender/editors/render/render_opengl.cc | 1 - .../editors/space_action/action_draw.cc | 16 +- .../editors/space_action/action_select.cc | 6 +- .../blender/editors/space_nla/nla_tracks.cc | 2 +- .../editors/space_outliner/outliner_draw.cc | 2 - .../editors/space_outliner/outliner_intern.hh | 1 - .../editors/space_outliner/outliner_tools.cc | 1 - .../space_outliner/tree/tree_element_id.cc | 1 - source/blender/makesdna/DNA_ID.h | 4 +- source/blender/makesdna/DNA_ID_enums.h | 1 - ..._anim_defaults.h => DNA_action_defaults.h} | 8 +- source/blender/makesdna/DNA_action_types.h | 203 +++- source/blender/makesdna/DNA_anim_types.h | 239 +---- source/blender/makesdna/intern/dna_defaults.c | 12 +- source/blender/makesrna/intern/CMakeLists.txt | 1 - source/blender/makesrna/intern/makesrna.cc | 3 - source/blender/makesrna/intern/rna_ID.cc | 17 +- source/blender/makesrna/intern/rna_action.cc | 890 +++++++++++++++++- .../blender/makesrna/intern/rna_animation.cc | 128 ++- .../makesrna/intern/rna_animation_id.cc | 862 ----------------- .../blender/makesrna/intern/rna_internal.hh | 6 - source/blender/makesrna/intern/rna_main.cc | 11 - .../blender/makesrna/intern/rna_main_api.cc | 63 +- source/blender/makesrna/intern/rna_space.cc | 6 +- tests/python/bl_animation_id.py | 59 +- 66 files changed, 2568 insertions(+), 2796 deletions(-) delete mode 100644 source/blender/animrig/ANIM_animation.hh delete mode 100644 source/blender/blenkernel/BKE_animation.hh delete mode 100644 source/blender/blenkernel/intern/animation.cc rename source/blender/makesdna/{DNA_anim_defaults.h => DNA_action_defaults.h} (78%) delete mode 100644 source/blender/makesrna/intern/rna_animation_id.cc 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():