WIP: Anim: Baklava, add Animation data-block to anim filtering code #119875

Closed
Sybren A. Stüvel wants to merge 4 commits from dr.sybren:pr/baklava-animation-filtering into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
16 changed files with 396 additions and 150 deletions

View File

@ -123,6 +123,13 @@ void animdata_fcurve_delete(bAnimContext *ac, AnimData *adt, FCurve *fcu)
*/
animdata_remove_empty_action(adt);
}
else if (adt->animation) {
/* TODO: support deleting FCurves from Animation data-blocks. */
return;
}
else {
BLI_assert_unreachable();
}
BKE_fcurve_free(fcu);
}

View File

@ -57,6 +57,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GREASE_PENCIL_V3)
add_definitions(-DWITH_ANIM_BAKLAVA)
endif()
blender_add_lib(bf_editor_animation "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@ -9,6 +9,7 @@
#include <cstdio>
#include "ANIM_action.hh"
#include "ANIM_animation.hh"
#include "ANIM_animdata.hh"
#include "ANIM_keyframing.hh"
@ -76,6 +77,8 @@
#include "WM_api.hh"
#include "WM_types.hh"
using namespace blender;
/* *********************************************** */
/* XXX constant defines to be moved elsewhere? */
@ -1246,6 +1249,94 @@ static bAnimChannelType ACF_NLACURVE = {
/*setting_ptr*/ acf_fcurve_setting_ptr,
};
/* Object Animation Expander ------------------------------------------- */
#ifdef WITH_ANIM_BAKLAVA
/* TODO: just get this from RNA? */
static int acf_fillanim_icon(bAnimListElem * /*ale*/)
{
return ICON_ACTION; /* TODO: give Animation its own icon? */
}
/* check if some setting exists for this channel */
static bool acf_fillanim_setting_valid(bAnimContext * /*ac*/,
bAnimListElem * /*ale*/,
eAnimChannel_Settings setting)
{
switch (setting) {
case ACHANNEL_SETTING_SELECT:
case ACHANNEL_SETTING_EXPAND:
return true;
default:
return false;
}
}
/* Get the appropriate flag(s) for the setting when it is valid. */
static int acf_fillanim_setting_flag(bAnimContext * /*ac*/,
eAnimChannel_Settings setting,
bool *r_neg)
{
*r_neg = false;
switch (setting) {
case ACHANNEL_SETTING_SELECT:
return ADT_UI_SELECTED;
case ACHANNEL_SETTING_EXPAND:
return ADT_UI_EXPANDED;
default:
return 0;
}
}
/* get pointer to the setting */
static void *acf_fillanim_setting_ptr(bAnimListElem *ale,
eAnimChannel_Settings setting,
short *r_type)
{
AnimData *adt = ale->adt;
BLI_assert(adt);
*r_type = 0;
switch (setting) {
case ACHANNEL_SETTING_SELECT:
return GET_ACF_FLAG_PTR(adt->flag, r_type);
case ACHANNEL_SETTING_EXPAND:
return GET_ACF_FLAG_PTR(adt->flag, r_type);
default:
return nullptr;
}
}
/** Object Animation expander type define. */
static bAnimChannelType ACF_FILLANIM = {
/*channel_type_name*/ "Ob-Animation Filler",
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
/*name*/ acf_generic_idblock_name,
/*name_prop*/ acf_generic_idfill_name_prop,
/*icon*/ acf_fillanim_icon,
/*has_setting*/ acf_fillanim_setting_valid,
/*setting_flag*/ acf_fillanim_setting_flag,
/*setting_ptr*/ acf_fillanim_setting_ptr,
};
#endif
/* Object Action Expander ------------------------------------------- */
/* TODO: just get this from RNA? */
@ -4280,6 +4371,11 @@ static void ANIM_init_channel_typeinfo_data()
animchannelTypeInfo[type++] = &ACF_NLACONTROLS; /* NLA Control FCurve Expander */
animchannelTypeInfo[type++] = &ACF_NLACURVE; /* NLA Control FCurve Channel */
#ifdef WITH_ANIM_BAKLAVA
animchannelTypeInfo[type++] = &ACF_FILLANIM; /* Object Animation Expander */
#else
animchannelTypeInfo[type++] = nullptr;
#endif
animchannelTypeInfo[type++] = &ACF_FILLACTD; /* Object Action Expander */
animchannelTypeInfo[type++] = &ACF_FILLDRIVERS; /* Drivers Expander */
@ -4325,6 +4421,11 @@ static void ANIM_init_channel_typeinfo_data()
animchannelTypeInfo[type++] = &ACF_NLATRACK; /* NLA Track */
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");
#endif
}
}

View File

@ -256,6 +256,7 @@ void ANIM_set_active_channel(bAnimContext *ac,
break;
}
case ANIMTYPE_FILLACTD: /* Action Expander */
case ANIMTYPE_FILLANIM: /* Animation Expander */
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
@ -312,6 +313,7 @@ void ANIM_set_active_channel(bAnimContext *ac,
break;
}
case ANIMTYPE_FILLACTD: /* Action Expander */
case ANIMTYPE_FILLANIM: /* Animation Expander */
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
@ -365,6 +367,7 @@ 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_DSLAM:
case ANIMTYPE_DSCAM:
@ -501,6 +504,7 @@ 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_DSLAM:
case ANIMTYPE_DSCAM:
@ -615,6 +619,7 @@ static void anim_channels_select_set(bAnimContext *ac,
break;
}
case ANIMTYPE_FILLACTD: /* Action Expander */
case ANIMTYPE_FILLANIM: /* Animation Expander */
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
@ -3832,6 +3837,7 @@ static int mouse_anim_channels(bContext *C,
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_DSLAM:
case ANIMTYPE_DSCAM:

View File

@ -55,7 +55,10 @@ 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->action != nullptr) {
if (adt->animation != nullptr) {
DEG_id_tag_update(&adt->animation->id, ID_RECALC_ANIMATION);
}
else if (adt->action != nullptr) {
DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION);
}
}

View File

@ -88,8 +88,11 @@
#include "SEQ_sequencer.hh"
#include "SEQ_utils.hh"
#include "ANIM_animation.hh"
#include "ANIM_bone_collections.hh"
using namespace blender;
/* ************************************************************ */
/* Blender Context <-> Animation Context mapping */
@ -446,14 +449,14 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
/* ............................... */
/* quick macro to test if AnimData is usable */
#define ANIMDATA_HAS_KEYS(id) ((id)->adt && (id)->adt->action)
/* quick macro to test if AnimData has usable Action */
#define ANIMDATA_HAS_KEYS(id) ((id)->adt && !(id)->adt->animation && (id)->adt->action)
/* 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->nla_tracks.first)
#define ANIMDATA_HAS_NLA(id) ((id)->adt && !(id)->adt->animation && (id)->adt->nla_tracks.first)
/**
* Quick macro to test for all three above usability tests, performing the appropriate provided
@ -478,6 +481,7 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
* - driversOk: line or block of code to execute for Drivers case
* - nlaKeysOk: line or block of code for NLA Strip Keyframes case
* - keysOk: line or block of code for Keyframes case
* - animOk: line or block of code for Keyframes from Animation data blocks case
*
* The checks for the various cases are as follows:
* 0) top level: checks for animdata and also that all the F-Curves for the block will be visible
@ -489,8 +493,9 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
* 3) drivers: include drivers from animdata block (for Drivers mode in Graph Editor)
* 4A) nla strip keyframes: these are the per-strip controls for time and influence
* 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) \
#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, nlaKeysOk, keysOk, animOk) \
{ \
if ((id)->adt) { \
if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || \
@ -511,6 +516,9 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
driversOk \
} \
} \
else if ((id)->adt->animation) { \
animOk \
} \
else { \
if (ANIMDATA_HAS_NLA(id)) { \
nlaKeysOk \
@ -580,6 +588,36 @@ bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
/* ----------- 'Private' Stuff --------------- */
/**
* Set `ale` so that it points to the top-most 'summary' channel of the given `adt`.
* So this is either the Animation or the Action, or empty.
*/
static void key_data_from_adt(bAnimListElem &ale, AnimData *adt)
{
ale.adt = adt;
if (!adt) {
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;
}
/* this function allocates memory for a new bAnimListElem struct for the
* provided animation channel-data.
*/
@ -634,6 +672,15 @@ static bAnimListElem *make_new_animlistelem(void *data,
ale->adt = BKE_animdata_from_id(&ob->id);
break;
}
case ANIMTYPE_FILLANIM: {
Animation *anim = (Animation *)data;
ale->flag = anim->flag;
ale->key_data = anim;
ale->datatype = ALE_ANIM;
break;
}
case ANIMTYPE_FILLACTD: {
bAction *act = (bAction *)data;
@ -655,244 +702,125 @@ static bAnimListElem *make_new_animlistelem(void *data,
}
case ANIMTYPE_DSMAT: {
Material *ma = (Material *)data;
AnimData *adt = ma->adt;
ale->flag = FILTER_MAT_OBJD(ma);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, ma->adt);
break;
}
case ANIMTYPE_DSLAM: {
Light *la = (Light *)data;
AnimData *adt = la->adt;
ale->flag = FILTER_LAM_OBJD(la);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, la->adt);
break;
}
case ANIMTYPE_DSCAM: {
Camera *ca = (Camera *)data;
AnimData *adt = ca->adt;
ale->flag = FILTER_CAM_OBJD(ca);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, ca->adt);
break;
}
case ANIMTYPE_DSCACHEFILE: {
CacheFile *cache_file = (CacheFile *)data;
AnimData *adt = cache_file->adt;
ale->flag = FILTER_CACHEFILE_OBJD(cache_file);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, cache_file->adt);
break;
}
case ANIMTYPE_DSCUR: {
Curve *cu = (Curve *)data;
AnimData *adt = cu->adt;
ale->flag = FILTER_CUR_OBJD(cu);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, cu->adt);
break;
}
case ANIMTYPE_DSARM: {
bArmature *arm = (bArmature *)data;
AnimData *adt = arm->adt;
ale->flag = FILTER_ARM_OBJD(arm);
dr.sybren marked this conversation as resolved Outdated

Love all these replacements. So much cleaner.

Love all these replacements. So much cleaner.
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, arm->adt);
break;
}
case ANIMTYPE_DSMESH: {
Mesh *mesh = (Mesh *)data;
AnimData *adt = mesh->adt;
ale->flag = FILTER_MESH_OBJD(mesh);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, mesh->adt);
break;
}
case ANIMTYPE_DSLAT: {
Lattice *lt = (Lattice *)data;
AnimData *adt = lt->adt;
ale->flag = FILTER_LATTICE_OBJD(lt);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, lt->adt);
break;
}
case ANIMTYPE_DSSPK: {
Speaker *spk = (Speaker *)data;
AnimData *adt = spk->adt;
ale->flag = FILTER_SPK_OBJD(spk);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, spk->adt);
break;
}
case ANIMTYPE_DSHAIR: {
Curves *curves = (Curves *)data;
AnimData *adt = curves->adt;
ale->flag = FILTER_CURVES_OBJD(curves);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, curves->adt);
break;
}
case ANIMTYPE_DSPOINTCLOUD: {
PointCloud *pointcloud = (PointCloud *)data;
AnimData *adt = pointcloud->adt;
ale->flag = FILTER_POINTS_OBJD(pointcloud);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, pointcloud->adt);
break;
}
case ANIMTYPE_DSVOLUME: {
Volume *volume = (Volume *)data;
AnimData *adt = volume->adt;
ale->flag = FILTER_VOLUME_OBJD(volume);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, volume->adt);
break;
}
case ANIMTYPE_DSSKEY: {
Key *key = (Key *)data;
AnimData *adt = key->adt;
ale->flag = FILTER_SKE_OBJD(key);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, key->adt);
break;
}
case ANIMTYPE_DSWOR: {
World *wo = (World *)data;
AnimData *adt = wo->adt;
ale->flag = FILTER_WOR_SCED(wo);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, wo->adt);
break;
}
case ANIMTYPE_DSNTREE: {
bNodeTree *ntree = (bNodeTree *)data;
AnimData *adt = ntree->adt;
ale->flag = FILTER_NTREE_DATA(ntree);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, ntree->adt);
break;
}
case ANIMTYPE_DSLINESTYLE: {
FreestyleLineStyle *linestyle = (FreestyleLineStyle *)data;
AnimData *adt = linestyle->adt;
ale->flag = FILTER_LS_SCED(linestyle);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, linestyle->adt);
break;
}
case ANIMTYPE_DSPART: {
ParticleSettings *part = (ParticleSettings *)ale->data;
AnimData *adt = part->adt;
ale->flag = FILTER_PART_OBJD(part);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, part->adt);
break;
}
case ANIMTYPE_DSTEX: {
Tex *tex = (Tex *)data;
AnimData *adt = tex->adt;
ale->flag = FILTER_TEX_DATA(tex);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, tex->adt);
break;
}
case ANIMTYPE_DSGPENCIL: {
bGPdata *gpd = (bGPdata *)data;
AnimData *adt = gpd->adt;
/* NOTE: we just reuse the same expand filter for this case */
ale->flag = EXPANDED_GPD(gpd);
/* XXX: currently, this is only used for access to its animation data */
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, gpd->adt);
break;
}
case ANIMTYPE_DSMCLIP: {
MovieClip *clip = (MovieClip *)data;
AnimData *adt = clip->adt;
ale->flag = EXPANDED_MCLIP(clip);
ale->key_data = (adt) ? adt->action : nullptr;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
key_data_from_adt(*ale, clip->adt);
break;
}
case ANIMTYPE_NLACONTROLS: {
@ -1366,6 +1294,37 @@ static size_t animfilter_fcurves(ListBase *anim_data,
return items;
}
static size_t animfilter_fcurves_span(ListBase * /*bAnimListElem*/ anim_data,
bDopeSheet * /*ads*/,
Span<FCurve *> fcurves,
const int filter_mode,
ID *owner_id,
ID *fcurve_owner_id)
{
size_t num_items = 0;
BLI_assert(owner_id);
for (FCurve *fcu : fcurves) {
/* make_new_animlistelem will return nullptr when fcu == nullptr, and that's
* going to cause problems. */
BLI_assert(fcu);
/* TODO: deal with `filter_mode` and `ads->filterflag`.
* See `animfilter_fcurve_next()`. */
if (filter_mode & ANIMFILTER_TMP_PEEK) {
/* Found an animation channel, which is good enough for the 'TMP_PEEK' mode. */
return 1;
}
bAnimListElem *ale = make_new_animlistelem(fcu, ANIMTYPE_FCURVE, owner_id, fcurve_owner_id);
BLI_addtail(anim_data, ale);
num_items++;
}
return num_items;
}
static size_t animfilter_act_group(bAnimContext *ac,
ListBase *anim_data,
bDopeSheet *ads,
@ -1501,6 +1460,32 @@ static size_t animfilter_action(bAnimContext *ac,
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)))
{
return 0;
}
/* 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);
}
/* Include NLA-Data for NLA-Editor:
* - When ANIMFILTER_LIST_CHANNELS is used, that means we should be filtering the list for display
* Although the evaluation order is from the first track to the last and then apply the
@ -1692,8 +1677,11 @@ static size_t animfilter_block_data(
{ /* NLA Control Keyframes */
items += animfilter_nla_controls(anim_data, ads, adt, filter_mode, id);
},
{ /* Keyframes */
{ /* Keyframes from Action. */
items += animfilter_action(ac, anim_data, ads, adt->action, filter_mode, id);
},
{ /* Keyframes from Animation. */
items += animfilter_animation_fcurves(anim_data, ads, adt, filter_mode, id);
});
}
@ -2968,10 +2956,15 @@ static size_t animdata_filter_ds_obanim(
expanded = EXPANDED_DRVD(adt);
},
{/* NLA Strip Controls - no dedicated channel for now (XXX) */},
{ /* Keyframes */
{ /* Keyframes from Action. */
type = ANIMTYPE_FILLACTD;
cdata = adt->action;
expanded = EXPANDED_ACTC(adt->action);
},
{ /* Keyframes from Animation. */
type = ANIMTYPE_FILLANIM;
cdata = adt->animation;
expanded = EXPANDED_ADT(adt);
});
/* add object-level animation channels */
@ -3147,10 +3140,15 @@ static size_t animdata_filter_ds_scene(
expanded = EXPANDED_DRVD(adt);
},
{/* NLA Strip Controls - no dedicated channel for now (XXX) */},
{ /* Keyframes */
{ /* Keyframes from Action. */
type = ANIMTYPE_FILLACTD;
cdata = adt->action;
expanded = EXPANDED_ACTC(adt->action);
},
{ /* Keyframes from Animation. */
type = ANIMTYPE_FILLANIM;
cdata = adt->animation;
expanded = EXPANDED_ADT(adt);
});
/* add scene-level animation channels */

View File

@ -376,6 +376,7 @@ enum class ChannelType {
SCENE,
OBJECT,
FCURVE,
ANIMATION,
ACTION,
ACTION_GROUP,
GREASE_PENCIL_CELS,
@ -401,6 +402,7 @@ struct ChannelListElement {
Object *ob;
AnimData *adt;
FCurve *fcu;
Animation *anim;
bAction *act;
bActionGroup *agrp;
bGPDlayer *gpl;
@ -429,6 +431,10 @@ 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);
break;
}
case ChannelType::ACTION: {
action_to_keylist(elem->adt, elem->act, elem->keylist, elem->saction_flag, range);
break;
@ -697,6 +703,25 @@ 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)
{
BLI_assert(anim);
const bool locked = (anim && (ID_IS_LINKED(anim) || ID_IS_OVERRIDE_LIBRARY(anim)));
saction_flag &= ~SACTION_SHOW_EXTREMES;
ChannelListElement *draw_elem = channel_list_add_element(
channel_list, ChannelType::ANIMATION, ypos, yscale_fac, eSAction_Flag(saction_flag));
draw_elem->adt = adt;
draw_elem->anim = anim;
draw_elem->channel_locked = locked;
}
void ED_add_action_channel(ChannelDrawList *channel_list,
AnimData *adt,
bAction *act,

View File

@ -30,6 +30,10 @@
#include "ED_keyframes_edit.hh"
#include "ED_markers.hh"
#include "ANIM_animation.hh"
using namespace blender;
/* This file defines an API and set of callback-operators for
* non-destructive editing of keyframe data.
*
@ -162,6 +166,32 @@ static short agrp_keyframes_loop(KeyframeEditData *ked,
return 0;
}
#ifdef WITH_ANIM_BAKLAVA
/* Loop over all keyframes in the Animation. */
static short anim_keyframes_loop(KeyframeEditData *ked,
animrig::Animation &anim,
animrig::Binding *binding,
KeyframeEditFunc key_ok,
KeyframeEditFunc key_cb,
FcuEditFunc fcu_cb)
{
if (!binding) {
/* Valid situation, and will not have any FCurves. */
return 0;
}
Span<FCurve *> fcurves = animrig::fcurves_for_animation(anim, binding->handle);
for (FCurve *fcurve : fcurves) {
if (ANIM_fcurve_keyframes_loop(ked, fcurve, key_ok, key_cb, fcu_cb)) {
return 1;
}
}
return 0;
}
#endif
/* This function is used to loop over the keyframe data in an Action */
static short act_keyframes_loop(KeyframeEditData *ked,
bAction *act,
@ -385,9 +415,20 @@ 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. */
dr.sybren marked this conversation as resolved Outdated

It wasn't immediately clear to me why this case only executes on a single binding, rather than all bindings within the Animation datablock. As it is, it feels redundant with the ALE_ANIM_BINDING case below.

I think the reason in this case is because this is for when the Animation datablock is hierarchically displayed under the relevant ID in the channel list, and thus only the single binding to that ID is relevant. Is that correct? If so, it might be worth documenting that in a short comment.

It wasn't immediately clear to me why this case only executes on a single binding, rather than all bindings within the `Animation` datablock. As it is, it feels redundant with the `ALE_ANIM_BINDING` case below. I *think* the reason in this case is because this is for when the `Animation` datablock is hierarchically displayed *under* the relevant ID in the channel list, and thus only the single binding to that ID is relevant. Is that correct? If so, it might be worth documenting that in a short comment.
#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();
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
return 0;
#endif
}
case ALE_ACT: /* action */
return act_keyframes_loop(ked, (bAction *)ale->key_data, key_ok, key_cb, fcu_cb);
case ALE_OB: /* object */
return ob_keyframes_loop(ked, ads, (Object *)ale->key_data, key_ok, key_cb, fcu_cb);
case ALE_SCE: /* scene */
@ -423,9 +464,12 @@ 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:
/* 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;
case ALE_ACT: /* action */
return act_keyframes_loop(ked, (bAction *)data, key_ok, key_cb, fcu_cb);
case ALE_OB: /* object */
return ob_keyframes_loop(ked, ads, (Object *)data, key_ok, key_cb, fcu_cb);
case ALE_SCE: /* scene */

View File

@ -36,6 +36,8 @@
#include "ED_anim_api.hh"
#include "ED_keyframes_keylist.hh"
#include "ANIM_animation.hh"
/* *************************** Keyframe Processing *************************** */
/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */
@ -1165,6 +1167,25 @@ 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,
AnimKeylist *keylist,

View File

@ -169,7 +169,7 @@ struct bAnimListElem {
*/
/** ID block that channel is attached to */
ID *id;
/** source of the animation data attached to ID block (for convenience) */
/** source of the animation data attached to ID block */
AnimData *adt;
/**
@ -212,6 +212,7 @@ enum eAnim_ChannelType {
ANIMTYPE_NLACONTROLS,
ANIMTYPE_NLACURVE,
ANIMTYPE_FILLANIM,
ANIMTYPE_FILLACTD,
ANIMTYPE_FILLDRIVERS,
@ -271,6 +272,7 @@ enum eAnim_KeyType {
ALE_OB, /* Object summary */
ALE_ACT, /* Action summary */
ALE_GROUP, /* Action Group summary */
ALE_ANIM, /* Animation data-block summary. */
ALE_GREASE_PENCIL_CEL, /* Grease Pencil Cels. */
ALE_GREASE_PENCIL_DATA, /* Grease Pencil Cels summary. */
@ -404,6 +406,7 @@ ENUM_OPERATORS(eAnimFilter_Flags, ANIMFILTER_TMP_IGNORE_ONLYSEL);
#define EXPANDED_ACTC(actc) ((actc->flag & ACT_COLLAPSED) == 0)
/* 'Sub-AnimData' channels */
#define EXPANDED_DRVD(adt) ((adt->flag & ADT_DRIVERS_COLLAPSED) == 0)
#define EXPANDED_ADT(adt) ((adt->flag & ADT_UI_EXPANDED) != 0)
/* Actions (also used for Dopesheet) */
/** Action Channel Group. */

View File

@ -12,6 +12,7 @@
#include "DNA_curve_types.h"
struct Animation;
struct AnimData;
struct ChannelDrawList;
struct FCurve;
@ -70,6 +71,13 @@ 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 */
void ED_add_action_channel(ChannelDrawList *draw_list,
AnimData *adt,

View File

@ -13,6 +13,7 @@
#include "DNA_curve_types.h"
struct Animation;
struct AnimData;
struct CacheFile;
struct FCurve;
@ -161,6 +162,9 @@ 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);

View File

@ -18,6 +18,7 @@
/* Types --------------------------------------------------------------- */
#include "DNA_anim_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
@ -221,6 +222,7 @@ static void draw_backdrops(bAnimContext *ac, ListBase &anim_data, View2D *v2d, u
break;
}
case ANIMTYPE_FILLACTD:
case ANIMTYPE_FILLANIM:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR: {
immUniformColor3ubvAlpha(col2b, sel ? col1[3] : col2b[3]);
@ -366,6 +368,14 @@ 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);
break;
case ALE_ACT:
ED_add_action_channel(draw_list,
adt,

View File

@ -112,6 +112,11 @@ 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);
break;
}
case ALE_ACT: {
bAction *act = (bAction *)ale->key_data;
action_to_keylist(adt, act, keylist, 0, range);

View File

@ -261,6 +261,9 @@ static int mouse_nla_tracks(bContext *C, bAnimContext *ac, int track_index, shor
}
break;
}
case ANIMTYPE_FILLANIM:
/* The NLA doesn't support Animation data-blocks. */
break;
default:
if (G.debug & G_DEBUG) {
printf("Error: Invalid track type in mouse_nla_tracks()\n");

View File

@ -1224,6 +1224,10 @@ typedef enum eAnimData_Flag {
/** F-Curves from this AnimData block are always visible. */
ADT_CURVES_ALWAYS_VISIBLE = (1 << 17),
/** Animation pointer to by this AnimData block is expanded in UI. This is stored on the AnimData
* so that each user of the Animation can have its own expansion/contraction state. */
ADT_UI_EXPANDED = (1 << 18),
} eAnimData_Flag;
/* Base Struct for Anim ------------------------------------- */
@ -1272,6 +1276,9 @@ typedef struct Animation {
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;