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
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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<const Layer *> layers() const;
|
||||
blender::MutableSpan<Layer *> 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<const Binding *> bindings() const;
|
||||
blender::MutableSpan<Binding *> 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<typename T> bool is() const;
|
||||
template<typename T> T &as();
|
||||
template<typename T> 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<Flags>(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<MixMode>(this->layer_mix_mode);
|
||||
}
|
||||
|
||||
/* Strip access. */
|
||||
blender::Span<const Strip *> strips() const;
|
||||
blender::MutableSpan<Strip *> 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<const ChannelBag *> channelbags() const;
|
||||
blender::MutableSpan<ChannelBag *> 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<KeyframeStrip>();
|
||||
template<> const KeyframeStrip &Strip::as<KeyframeStrip>() 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<const FCurve *> fcurves() const;
|
||||
blender::MutableSpan<FCurve *> 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<FCurve *> fcurves_for_animation(Action &anim, binding_handle_t binding_handle);
|
||||
Span<const FCurve *> fcurves_for_animation(const Action &anim, binding_handle_t binding_handle);
|
||||
|
||||
/**
|
||||
* 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<blender::animrig::Action *>(this);
|
||||
}
|
||||
inline const blender::animrig::Action &bAction::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Action *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::Layer &ActionLayer::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::Layer *>(this);
|
||||
}
|
||||
inline const blender::animrig::Layer &ActionLayer::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Layer *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::Binding &ActionBinding::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::Binding *>(this);
|
||||
}
|
||||
inline const blender::animrig::Binding &ActionBinding::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Binding *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::Strip &ActionStrip::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::Strip *>(this);
|
||||
}
|
||||
inline const blender::animrig::Strip &ActionStrip::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Strip *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::KeyframeStrip &KeyframeActionStrip::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::KeyframeStrip *>(this);
|
||||
}
|
||||
inline const blender::animrig::KeyframeStrip &KeyframeActionStrip::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::KeyframeStrip *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::ChannelBag &ActionChannelBag::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::ChannelBag *>(this);
|
||||
}
|
||||
inline const blender::animrig::ChannelBag &ActionChannelBag::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::ChannelBag *>(this);
|
||||
}
|
||||
|
||||
@@ -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<const Layer *> layers() const;
|
||||
blender::MutableSpan<Layer *> 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<const Binding *> bindings() const;
|
||||
blender::MutableSpan<Binding *> 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<typename T> bool is() const;
|
||||
template<typename T> T &as();
|
||||
template<typename T> 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<Flags>(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<MixMode>(this->layer_mix_mode);
|
||||
}
|
||||
|
||||
/* Strip access. */
|
||||
blender::Span<const Strip *> strips() const;
|
||||
blender::MutableSpan<Strip *> 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<const ChannelBag *> channelbags() const;
|
||||
blender::MutableSpan<ChannelBag *> 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<KeyframeStrip>();
|
||||
template<> const KeyframeStrip &Strip::as<KeyframeStrip>() 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<const FCurve *> fcurves() const;
|
||||
blender::MutableSpan<FCurve *> 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<FCurve *> fcurves_for_animation(Animation &anim, binding_handle_t binding_handle);
|
||||
Span<const FCurve *> 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<blender::animrig::Animation *>(this);
|
||||
}
|
||||
inline const blender::animrig::Animation &Animation::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Animation *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::Layer &AnimationLayer::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::Layer *>(this);
|
||||
}
|
||||
inline const blender::animrig::Layer &AnimationLayer::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Layer *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::Binding &AnimationBinding::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::Binding *>(this);
|
||||
}
|
||||
inline const blender::animrig::Binding &AnimationBinding::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Binding *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::Strip &AnimationStrip::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::Strip *>(this);
|
||||
}
|
||||
inline const blender::animrig::Strip &AnimationStrip::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::Strip *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::KeyframeStrip &KeyframeAnimationStrip::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::KeyframeStrip *>(this);
|
||||
}
|
||||
inline const blender::animrig::KeyframeStrip &KeyframeAnimationStrip::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::KeyframeStrip *>(this);
|
||||
}
|
||||
|
||||
inline blender::animrig::ChannelBag &AnimationChannelBag::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::animrig::ChannelBag *>(this);
|
||||
}
|
||||
inline const blender::animrig::ChannelBag &AnimationChannelBag::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::animrig::ChannelBag *>(this);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <cstdio>
|
||||
@@ -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<KeyframeAnimationStrip>(__func__);
|
||||
KeyframeActionStrip *key_strip = MEM_new<KeyframeActionStrip>(__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<typename T> static void shrink_array(T **array, int *num, const int shr
|
||||
|
||||
/* ----- Animation implementation ----------- */
|
||||
|
||||
blender::Span<const Layer *> 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<const Layer *> Action::layers() const
|
||||
{
|
||||
return blender::Span<Layer *>{reinterpret_cast<Layer **>(this->layer_array),
|
||||
this->layer_array_num};
|
||||
}
|
||||
blender::MutableSpan<Layer *> Animation::layers()
|
||||
blender::MutableSpan<Layer *> Action::layers()
|
||||
{
|
||||
return blender::MutableSpan<Layer *>{reinterpret_cast<Layer **>(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<const Binding *> Animation::bindings() const
|
||||
blender::Span<const Binding *> Action::bindings() const
|
||||
{
|
||||
return blender::Span<Binding *>{reinterpret_cast<Binding **>(this->binding_array),
|
||||
this->binding_array_num};
|
||||
}
|
||||
blender::MutableSpan<Binding *> Animation::bindings()
|
||||
blender::MutableSpan<Binding *> Action::bindings()
|
||||
{
|
||||
return blender::MutableSpan<Binding *>{reinterpret_cast<Binding **>(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<const Animation *>(this)->binding_for_handle(handle);
|
||||
const Binding *binding = const_cast<const Action *>(this)->binding_for_handle(handle);
|
||||
return const_cast<Binding *>(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<const Animation *>(this)->binding_for_id(animated_id);
|
||||
const Binding *binding = const_cast<const Action *>(this)->binding_for_id(animated_id);
|
||||
return const_cast<Binding *>(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<AnimationBinding>(__func__)->wrap();
|
||||
Binding &binding = MEM_new<ActionBinding>(__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<AnimationStrip *>(other.strip_array_num, __func__);
|
||||
this->strip_array = MEM_cnew_array<ActionStrip *>(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<short *>(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<AnimationChannelBag *>(other.channelbags_array_num,
|
||||
__func__);
|
||||
this->channelbags_array = MEM_cnew_array<ActionChannelBag *>(other.channelbags_array_num,
|
||||
__func__);
|
||||
Span<const ChannelBag *> channelbags_src = other.channelbags();
|
||||
for (int i : channelbags_src.index_range()) {
|
||||
this->channelbags_array[i] = MEM_new<animrig::ChannelBag>(__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<AnimationChannelBag>(__func__)->wrap();
|
||||
ChannelBag &channels = MEM_new<ActionChannelBag>(__func__)->wrap();
|
||||
channels.binding_handle = binding.handle;
|
||||
|
||||
grow_array_and_append<AnimationChannelBag *>(
|
||||
grow_array_and_append<ActionChannelBag *>(
|
||||
&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<const Animation &>(anim), binding_handle);
|
||||
const animrig::ChannelBag *const_bag = channelbag_for_animation(const_cast<const Action &>(anim),
|
||||
binding_handle);
|
||||
return const_cast<animrig::ChannelBag *>(const_bag);
|
||||
}
|
||||
|
||||
Span<FCurve *> fcurves_for_animation(Animation &anim, const binding_handle_t binding_handle)
|
||||
Span<FCurve *> 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<FCurve *> fcurves_for_animation(Animation &anim, const binding_handle_t bin
|
||||
return bag->fcurves();
|
||||
}
|
||||
|
||||
Span<const FCurve *> fcurves_for_animation(const Animation &anim,
|
||||
Span<const FCurve *> fcurves_for_animation(const Action &anim,
|
||||
const binding_handle_t binding_handle)
|
||||
{
|
||||
const animrig::ChannelBag *bag = channelbag_for_animation(anim, binding_handle);
|
||||
|
||||
@@ -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<Animation *>(BKE_id_new(bmain, ID_AN, "ANÄnimåtië"));
|
||||
anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACÄnimåtië"));
|
||||
cube = BKE_object_add_only_object(bmain, OB_EMPTY, "Küüübus");
|
||||
suzanne = BKE_object_add_only_object(bmain, OB_EMPTY, "OBSuzanne");
|
||||
}
|
||||
@@ -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<Animation *>(BKE_id_new(bmain, ID_AN, "ANOtherAnim"));
|
||||
Action *other_anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACOtherAnim"));
|
||||
Layer &other_layer = other_anim->layer_add("Another Layer");
|
||||
EXPECT_FALSE(anim->layer_remove(other_layer))
|
||||
<< "Removing a layer not owned by the animation should be gracefully rejected";
|
||||
@@ -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<Animation *>(BKE_id_new(bmain, ID_AN, "ANOtherAnim"));
|
||||
Action *another_anim = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACOtherAnim"));
|
||||
Binding &another_binding = another_anim->binding_add();
|
||||
ASSERT_FALSE(another_anim->assign_id(&another_binding, cube->id))
|
||||
<< "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<float>::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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <optional>
|
||||
|
||||
#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<Action *>(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. */
|
||||
|
||||
@@ -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[]);
|
||||
@@ -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;
|
||||
|
||||
@@ -210,7 +210,6 @@ struct Main {
|
||||
ListBase collections;
|
||||
ListBase armatures;
|
||||
ListBase actions;
|
||||
ListBase animations;
|
||||
ListBase nodetrees;
|
||||
ListBase brushes;
|
||||
ListBase particles;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<bAction *>(id_dst);
|
||||
animrig::Action &action_dst = dna_action_dst->wrap();
|
||||
|
||||
const bAction *dna_action_src = reinterpret_cast<const bAction *>(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<FCurve *>(action_src->curves.first); fcurve_src;
|
||||
for (fcurve_src = static_cast<FCurve *>(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<bActionGroup *>(action_dst->groups.first),
|
||||
group_src = static_cast<bActionGroup *>(action_src->groups.first);
|
||||
for (group_dst = static_cast<bActionGroup *>(action_dst.groups.first),
|
||||
group_src = static_cast<bActionGroup *>(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<ActionLayer *>(action_src.layer_array_num, __func__);
|
||||
for (int i : action_src.layers().index_range()) {
|
||||
action_dst.layer_array[i] = MEM_new<animrig::Layer>(__func__, *action_src.layer(i));
|
||||
}
|
||||
|
||||
/* Bindings. */
|
||||
action_dst.binding_array = MEM_cnew_array<ActionBinding *>(action_src.binding_array_num,
|
||||
__func__);
|
||||
for (int i : action_src.bindings().index_range()) {
|
||||
action_dst.binding_array[i] = MEM_new<animrig::Binding>(__func__, *action_src.binding(i));
|
||||
}
|
||||
|
||||
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<bAction *>(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<bAction *>(id);
|
||||
animrig::Action &action = reinterpret_cast<bAction *>(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<animrig::KeyframeStrip>();
|
||||
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<FCurve *> 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<animrig::Strip *> 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<animrig::KeyframeStrip>();
|
||||
write_keyframe_strip(writer, key_strip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_layers(BlendWriter *writer, Span<animrig::Layer *> 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<animrig::Binding *> 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<bAction *>(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<void **>(&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<void **>(&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<void **>(&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<void **>(&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<animrig::KeyframeStrip>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void read_bindings(BlendDataReader *reader, animrig::Action &anim)
|
||||
{
|
||||
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&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<bAction *>(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,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
#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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Library *> /*owner_library*/,
|
||||
ID *id_dst,
|
||||
const ID *id_src,
|
||||
const int /*flag*/)
|
||||
{
|
||||
Animation *dna_anim_dst = reinterpret_cast<Animation *>(id_dst);
|
||||
animrig::Animation &anim_dst = dna_anim_dst->wrap();
|
||||
|
||||
const Animation *dna_anim_src = reinterpret_cast<const Animation *>(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<AnimationLayer *>(anim_src.layer_array_num, __func__);
|
||||
for (int i : anim_src.layers().index_range()) {
|
||||
anim_dst.layer_array[i] = MEM_new<animrig::Layer>(__func__, *anim_src.layer(i));
|
||||
}
|
||||
|
||||
/* Bindings. */
|
||||
anim_dst.binding_array = MEM_cnew_array<AnimationBinding *>(anim_src.binding_array_num,
|
||||
__func__);
|
||||
for (int i : anim_src.bindings().index_range()) {
|
||||
anim_dst.binding_array[i] = MEM_new<animrig::Binding>(__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<Animation *>(id)->wrap().free_data();
|
||||
}
|
||||
|
||||
static void animation_foreach_id(ID *id, LibraryForeachIDData *data)
|
||||
{
|
||||
animrig::Animation &anim = reinterpret_cast<Animation *>(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<animrig::KeyframeStrip>();
|
||||
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<FCurve *> 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<animrig::Strip *> 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<animrig::KeyframeStrip>();
|
||||
write_keyframe_strip(writer, key_strip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_layers(BlendWriter *writer, Span<animrig::Layer *> 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<animrig::Binding *> 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<Animation *>(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<void **>(&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<void **>(&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<void **>(&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<void **>(&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<animrig::KeyframeStrip>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void read_animation_bindings(BlendDataReader *reader, animrig::Animation &anim)
|
||||
{
|
||||
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&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<Animation *>(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<Animation *>(BKE_id_new(bmain, ID_AN, name));
|
||||
return anim;
|
||||
}
|
||||
@@ -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<FCurve *>(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<FCurve *>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <cstdio>
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FCurve *>(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<FCurve *>(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<FCurve *>(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<FCurve *>(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<FCurve *> fcurves = animrig::fcurves_for_animation(anim, adt->binding_handle);
|
||||
return animfilter_fcurves_span(anim_data, ads, fcurves, filter_mode, owner_id, &anim.id);
|
||||
Span<FCurve *> 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<bAction *>(data)->wrap();
|
||||
const animrig::binding_handle_t binding_handle = obact->adt->binding_handle;
|
||||
items += animfilter_action(
|
||||
ac, anim_data, ads, static_cast<bAction *>(data), filter_mode, (ID *)obact);
|
||||
ac, anim_data, ads, action, binding_handle, filter_mode, (ID *)obact);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Animation *>(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<bAction *>(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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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<Animation *>(ale->key_data),
|
||||
ycenter,
|
||||
scale_factor,
|
||||
action_flag);
|
||||
case ALE_ACTION_LAYERED:
|
||||
ED_add_action_layered_channel(draw_list,
|
||||
adt,
|
||||
static_cast<bAction *>(ale->key_data),
|
||||
ycenter,
|
||||
scale_factor,
|
||||
action_flag);
|
||||
break;
|
||||
case ALE_ACT:
|
||||
ED_add_action_channel(draw_list,
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -139,7 +139,6 @@ struct TreeElementIcon {
|
||||
ID_GR, \
|
||||
ID_AR, \
|
||||
ID_AC, \
|
||||
ID_AN, \
|
||||
ID_BR, \
|
||||
ID_PA, \
|
||||
ID_GD_LEGACY, \
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -88,7 +88,6 @@ std::unique_ptr<TreeElementID> 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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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, \
|
||||
@@ -20,7 +20,13 @@
|
||||
#include "DNA_vec_types.h"
|
||||
#include "DNA_view2d_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <type_traits>
|
||||
#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<decltype(ActionBinding::handle), decltype(bAction::last_binding_handle)>);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(ActionBinding::handle), decltype(ActionChannelBag::binding_handle)>);
|
||||
#endif
|
||||
|
||||
@@ -21,13 +21,6 @@
|
||||
# include <type_traits>
|
||||
#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<decltype(ActionBinding::handle), decltype(AnimData::binding_handle)>);
|
||||
static_assert(std::is_same_v<decltype(ActionBinding::name), decltype(AnimData::binding_name)>);
|
||||
#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<decltype(AnimationBinding::handle), decltype(AnimData::binding_handle)>);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(AnimationBinding::handle), decltype(Animation::last_binding_handle)>);
|
||||
static_assert(std::is_same_v<decltype(AnimationBinding::handle),
|
||||
decltype(AnimationChannelBag::binding_handle)>);
|
||||
static_assert(std::is_same_v<decltype(AnimationBinding::name), decltype(AnimData::binding_name)>);
|
||||
#endif
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 <algorithm>
|
||||
@@ -44,6 +88,370 @@
|
||||
|
||||
# include "WM_api.hh"
|
||||
|
||||
# include "DEG_depsgraph.hh"
|
||||
|
||||
# include "ANIM_keyframing.hh"
|
||||
|
||||
# include <fmt/format.h>
|
||||
|
||||
# ifdef WITH_ANIM_BAKLAVA
|
||||
|
||||
static animrig::Action &rna_action(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<bAction *>(ptr->owner_id)->wrap();
|
||||
}
|
||||
|
||||
static animrig::Binding &rna_data_binding(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<ActionBinding *>(ptr->data)->wrap();
|
||||
}
|
||||
|
||||
static animrig::Layer &rna_data_layer(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<ActionLayer *>(ptr->data)->wrap();
|
||||
}
|
||||
|
||||
static animrig::Strip &rna_data_strip(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<ActionStrip *>(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<ActionStrip *>(ptr->data)->wrap();
|
||||
return strip.as<animrig::KeyframeStrip>();
|
||||
}
|
||||
|
||||
static animrig::ChannelBag &rna_data_channelbag(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<ActionChannelBag *>(ptr->data)->wrap();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void rna_iterator_array_begin(CollectionPropertyIterator *iter, Span<T *> items)
|
||||
{
|
||||
rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void rna_iterator_array_begin(CollectionPropertyIterator *iter, MutableSpan<T *> 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<std::string> 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<std::string> 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<std::string> 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<animrig::Strip *> strips = layer->strips();
|
||||
const int index = strips.first_index_try(&strip_to_find);
|
||||
if (index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PointerRNA layer_ptr = RNA_pointer_create(&anim.id, &RNA_ActionLayer, layer);
|
||||
const std::optional<std::string> layer_path = rna_ActionLayer_path(&layer_ptr);
|
||||
BLI_assert_msg(layer_path, "Every animation layer should have a valid RNA path.");
|
||||
const std::string strip_path = fmt::format("{}.strips[{}]", *layer_path, index);
|
||||
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
|
||||
|
||||
@@ -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<bAction *>(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<bAction *>(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<Animation *>(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<AnimData *>(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
|
||||
|
||||
|
||||
@@ -1,862 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup RNA
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
#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 <fmt/format.h>
|
||||
|
||||
static animrig::Animation &rna_animation(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<Animation *>(ptr->owner_id)->wrap();
|
||||
}
|
||||
|
||||
static animrig::Binding &rna_data_binding(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<AnimationBinding *>(ptr->data)->wrap();
|
||||
}
|
||||
|
||||
static animrig::Layer &rna_data_layer(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<AnimationLayer *>(ptr->data)->wrap();
|
||||
}
|
||||
|
||||
static animrig::Strip &rna_data_strip(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<AnimationStrip *>(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<AnimationStrip *>(ptr->data)->wrap();
|
||||
return strip.as<animrig::KeyframeStrip>();
|
||||
}
|
||||
|
||||
static animrig::ChannelBag &rna_data_channelbag(const PointerRNA *ptr)
|
||||
{
|
||||
return reinterpret_cast<AnimationChannelBag *>(ptr->data)->wrap();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void rna_iterator_array_begin(CollectionPropertyIterator *iter, Span<T *> items)
|
||||
{
|
||||
rna_iterator_array_begin(iter, (void *)items.data(), sizeof(T *), items.size(), 0, nullptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void rna_iterator_array_begin(CollectionPropertyIterator *iter, MutableSpan<T *> 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<std::string> 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<std::string> 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<std::string> 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<animrig::Strip *> strips = layer->strips();
|
||||
const int index = strips.first_index_try(&strip_to_find);
|
||||
if (index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PointerRNA layer_ptr = RNA_pointer_create(&anim.id, &RNA_AnimationLayer, layer);
|
||||
const std::optional<std::string> 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user