Anim: show animated properties with the appropriate color in the GUI

Use the `Animation` data-block to find F-Curves, for drawing the
background color of properties in the GUI (green for 'animated', yellow
for 'keyed on this frame', etc.).

This assumes (correctly) that the `Animation` is limited to a single
layer with a single strip. As such, there is only one set of F-Curves
for every animated ID, which means that the correct F-Curve will be
found. This will need adjustment when the same property can have
multiple F-Curves (due to layers) or when an F-Curve may not be
applicable for each point in time (due to the use of finite strips).

Pull Request: https://projects.blender.org/blender/blender/pulls/118677
This commit is contained in:
Sybren A. Stüvel 2024-03-11 12:36:58 +01:00
parent 631f72265d
commit 694e5b50f4
6 changed files with 114 additions and 14 deletions

@ -10,6 +10,8 @@
#pragma once
#include "BLI_string_ref.hh"
struct ID;
struct Main;
@ -20,6 +22,8 @@ struct bAction;
namespace blender::animrig {
class Animation;
/**
* Get (or add relevant data to be able to do so) the Active Action for the given
* Animation Data block, given an ID block where the Animation Data should reside.
@ -44,4 +48,27 @@ void reevaluate_fcurve_errors(bAnimContext *ac);
*/
bool animdata_remove_empty_action(AnimData *adt);
/**
* Compatibility helper function for `BKE_animadata_fcurve_find_by_rna_path()`.
*
* Searches each layer (top to bottom) to find an FCurve that matches the given
* RNA path & index.
*
* \see BKE_animadata_fcurve_find_by_rna_path
*
* \note The returned FCurve should NOT be used for keyframe manipulation. Its
* existence is an indicator for "this property is animated".
*
* This function should probably be limited to the active layer (for the given
* property, once pinning to layers is there), so that the "this is keyed" color
* is more accurate.
*
* 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 ID &animated_id,
StringRefNull rna_path,
int array_index);
} // namespace blender::animrig

@ -6,6 +6,7 @@
* \ingroup animrig
*/
#include "ANIM_animation.hh"
#include "ANIM_animdata.hh"
#include "BKE_action.h"
@ -174,4 +175,46 @@ void reevaluate_fcurve_errors(bAnimContext *ac)
}
}
const FCurve *fcurve_find_by_rna_path(const Animation &anim,
const ID &animated_id,
const StringRefNull rna_path,
const int array_index)
{
const Binding *binding = anim.binding_for_id(animated_id);
if (!binding) {
/* No need to inspect anything if this ID does not have an animation Binding. */
return nullptr;
}
/* Iterate the layers top-down, as higher-up animation overrides (or at least can override)
* lower-down animation. */
for (int layer_idx = anim.layer_array_num - 1; layer_idx >= 0; layer_idx--) {
const Layer *layer = anim.layer(layer_idx);
/* TODO: refactor this into something nicer once we have different strip types. */
for (const Strip *strip : layer->strips()) {
switch (strip->type()) {
case Strip::Type::Keyframe: {
const KeyframeStrip &key_strip = strip->as<KeyframeStrip>();
const ChannelBag *channelbag_for_binding = key_strip.channelbag_for_binding(*binding);
if (!channelbag_for_binding) {
continue;
}
const FCurve *fcu = channelbag_for_binding->fcurve_find(rna_path, array_index);
if (!fcu) {
continue;
}
/* This code assumes that there is only one strip, and that it's infinite. When that
* changes, this code needs to be expanded to check for strip boundaries. */
return fcu;
}
}
/* Explicit lack of 'default' clause, to get compiler warnings when strip types are added. */
}
}
return nullptr;
}
} // namespace blender::animrig

@ -292,8 +292,10 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con
/**
* Find an F-Curve from its rna path and index.
*
* If there is an action assigned to the `animdata`, it will be searched for a matching F-curve
* first. Drivers are searched only if no valid action F-curve could be found.
* The search order is as follows. The first match will be returned:
* - Animation
* - Action
* - Drivers
*
* \note Typically, indices in RNA arrays are stored separately in F-curves, so the rna_path
* should not include them (e.g. `rna_path='location[0]'` will not match any F-Curve on an Object,
@ -301,8 +303,13 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con
*
* \note Return pointer parameters (`r_action`, `r_driven` and `r_special`) are all optional and
* may be NULL.
*
* \note since Animation data-blocks may have multiple layers all containing an F-Curve for this
* property, what is returned is a best-effort guess. The topmost layer has priority, and it is
* assumed that when it has a strip, it's infinite.
*/
FCurve *BKE_animadata_fcurve_find_by_rna_path(AnimData *animdata,
FCurve *BKE_animadata_fcurve_find_by_rna_path(const ID *id,
AnimData *animdata,
const char *rna_path,
const int rna_index,
bAction **r_action,

@ -14,6 +14,9 @@
#include "MEM_guardedalloc.h"
#include "ANIM_animation.hh"
#include "ANIM_animdata.hh"
#include "DNA_anim_types.h"
#include "DNA_object_types.h"
#include "DNA_text_types.h"
@ -38,6 +41,7 @@
#include "BKE_idprop.hh"
#include "BKE_lib_query.hh"
#include "BKE_nla.h"
#include "BKE_scene.hh"
#include "BLO_read_write.hh"
@ -247,7 +251,7 @@ FCurve *id_data_find_fcurve(
* needs to be re-checked I think?. */
bool is_driven = false;
FCurve *fcu = BKE_animadata_fcurve_find_by_rna_path(
adt, path->c_str(), index, nullptr, &is_driven);
id, adt, path->c_str(), index, nullptr, &is_driven);
if (is_driven) {
if (r_driven != nullptr) {
*r_driven = is_driven;
@ -344,8 +348,12 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con
return matches;
}
FCurve *BKE_animadata_fcurve_find_by_rna_path(
AnimData *animdata, const char *rna_path, int rna_index, bAction **r_action, bool *r_driven)
FCurve *BKE_animadata_fcurve_find_by_rna_path(const ID *id,
AnimData *animdata,
const char *rna_path,
int rna_index,
bAction **r_action,
bool *r_driven)
{
if (r_driven != nullptr) {
*r_driven = false;
@ -354,11 +362,24 @@ FCurve *BKE_animadata_fcurve_find_by_rna_path(
*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);
const bool has_drivers = !BLI_listbase_is_empty(&animdata->drivers);
/* Animation takes priority over drivers. */
if (has_action_fcurves) {
FCurve *fcu = BKE_fcurve_find(&animdata->action->curves, rna_path, rna_index);
@ -371,6 +392,7 @@ FCurve *BKE_animadata_fcurve_find_by_rna_path(
}
/* If not animated, check if driven. */
const bool has_drivers = !BLI_listbase_is_empty(&animdata->drivers);
if (has_drivers) {
FCurve *fcu = BKE_fcurve_find(&animdata->drivers, rna_path, rna_index);
@ -460,7 +482,7 @@ FCurve *BKE_fcurve_find_by_rna_context_ui(bContext * /*C*/,
/* Standard F-Curve from animdata - Animation (Action) or Drivers. */
FCurve *fcu = BKE_animadata_fcurve_find_by_rna_path(
adt, rna_path->c_str(), rnaindex, r_action, r_driven);
ptr->owner_id, adt, rna_path->c_str(), rnaindex, r_action, r_driven);
if (fcu != nullptr && r_animdata != nullptr) {
*r_animdata = adt;

@ -360,12 +360,12 @@ bool BKE_lib_override_library_property_is_animated(
const char index_token_start_backup = *index_token_start;
*index_token_start = '\0';
fcurve = BKE_animadata_fcurve_find_by_rna_path(
anim_data, liboverride_prop->rna_path, rnaprop_index, nullptr, nullptr);
id, anim_data, liboverride_prop->rna_path, rnaprop_index, nullptr, nullptr);
*index_token_start = index_token_start_backup;
}
else {
fcurve = BKE_animadata_fcurve_find_by_rna_path(
anim_data, liboverride_prop->rna_path, 0, nullptr, nullptr);
id, anim_data, liboverride_prop->rna_path, 0, nullptr, nullptr);
}
if (fcurve != nullptr) {
return true;

@ -39,6 +39,7 @@
#include "BKE_lib_id.hh"
#include "BKE_mask.h"
#include "BKE_nla.h"
#include "BKE_scene.hh"
#include "BKE_screen.hh"
#include "BKE_workspace.h"
@ -4576,7 +4577,7 @@ static blender::Vector<FCurve *> get_fcurves_of_property(
const int length = RNA_property_array_length(ptr, prop);
for (int i = 0; i < length; i++) {
FCurve *fcurve = BKE_animadata_fcurve_find_by_rna_path(
anim_data, path->c_str(), i, nullptr, nullptr);
id, anim_data, path->c_str(), i, nullptr, nullptr);
if (fcurve != nullptr) {
fcurves.append(fcurve);
}
@ -4584,7 +4585,7 @@ static blender::Vector<FCurve *> get_fcurves_of_property(
}
else {
FCurve *fcurve = BKE_animadata_fcurve_find_by_rna_path(
anim_data, path->c_str(), index, nullptr, nullptr);
id, anim_data, path->c_str(), index, nullptr, nullptr);
if (fcurve != nullptr) {
fcurves.append(fcurve);
}