From 90ef46baa1bcacc1a886ac5ac4493fc46eaac7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 7 Mar 2024 16:41:25 +0100 Subject: [PATCH] Anim: DNA for `Animation` data-block Introduce new DNA for the `Animation` data-block and its sub-data. This includes the blenkernel code for reading & writing to blend files, and for memory management (freeing, duplicating). Minimal C++ wrappers are included, with just the functionality needed for blenkernel to do its job. The Outliner code is extended so that it knows about the new data-type, nothing more. For more info, see issue #113594. Pull Request: https://projects.blender.org/blender/blender/pulls/119077 --- source/blender/animrig/ANIM_animation.hh | 297 ++++++++++++++++++ source/blender/animrig/CMakeLists.txt | 3 + source/blender/animrig/intern/animation.cc | 260 +++++++++++++++ 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/anim_data.cc | 1 + source/blender/blenkernel/intern/animation.cc | 255 +++++++++++++++ source/blender/blenkernel/intern/idtype.cc | 3 + source/blender/blenkernel/intern/main.cc | 3 + .../blentranslation/BLT_translation.hh | 1 + .../intern/builder/deg_builder_nodes.cc | 4 + .../intern/builder/deg_builder_relations.cc | 4 + .../editors/interface/interface_icons.cc | 2 + .../editors/interface/interface_templates.cc | 2 + .../blender/editors/render/render_opengl.cc | 1 + .../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 + source/blender/makesdna/DNA_anim_defaults.h | 39 +++ source/blender/makesdna/DNA_anim_types.h | 217 ++++++++++++- source/blender/makesdna/intern/dna_defaults.c | 10 + source/blender/makesrna/intern/rna_ID.cc | 6 +- 27 files changed, 1136 insertions(+), 3 deletions(-) create mode 100644 source/blender/animrig/ANIM_animation.hh create mode 100644 source/blender/animrig/intern/animation.cc create mode 100644 source/blender/blenkernel/BKE_animation.hh create mode 100644 source/blender/blenkernel/intern/animation.cc create mode 100644 source/blender/makesdna/DNA_anim_defaults.h diff --git a/source/blender/animrig/ANIM_animation.hh b/source/blender/animrig/ANIM_animation.hh new file mode 100644 index 00000000000..5c312f771e2 --- /dev/null +++ b/source/blender/animrig/ANIM_animation.hh @@ -0,0 +1,297 @@ +/* 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); + + /* Animation Binding access. */ + blender::Span bindings() const; + blender::MutableSpan bindings(); + const Binding *binding(int64_t index) const; + Binding *binding(int64_t index); + + /** Free all data in the `Animation`. Doesn't delete the `Animation` itself. */ + void free_data(); +}; +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() = default; + /** + * 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; +}; +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); +}; +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 an 'binding' because it acts like an binding socket of the + * Animation data-block, into which an animatable ID can be noodled. + * + * \see AnimData::binding_handle + */ +class Binding : public ::AnimationBinding { + public: + Binding() = default; + Binding(const Binding &other) = default; + ~Binding() = default; +}; +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); +}; +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); +}; +static_assert(sizeof(ChannelBag) == sizeof(::AnimationChannelBag), + "DNA struct and its C++ wrapper must have the same size"); + +} // 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/CMakeLists.txt b/source/blender/animrig/CMakeLists.txt index feb8df8e8b7..50cca2acd17 100644 --- a/source/blender/animrig/CMakeLists.txt +++ b/source/blender/animrig/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC_SYS set(SRC intern/action.cc intern/anim_rna.cc + intern/animation.cc intern/animdata.cc intern/bone_collections.cc intern/bonecolor.cc @@ -31,6 +32,7 @@ set(SRC intern/visualkey.cc ANIM_action.hh + ANIM_animation.hh ANIM_animdata.hh ANIM_armature_iter.hh ANIM_bone_collections.hh @@ -50,6 +52,7 @@ set(LIB bf::dna PRIVATE bf_editor_interface PRIVATE bf::intern::guardedalloc + PRIVATE bf::intern::atomic ) diff --git a/source/blender/animrig/intern/animation.cc b/source/blender/animrig/intern/animation.cc new file mode 100644 index 00000000000..e026c0701db --- /dev/null +++ b/source/blender/animrig/intern/animation.cc @@ -0,0 +1,260 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_anim_defaults.h" +#include "DNA_anim_types.h" +#include "DNA_defaults.h" + +#include "BLI_listbase.h" +#include "BLI_listbase_wrapper.hh" +#include "BLI_math_base.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_string_utils.hh" + +#include "BKE_anim_data.hh" +#include "BKE_animation.hh" +#include "BKE_fcurve.hh" +#include "BKE_lib_id.hh" +#include "BKE_main.hh" + +#include "ED_keyframing.hh" + +#include "MEM_guardedalloc.h" + +#include "atomic_ops.h" + +#include "ANIM_animation.hh" +#include "ANIM_fcurve.hh" + +#include +#include + +namespace blender::animrig { + +/* ----- Animation implementation ----------- */ + +blender::Span Animation::layers() const +{ + return blender::Span{reinterpret_cast(this->layer_array), + this->layer_array_num}; +} +blender::MutableSpan Animation::layers() +{ + return blender::MutableSpan{reinterpret_cast(this->layer_array), + this->layer_array_num}; +} +const Layer *Animation::layer(const int64_t index) const +{ + return &this->layer_array[index]->wrap(); +} +Layer *Animation::layer(const int64_t index) +{ + return &this->layer_array[index]->wrap(); +} + +blender::Span Animation::bindings() const +{ + return blender::Span{reinterpret_cast(this->binding_array), + this->binding_array_num}; +} +blender::MutableSpan Animation::bindings() +{ + return blender::MutableSpan{reinterpret_cast(this->binding_array), + this->binding_array_num}; +} +const Binding *Animation::binding(const int64_t index) const +{ + return &this->binding_array[index]->wrap(); +} +Binding *Animation::binding(const int64_t index) +{ + return &this->binding_array[index]->wrap(); +} + +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; +} + +/* ----- AnimationLayer implementation ----------- */ + +Layer::Layer(const Layer &other) +{ + memcpy(this, &other, sizeof(*this)); + + /* Strips. */ + 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__); + } +} + +Layer::~Layer() +{ + for (Strip *strip : this->strips()) { + MEM_delete(strip); + } + MEM_SAFE_FREE(this->strip_array); + this->strip_array_num = 0; +} + +blender::Span Layer::strips() const +{ + return blender::Span{reinterpret_cast(this->strip_array), + this->strip_array_num}; +} +blender::MutableSpan Layer::strips() +{ + return blender::MutableSpan{reinterpret_cast(this->strip_array), + this->strip_array_num}; +} +const Strip *Layer::strip(const int64_t index) const +{ + return &this->strip_array[index]->wrap(); +} +Strip *Layer::strip(const int64_t index) +{ + return &this->strip_array[index]->wrap(); +} + +/* ----- AnimationBinding implementation ----------- */ + +/* ----- AnimationStrip implementation ----------- */ + +Strip *Strip::duplicate(const StringRefNull allocation_name) const +{ + switch (this->type()) { + case Type::Keyframe: { + const KeyframeStrip &source = this->as(); + KeyframeStrip *copy = MEM_new(allocation_name.c_str(), source); + return ©->strip.wrap(); + } + } + BLI_assert_unreachable(); + return nullptr; +} + +Strip::~Strip() +{ + switch (this->type()) { + case Type::Keyframe: + this->as().~KeyframeStrip(); + return; + } + BLI_assert_unreachable(); +} + +/* ----- KeyframeAnimationStrip implementation ----------- */ + +KeyframeStrip::KeyframeStrip(const KeyframeStrip &other) +{ + memcpy(this, &other, sizeof(*this)); + + 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)); + } +} + +KeyframeStrip::~KeyframeStrip() +{ + for (ChannelBag *channelbag_for_binding : this->channelbags()) { + MEM_delete(channelbag_for_binding); + } + MEM_SAFE_FREE(this->channelbags_array); + this->channelbags_array_num = 0; +} + +template<> bool Strip::is() const +{ + return this->type() == Type::Keyframe; +} + +template<> KeyframeStrip &Strip::as() +{ + BLI_assert_msg(this->is(), "Strip is not a KeyframeStrip"); + return *reinterpret_cast(this); +} + +template<> const KeyframeStrip &Strip::as() const +{ + BLI_assert_msg(this->is(), "Strip is not a KeyframeStrip"); + return *reinterpret_cast(this); +} + +blender::Span KeyframeStrip::channelbags() const +{ + return blender::Span{reinterpret_cast(this->channelbags_array), + this->channelbags_array_num}; +} +blender::MutableSpan KeyframeStrip::channelbags() +{ + return blender::MutableSpan{ + reinterpret_cast(this->channelbags_array), this->channelbags_array_num}; +} +const ChannelBag *KeyframeStrip::channelbag(const int64_t index) const +{ + return &this->channelbags_array[index]->wrap(); +} +ChannelBag *KeyframeStrip::channelbag(const int64_t index) +{ + return &this->channelbags_array[index]->wrap(); +} + +/* AnimationChannelBag implementation. */ + +ChannelBag::ChannelBag(const ChannelBag &other) +{ + this->binding_handle = other.binding_handle; + this->fcurve_array_num = other.fcurve_array_num; + + this->fcurve_array = MEM_cnew_array(other.fcurve_array_num, __func__); + for (int i = 0; i < other.fcurve_array_num; i++) { + const FCurve *fcu_src = other.fcurve_array[i]; + this->fcurve_array[i] = BKE_fcurve_copy(fcu_src); + } +} + +ChannelBag::~ChannelBag() +{ + for (FCurve *fcu : this->fcurves()) { + BKE_fcurve_free(fcu); + } + MEM_SAFE_FREE(this->fcurve_array); + this->fcurve_array_num = 0; +} + +blender::Span ChannelBag::fcurves() const +{ + return blender::Span{this->fcurve_array, this->fcurve_array_num}; +} +blender::MutableSpan ChannelBag::fcurves() +{ + return blender::MutableSpan{this->fcurve_array, this->fcurve_array_num}; +} +const FCurve *ChannelBag::fcurve(const int64_t index) const +{ + return this->fcurve_array[index]; +} +FCurve *ChannelBag::fcurve(const int64_t index) +{ + return this->fcurve_array[index]; +} + +} // namespace blender::animrig diff --git a/source/blender/blenkernel/BKE_animation.hh b/source/blender/blenkernel/BKE_animation.hh new file mode 100644 index 00000000000..cc245765f36 --- /dev/null +++ b/source/blender/blenkernel/BKE_animation.hh @@ -0,0 +1,17 @@ +/* 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 f10d0365a50..b2c84877e9c 100644 --- a/source/blender/blenkernel/BKE_idtype.hh +++ b/source/blender/blenkernel/BKE_idtype.hh @@ -273,6 +273,7 @@ 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 ec0f512620c..8b3720304a2 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -210,6 +210,7 @@ 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 aa0d24d635e..b17961520c3 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -55,6 +55,7 @@ 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 @@ -327,6 +328,7 @@ 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/anim_data.cc b/source/blender/blenkernel/intern/anim_data.cc index d4658967b67..d5d41a44865 100644 --- a/source/blender/blenkernel/intern/anim_data.cc +++ b/source/blender/blenkernel/intern/anim_data.cc @@ -292,6 +292,7 @@ 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/animation.cc b/source/blender/blenkernel/intern/animation.cc new file mode 100644 index 00000000000..cf3c93b8d5e --- /dev/null +++ b/source/blender/blenkernel/intern/animation.cc @@ -0,0 +1,255 @@ +/* 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_data_address(reader, &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_data_address(reader, &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_data_address(reader, &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_data_address(reader, &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_data_address(reader, &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/idtype.cc b/source/blender/blenkernel/intern/idtype.cc index 1546156f33b..13d0d599553 100644 --- a/source/blender/blenkernel/intern/idtype.cc +++ b/source/blender/blenkernel/intern/idtype.cc @@ -83,6 +83,7 @@ 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); @@ -225,6 +226,7 @@ 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); @@ -284,6 +286,7 @@ 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 29c379aea31..a5c3568dfd3 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -849,6 +849,8 @@ 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: @@ -894,6 +896,7 @@ 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/blentranslation/BLT_translation.hh b/source/blender/blentranslation/BLT_translation.hh index 1ecb53812ee..00378ab489d 100644 --- a/source/blender/blentranslation/BLT_translation.hh +++ b/source/blender/blentranslation/BLT_translation.hh @@ -86,6 +86,7 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid /* ID-types contexts. */ /* WARNING! Keep it in sync with ID-types in `blenkernel/intern/idtype.cc`. */ #define BLT_I18NCONTEXT_ID_ACTION "Action" +#define BLT_I18NCONTEXT_ID_ANIMATION "Animation" #define BLT_I18NCONTEXT_ID_ARMATURE "Armature" #define BLT_I18NCONTEXT_ID_BRUSH "Brush" #define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index d37e5a027dc..38cefdff685 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -557,6 +557,10 @@ void DepsgraphNodeBuilder::build_id(ID *id, const bool force_be_visible) case ID_AC: build_action((bAction *)id); break; + case ID_AN: + /* TODO: actually handle this ID type properly, will be done in a followup commit. */ + build_generic_id(id); + break; case ID_AR: build_armature((bArmature *)id); break; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 4c92053a120..22632a4b616 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -525,6 +525,10 @@ void DepsgraphRelationBuilder::build_id(ID *id) case ID_AC: build_action((bAction *)id); break; + case ID_AN: + /* TODO: actually handle this ID type properly, will be done in a followup commit. */ + build_generic_id(id); + break; case ID_AR: build_armature((bArmature *)id); break; diff --git a/source/blender/editors/interface/interface_icons.cc b/source/blender/editors/interface/interface_icons.cc index 80fc5484850..64cad98d53b 100644 --- a/source/blender/editors/interface/interface_icons.cc +++ b/source/blender/editors/interface/interface_icons.cc @@ -2392,6 +2392,8 @@ 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_templates.cc b/source/blender/editors/interface/interface_templates.cc index 0626a2630f3..d9185cc23e7 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -1118,6 +1118,8 @@ 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 2885b401afc..7f635608f86 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -616,6 +616,7 @@ 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_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 4e393ccc078..5b56a5cb62d 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -2433,6 +2433,8 @@ 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 c3ec8080b78..7e7204eaadf 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -139,6 +139,7 @@ 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 d1cc65d222e..91530b043d1 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -147,6 +147,7 @@ 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 0875b7836f1..1c48221166a 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -88,6 +88,7 @@ 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 44e45b2f26e..d8af3ab6377 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -1210,6 +1210,7 @@ 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 | \ @@ -1219,7 +1220,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_IP | FILTER_ID_AN) /** * This enum defines the index assigned to each type of IDs in the array returned by @@ -1258,6 +1259,7 @@ 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 7ce8785aed0..a4db7befa1c 100644 --- a/source/blender/makesdna/DNA_ID_enums.h +++ b/source/blender/makesdna/DNA_ID_enums.h @@ -84,6 +84,7 @@ 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_anim_defaults.h new file mode 100644 index 00000000000..bb5b2fba8fd --- /dev/null +++ b/source/blender/makesdna/DNA_anim_defaults.h @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup DNA + */ + +#pragma once + +#include + +/* Struct members on own line. */ +/* clang-format off */ + +/* -------------------------------------------------------------------- */ +/** \name AnimationLayer Struct + * \{ */ + +#define _DNA_DEFAULT_AnimationLayer \ + { \ + .influence = 1.0f, \ + } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name AnimationStrip Struct + * \{ */ + +#define _DNA_DEFAULT_AnimationStrip \ + { \ + .frame_start = -INFINITY, \ + .frame_end = INFINITY, \ + } + +/** \} */ + +/* clang-format on */ diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 02763118d5f..67d0aa512cc 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -15,6 +15,19 @@ #include "DNA_curve_types.h" #include "DNA_listBase.h" +#ifdef __cplusplus +# include "BLI_span.hh" + +# 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 */ @@ -1146,10 +1159,32 @@ 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; - char _pad[4]; /* settings for active action evaluation (based on NLA strip settings) */ /** Accumulation mode for active action. */ @@ -1208,3 +1243,183 @@ typedef struct IdAdtTemplate { #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; + +#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 reorganisation 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 dd2b17d8971..f5a65bd5c63 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -75,6 +75,7 @@ #include "DNA_defaults.h" +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_asset_types.h" #include "DNA_brush_types.h" @@ -108,6 +109,7 @@ #include "DNA_volume_types.h" #include "DNA_world_types.h" +#include "DNA_anim_defaults.h" #include "DNA_armature_defaults.h" #include "DNA_asset_defaults.h" #include "DNA_brush_defaults.h" @@ -142,6 +144,10 @@ #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_asset_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(AssetMetaData); SDNA_DEFAULT_DECL_STRUCT(AssetLibraryReference); @@ -381,6 +387,10 @@ 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), + /* DNA_asset_defaults.h */ SDNA_DEFAULT_DECL(AssetMetaData), SDNA_DEFAULT_DECL(AssetLibraryReference), diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index d1ea56e310b..4567a915ecf 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -34,6 +34,7 @@ */ 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", ""}, @@ -498,6 +499,8 @@ StructRNA *ID_code_to_RNA_type(short idcode) switch ((ID_Type)idcode) { case ID_AC: return &RNA_Action; + case ID_AN: + break; case ID_AR: return &RNA_Armature; case ID_BR: @@ -1057,7 +1060,8 @@ 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: + case ID_AC: /* Fallthrough. */ + case ID_AN: allow_flag = ID_RECALC_ANIMATION; break; default: