Blender V4.3
anim_filter.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9/* This file contains a system used to provide a layer of abstraction between sources
10 * of animation data and tools in Animation Editors. The method used here involves
11 * generating a list of edit structures which enable tools to naively perform the actions
12 * they require without all the boiler-plate associated with loops within loops and checking
13 * for cases to ignore.
14 *
15 * While this is primarily used for the Action/Dopesheet Editor (and its accessory modes),
16 * the Graph Editor also uses this for its channel list and for determining which curves
17 * are being edited. Likewise, the NLA Editor also uses this for its channel list and in
18 * its operators.
19 *
20 * NOTE: much of the original system this was based on was built before the creation of the RNA
21 * system. In future, it would be interesting to replace some parts of this code with RNA queries,
22 * however, RNA does not eliminate some of the boiler-plate reduction benefits presented by this
23 * system, so if any such work does occur, it should only be used for the internals used here...
24 *
25 * -- Joshua Leung, Dec 2008 (Last revision July 2009)
26 */
27
28#include <cstring>
29
30#include "DNA_anim_types.h"
31#include "DNA_armature_types.h"
32#include "DNA_brush_types.h"
33#include "DNA_cachefile_types.h"
34#include "DNA_camera_types.h"
35#include "DNA_curves_types.h"
38#include "DNA_key_types.h"
39#include "DNA_lattice_types.h"
40#include "DNA_layer_types.h"
41#include "DNA_light_types.h"
42#include "DNA_linestyle_types.h"
43#include "DNA_mask_types.h"
44#include "DNA_material_types.h"
45#include "DNA_mesh_types.h"
46#include "DNA_meta_types.h"
47#include "DNA_movieclip_types.h"
48#include "DNA_node_types.h"
50#include "DNA_object_types.h"
51#include "DNA_particle_types.h"
53#include "DNA_scene_types.h"
54#include "DNA_screen_types.h"
55#include "DNA_sequence_types.h"
56#include "DNA_space_types.h"
57#include "DNA_speaker_types.h"
58#include "DNA_userdef_types.h"
59#include "DNA_volume_types.h"
60#include "DNA_world_types.h"
61
62#include "MEM_guardedalloc.h"
63
64#include "BLI_alloca.h"
65#include "BLI_ghash.h"
66#include "BLI_string.h"
67#include "BLI_utildefines.h"
68
69#include "BKE_action.hh"
70#include "BKE_anim_data.hh"
71#include "BKE_collection.hh"
72#include "BKE_context.hh"
73#include "BKE_fcurve.hh"
74#include "BKE_fcurve_driver.h"
75#include "BKE_global.hh"
76#include "BKE_grease_pencil.hh"
77#include "BKE_key.hh"
78#include "BKE_layer.hh"
79#include "BKE_main.hh"
80#include "BKE_mask.h"
81#include "BKE_material.h"
82#include "BKE_modifier.hh"
83#include "BKE_node.hh"
84
85#include "ED_anim_api.hh"
86#include "ED_markers.hh"
87
88#include "SEQ_sequencer.hh"
89#include "SEQ_utils.hh"
90
91#include "ANIM_action.hh"
93
94#include "anim_intern.hh"
95
96using namespace blender;
97
98/* ************************************************************ */
99/* Blender Context <-> Animation Context mapping */
100
101/* ----------- Private Stuff - Action Editor ------------- */
102
103/* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */
104/* NOTE: there's a similar function in `key.cc` #BKE_key_from_object. */
106{
107 Scene *scene = ac->scene;
108 ViewLayer *view_layer = ac->view_layer;
109 Object *ob;
110
111 BKE_view_layer_synced_ensure(scene, view_layer);
112 ob = BKE_view_layer_active_object_get(view_layer);
113 if (ob == nullptr) {
114 return nullptr;
115 }
116
117 /* XXX pinning is not available in 'ShapeKey' mode... */
118 // if (saction->pin) { return nullptr; }
119
120 /* shapekey data is stored with geometry data */
121 return BKE_key_from_object(ob);
122}
123
124/* Get data being edited in Action Editor (depending on current 'mode') */
126{
127 /* get dopesheet */
128 ac->ads = &saction->ads;
129 ac->dopesheet_mode = eAnimEdit_Context(saction->mode);
130
131 /* sync settings with current view status, then return appropriate data */
132 switch (saction->mode) {
133 case SACTCONT_ACTION: /* 'Action Editor' */
134 /* if not pinned, sync with active object */
135 if (/* `saction->pin == 0` */ true) {
136 if (ac->obact && ac->obact->adt) {
137 saction->action = ac->obact->adt->action;
138 }
139 else {
140 saction->action = nullptr;
141 }
142 }
143
145 ac->data = saction->action;
146
147 return true;
148
149 case SACTCONT_SHAPEKEY: /* 'ShapeKey Editor' */
151 ac->data = actedit_get_shapekeys(ac);
152
153 /* if not pinned, sync with active object */
154 if (/* `saction->pin == 0` */ true) {
155 Key *key = (Key *)ac->data;
156
157 if (key && key->adt) {
158 saction->action = key->adt->action;
159 }
160 else {
161 saction->action = nullptr;
162 }
163 }
164 return true;
165
166 case SACTCONT_GPENCIL: /* Grease Pencil */ /* XXX review how this mode is handled... */
167 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
168 saction->ads.source = (ID *)ac->scene;
169
171 ac->data = &saction->ads;
172 return true;
173
174 case SACTCONT_CACHEFILE: /* Cache File */ /* XXX review how this mode is handled... */
175 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
176 saction->ads.source = (ID *)ac->scene;
177
179 ac->data = &saction->ads;
180 return true;
181
182 case SACTCONT_MASK: /* Mask */ /* XXX: review how this mode is handled. */
183 {
184 /* TODO: other methods to get the mask. */
185#if 0
187 MovieClip *clip = ac->scene->clip;
188 struct Mask *mask = seq ? seq->mask : nullptr;
189#endif
190
191 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
192 saction->ads.source = (ID *)ac->scene;
193
195 ac->data = &saction->ads;
196 return true;
197 }
198
199 case SACTCONT_DOPESHEET: /* DopeSheet */
200 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
201 saction->ads.source = (ID *)ac->scene;
202
204 ac->data = &saction->ads;
205 return true;
206
207 case SACTCONT_TIMELINE: /* Timeline */
208 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
209 saction->ads.source = (ID *)ac->scene;
210
211 /* sync scene's "selected keys only" flag with our "only selected" flag
212 *
213 * XXX: This is a workaround for #55525. We shouldn't really be syncing the flags like this,
214 * but it's a simpler fix for now than also figuring out how the next/prev keyframe
215 * tools should work in the 3D View if we allowed full access to the timeline's
216 * dopesheet filters (i.e. we'd have to figure out where to host those settings,
217 * to be on a scene level like this flag currently is, along with several other unknowns).
218 */
219 if (ac->scene->flag & SCE_KEYS_NO_SELONLY) {
220 saction->ads.filterflag &= ~ADS_FILTER_ONLYSEL;
221 }
222 else {
224 }
225
227 ac->data = &saction->ads;
228 return true;
229
230 default: /* unhandled yet */
232 ac->data = nullptr;
233 return false;
234 }
235}
236
237/* ----------- Private Stuff - Graph Editor ------------- */
238
239/* Get data being edited in Graph Editor (depending on current 'mode') */
241{
242 /* init dopesheet data if non-existent (i.e. for old files) */
243 if (sipo->ads == nullptr) {
244 sipo->ads = static_cast<bDopeSheet *>(MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet"));
245 sipo->ads->source = (ID *)ac->scene;
246 }
247 ac->ads = sipo->ads;
249
250 /* set settings for Graph Editor - "Selected = Editable" */
251 if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
253 }
254 else {
255 sipo->ads->filterflag &= ~ADS_FILTER_SELEDIT;
256 }
257
258 /* sync settings with current view status, then return appropriate data */
259 switch (sipo->mode) {
260 case SIPO_MODE_ANIMATION: /* Animation F-Curve Editor */
261 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
262 sipo->ads->source = (ID *)ac->scene;
263 sipo->ads->filterflag &= ~ADS_FILTER_ONLYDRIVERS;
264
266 ac->data = sipo->ads;
267 return true;
268
269 case SIPO_MODE_DRIVERS: /* Driver F-Curve Editor */
270 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
271 sipo->ads->source = (ID *)ac->scene;
273
275 ac->data = sipo->ads;
276 return true;
277
278 default: /* unhandled yet */
280 ac->data = nullptr;
281 return false;
282 }
283}
284
285/* ----------- Private Stuff - NLA Editor ------------- */
286
287/* Get data being edited in Graph Editor (depending on current 'mode') */
289{
290 /* init dopesheet data if non-existent (i.e. for old files) */
291 if (snla->ads == nullptr) {
292 snla->ads = static_cast<bDopeSheet *>(MEM_callocN(sizeof(bDopeSheet), "NlaEdit DopeSheet"));
293 }
294 ac->ads = snla->ads;
295
296 /* sync settings with current view status, then return appropriate data */
297 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
298 snla->ads->source = (ID *)ac->scene;
300
302 ac->data = snla->ads;
303
304 return true;
305}
306
307/* ----------- Public API --------------- */
308
310{
311 SpaceLink *sl = ac->sl;
312 bool ok = false;
313
314 /* context depends on editor we are currently in */
315 if (sl) {
316 switch (ac->spacetype) {
317 case SPACE_ACTION: {
318 SpaceAction *saction = (SpaceAction *)sl;
319 ok = actedit_get_context(ac, saction);
320 break;
321 }
322 case SPACE_GRAPH: {
323 SpaceGraph *sipo = (SpaceGraph *)sl;
324 ok = graphedit_get_context(ac, sipo);
325 break;
326 }
327 case SPACE_NLA: {
328 SpaceNla *snla = (SpaceNla *)sl;
329 ok = nlaedit_get_context(ac, snla);
330 break;
331 }
332 case SPACE_EMPTY:
333 case SPACE_VIEW3D:
334 case SPACE_OUTLINER:
335 case SPACE_PROPERTIES:
336 case SPACE_FILE:
337 case SPACE_IMAGE:
338 case SPACE_INFO:
339 case SPACE_SEQ:
340 case SPACE_TEXT:
341 case SPACE_SCRIPT:
342 case SPACE_NODE:
343 case SPACE_CONSOLE:
344 case SPACE_USERPREF:
345 case SPACE_CLIP:
346 case SPACE_TOPBAR:
347 case SPACE_STATUSBAR:
349 break;
350 }
351 }
352
353 /* check if there's any valid data */
354 return (ok && ac->data);
355}
356
358{
359 Main *bmain = CTX_data_main(C);
360 ScrArea *area = CTX_wm_area(C);
361 ARegion *region = CTX_wm_region(C);
363 Scene *scene = CTX_data_scene(C);
364
365 /* clear old context info */
366 if (ac == nullptr) {
367 return false;
368 }
369 memset(ac, 0, sizeof(bAnimContext));
370
371 /* get useful default context settings from context */
372 ac->bmain = bmain;
373 ac->scene = scene;
375 if (scene) {
378 }
381 ac->area = area;
382 ac->region = region;
383 ac->sl = sl;
384 ac->spacetype = eSpace_Type((area) ? area->spacetype : 0);
385 ac->regiontype = eRegion_Type((region) ? region->regiontype : 0);
386
387 /* get data context info */
388 /* XXX: if the below fails, try to grab this info from context instead...
389 * (to allow for scripting). */
391}
392
397
398/* ************************************************************ */
399/* Blender Data <-- Filter --> Channels to be operated on */
400
401/* macros to use before/after getting the sub-channels of some channel,
402 * to abstract away some of the tricky logic involved
403 *
404 * cases:
405 * 1) Graph Edit main area (just data) OR channels visible in Channel List
406 * 2) If not showing channels, we're only interested in the data (Action Editor's editing)
407 * 3) We don't care what data, we just care there is some (so that a collapsed
408 * channel can be kept around). No need to clear channels-flag in order to
409 * keep expander channels with no sub-data out, as those cases should get
410 * dealt with by the recursive detection idiom in place.
411 *
412 * Implementation NOTE:
413 * YES the _doSubChannels variable is NOT read anywhere. BUT, this is NOT an excuse
414 * to go steamrolling the logic into a single-line expression as from experience,
415 * those are notoriously difficult to read + debug when extending later on. The code
416 * below is purposefully laid out so that each case noted above corresponds clearly to
417 * one case below.
418 */
419#define BEGIN_ANIMFILTER_SUBCHANNELS(expanded_check) \
420 { \
421 const eAnimFilter_Flags _filter = filter_mode; \
422 short _doSubChannels = 0; \
423 if (!(filter_mode & ANIMFILTER_LIST_VISIBLE) || (expanded_check)) { \
424 _doSubChannels = 1; \
425 } \
426 else if (!(filter_mode & ANIMFILTER_LIST_CHANNELS)) { \
427 _doSubChannels = 2; \
428 } \
429 else { \
430 filter_mode |= ANIMFILTER_TMP_PEEK; \
431 } \
432\
433 { \
434 (void)_doSubChannels; \
435 }
436/* ... standard sub-channel filtering can go on here now ... */
437#define END_ANIMFILTER_SUBCHANNELS \
438 filter_mode = _filter; \
439 } \
440 (void)0
441
442/* ............................... */
443
444/* Test whether AnimData has a usable Action. */
445#define ANIMDATA_HAS_ACTION_LEGACY(id) \
446 ((id)->adt && (id)->adt->action && (id)->adt->action->wrap().is_action_legacy())
447
448#ifdef WITH_ANIM_BAKLAVA
449# define ANIMDATA_HAS_ACTION_LAYERED(id) \
450 ((id)->adt && (id)->adt->action && (id)->adt->action->wrap().is_action_layered())
451#else
452# define ANIMDATA_HAS_ACTION_LAYERED(id) false
453#endif
454
455/* quick macro to test if AnimData is usable for drivers */
456#define ANIMDATA_HAS_DRIVERS(id) ((id)->adt && (id)->adt->drivers.first)
457
458/* quick macro to test if AnimData is usable for NLA */
459#define ANIMDATA_HAS_NLA(id) \
460 ((id)->adt && (id)->adt->nla_tracks.first && \
461 (!(id)->adt->action || (id)->adt->action->wrap().is_action_legacy()))
462
500#define ANIMDATA_FILTER_CASES( \
501 id, adtOk, nlaOk, driversOk, nlaKeysOk, legacyActionOk, layeredActionOk) \
502 { \
503 if ((id)->adt) { \
504 if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || \
505 !((id)->adt->flag & ADT_CURVES_NOT_VISIBLE)) { \
506 if (filter_mode & ANIMFILTER_ANIMDATA) { \
507 adtOk \
508 } \
509 else if (ac->ads->filterflag & ADS_FILTER_ONLYNLA) { \
510 if (ANIMDATA_HAS_NLA(id)) { \
511 nlaOk \
512 } \
513 else if (!(ac->ads->filterflag & ADS_FILTER_NLA_NOACT) || \
514 ANIMDATA_HAS_ACTION_LEGACY(id)) { \
515 nlaOk \
516 } \
517 } \
518 else if (ac->ads->filterflag & ADS_FILTER_ONLYDRIVERS) { \
519 if (ANIMDATA_HAS_DRIVERS(id)) { \
520 driversOk \
521 } \
522 } \
523 else if (ANIMDATA_HAS_ACTION_LAYERED(id)) { \
524 layeredActionOk \
525 } \
526 else { \
527 if (ANIMDATA_HAS_NLA(id)) { \
528 nlaKeysOk \
529 } \
530 if (ANIMDATA_HAS_ACTION_LEGACY(id)) { \
531 legacyActionOk \
532 } \
533 } \
534 } \
535 } \
536 } \
537 (void)0
538
539/* ............................... */
540
547#define ANIMCHANNEL_NEW_CHANNEL_FULL( \
548 bmain, channel_data, channel_type, owner_id, fcurve_owner_id, ale_statement) \
549 if (filter_mode & ANIMFILTER_TMP_PEEK) { \
550 return 1; \
551 } \
552 { \
553 bAnimListElem *ale = make_new_animlistelem( \
554 bmain, channel_data, channel_type, (ID *)owner_id, fcurve_owner_id); \
555 if (ale) { \
556 BLI_addtail(anim_data, ale); \
557 items++; \
558 ale_statement \
559 } \
560 } \
561 (void)0
562
563#define ANIMCHANNEL_NEW_CHANNEL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id) \
564 ANIMCHANNEL_NEW_CHANNEL_FULL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id, {})
565
566/* ............................... */
567
568/* quick macro to test if an anim-channel representing an AnimData block is suitably active */
569#define ANIMCHANNEL_ACTIVEOK(ale) \
570 (!(filter_mode & ANIMFILTER_ACTIVE) || !(ale->adt) || (ale->adt->flag & ADT_UI_ACTIVE))
571
572/* Quick macro to test if an anim-channel (F-Curve, Group, etc.)
573 * is selected in an acceptable way. */
574#define ANIMCHANNEL_SELOK(test_func) \
575 (!(filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) || \
576 ((filter_mode & ANIMFILTER_SEL) && test_func) || \
577 ((filter_mode & ANIMFILTER_UNSEL) && test_func == 0))
578
588#define ANIMCHANNEL_SELEDITOK(test_func) \
589 (!(filter_mode & ANIMFILTER_SELEDIT) || !(filter_mode & ANIMFILTER_FOREDIT) || (test_func))
590
591/* ----------- 'Private' Stuff --------------- */
592
598{
599 ale.adt = adt;
600
601 if (!adt || !adt->action) {
602 ale.key_data = nullptr;
603 ale.datatype = ALE_NONE;
604 return;
605 }
606
607 blender::animrig::Action &action = adt->action->wrap();
608 ale.key_data = &action;
610}
611
612/* this function allocates memory for a new bAnimListElem struct for the
613 * provided animation channel-data.
614 */
616 Main *bmain, void *data, const eAnim_ChannelType datatype, ID *owner_id, ID *fcurve_owner_id)
617{
618 /* Only allocate memory if there is data to convert. */
619 if (!data) {
620 return nullptr;
621 }
622
623 /* Allocate and set generic data. */
624 bAnimListElem *ale = static_cast<bAnimListElem *>(
625 MEM_callocN(sizeof(bAnimListElem), "bAnimListElem"));
626
627 ale->data = data;
628 ale->type = datatype;
629
630 ale->bmain = bmain;
631 ale->id = owner_id;
632 ale->adt = BKE_animdata_from_id(owner_id);
633 ale->fcurve_owner_id = fcurve_owner_id;
634
635 /* do specifics */
636 switch (datatype) {
637 case ANIMTYPE_SUMMARY: {
638 /* Nothing to include for now... this is just a dummy wrapper around
639 * all the other channels in the DopeSheet, and gets included at the start of the list. */
640 ale->key_data = nullptr;
641 ale->datatype = ALE_ALL;
642 break;
643 }
644 case ANIMTYPE_SCENE: {
645 Scene *sce = (Scene *)data;
646
647 ale->flag = sce->flag;
648
649 ale->key_data = sce;
650 ale->datatype = ALE_SCE;
651
652 ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
653 break;
654 }
655 case ANIMTYPE_OBJECT: {
656 Base *base = (Base *)data;
657 Object *ob = base->object;
658
659 ale->flag = ob->flag;
660
661 ale->key_data = ob;
662 ale->datatype = ALE_OB;
663
664 ale->adt = BKE_animdata_from_id(&ob->id);
665 break;
666 }
668 bAction *action = (bAction *)data;
669
670 ale->flag = action->flag;
671
672 ale->key_data = action;
674 break;
675 }
677 animrig::Slot *slot = static_cast<animrig::Slot *>(data);
678 ale->flag = slot->slot_flags;
679
680 BLI_assert_msg(GS(fcurve_owner_id->name) == ID_AC, "fcurve_owner_id should be an Action");
681 /* ale->data = the slot itself, key_data = the Action. */
682 ale->key_data = fcurve_owner_id;
684 break;
685 }
686 case ANIMTYPE_FILLACTD: {
687 bAction *act = (bAction *)data;
688
689 ale->flag = act->flag;
690
691 ale->key_data = act;
692 ale->datatype = ALE_ACT;
693 break;
694 }
696 AnimData *adt = (AnimData *)data;
697
698 ale->flag = adt->flag;
699
700 /* XXX drivers don't show summary for now. */
701 ale->key_data = nullptr;
702 ale->datatype = ALE_NONE;
703 break;
704 }
705 case ANIMTYPE_DSMAT: {
706 Material *ma = (Material *)data;
707 ale->flag = FILTER_MAT_OBJD(ma);
708 key_data_from_adt(*ale, ma->adt);
709 break;
710 }
711 case ANIMTYPE_DSLAM: {
712 Light *la = (Light *)data;
713 ale->flag = FILTER_LAM_OBJD(la);
714 key_data_from_adt(*ale, la->adt);
715 break;
716 }
717 case ANIMTYPE_DSCAM: {
718 Camera *ca = (Camera *)data;
719 ale->flag = FILTER_CAM_OBJD(ca);
720 key_data_from_adt(*ale, ca->adt);
721 break;
722 }
724 CacheFile *cache_file = (CacheFile *)data;
725 ale->flag = FILTER_CACHEFILE_OBJD(cache_file);
726 key_data_from_adt(*ale, cache_file->adt);
727 break;
728 }
729 case ANIMTYPE_DSCUR: {
730 Curve *cu = (Curve *)data;
731 ale->flag = FILTER_CUR_OBJD(cu);
732 key_data_from_adt(*ale, cu->adt);
733 break;
734 }
735 case ANIMTYPE_DSARM: {
736 bArmature *arm = (bArmature *)data;
737 ale->flag = FILTER_ARM_OBJD(arm);
738 key_data_from_adt(*ale, arm->adt);
739 break;
740 }
741 case ANIMTYPE_DSMESH: {
742 Mesh *mesh = (Mesh *)data;
743 ale->flag = FILTER_MESH_OBJD(mesh);
744 key_data_from_adt(*ale, mesh->adt);
745 break;
746 }
747 case ANIMTYPE_DSLAT: {
748 Lattice *lt = (Lattice *)data;
749 ale->flag = FILTER_LATTICE_OBJD(lt);
750 key_data_from_adt(*ale, lt->adt);
751 break;
752 }
753 case ANIMTYPE_DSSPK: {
754 Speaker *spk = (Speaker *)data;
755 ale->flag = FILTER_SPK_OBJD(spk);
756 key_data_from_adt(*ale, spk->adt);
757 break;
758 }
759 case ANIMTYPE_DSHAIR: {
760 Curves *curves = (Curves *)data;
761 ale->flag = FILTER_CURVES_OBJD(curves);
762 key_data_from_adt(*ale, curves->adt);
763 break;
764 }
766 PointCloud *pointcloud = (PointCloud *)data;
767 ale->flag = FILTER_POINTS_OBJD(pointcloud);
768 key_data_from_adt(*ale, pointcloud->adt);
769 break;
770 }
771 case ANIMTYPE_DSVOLUME: {
772 Volume *volume = (Volume *)data;
773 ale->flag = FILTER_VOLUME_OBJD(volume);
774 key_data_from_adt(*ale, volume->adt);
775 break;
776 }
777 case ANIMTYPE_DSSKEY: {
778 Key *key = (Key *)data;
779 ale->flag = FILTER_SKE_OBJD(key);
780 key_data_from_adt(*ale, key->adt);
781 break;
782 }
783 case ANIMTYPE_DSWOR: {
784 World *wo = (World *)data;
785 ale->flag = FILTER_WOR_SCED(wo);
786 key_data_from_adt(*ale, wo->adt);
787 break;
788 }
789 case ANIMTYPE_DSNTREE: {
790 bNodeTree *ntree = (bNodeTree *)data;
791 ale->flag = FILTER_NTREE_DATA(ntree);
792 key_data_from_adt(*ale, ntree->adt);
793 break;
794 }
799 break;
800 }
801 case ANIMTYPE_DSPART: {
802 ParticleSettings *part = (ParticleSettings *)ale->data;
803 ale->flag = FILTER_PART_OBJD(part);
804 key_data_from_adt(*ale, part->adt);
805 break;
806 }
807 case ANIMTYPE_DSTEX: {
808 Tex *tex = (Tex *)data;
809 ale->flag = FILTER_TEX_DATA(tex);
810 key_data_from_adt(*ale, tex->adt);
811 break;
812 }
813 case ANIMTYPE_DSGPENCIL: {
814 bGPdata *gpd = (bGPdata *)data;
815 /* NOTE: we just reuse the same expand filter for this case */
816 ale->flag = EXPANDED_GPD(gpd);
817
818 /* XXX: currently, this is only used for access to its animation data */
819 key_data_from_adt(*ale, gpd->adt);
820 break;
821 }
822 case ANIMTYPE_DSMCLIP: {
823 MovieClip *clip = (MovieClip *)data;
824 ale->flag = EXPANDED_MCLIP(clip);
825 key_data_from_adt(*ale, clip->adt);
826 break;
827 }
829 AnimData *adt = (AnimData *)data;
830
831 ale->flag = adt->flag;
832
833 ale->key_data = nullptr;
834 ale->datatype = ALE_NONE;
835 break;
836 }
837 case ANIMTYPE_GROUP: {
838 BLI_assert_msg(GS(fcurve_owner_id->name) == ID_AC, "fcurve_owner_id should be an Action");
839
840 bActionGroup *agrp = (bActionGroup *)data;
841
842 ale->flag = agrp->flag;
843
844 ale->key_data = nullptr;
845 ale->datatype = ALE_GROUP;
846 break;
847 }
848 case ANIMTYPE_FCURVE:
849 case ANIMTYPE_NLACURVE: /* practically the same as ANIMTYPE_FCURVE.
850 * Differences are applied post-creation */
851 {
852 FCurve *fcu = (FCurve *)data;
853
854 ale->flag = fcu->flag;
855
856 ale->key_data = fcu;
857 ale->datatype = ALE_FCURVE;
858 break;
859 }
860 case ANIMTYPE_SHAPEKEY: {
861 KeyBlock *kb = (KeyBlock *)data;
862 Key *key = (Key *)ale->id;
863
864 ale->flag = kb->flag;
865
866 /* whether we have keyframes depends on whether there is a Key block to find it from */
867 if (key) {
868 /* index of shapekey is defined by place in key's list */
869 ale->index = BLI_findindex(&key->block, kb);
870
871 /* the corresponding keyframes are from the animdata */
872 if (ale->adt && ale->adt->action) {
873 /* Try to find the F-Curve which corresponds to this exactly. */
874 if (std::optional<std::string> rna_path = BKE_keyblock_curval_rnapath_get(key, kb)) {
876 {*rna_path, 0});
877 }
878 }
879 ale->datatype = (ale->key_data) ? ALE_FCURVE : ALE_NONE;
880 }
881 break;
882 }
883 case ANIMTYPE_GPLAYER: {
884 bGPDlayer *gpl = (bGPDlayer *)data;
885
886 ale->flag = gpl->flag;
887
888 ale->key_data = nullptr;
889 ale->datatype = ALE_GPFRAME;
890 break;
891 }
893 GreasePencilLayer *layer = static_cast<GreasePencilLayer *>(data);
894
895 ale->flag = layer->base.flag;
896
897 ale->key_data = nullptr;
899 break;
900 }
902 GreasePencilLayerTreeGroup *layer_group = static_cast<GreasePencilLayerTreeGroup *>(data);
903
904 ale->flag = layer_group->base.flag;
905
906 ale->key_data = nullptr;
908 break;
909 }
911 GreasePencil *grease_pencil = static_cast<GreasePencil *>(data);
912
913 ale->flag = grease_pencil->flag;
914
915 ale->key_data = nullptr;
917 break;
918 }
919 case ANIMTYPE_MASKLAYER: {
920 MaskLayer *masklay = (MaskLayer *)data;
921
922 ale->flag = masklay->flag;
923
924 ale->key_data = nullptr;
925 ale->datatype = ALE_MASKLAY;
926 break;
927 }
928 case ANIMTYPE_NLATRACK: {
929 NlaTrack *nlt = (NlaTrack *)data;
930
931 ale->flag = nlt->flag;
932
933 ale->key_data = &nlt->strips;
934 ale->datatype = ALE_NLASTRIP;
935 break;
936 }
937 case ANIMTYPE_NLAACTION: {
938 /* nothing to include for now... nothing editable from NLA-perspective here */
939 ale->key_data = nullptr;
940 ale->datatype = ALE_NONE;
941 break;
942 }
943
944 case ANIMTYPE_NONE:
947 case ANIMTYPE_DSMBALL:
950 case ANIMTYPE_PALETTE:
952 break;
953 }
954
955 return ale;
956}
957
958/* ----------------------------------------- */
959
960/* 'Only Selected' selected data and/or 'Include Hidden' filtering
961 * NOTE: when this function returns true, the F-Curve is to be skipped
962 */
964 FCurve *fcu,
965 ID *owner_id,
966 const eAnimFilter_Flags filter_mode)
967{
968 if (fcu->grp != nullptr && fcu->grp->flag & ADT_CURVES_ALWAYS_VISIBLE) {
969 return false;
970 }
971 /* hidden items should be skipped if we only care about visible data,
972 * but we aren't interested in hidden stuff */
973 const bool skip_hidden = (filter_mode & ANIMFILTER_DATA_VISIBLE) &&
975
976 if (GS(owner_id->name) == ID_OB) {
977 Object *ob = (Object *)owner_id;
978 bPoseChannel *pchan = nullptr;
979 char bone_name[sizeof(pchan->name)];
980
981 /* Only consider if F-Curve involves `pose.bones`. */
982 if (fcu->rna_path &&
983 BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name)))
984 {
985 /* Get bone-name, and check if this bone is selected. */
986 pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
987
988 /* check whether to continue or skip */
989 if (pchan && pchan->bone) {
990 /* If only visible channels,
991 * skip if bone not visible unless user wants channels from hidden data too. */
992 if (skip_hidden) {
993 bArmature *arm = (bArmature *)ob->data;
994
995 /* skipping - not visible on currently visible layers */
996 if (!ANIM_bonecoll_is_visible_pchan(arm, pchan)) {
997 return true;
998 }
999 /* skipping - is currently hidden */
1000 if (pchan->bone->flag & BONE_HIDDEN_P) {
1001 return true;
1002 }
1003 }
1004
1005 /* can only add this F-Curve if it is selected */
1006 if (ac->ads->filterflag & ADS_FILTER_ONLYSEL) {
1007 if ((pchan->bone->flag & BONE_SELECTED) == 0) {
1008 return true;
1009 }
1010 }
1011 }
1012 }
1013 }
1014 else if (GS(owner_id->name) == ID_SCE) {
1015 Scene *scene = (Scene *)owner_id;
1016 Sequence *seq = nullptr;
1017 char seq_name[sizeof(seq->name)];
1018
1019 /* Only consider if F-Curve involves `sequence_editor.sequences`. */
1020 if (fcu->rna_path &&
1021 BLI_str_quoted_substr(fcu->rna_path, "sequences_all[", seq_name, sizeof(seq_name)))
1022 {
1023 /* Get strip name, and check if this strip is selected. */
1024 Editing *ed = SEQ_editing_get(scene);
1025 if (ed) {
1026 seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false);
1027 }
1028
1029 /* Can only add this F-Curve if it is selected. */
1030 if (ac->ads->filterflag & ADS_FILTER_ONLYSEL) {
1031
1032 /* NOTE(@ideasman42): The `seq == nullptr` check doesn't look right
1033 * (compared to other checks in this function which skip data that can't be found).
1034 *
1035 * This is done since the search for sequence strips doesn't use a global lookup:
1036 * - Nested meta-strips are excluded.
1037 * - When inside a meta-strip - strips outside the meta-strip excluded.
1038 *
1039 * Instead, only the strips directly visible to the user are considered for selection.
1040 * The nullptr check here means everything else is considered unselected and is not shown.
1041 *
1042 * There is a subtle difference between nodes, pose-bones ... etc
1043 * since data-paths that point to missing strips are not shown.
1044 * If this is an important difference, the nullptr case could perform a global lookup,
1045 * only returning `true` if the sequence strip exists elsewhere
1046 * (ignoring its selection state). */
1047 if (seq == nullptr) {
1048 return true;
1049 }
1050
1051 if ((seq->flag & SELECT) == 0) {
1052 return true;
1053 }
1054 }
1055 }
1056 }
1057 else if (GS(owner_id->name) == ID_NT) {
1058 bNodeTree *ntree = (bNodeTree *)owner_id;
1059 bNode *node = nullptr;
1060 char node_name[sizeof(node->name)];
1061
1062 /* Check for selected nodes. */
1063 if (fcu->rna_path &&
1064 BLI_str_quoted_substr(fcu->rna_path, "nodes[", node_name, sizeof(node_name)))
1065 {
1066 /* Get strip name, and check if this strip is selected. */
1067 node = bke::node_find_node_by_name(ntree, node_name);
1068
1069 /* Can only add this F-Curve if it is selected. */
1070 if (node) {
1071 if (ac->ads->filterflag & ADS_FILTER_ONLYSEL) {
1072 if ((node->flag & NODE_SELECT) == 0) {
1073 return true;
1074 }
1075 }
1076 }
1077 }
1078 }
1079
1080 return false;
1081}
1082
1083/* Helper for name-based filtering - Perform "partial/fuzzy matches" (as in 80a7efd) */
1084static bool name_matches_dopesheet_filter(const bDopeSheet *ads, const char *name)
1085{
1086 if (ads->flag & ADS_FLAG_FUZZY_NAMES) {
1087 /* full fuzzy, multi-word, case insensitive matches */
1088 const size_t str_len = strlen(ads->searchstr);
1089 const int words_max = BLI_string_max_possible_word_count(str_len);
1090
1091 int(*words)[2] = static_cast<int(*)[2]>(BLI_array_alloca(words, words_max));
1092 const int words_len = BLI_string_find_split_words(
1093 ads->searchstr, str_len, ' ', words, words_max);
1094 bool found = false;
1095
1096 /* match name against all search words */
1097 for (int index = 0; index < words_len; index++) {
1098 if (BLI_strncasestr(name, ads->searchstr + words[index][0], words[index][1])) {
1099 found = true;
1100 break;
1101 }
1102 }
1103
1104 /* if we have a match somewhere, this returns true */
1105 return ((ads->flag & ADS_FLAG_INVERT_FILTER) == 0) ? found : !found;
1106 }
1107 /* fallback/default - just case insensitive, but starts from start of word */
1108 bool found = BLI_strcasestr(name, ads->searchstr) != nullptr;
1109 return ((ads->flag & ADS_FLAG_INVERT_FILTER) == 0) ? found : !found;
1110}
1111
1112/* (Display-)Name-based F-Curve filtering
1113 * NOTE: when this function returns true, the F-Curve is to be skipped
1114 */
1116 bAnimContext *ac, FCurve *fcu, eAnim_ChannelType channel_type, void *owner, ID *owner_id)
1117{
1118 bAnimListElem ale_dummy = {nullptr};
1119 const bAnimChannelType *acf;
1120
1121 /* create a dummy wrapper for the F-Curve, so we can get typeinfo for it */
1122 ale_dummy.type = channel_type;
1123 ale_dummy.owner = owner;
1124 ale_dummy.id = owner_id;
1125 ale_dummy.data = fcu;
1126
1127 /* get type info for channel */
1128 acf = ANIM_channel_get_typeinfo(&ale_dummy);
1129 if (acf && acf->name) {
1130 char name[256]; /* hopefully this will be enough! */
1131
1132 /* get name */
1133 acf->name(&ale_dummy, name);
1134
1135 /* check for partial match with the match string, assuming case insensitive filtering
1136 * if match, this channel shouldn't be ignored!
1137 */
1138 return !name_matches_dopesheet_filter(ac->ads, name);
1139 }
1140
1141 /* just let this go... */
1142 return true;
1143}
1144
1146{
1148 if (!acf) {
1150 /* Do not filter out stuff unless we know it can be filtered out. */
1151 return true;
1152 }
1153
1154 char name[ANIM_CHAN_NAME_SIZE];
1155 acf->name(&ale, name);
1156 return name_matches_dopesheet_filter(&ads, name);
1157}
1158
1164static bool fcurve_has_errors(bAnimContext *ac, const FCurve *fcu)
1165{
1166 /* F-Curve disabled (path evaluation error). */
1167 if (fcu->flag & FCURVE_DISABLED) {
1168 return true;
1169 }
1170
1171 /* driver? */
1172 if (fcu->driver) {
1173 const ChannelDriver *driver = fcu->driver;
1174
1175 /* error flag on driver usually means that there is an error
1176 * BUT this may not hold with PyDrivers as this flag gets cleared
1177 * if no critical errors prevent the driver from working...
1178 */
1179 if (driver->flag & DRIVER_FLAG_INVALID) {
1180 return true;
1181 }
1182
1183 /* check variables for other things that need linting... */
1184 /* TODO: maybe it would be more efficient just to have a quick flag for this? */
1185 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
1187 if (dtar->flag & DTAR_FLAG_INVALID) {
1188 return true;
1189 }
1190
1191 if ((dtar->flag & DTAR_FLAG_FALLBACK_USED) &&
1193 {
1194 return true;
1195 }
1196 }
1198 }
1199 }
1200
1201 /* no errors found */
1202 return false;
1203}
1204
1205/* find the next F-Curve that is usable for inclusion */
1207 FCurve *first,
1208 eAnim_ChannelType channel_type,
1209 const eAnimFilter_Flags filter_mode,
1210 void *owner,
1211 ID *owner_id)
1212{
1213 bActionGroup *grp = (channel_type == ANIMTYPE_FCURVE) ? static_cast<bActionGroup *>(owner) :
1214 nullptr;
1215 FCurve *fcu = nullptr;
1216
1217 /* Loop over F-Curves - assume that the caller of this has already checked
1218 * that these should be included.
1219 * NOTE: we need to check if the F-Curves belong to the same group,
1220 * as this gets called for groups too...
1221 */
1222 for (fcu = first; ((fcu) && (fcu->grp == grp)); fcu = fcu->next) {
1223 /* special exception for Pose-Channel/Sequence-Strip/Node Based F-Curves:
1224 * - The 'Only Selected' and 'Include Hidden' data filters should be applied to sub-ID data
1225 * which can be independently selected/hidden, such as Pose-Channels, Sequence Strips,
1226 * and Nodes. Since these checks were traditionally done as first check for objects,
1227 * we do the same here.
1228 * - We currently use an 'approximate' method for getting these F-Curves that doesn't require
1229 * carefully checking the entire path.
1230 * - This will also affect things like Drivers, and also works for Bone Constraints.
1231 */
1232 if (ac->ads && owner_id) {
1233 if ((filter_mode & ANIMFILTER_TMP_IGNORE_ONLYSEL) == 0) {
1234 if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) ||
1236 {
1237 if (skip_fcurve_selected_data(ac, fcu, owner_id, filter_mode)) {
1238 continue;
1239 }
1240 }
1241 }
1242 }
1243
1244 /* only include if visible (Graph Editor check, not channels check) */
1245 if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || (fcu->flag & FCURVE_VISIBLE)) {
1246 /* only work with this channel and its subchannels if it is editable */
1247 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_FCU(fcu)) {
1248 /* Only include this curve if selected in a way consistent
1249 * with the filtering requirements. */
1251 /* only include if this curve is active */
1252 if (!(filter_mode & ANIMFILTER_ACTIVE) || (fcu->flag & FCURVE_ACTIVE)) {
1253 /* name based filtering... */
1254 if (((ac->ads) && (ac->ads->searchstr[0] != '\0')) && (owner_id)) {
1255 if (skip_fcurve_with_name(ac, fcu, channel_type, owner, owner_id)) {
1256 continue;
1257 }
1258 }
1259
1260 /* error-based filtering... */
1261 if ((ac->ads) && (ac->ads->filterflag & ADS_FILTER_ONLY_ERRORS)) {
1262 /* skip if no errors... */
1263 if (!fcurve_has_errors(ac, fcu)) {
1264 continue;
1265 }
1266 }
1267
1268 /* this F-Curve can be used, so return it */
1269 return fcu;
1270 }
1271 }
1272 }
1273 }
1274 }
1275
1276 /* no (more) F-Curves from the list are suitable... */
1277 return nullptr;
1278}
1279
1281 ListBase *anim_data,
1282 FCurve *first,
1283 eAnim_ChannelType fcurve_type,
1284 const eAnimFilter_Flags filter_mode,
1285 void *owner,
1286 ID *owner_id,
1287 ID *fcurve_owner_id)
1288{
1289 FCurve *fcu;
1290 size_t items = 0;
1291
1292 /* Loop over every F-Curve able to be included.
1293 *
1294 * This for-loop works like this:
1295 * 1) The starting F-Curve is assigned to the fcu pointer
1296 * so that we have a starting point to search from.
1297 * 2) The first valid F-Curve to start from (which may include the one given as 'first')
1298 * in the remaining list of F-Curves is found, and verified to be non-null.
1299 * 3) The F-Curve referenced by fcu pointer is added to the list
1300 * 4) The fcu pointer is set to the F-Curve after the one we just added,
1301 * so that we can keep going through the rest of the F-Curve list without an eternal loop.
1302 * Back to step 2 :)
1303 */
1304 for (fcu = first;
1305 (fcu = animfilter_fcurve_next(ac, fcu, fcurve_type, filter_mode, owner, owner_id));
1306 fcu = fcu->next)
1307 {
1308 if (UNLIKELY(fcurve_type == ANIMTYPE_NLACURVE)) {
1309 /* NLA Control Curve - Basically the same as normal F-Curves,
1310 * except we need to set some stuff differently */
1311 ANIMCHANNEL_NEW_CHANNEL_FULL(ac->bmain, fcu, ANIMTYPE_NLACURVE, owner_id, fcurve_owner_id, {
1312 ale->owner = owner; /* strip */
1313 ale->adt = nullptr; /* to prevent time mapping from causing problems */
1314 });
1315 }
1316 else {
1317 /* Normal FCurve */
1318 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, fcu, ANIMTYPE_FCURVE, owner_id, fcurve_owner_id);
1319 }
1320 }
1321
1322 /* return the number of items added to the list */
1323 return items;
1324}
1325
1326static inline bool fcurve_span_selection_matters(const eAnimFilter_Flags filter_mode)
1327{
1328 /* This means that ANIMFILTER_SELEDIT only works if ANIMFILTER_FOREDIT is also set. Given the
1329 * description on ANIMFILTER_SELEDIT this seems reasonable. */
1330 if ((filter_mode & ANIMFILTER_FOREDIT) && (filter_mode & ANIMFILTER_SELEDIT)) {
1331 return true;
1332 }
1333 return filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL);
1334}
1335
1336static inline bool fcurve_span_must_be_selected(const eAnimFilter_Flags filter_mode)
1337{
1338 if ((filter_mode & ANIMFILTER_FOREDIT) && (filter_mode & ANIMFILTER_SELEDIT)) {
1339 return true;
1340 }
1341 return filter_mode & ANIMFILTER_SEL;
1342}
1343
1360 ListBase * /*bAnimListElem*/ anim_data,
1361 Span<FCurve *> fcurves,
1362 const animrig::slot_handle_t slot_handle,
1363 const eAnimFilter_Flags filter_mode,
1364 ID *animated_id,
1365 ID *fcurve_owner_id)
1366{
1367 size_t num_items = 0;
1368 BLI_assert(animated_id);
1369
1370 const bool active_matters = filter_mode & ANIMFILTER_ACTIVE;
1371 const bool selection_matters = fcurve_span_selection_matters(filter_mode);
1372 const bool must_be_selected = fcurve_span_must_be_selected(filter_mode);
1373 const bool visibility_matters = filter_mode & ANIMFILTER_CURVE_VISIBLE;
1374 const bool editability_matters = filter_mode & ANIMFILTER_FOREDIT;
1375 const bool show_only_errors = ac->ads && (ac->ads->filterflag & ADS_FILTER_ONLY_ERRORS);
1376 const bool filter_by_name = ac->ads && (ac->ads->searchstr[0] != '\0');
1377
1378 for (FCurve *fcu : fcurves) {
1379 /* make_new_animlistelem will return nullptr when fcu == nullptr, and that's
1380 * going to cause problems. */
1381 BLI_assert(fcu);
1382
1383 if (editability_matters && (fcu->flag & FCURVE_PROTECTED)) {
1384 continue;
1385 }
1386
1387 if (selection_matters && bool(fcu->flag & FCURVE_SELECTED) != must_be_selected) {
1388 continue;
1389 }
1390 if (active_matters && !(fcu->flag & FCURVE_ACTIVE)) {
1391 continue;
1392 }
1393 if (visibility_matters && !(fcu->flag & FCURVE_VISIBLE)) {
1394 continue;
1395 }
1396 if (show_only_errors && !fcurve_has_errors(ac, fcu)) {
1397 continue;
1398 }
1399 if (skip_fcurve_selected_data(ac, fcu, animated_id, filter_mode)) {
1400 continue;
1401 }
1402
1403 if (filter_mode & ANIMFILTER_TMP_PEEK) {
1404 /* Found an animation channel, which is good enough for the 'TMP_PEEK' mode. */
1405 return 1;
1406 }
1407
1409 ac->bmain, fcu, ANIMTYPE_FCURVE, animated_id, fcurve_owner_id);
1410
1411 /* Filtering by name needs a way to look up the name, which is easiest if
1412 * there is already an #bAnimListElem. */
1413 if (filter_by_name && !ale_name_matches_dopesheet_filter(*ac->ads, *ale)) {
1414 MEM_freeN(ale);
1415 continue;
1416 }
1417
1418 /* bAnimListElem::slot_handle is exposed as int32_t and not as slot_handle_t, so better
1419 * ensure that these are still equivalent.
1420 * TODO: move to another part of the code. */
1421 static_assert(
1422 std::is_same_v<decltype(ActionSlot::handle), decltype(bAnimListElem::slot_handle)>);
1423
1424 /* Note that this might not be the same as ale->adt->slot_handle. The reason this F-Curve is
1425 * shown could be because it's in the Action editor, showing ale->adt->action with _all_
1426 * slots, and this F-Curve could be from a different slot than what's used by the owner
1427 * of `ale->adt`. */
1428 ale->slot_handle = slot_handle;
1429
1430 BLI_addtail(anim_data, ale);
1431 num_items++;
1432 }
1433
1434 return num_items;
1435}
1436
1446 ListBase *anim_data,
1447 bAction *act,
1448 animrig::slot_handle_t slot_handle,
1449 bActionGroup *agrp,
1450 eAnimFilter_Flags filter_mode,
1451 ID *owner_id)
1452{
1453 BLI_assert(act != nullptr);
1454 BLI_assert(agrp != nullptr);
1455
1456 ListBase tmp_data = {nullptr, nullptr};
1457 size_t tmp_items = 0;
1458 size_t items = 0;
1459
1460 animrig::Action &action = act->wrap();
1461
1462 /* if we care about the selection status of the channels,
1463 * but the group isn't expanded (1)...
1464 * (1) this only matters if we actually care about the hierarchy though.
1465 * - Hierarchy matters: this hack should be applied
1466 * - Hierarchy ignored: cases like #21276 won't work properly, unless we skip this hack
1467 */
1468 if (
1469 /* Care about hierarchy but group isn't expanded. */
1470 ((filter_mode & ANIMFILTER_LIST_VISIBLE) && EXPANDED_AGRP(ac, agrp) == 0) &&
1471 /* Care about selection status. */
1472 (filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)))
1473 {
1474 /* If the group itself isn't selected appropriately,
1475 * we shouldn't consider its children either. */
1476 if (ANIMCHANNEL_SELOK(SEL_AGRP(agrp)) == 0) {
1477 return 0;
1478 }
1479
1480 /* if we're still here,
1481 * then the selection status of the curves within this group should not matter,
1482 * since this creates too much overhead for animators (i.e. making a slow workflow).
1483 *
1484 * Tools affected by this at time of coding (2010 Feb 09):
1485 * - Inserting keyframes on selected channels only.
1486 * - Pasting keyframes.
1487 * - Creating ghost curves in Graph Editor.
1488 */
1490 }
1491
1492 /* add grouped F-Curves */
1494 /* special filter so that we can get just the F-Curves within the active group */
1495 if (!(filter_mode & ANIMFILTER_ACTGROUPED) || (agrp->flag & AGRP_ACTIVE)) {
1496 /* for the Graph Editor, curves may be set to not be visible in the view to lessen
1497 * clutter, but to do this, we need to check that the group doesn't have its
1498 * not-visible flag set preventing all its sub-curves to be shown
1499 */
1500 if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || !(agrp->flag & AGRP_NOTVISIBLE)) {
1501 /* group must be editable for its children to be editable (if we care about this) */
1502 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) {
1503 /* Filter the fcurves in this group, adding them to the temporary
1504 * filter list. */
1505 if (action.is_action_legacy()) {
1506 /* get first F-Curve which can be used here */
1507 FCurve *first_fcu = animfilter_fcurve_next(ac,
1508 static_cast<FCurve *>(agrp->channels.first),
1510 filter_mode,
1511 agrp,
1512 owner_id);
1513
1514 /* filter list, starting from this F-Curve */
1515 tmp_items += animfilter_fcurves(
1516 ac, &tmp_data, first_fcu, ANIMTYPE_FCURVE, filter_mode, agrp, owner_id, &act->id);
1517 }
1518 else {
1519 BLI_assert(agrp->channel_bag != nullptr);
1520 Span<FCurve *> fcurves = agrp->wrap().fcurves();
1521 tmp_items += animfilter_fcurves_span(
1522 ac, &tmp_data, fcurves, slot_handle, filter_mode, owner_id, &act->id);
1523 }
1524 }
1525 }
1526 }
1527 }
1529
1530 /* did we find anything? */
1531 if (tmp_items) {
1532 /* add this group as a channel first */
1533 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1534 /* filter selection of channel specially here again,
1535 * since may be open and not subject to previous test */
1536 if (ANIMCHANNEL_SELOK(SEL_AGRP(agrp))) {
1537 if (action.is_action_legacy()) {
1538 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, agrp, ANIMTYPE_GROUP, owner_id, &act->id);
1539 }
1540 else {
1541 ANIMCHANNEL_NEW_CHANNEL_FULL(ac->bmain, agrp, ANIMTYPE_GROUP, owner_id, &act->id, {
1542 ale->slot_handle = slot_handle;
1543 });
1544 }
1545 }
1546 }
1547
1548 /* now add the list of collected channels */
1549 BLI_movelisttolist(anim_data, &tmp_data);
1551 items += tmp_items;
1552 }
1553
1554 /* return the number of items added to the list */
1555 return items;
1556}
1557
1562 ListBase *anim_data,
1563 animrig::Action &action,
1564 animrig::Slot &slot,
1565 const eAnimFilter_Flags filter_mode,
1566 ID *animated_id)
1567{
1568 /* Don't include anything from this animation if it is linked in from another
1569 * file, and we're getting stuff for editing... */
1570 if ((filter_mode & ANIMFILTER_FOREDIT) &&
1571 (ID_IS_LINKED(&action) || ID_IS_OVERRIDE_LIBRARY(&action)))
1572 {
1573 return 0;
1574 }
1575
1576 const bool selection_matters = filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL);
1577 const bool must_be_selected = filter_mode & ANIMFILTER_SEL;
1578 const bool selection_ok_for_slot = !selection_matters || slot.is_selected() == must_be_selected;
1579
1580 int items = 0;
1581
1582 /* Add a list element for the Slot itself, but only if in Action mode. The Dopesheet mode
1583 * shouldn't display Slots, as F-Curves are always shown in the context of the animated ID
1584 * anyway. */
1585 const bool is_action_mode = (ac->spacetype == SPACE_ACTION &&
1587 const bool show_active_group_only = filter_mode & ANIMFILTER_ACTGROUPED;
1588 const bool include_summary_channels = (filter_mode & ANIMFILTER_LIST_CHANNELS);
1589 const bool show_slot_channel = (is_action_mode && selection_ok_for_slot &&
1590 include_summary_channels);
1591 if (show_slot_channel) {
1592 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, &slot, ANIMTYPE_ACTION_SLOT, animated_id, &action.id);
1593 items++;
1594 }
1595
1596 /* If the 'list visible' flag is used, the expansion state of the Slot
1597 * matters. Otherwise the sub-channels can always be listed. */
1598 const bool visible_only = (filter_mode & ANIMFILTER_LIST_VISIBLE);
1599 const bool expansion_is_ok = !visible_only || !show_slot_channel || slot.is_expanded();
1600
1602 if (channel_bag == nullptr) {
1603 return items;
1604 }
1605
1606 if (!expansion_is_ok) {
1607 return items;
1608 }
1609
1610 /* Add channel groups and their member channels. */
1611 for (bActionGroup *group : channel_bag->channel_groups()) {
1612 items += animfilter_act_group(
1613 ac, anim_data, &action, slot.handle, group, filter_mode, animated_id);
1614 }
1615
1616 /* Add ungrouped channels. */
1617 if (!show_active_group_only) {
1618 int first_ungrouped_fcurve_index = 0;
1619 if (!channel_bag->channel_groups().is_empty()) {
1620 const bActionGroup *last_group = channel_bag->channel_groups().last();
1621 first_ungrouped_fcurve_index = last_group->fcurve_range_start +
1622 last_group->fcurve_range_length;
1623 }
1624
1625 Span<FCurve *> fcurves = channel_bag->fcurves().drop_front(first_ungrouped_fcurve_index);
1626 items += animfilter_fcurves_span(
1627 ac, anim_data, fcurves, slot.handle, filter_mode, animated_id, &action.id);
1628 }
1629
1630 return items;
1631}
1632
1634 ListBase *anim_data,
1635 animrig::Action &action,
1636 const eAnimFilter_Flags filter_mode,
1637 ID *owner_id)
1638{
1639 /* Don't include anything from this animation if it is linked in from another
1640 * file, and we're getting stuff for editing... */
1641 if ((filter_mode & ANIMFILTER_FOREDIT) &&
1642 (ID_IS_LINKED(&action) || ID_IS_OVERRIDE_LIBRARY(&action)))
1643 {
1644 return 0;
1645 }
1646
1647 int num_items = 0;
1648 for (animrig::Slot *slot : action.slots()) {
1649 BLI_assert(slot);
1650
1651 /* In some cases (see `ob_to_keylist()` and friends) fake bDopeSheet and fake bAnimContext are
1652 * created. These are mostly null-initialized, and so do not have a bmain. This means that
1653 * lookup of the animated ID is not possible, which can result in failure to look up the proper
1654 * F-Curve display name. For the `..._to_keylist` functions that doesn't matter, as those are
1655 * only interested in the key data anyway. So rather than trying to get a reliable `bmain`
1656 * through the maze, this code just treats it as optional (even though ideally it should always
1657 * be known). */
1658 ID *animated_id = nullptr;
1659 if (ac->bmain) {
1660 animated_id = animrig::action_slot_get_id_best_guess(*ac->bmain, *slot, owner_id);
1661 }
1662 if (!animated_id) {
1663 /* This is not necessarily correct, but at least it prevents nullptr dereference. */
1664 animated_id = owner_id;
1665 }
1666 num_items += animfilter_action_slot(ac, anim_data, action, *slot, filter_mode, animated_id);
1667 }
1668
1669 return num_items;
1670}
1671
1673 ListBase *anim_data,
1674 animrig::Action &action,
1675 const animrig::slot_handle_t slot_handle,
1676 const eAnimFilter_Flags filter_mode,
1677 ID *owner_id)
1678{
1679 FCurve *lastchan = nullptr;
1680 size_t items = 0;
1681
1682 if (action.is_empty()) {
1683 return 0;
1684 }
1685
1686 /* don't include anything from this action if it is linked in from another file,
1687 * and we're getting stuff for editing...
1688 */
1689 if ((filter_mode & ANIMFILTER_FOREDIT) &&
1690 (!ID_IS_EDITABLE(&action) || ID_IS_OVERRIDE_LIBRARY(&action)))
1691 {
1692 return 0;
1693 }
1694
1695 if (action.is_action_legacy()) {
1696 LISTBASE_FOREACH (bActionGroup *, agrp, &action.groups) {
1697 /* Store reference to last channel of group. */
1698 if (agrp->channels.last) {
1699 lastchan = static_cast<FCurve *>(agrp->channels.last);
1700 }
1701
1702 items += animfilter_act_group(
1703 ac, anim_data, &action, animrig::Slot::unassigned, agrp, filter_mode, owner_id);
1704 }
1705
1706 /* Un-grouped F-Curves (only if we're not only considering those channels in
1707 * the active group) */
1708 if (!(filter_mode & ANIMFILTER_ACTGROUPED)) {
1709 FCurve *firstfcu = (lastchan) ? (lastchan->next) :
1710 static_cast<FCurve *>(action.curves.first);
1711 items += animfilter_fcurves(
1712 ac, anim_data, firstfcu, ANIMTYPE_FCURVE, filter_mode, nullptr, owner_id, &action.id);
1713 }
1714
1715 return items;
1716 }
1717
1718 /* For now we don't show layers anywhere, just the contained F-Curves. */
1719
1720 /* Only show all Slots in Action editor mode. Otherwise the F-Curves ought to be displayed
1721 * underneath their animated ID anyway. */
1722 const bool is_action_mode = (ac->spacetype == SPACE_ACTION &&
1724 const bool show_all_slots = (ac->ads->filterflag & ADS_FILTER_ALL_SLOTS);
1725 if (is_action_mode && show_all_slots) {
1726 return animfilter_action_slots(ac, anim_data, action, filter_mode, owner_id);
1727 }
1728
1729 animrig::Slot *slot = action.slot_for_handle(slot_handle);
1730 if (!slot) {
1731 /* Can happen when an Action is assigned, but not a Slot. */
1732 return 0;
1733 }
1734 return animfilter_action_slot(ac, anim_data, action, *slot, filter_mode, owner_id);
1735}
1736
1737/* Include NLA-Data for NLA-Editor:
1738 * - When ANIMFILTER_LIST_CHANNELS is used, that means we should be filtering the list for display
1739 * Although the evaluation order is from the first track to the last and then apply the
1740 * Action on top, we present this in the UI as the Active Action followed by the last track
1741 * to the first so that we get the evaluation order presented as per a stack.
1742 * - For normal filtering (i.e. for editing),
1743 * we only need the NLA-tracks but they can be in 'normal' evaluation order, i.e. first to last.
1744 * Otherwise, some tools may get screwed up.
1745 */
1747 ListBase *anim_data,
1748 AnimData *adt,
1749 const eAnimFilter_Flags filter_mode,
1750 ID *owner_id)
1751{
1752 NlaTrack *nlt;
1753 NlaTrack *first = nullptr, *next = nullptr;
1754 size_t items = 0;
1755
1756 /* if showing channels, include active action */
1757 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1758 /* if NLA action-line filtering is off, don't show unless there are keyframes,
1759 * in order to keep things more compact for doing transforms
1760 */
1761 if (!(ac->ads->filterflag & ADS_FILTER_NLA_NOACT) || (adt->action)) {
1762 /* there isn't really anything editable here, so skip if need editable */
1763 if ((filter_mode & ANIMFILTER_FOREDIT) == 0) {
1764 /* Just add the action track now (this MUST appear for drawing):
1765 * - As AnimData may not have an action,
1766 * we pass a dummy pointer just to get the list elem created,
1767 * then overwrite this with the real value - REVIEW THIS.
1768 */
1770 ac->bmain, (void *)(&adt->action), ANIMTYPE_NLAACTION, owner_id, nullptr, {
1771 ale->data = adt->action ? adt->action : nullptr;
1772 });
1773 }
1774 }
1775
1776 /* first track to include will be the last one if we're filtering by channels */
1777 first = static_cast<NlaTrack *>(adt->nla_tracks.last);
1778 }
1779 else {
1780 /* first track to include will the first one (as per normal) */
1781 first = static_cast<NlaTrack *>(adt->nla_tracks.first);
1782 }
1783
1784 /* loop over NLA Tracks -
1785 * assume that the caller of this has already checked that these should be included */
1786 for (nlt = first; nlt; nlt = next) {
1787 /* 'next' NLA-Track to use depends on whether we're filtering for drawing or not */
1788 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1789 next = nlt->prev;
1790 }
1791 else {
1792 next = nlt->next;
1793 }
1794
1795 /* only work with this channel and its subchannels if it is editable */
1796 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_NLT(nlt)) {
1797 /* only include this track if selected in a way consistent with the filtering requirements */
1798 if (ANIMCHANNEL_SELOK(SEL_NLT(nlt))) {
1799 /* only include if this track is active */
1800 if (!(filter_mode & ANIMFILTER_ACTIVE) || (nlt->flag & NLATRACK_ACTIVE)) {
1801 /* name based filtering... */
1802 if (((ac->ads) && (ac->ads->searchstr[0] != '\0')) && (owner_id)) {
1803 bool track_ok = false, strip_ok = false;
1804
1805 /* check if the name of the track, or the strips it has are ok... */
1806 track_ok = name_matches_dopesheet_filter(ac->ads, nlt->name);
1807
1808 if (track_ok == false) {
1809 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
1810 if (name_matches_dopesheet_filter(ac->ads, strip->name)) {
1811 strip_ok = true;
1812 break;
1813 }
1814 }
1815 }
1816
1817 /* skip if both fail this test... */
1818 if (!track_ok && !strip_ok) {
1819 continue;
1820 }
1821 }
1822
1823 /* add the track now that it has passed all our tests */
1824 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, nlt, ANIMTYPE_NLATRACK, owner_id, nullptr);
1825 }
1826 }
1827 }
1828 }
1829
1830 /* return the number of items added to the list */
1831 return items;
1832}
1833
1834/* Include the control FCurves per NLA Strip in the channel list
1835 * NOTE: This is includes the expander too...
1836 */
1838 ListBase *anim_data,
1839 AnimData *adt,
1840 eAnimFilter_Flags filter_mode,
1841 ID *owner_id)
1842{
1843 ListBase tmp_data = {nullptr, nullptr};
1844 size_t tmp_items = 0;
1845 size_t items = 0;
1846
1847 /* add control curves from each NLA strip... */
1848 /* NOTE: ANIMTYPE_FCURVES are created here, to avoid duplicating the code needed */
1850 /* for now, we only go one level deep - so controls on grouped FCurves are not handled */
1852 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
1853 /* pass strip as the "owner",
1854 * so that the name lookups (used while filtering) will resolve */
1855 /* NLA tracks are coming from AnimData, so owner of f-curves
1856 * is the same as owner of animation data. */
1857 tmp_items += animfilter_fcurves(ac,
1858 &tmp_data,
1859 static_cast<FCurve *>(strip->fcurves.first),
1861 filter_mode,
1862 strip,
1863 owner_id,
1864 owner_id);
1865 }
1866 }
1867 }
1869
1870 /* did we find anything? */
1871 if (tmp_items) {
1872 /* add the expander as a channel first */
1873 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1874 /* currently these channels cannot be selected, so they should be skipped */
1875 if ((filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) == 0) {
1876 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, adt, ANIMTYPE_NLACONTROLS, owner_id, nullptr);
1877 }
1878 }
1879
1880 /* now add the list of collected channels */
1881 BLI_movelisttolist(anim_data, &tmp_data);
1883 items += tmp_items;
1884 }
1885
1886 /* return the number of items added to the list */
1887 return items;
1888}
1889
1890/* determine what animation data from AnimData block should get displayed */
1892 ListBase *anim_data,
1893 ID *id,
1894 const eAnimFilter_Flags filter_mode)
1895{
1897 size_t items = 0;
1898
1899 /* image object data-blocks have no anim-data so check for nullptr */
1900 if (adt) {
1901 IdAdtTemplate *iat = (IdAdtTemplate *)id;
1902
1903 /* NOTE: this macro is used instead of inlining the logic here,
1904 * since this sort of filtering is still needed in a few places in the rest of the code still -
1905 * notably for the few cases where special mode-based
1906 * different types of data expanders are required.
1907 */
1909 iat,
1910 { /* AnimData */
1911 /* specifically filter animdata block */
1914 }
1915 },
1916 { /* NLA */
1917 items += animfilter_nla(ac, anim_data, adt, filter_mode, id);
1918 },
1919 { /* Drivers */
1920 items += animfilter_fcurves(ac,
1921 anim_data,
1922 static_cast<FCurve *>(adt->drivers.first),
1924 filter_mode,
1925 nullptr,
1926 id,
1927 id);
1928 },
1929 { /* NLA Control Keyframes */
1930 items += animfilter_nla_controls(ac, anim_data, adt, filter_mode, id);
1931 },
1932 { /* Keyframes from legacy Action. */
1933 items += animfilter_action(ac,
1934 anim_data,
1935 adt->action->wrap(),
1937 eAnimFilter_Flags(filter_mode),
1938 id);
1939 },
1940 { /* Keyframes from layered Action. */
1941 items += animfilter_action(ac,
1942 anim_data,
1943 adt->action->wrap(),
1945 eAnimFilter_Flags(filter_mode),
1946 id);
1947 });
1948 }
1949
1950 return items;
1951}
1952
1953/* Include ShapeKey Data for ShapeKey Editor */
1955 ListBase *anim_data,
1956 Key *key,
1957 const eAnimFilter_Flags filter_mode)
1958{
1959 size_t items = 0;
1960
1961 /* check if channels or only F-Curves */
1962 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1963 bDopeSheet *ads = ac->ads;
1964
1965 if (key->type == KEY_RELATIVE) {
1966 /* TODO: This currently doesn't take into account the animatable "Range Min/Max" keys on the
1967 * key-blocks. */
1968
1969 /* loop through the channels adding ShapeKeys as appropriate */
1970 LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
1971 /* skip the first one, since that's the non-animatable basis */
1972 if (kb == key->block.first) {
1973 continue;
1974 }
1975
1976 /* Skip shapekey if the name doesn't match the filter string. */
1977 if (ads != nullptr && ads->searchstr[0] != '\0' &&
1978 name_matches_dopesheet_filter(ads, kb->name) == false)
1979 {
1980 continue;
1981 }
1982
1983 /* only work with this channel and its subchannels if it is editable */
1984 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_SHAPEKEY(kb)) {
1985 /* Only include this track if selected in a way consistent
1986 * with the filtering requirements. */
1988 /* TODO: consider 'active' too? */
1989
1990 /* owner-id here must be key so that the F-Curve can be resolved... */
1991 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, kb, ANIMTYPE_SHAPEKEY, key, nullptr);
1992 }
1993 }
1994 }
1995 }
1996 else {
1997 /* key->type == KEY_NORMAL */
1998 if (!key->adt || !key->adt->action) {
1999 return 0;
2000 }
2001 bAction *action = key->adt->action;
2002 FCurve *first_fcu = static_cast<FCurve *>(action->curves.first);
2003 for (FCurve *fcu = first_fcu;
2004 (fcu =
2005 animfilter_fcurve_next(ac, fcu, ANIMTYPE_FCURVE, filter_mode, nullptr, (ID *)key));
2006 fcu = fcu->next)
2007 {
2008 /* Check if this is a "KEY_NORMAL" type keyframe */
2009 if (STREQ(fcu->rna_path, "eval_time") || BLI_str_endswith(fcu->rna_path, ".interpolation"))
2010 {
2011 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, fcu, ANIMTYPE_FCURVE, (ID *)key, &action->id);
2012 }
2013 }
2014 }
2015 }
2016 else {
2017 /* just use the action associated with the shapekey */
2018 /* TODO: somehow manage to pass dopesheet info down here too? */
2019 if (key->adt) {
2020 if (filter_mode & ANIMFILTER_ANIMDATA) {
2021 if (ANIMCHANNEL_SELOK(SEL_ANIMDATA(key->adt))) {
2022 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, key->adt, ANIMTYPE_ANIMDATA, key, nullptr);
2023 }
2024 }
2025 else if (key->adt->action) {
2026 items = animfilter_action(ac,
2027 anim_data,
2028 key->adt->action->wrap(),
2029 key->adt->slot_handle,
2030 eAnimFilter_Flags(filter_mode),
2031 (ID *)key);
2032 }
2033 }
2034 }
2035
2036 /* return the number of items added to the list */
2037 return items;
2038}
2039
2040/* Helper for Grease Pencil - layers within a data-block. */
2041
2043 ListBase *anim_data,
2044 GreasePencil *grease_pencil,
2046 const eAnimFilter_Flags filter_mode)
2047{
2048
2049 size_t items = 0;
2050
2051 /* Only if the layer is selected. */
2052 if (!ANIMCHANNEL_SELOK(layer.is_selected())) {
2053 return items;
2054 }
2055
2056 /* Only if the layer is editable. */
2057 if ((filter_mode & ANIMFILTER_FOREDIT) && layer.is_locked()) {
2058 return items;
2059 }
2060
2061 /* Only if the layer is active. */
2062 if ((filter_mode & ANIMFILTER_ACTIVE) && grease_pencil->is_layer_active(&layer)) {
2063 return items;
2064 }
2065
2066 /* Skip empty layers. */
2067 if (layer.is_empty()) {
2068 return items;
2069 }
2070
2071 /* Add layer channel. */
2073 static_cast<void *>(&layer),
2075 grease_pencil,
2076 nullptr);
2077
2078 return items;
2079}
2080
2082 bAnimContext *ac,
2083 ListBase *anim_data,
2084 GreasePencil *grease_pencil,
2086 eAnimFilter_Flags filter_mode)
2087{
2088 using namespace blender::bke::greasepencil;
2089 size_t items = 0;
2090
2091 /* Skip node if the name doesn't match the filter string. */
2092 const bool name_search = (ac->ads->searchstr[0] != '\0');
2093 const bool skip_node = name_search &&
2094 !name_matches_dopesheet_filter(ac->ads, node.name().c_str());
2095
2096 if (node.is_layer() && !skip_node) {
2098 ac, anim_data, grease_pencil, node.as_layer(), filter_mode);
2099 }
2100 else if (node.is_group()) {
2101 const LayerGroup &layer_group = node.as_group();
2102
2103 ListBase tmp_data = {nullptr, nullptr};
2104 size_t tmp_items = 0;
2105
2106 /* Add grease pencil layer channels. */
2110 ac, &tmp_data, grease_pencil, node_->wrap(), filter_mode);
2111 }
2112 }
2114
2115 if ((tmp_items == 0) && !name_search) {
2116 /* If no sub-channels, return early.
2117 * Except if the search by name is on, because we might want to display the layer group alone
2118 * in that case. */
2119 return items;
2120 }
2121
2122 if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && !skip_node) {
2123 /* Add data block container (if for drawing, and it contains sub-channels). */
2125 static_cast<void *>(&node),
2127 grease_pencil,
2128 nullptr);
2129 }
2130
2131 /* Add the list of collected channels. */
2132 BLI_movelisttolist(anim_data, &tmp_data);
2134 items += tmp_items;
2135 }
2136 return items;
2137}
2138
2140 ListBase *anim_data,
2141 GreasePencil *grease_pencil,
2142 const eAnimFilter_Flags filter_mode)
2143{
2144 size_t items = 0;
2145
2147 GreasePencilLayerTreeNode *, node, &grease_pencil->root_group_ptr->children)
2148 {
2150 ac, anim_data, grease_pencil, node->wrap(), filter_mode);
2151 }
2152
2153 return items;
2154}
2155
2156/* Helper for Grease Pencil - layers within a data-block. */
2158 ListBase *anim_data,
2159 bGPdata *gpd,
2160 const eAnimFilter_Flags filter_mode)
2161{
2162 size_t items = 0;
2163
2164 /* loop over layers as the conditions are acceptable (top-Down order) */
2166 /* only if selected */
2167 if (!ANIMCHANNEL_SELOK(SEL_GPL(gpl))) {
2168 continue;
2169 }
2170
2171 /* only if editable */
2172 if ((filter_mode & ANIMFILTER_FOREDIT) && !EDITABLE_GPL(gpl)) {
2173 continue;
2174 }
2175
2176 /* active... */
2177 if ((filter_mode & ANIMFILTER_ACTIVE) && (gpl->flag & GP_LAYER_ACTIVE) == 0) {
2178 continue;
2179 }
2180
2181 /* skip layer if the name doesn't match the filter string */
2182 if (ac->ads != nullptr && ac->ads->searchstr[0] != '\0' &&
2183 name_matches_dopesheet_filter(ac->ads, gpl->info) == false)
2184 {
2185 continue;
2186 }
2187
2188 /* Skip empty layers. */
2189 if (BLI_listbase_is_empty(&gpl->frames)) {
2190 continue;
2191 }
2192
2193 /* add to list */
2194 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, gpl, ANIMTYPE_GPLAYER, gpd, nullptr);
2195 }
2196
2197 return items;
2198}
2199
2201 ListBase *anim_data,
2202 GreasePencil *grease_pencil,
2203 eAnimFilter_Flags filter_mode)
2204{
2205 using namespace blender;
2206
2207 size_t items = 0;
2208
2209 /* The Grease Pencil mode is not supposed to show channels for regular F-Curves from regular
2210 * Actions. At some point this might be desirable, but it would also require changing the
2211 * filtering flags for pretty much all operators running there. */
2212 const bool show_animdata = grease_pencil->adt && (ac->datatype != ANIMCONT_GPENCIL);
2213
2214 /* When asked from "AnimData" blocks (i.e. the top-level containers for normal animation),
2215 * for convenience, this will return grease pencil data-blocks instead.
2216 * This may cause issues down the track, but for now, this will do.
2217 */
2218 if (filter_mode & ANIMFILTER_ANIMDATA) {
2219 if (show_animdata) {
2220 items += animfilter_block_data(ac, anim_data, (ID *)grease_pencil, filter_mode);
2221 }
2223 ac->bmain, grease_pencil, ANIMTYPE_GREASE_PENCIL_DATABLOCK, grease_pencil, nullptr);
2224 }
2225 else {
2226 ListBase tmp_data = {nullptr, nullptr};
2227 size_t tmp_items = 0;
2228
2229 /* Add grease pencil layer channels. */
2231 if (show_animdata) {
2232 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)grease_pencil, filter_mode);
2233 }
2234
2235 if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
2237 ac, &tmp_data, grease_pencil, filter_mode);
2238 }
2239 }
2241
2242 if (tmp_items == 0) {
2243 /* If no sub-channels, return early. */
2244 return items;
2245 }
2246
2247 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2248 /* Add data block container (if for drawing, and it contains sub-channels). */
2250 ac->bmain, grease_pencil, ANIMTYPE_GREASE_PENCIL_DATABLOCK, grease_pencil, nullptr);
2251 }
2252
2253 /* Add the list of collected channels. */
2254 BLI_movelisttolist(anim_data, &tmp_data);
2256 items += tmp_items;
2257 }
2258
2259 return items;
2260}
2261
2263 ListBase *anim_data,
2264 const eAnimFilter_Flags filter_mode)
2265{
2266 size_t items = 0;
2267 Scene *scene = ac->scene;
2268 ViewLayer *view_layer = (ViewLayer *)ac->view_layer;
2269 bDopeSheet *ads = ac->ads;
2270
2271 BKE_view_layer_synced_ensure(scene, view_layer);
2273 if (!base->object || (base->object->type != OB_GREASE_PENCIL)) {
2274 continue;
2275 }
2276 Object *ob = base->object;
2277
2278 if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ac->ads->filterflag & ADS_FILTER_INCL_HIDDEN))
2279 {
2280 /* Layer visibility - we check both object and base,
2281 * since these may not be in sync yet. */
2282 if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0 ||
2284 {
2285 continue;
2286 }
2287
2288 /* Outliner restrict-flag */
2290 continue;
2291 }
2292 }
2293
2294 /* Check selection and object type filters */
2295 if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) && !(base->flag & BASE_SELECTED)) {
2296 /* Only selected should be shown */
2297 continue;
2298 }
2299
2300 if (ads->filter_grp != nullptr) {
2302 continue;
2303 }
2304 }
2305
2307 ac, anim_data, static_cast<GreasePencil *>(ob->data), filter_mode);
2308 }
2309
2310 /* Return the number of items added to the list */
2311 return items;
2312}
2313
2314/* Helper for Grease Pencil data integrated with main DopeSheet */
2316 ListBase *anim_data,
2317 bGPdata *gpd,
2318 eAnimFilter_Flags filter_mode)
2319{
2320 ListBase tmp_data = {nullptr, nullptr};
2321 size_t tmp_items = 0;
2322 size_t items = 0;
2323
2324 /* add relevant animation channels for Grease Pencil */
2326 /* add animation channels */
2327 tmp_items += animfilter_block_data(ac, &tmp_data, &gpd->id, filter_mode);
2328
2329 /* add Grease Pencil layers */
2330 if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
2331 tmp_items += animdata_filter_gpencil_layers_data_legacy(ac, &tmp_data, gpd, filter_mode);
2332 }
2333
2334 /* TODO: do these need a separate expander?
2335 * XXX: what order should these go in? */
2336 }
2338
2339 /* did we find anything? */
2340 if (tmp_items) {
2341 /* include data-expand widget first */
2342 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2343 /* check if filtering by active status */
2344 /* XXX: active check here needs checking */
2345 if (ANIMCHANNEL_ACTIVEOK(gpd)) {
2346 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, gpd, ANIMTYPE_DSGPENCIL, gpd, nullptr);
2347 }
2348 }
2349
2350 /* now add the list of collected channels */
2351 BLI_movelisttolist(anim_data, &tmp_data);
2353 items += tmp_items;
2354 }
2355
2356 /* return the number of items added to the list */
2357 return items;
2358}
2359
2360/* Helper for Cache File data integrated with main DopeSheet */
2362 ListBase *anim_data,
2363 CacheFile *cache_file,
2364 eAnimFilter_Flags filter_mode)
2365{
2366 ListBase tmp_data = {nullptr, nullptr};
2367 size_t tmp_items = 0;
2368 size_t items = 0;
2369
2370 /* add relevant animation channels for Cache File */
2372 /* add animation channels */
2373 tmp_items += animfilter_block_data(ac, &tmp_data, &cache_file->id, filter_mode);
2374 }
2376
2377 /* did we find anything? */
2378 if (tmp_items) {
2379 /* include data-expand widget first */
2380 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2381 /* check if filtering by active status */
2382 /* XXX: active check here needs checking */
2383 if (ANIMCHANNEL_ACTIVEOK(cache_file)) {
2384 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, cache_file, ANIMTYPE_DSCACHEFILE, cache_file, nullptr);
2385 }
2386 }
2387
2388 /* now add the list of collected channels */
2389 BLI_movelisttolist(anim_data, &tmp_data);
2391 items += tmp_items;
2392 }
2393
2394 /* return the number of items added to the list */
2395 return items;
2396}
2397
2398/* Helper for Mask Editing - mask layers */
2400 ListBase *anim_data,
2401 Mask *mask,
2402 const eAnimFilter_Flags filter_mode)
2403{
2405 size_t items = 0;
2406
2407 LISTBASE_FOREACH (MaskLayer *, masklay, &mask->masklayers) {
2408 if (!ANIMCHANNEL_SELOK(SEL_MASKLAY(masklay))) {
2409 continue;
2410 }
2411
2412 if ((filter_mode & ANIMFILTER_FOREDIT) && !EDITABLE_MASK(masklay)) {
2413 continue;
2414 }
2415
2416 if ((filter_mode & ANIMFILTER_ACTIVE) & (masklay_act != masklay)) {
2417 continue;
2418 }
2419
2420 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, masklay, ANIMTYPE_MASKLAYER, mask, nullptr);
2421 }
2422
2423 return items;
2424}
2425
2426/* Grab all mask data */
2428 ListBase *anim_data,
2429 void * /*data*/,
2430 eAnimFilter_Flags filter_mode)
2431{
2432 size_t items = 0;
2433
2434 /* For now, grab mask data-blocks directly from main. */
2435 /* XXX: this is not good... */
2436 LISTBASE_FOREACH (Mask *, mask, &ac->bmain->masks) {
2437 ListBase tmp_data = {nullptr, nullptr};
2438 size_t tmp_items = 0;
2439
2440 /* only show if mask is used by something... */
2441 if (ID_REAL_USERS(mask) < 1) {
2442 continue;
2443 }
2444
2445 /* add mask animation channels */
2446 if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
2448 tmp_items += animdata_filter_mask_data(ac, &tmp_data, mask, filter_mode);
2449 }
2451 }
2452
2453 /* did we find anything? */
2454 if (!tmp_items) {
2455 continue;
2456 }
2457
2458 /* include data-expand widget first */
2459 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2460 /* add mask data-block as channel too (if for drawing, and it has layers) */
2461 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, mask, ANIMTYPE_MASKDATABLOCK, nullptr, nullptr);
2462 }
2463
2464 /* now add the list of collected channels */
2465 BLI_movelisttolist(anim_data, &tmp_data);
2467 items += tmp_items;
2468 }
2469
2470 /* return the number of items added to the list */
2471 return items;
2472}
2473
2474/* NOTE: owner_id is scene, material, or texture block,
2475 * which is the direct owner of the node tree in question. */
2477 ListBase *anim_data,
2478 ID *owner_id,
2479 bNodeTree *ntree,
2480 eAnimFilter_Flags filter_mode)
2481{
2482 ListBase tmp_data = {nullptr, nullptr};
2483 size_t tmp_items = 0;
2484 size_t items = 0;
2485
2486 /* add nodetree animation channels */
2488 /* animation data filtering */
2489 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)ntree, filter_mode);
2490 }
2492
2493 /* did we find anything? */
2494 if (tmp_items) {
2495 /* include data-expand widget first */
2496 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2497 /* check if filtering by active status */
2498 if (ANIMCHANNEL_ACTIVEOK(ntree)) {
2499 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, ntree, ANIMTYPE_DSNTREE, owner_id, nullptr);
2500 }
2501 }
2502
2503 /* now add the list of collected channels */
2504 BLI_movelisttolist(anim_data, &tmp_data);
2506 items += tmp_items;
2507 }
2508
2509 /* return the number of items added to the list */
2510 return items;
2511}
2512
2514 ListBase *anim_data,
2515 ID *owner_id,
2516 bNodeTree *ntree,
2517 const eAnimFilter_Flags filter_mode)
2518{
2519 size_t items = 0;
2520
2521 items += animdata_filter_ds_nodetree_group(ac, anim_data, owner_id, ntree, filter_mode);
2522
2523 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
2524 if (node->type == NODE_GROUP) {
2525 if (node->id) {
2526 if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) && (node->flag & NODE_SELECT) == 0) {
2527 continue;
2528 }
2529 /* Recurse into the node group */
2530 items += animdata_filter_ds_nodetree(ac,
2531 anim_data,
2532 owner_id,
2533 (bNodeTree *)node->id,
2534 filter_mode | ANIMFILTER_TMP_IGNORE_ONLYSEL);
2535 }
2536 }
2537 }
2538
2539 return items;
2540}
2541
2543 ListBase *anim_data,
2544 Scene *sce,
2545 eAnimFilter_Flags filter_mode)
2546{
2547 size_t items = 0;
2548
2549 LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
2550 LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer->freestyle_config.linesets) {
2551 if (lineset->linestyle) {
2552 lineset->linestyle->id.tag |= ID_TAG_DOIT;
2553 }
2554 }
2555 }
2556
2557 LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
2558 /* skip render layers without Freestyle enabled */
2559 if ((view_layer->flag & VIEW_LAYER_FREESTYLE) == 0) {
2560 continue;
2561 }
2562
2563 /* loop over linesets defined in the render layer */
2564 LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer->freestyle_config.linesets) {
2565 FreestyleLineStyle *linestyle = lineset->linestyle;
2566 ListBase tmp_data = {nullptr, nullptr};
2567 size_t tmp_items = 0;
2568
2569 if ((linestyle == nullptr) || !(linestyle->id.tag & ID_TAG_DOIT)) {
2570 continue;
2571 }
2572 linestyle->id.tag &= ~ID_TAG_DOIT;
2573
2574 /* add scene-level animation channels */
2576 /* animation data filtering */
2577 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)linestyle, filter_mode);
2578 }
2580
2581 /* did we find anything? */
2582 if (tmp_items) {
2583 /* include anim-expand widget first */
2584 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2585 /* check if filtering by active status */
2588 }
2589 }
2590
2591 /* now add the list of collected channels */
2592 BLI_movelisttolist(anim_data, &tmp_data);
2594 items += tmp_items;
2595 }
2596 }
2597 }
2598
2599 /* return the number of items added to the list */
2600 return items;
2601}
2602
2604 bAnimContext *ac, ListBase *anim_data, Tex *tex, ID *owner_id, eAnimFilter_Flags filter_mode)
2605{
2606 ListBase tmp_data = {nullptr, nullptr};
2607 size_t tmp_items = 0;
2608 size_t items = 0;
2609
2610 /* add texture's animation data to temp collection */
2612 /* texture animdata */
2613 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)tex, filter_mode);
2614
2615 /* nodes */
2616 if ((tex->nodetree) && !(ac->ads->filterflag & ADS_FILTER_NONTREE)) {
2617 /* owner_id as id instead of texture,
2618 * since it'll otherwise be impossible to track the depth. */
2619
2620 /* FIXME: perhaps as a result, textures should NOT be included under materials,
2621 * but under their own section instead so that free-floating textures can also be animated.
2622 */
2623 tmp_items += animdata_filter_ds_nodetree(
2624 ac, &tmp_data, (ID *)tex, tex->nodetree, filter_mode);
2625 }
2626 }
2628
2629 /* did we find anything? */
2630 if (tmp_items) {
2631 /* include texture-expand widget? */
2632 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2633 /* check if filtering by active status */
2635 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, tex, ANIMTYPE_DSTEX, owner_id, nullptr);
2636 }
2637 }
2638
2639 /* now add the list of collected channels */
2640 BLI_movelisttolist(anim_data, &tmp_data);
2642 items += tmp_items;
2643 }
2644
2645 /* return the number of items added to the list */
2646 return items;
2647}
2648
2649/* NOTE: owner_id is the direct owner of the texture stack in question
2650 * It used to be Material/Light/World before the Blender Internal removal for 2.8
2651 */
2653 ListBase *anim_data,
2654 ID *owner_id,
2655 const eAnimFilter_Flags filter_mode)
2656{
2657 MTex **mtex = nullptr;
2658 size_t items = 0;
2659 int a = 0;
2660
2661 /* get datatype specific data first */
2662 if (owner_id == nullptr) {
2663 return 0;
2664 }
2665
2666 switch (GS(owner_id->name)) {
2667 case ID_PA: {
2668 ParticleSettings *part = (ParticleSettings *)owner_id;
2669 mtex = (MTex **)(&part->mtex);
2670 break;
2671 }
2672 default: {
2673 /* invalid/unsupported option */
2674 if (G.debug & G_DEBUG) {
2675 printf("ERROR: Unsupported owner_id (i.e. texture stack) for filter textures - %s\n",
2676 owner_id->name);
2677 }
2678 return 0;
2679 }
2680 }
2681
2682 /* Firstly check that we actually have some textures,
2683 * by gathering all textures in a temp list. */
2684 for (a = 0; a < MAX_MTEX; a++) {
2685 Tex *tex = (mtex[a]) ? mtex[a]->tex : nullptr;
2686
2687 /* for now, if no texture returned, skip (this shouldn't confuse the user I hope) */
2688 if (tex == nullptr) {
2689 continue;
2690 }
2691
2692 /* add texture's anim channels */
2693 items += animdata_filter_ds_texture(ac, anim_data, tex, owner_id, filter_mode);
2694 }
2695
2696 /* return the number of items added to the list */
2697 return items;
2698}
2699
2701 ListBase *anim_data,
2702 Material *ma,
2703 eAnimFilter_Flags filter_mode)
2704{
2705 ListBase tmp_data = {nullptr, nullptr};
2706 size_t tmp_items = 0;
2707 size_t items = 0;
2708
2709 /* add material's animation data to temp collection */
2711 /* material's animation data */
2712 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)ma, filter_mode);
2713
2714 /* nodes */
2715 if ((ma->nodetree) && !(ac->ads->filterflag & ADS_FILTER_NONTREE)) {
2716 tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, (ID *)ma, ma->nodetree, filter_mode);
2717 }
2718 }
2720
2721 /* did we find anything? */
2722 if (tmp_items) {
2723 /* include material-expand widget first */
2724 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2725 /* check if filtering by active status */
2726 if (ANIMCHANNEL_ACTIVEOK(ma)) {
2727 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, ma, ANIMTYPE_DSMAT, ma, nullptr);
2728 }
2729 }
2730
2731 /* now add the list of collected channels */
2732 BLI_movelisttolist(anim_data, &tmp_data);
2734 items += tmp_items;
2735 }
2736
2737 return items;
2738}
2739
2741 ListBase *anim_data,
2742 Object *ob,
2743 const eAnimFilter_Flags filter_mode)
2744{
2745 size_t items = 0;
2746 int a = 0;
2747
2748 /* First pass: take the materials referenced via the Material slots of the object. */
2749 for (a = 1; a <= ob->totcol; a++) {
2750 Material *ma = BKE_object_material_get(ob, a);
2751
2752 /* if material is valid, try to add relevant contents from here */
2753 if (ma) {
2754 /* add channels */
2755 items += animdata_filter_ds_material(ac, anim_data, ma, filter_mode);
2756 }
2757 }
2758
2759 /* return the number of items added to the list */
2760 return items;
2761}
2762
2763/* ............ */
2764
2765/* Temporary context for modifier linked-data channel extraction */
2767 bAnimContext *ac; /* anim editor context */
2768 bDopeSheet *ads; /* TODO: Remove this pointer from the struct and just use ac->ads. */
2769
2770 ListBase tmp_data; /* list of channels created (but not yet added to the main list) */
2771 size_t items; /* number of channels created */
2772
2773 eAnimFilter_Flags filter_mode; /* flags for stuff we want to filter */
2774};
2775
2776/* dependency walker callback for modifier dependencies */
2777static void animfilter_modifier_idpoin_cb(void *afm_ptr, Object *ob, ID **idpoin, int /*cb_flag*/)
2778{
2780 ID *owner_id = &ob->id;
2781 ID *id = *idpoin;
2782
2783 /* NOTE: the walker only guarantees to give us all the ID-ptr *slots*,
2784 * not just the ones which are actually used, so be careful!
2785 */
2786 if (id == nullptr) {
2787 return;
2788 }
2789
2790 /* check if this is something we're interested in... */
2791 switch (GS(id->name)) {
2792 case ID_TE: /* Textures */
2793 {
2794 Tex *tex = (Tex *)id;
2795 if (!(afm->ac->ads->filterflag & ADS_FILTER_NOTEX)) {
2796 BLI_assert(afm->ac->ads == afm->ads);
2798 afm->ac, &afm->tmp_data, tex, owner_id, afm->filter_mode);
2799 }
2800 break;
2801 }
2802 case ID_NT: {
2803 bNodeTree *node_tree = (bNodeTree *)id;
2804 if (!(afm->ac->ads->filterflag & ADS_FILTER_NONTREE)) {
2805 BLI_assert(afm->ac->ads == afm->ads);
2807 afm->ac, &afm->tmp_data, owner_id, node_tree, afm->filter_mode);
2808 }
2809 }
2810
2811 /* TODO: images? */
2812 default:
2813 break;
2814 }
2815}
2816
2817/* animation linked to data used by modifiers
2818 * NOTE: strictly speaking, modifier animation is already included under Object level
2819 * but for some modifiers (e.g. Displace), there can be linked data that has settings
2820 * which would be nice to animate (i.e. texture parameters) but which are not actually
2821 * attached to any other objects/materials/etc. in the scene
2822 */
2823/* TODO: do we want an expander for this? */
2825 ListBase *anim_data,
2826 Object *ob,
2827 const eAnimFilter_Flags filter_mode)
2828{
2829 tAnimFilterModifiersContext afm = {nullptr};
2830 size_t items = 0;
2831
2832 /* 1) create a temporary "context" containing all the info we have here to pass to the callback
2833 * use to walk through the dependencies of the modifiers
2834 *
2835 * Assumes that all other unspecified values (i.e. accumulation buffers)
2836 * are zero'd out properly!
2837 */
2838 afm.ac = ac;
2839 afm.ads = ac->ads; /* TODO: Remove this pointer from the struct and just use afm.ac->ads. */
2840 afm.filter_mode = filter_mode;
2841
2842 /* 2) walk over dependencies */
2844
2845 /* 3) extract data from the context, merging it back into the standard list */
2846 if (afm.items) {
2847 /* now add the list of collected channels */
2848 BLI_movelisttolist(anim_data, &afm.tmp_data);
2850 items += afm.items;
2851 }
2852
2853 return items;
2854}
2855
2856/* ............ */
2857
2859 ListBase *anim_data,
2860 Object *ob,
2861 eAnimFilter_Flags filter_mode)
2862{
2863 size_t items = 0;
2864
2866 ListBase tmp_data = {nullptr, nullptr};
2867 size_t tmp_items = 0;
2868
2869 /* Note that when psys->part->adt is nullptr the textures can still be
2870 * animated. */
2871 if (psys->part == nullptr) {
2872 continue;
2873 }
2874
2875 /* add particle-system's animation data to temp collection */
2877 /* particle system's animation data */
2878 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)psys->part, filter_mode);
2879
2880 /* textures */
2881 if (!(ac->ads->filterflag & ADS_FILTER_NOTEX)) {
2882 tmp_items += animdata_filter_ds_textures(ac, &tmp_data, (ID *)psys->part, filter_mode);
2883 }
2884 }
2886
2887 /* did we find anything? */
2888 if (tmp_items) {
2889 /* include particle-expand widget first */
2890 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2891 /* check if filtering by active status */
2892 if (ANIMCHANNEL_ACTIVEOK(psys->part)) {
2893 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, psys->part, ANIMTYPE_DSPART, psys->part, nullptr);
2894 }
2895 }
2896
2897 /* now add the list of collected channels */
2898 BLI_movelisttolist(anim_data, &tmp_data);
2900 items += tmp_items;
2901 }
2902 }
2903
2904 /* return the number of items added to the list */
2905 return items;
2906}
2907
2909 ListBase *anim_data,
2910 Object *ob,
2911 eAnimFilter_Flags filter_mode)
2912{
2913 ListBase tmp_data = {nullptr, nullptr};
2914 size_t tmp_items = 0;
2915 size_t items = 0;
2916
2917 IdAdtTemplate *iat = static_cast<IdAdtTemplate *>(ob->data);
2919 short expanded = 0;
2920 const eAnimFilter_Flags ads_filterflag = eAnimFilter_Flags(ac->ads->filterflag);
2921 const eDopeSheet_FilterFlag2 ads_filterflag2 = eDopeSheet_FilterFlag2(ac->ads->filterflag2);
2922
2923 /* get settings based on data type */
2924 switch (ob->type) {
2925 case OB_CAMERA: /* ------- Camera ------------ */
2926 {
2927 Camera *ca = (Camera *)ob->data;
2928
2929 if (ads_filterflag & ADS_FILTER_NOCAM) {
2930 return 0;
2931 }
2932
2933 type = ANIMTYPE_DSCAM;
2934 expanded = FILTER_CAM_OBJD(ca);
2935 break;
2936 }
2937 case OB_LAMP: /* ---------- Light ----------- */
2938 {
2939 Light *la = (Light *)ob->data;
2940
2941 if (ads_filterflag & ADS_FILTER_NOLAM) {
2942 return 0;
2943 }
2944
2945 type = ANIMTYPE_DSLAM;
2946 expanded = FILTER_LAM_OBJD(la);
2947 break;
2948 }
2949 case OB_CURVES_LEGACY: /* ------- Curve ---------- */
2950 case OB_SURF: /* ------- Nurbs Surface ---------- */
2951 case OB_FONT: /* ------- Text Curve ---------- */
2952 {
2953 Curve *cu = (Curve *)ob->data;
2954
2955 if (ads_filterflag & ADS_FILTER_NOCUR) {
2956 return 0;
2957 }
2958
2959 type = ANIMTYPE_DSCUR;
2960 expanded = FILTER_CUR_OBJD(cu);
2961 break;
2962 }
2963 case OB_MBALL: /* ------- MetaBall ---------- */
2964 {
2965 MetaBall *mb = (MetaBall *)ob->data;
2966
2967 if (ads_filterflag & ADS_FILTER_NOMBA) {
2968 return 0;
2969 }
2970
2971 type = ANIMTYPE_DSMBALL;
2972 expanded = FILTER_MBALL_OBJD(mb);
2973 break;
2974 }
2975 case OB_ARMATURE: /* ------- Armature ---------- */
2976 {
2977 bArmature *arm = (bArmature *)ob->data;
2978
2979 if (ads_filterflag & ADS_FILTER_NOARM) {
2980 return 0;
2981 }
2982
2983 type = ANIMTYPE_DSARM;
2984 expanded = FILTER_ARM_OBJD(arm);
2985 break;
2986 }
2987 case OB_MESH: /* ------- Mesh ---------- */
2988 {
2989 Mesh *mesh = (Mesh *)ob->data;
2990
2991 if (ads_filterflag & ADS_FILTER_NOMESH) {
2992 return 0;
2993 }
2994
2995 type = ANIMTYPE_DSMESH;
2996 expanded = FILTER_MESH_OBJD(mesh);
2997 break;
2998 }
2999 case OB_LATTICE: /* ---- Lattice ---- */
3000 {
3001 Lattice *lt = (Lattice *)ob->data;
3002
3003 if (ads_filterflag & ADS_FILTER_NOLAT) {
3004 return 0;
3005 }
3006
3007 type = ANIMTYPE_DSLAT;
3008 expanded = FILTER_LATTICE_OBJD(lt);
3009 break;
3010 }
3011 case OB_SPEAKER: /* ---------- Speaker ----------- */
3012 {
3013 Speaker *spk = (Speaker *)ob->data;
3014
3015 type = ANIMTYPE_DSSPK;
3016 expanded = FILTER_SPK_OBJD(spk);
3017 break;
3018 }
3019 case OB_CURVES: /* ---------- Curves ----------- */
3020 {
3021 Curves *curves = (Curves *)ob->data;
3022
3023 if (ads_filterflag2 & ADS_FILTER_NOHAIR) {
3024 return 0;
3025 }
3026
3027 type = ANIMTYPE_DSHAIR;
3028 expanded = FILTER_CURVES_OBJD(curves);
3029 break;
3030 }
3031 case OB_POINTCLOUD: /* ---------- PointCloud ----------- */
3032 {
3033 PointCloud *pointcloud = (PointCloud *)ob->data;
3034
3035 if (ads_filterflag2 & ADS_FILTER_NOPOINTCLOUD) {
3036 return 0;
3037 }
3038
3039 type = ANIMTYPE_DSPOINTCLOUD;
3040 expanded = FILTER_POINTS_OBJD(pointcloud);
3041 break;
3042 }
3043 case OB_VOLUME: /* ---------- Volume ----------- */
3044 {
3045 Volume *volume = (Volume *)ob->data;
3046
3047 if (ads_filterflag2 & ADS_FILTER_NOVOLUME) {
3048 return 0;
3049 }
3050
3051 type = ANIMTYPE_DSVOLUME;
3052 expanded = FILTER_VOLUME_OBJD(volume);
3053 break;
3054 }
3055 }
3056
3057 /* add object data animation channels */
3058 BEGIN_ANIMFILTER_SUBCHANNELS (expanded) {
3059 /* animation data filtering */
3060 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)iat, filter_mode);
3061
3062 /* sub-data filtering... */
3063 switch (ob->type) {
3064 case OB_LAMP: /* light - textures + nodetree */
3065 {
3066 Light *la = static_cast<Light *>(ob->data);
3067 bNodeTree *ntree = la->nodetree;
3068
3069 /* nodetree */
3070 if ((ntree) && !(ads_filterflag & ADS_FILTER_NONTREE)) {
3071 tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, &la->id, ntree, filter_mode);
3072 }
3073 break;
3074 }
3075 }
3076 }
3078
3079 /* did we find anything? */
3080 if (tmp_items) {
3081 /* include data-expand widget first */
3082 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3083 /* check if filtering by active status */
3084 if (ANIMCHANNEL_ACTIVEOK(iat)) {
3085 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, iat, type, iat, nullptr);
3086 }
3087 }
3088
3089 /* now add the list of collected channels */
3090 BLI_movelisttolist(anim_data, &tmp_data);
3092 items += tmp_items;
3093 }
3094
3095 /* return the number of items added to the list */
3096 return items;
3097}
3098
3099/* shapekey-level animation */
3101 bAnimContext *ac, ListBase *anim_data, Object *ob, Key *key, eAnimFilter_Flags filter_mode)
3102{
3103 ListBase tmp_data = {nullptr, nullptr};
3104 size_t tmp_items = 0;
3105 size_t items = 0;
3106
3107 /* add shapekey-level animation channels */
3109 /* animation data filtering */
3110 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)key, filter_mode);
3111 }
3113
3114 /* did we find anything? */
3115 if (tmp_items) {
3116 /* include key-expand widget first */
3117 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3118 if (ANIMCHANNEL_ACTIVEOK(key)) {
3119 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, key, ANIMTYPE_DSSKEY, ob, nullptr);
3120 }
3121 }
3122
3123 /* now add the list of collected channels */
3124 BLI_movelisttolist(anim_data, &tmp_data);
3126 items += tmp_items;
3127 }
3128
3129 /* return the number of items added to the list */
3130 return items;
3131}
3132
3133/* object-level animation */
3135 ListBase *anim_data,
3136 Object *ob,
3137 eAnimFilter_Flags filter_mode)
3138{
3139 ListBase tmp_data = {nullptr, nullptr};
3140 size_t tmp_items = 0;
3141 size_t items = 0;
3142
3143 AnimData *adt = ob->adt;
3145 short expanded = 1;
3146 void *cdata = nullptr;
3147
3148 /* determine the type of expander channels to use */
3149 /* this is the best way to do this for now... */
3151 ob, /* Some useless long comment to prevent wrapping by old clang-format versions... */
3152 {/* AnimData - no channel, but consider data */},
3153 {/* NLA - no channel, but consider data */},
3154 { /* Drivers */
3155 type = ANIMTYPE_FILLDRIVERS;
3156 cdata = adt;
3157 expanded = EXPANDED_DRVD(adt);
3158 },
3159 {/* NLA Strip Controls - no dedicated channel for now (XXX) */},
3160 { /* Keyframes from legacy Action. */
3161 type = ANIMTYPE_FILLACTD;
3162 cdata = adt->action;
3163 expanded = EXPANDED_ACTC(adt->action);
3164 },
3165 { /* Keyframes from layered action. */
3167 cdata = adt->action;
3168 expanded = EXPANDED_ADT(adt);
3169 });
3170
3171 /* add object-level animation channels */
3172 BEGIN_ANIMFILTER_SUBCHANNELS (expanded) {
3173 /* animation data filtering */
3174 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)ob, filter_mode);
3175 }
3177
3178 /* did we find anything? */
3179 if (tmp_items) {
3180 /* include anim-expand widget first */
3181 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3182 if (type != ANIMTYPE_NONE) {
3183 /* NOTE: active-status (and the associated checks) don't apply here... */
3184 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, cdata, type, ob, nullptr);
3185 }
3186 }
3187
3188 /* now add the list of collected channels */
3189 BLI_movelisttolist(anim_data, &tmp_data);
3191 items += tmp_items;
3192 }
3193
3194 /* return the number of items added to the list */
3195 return items;
3196}
3197
3198/* get animation channels from object2 */
3200 ListBase *anim_data,
3201 Base *base,
3202 eAnimFilter_Flags filter_mode)
3203{
3204 ListBase tmp_data = {nullptr, nullptr};
3205 Object *ob = base->object;
3206 size_t tmp_items = 0;
3207 size_t items = 0;
3208 const eAnimFilter_Flags ads_filterflag = eAnimFilter_Flags(ac->ads->filterflag);
3209
3210 /* filter data contained under object first */
3212 Key *key = BKE_key_from_object(ob);
3213
3214 /* object-level animation */
3215 if ((ob->adt) && !(ads_filterflag & ADS_FILTER_NOOBJ)) {
3216 tmp_items += animdata_filter_ds_obanim(ac, &tmp_data, ob, filter_mode);
3217 }
3218
3219 /* particle deflector textures */
3220 if (ob->pd != nullptr && ob->pd->tex != nullptr && !(ads_filterflag & ADS_FILTER_NOTEX)) {
3221 tmp_items += animdata_filter_ds_texture(ac, &tmp_data, ob->pd->tex, &ob->id, filter_mode);
3222 }
3223
3224 /* shape-key */
3225 if ((key && key->adt) && !(ads_filterflag & ADS_FILTER_NOSHAPEKEYS)) {
3226 tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ob, key, filter_mode);
3227 }
3228
3229 /* modifiers */
3230 if ((ob->modifiers.first) && !(ads_filterflag & ADS_FILTER_NOMODIFIERS)) {
3231 tmp_items += animdata_filter_ds_modifiers(ac, &tmp_data, ob, filter_mode);
3232 }
3233
3234 /* materials */
3235 if ((ob->totcol) && !(ads_filterflag & ADS_FILTER_NOMAT)) {
3236 tmp_items += animdata_filter_ds_materials(ac, &tmp_data, ob, filter_mode);
3237 }
3238
3239 /* object data */
3240 if ((ob->data) && (ob->type != OB_GPENCIL_LEGACY)) {
3241 tmp_items += animdata_filter_ds_obdata(ac, &tmp_data, ob, filter_mode);
3242 }
3243
3244 /* particles */
3245 if ((ob->particlesystem.first) && !(ads_filterflag & ADS_FILTER_NOPART)) {
3246 tmp_items += animdata_filter_ds_particles(ac, &tmp_data, ob, filter_mode);
3247 }
3248
3249 /* grease pencil */
3250 if (ELEM(ob->type, OB_GREASE_PENCIL, OB_GPENCIL_LEGACY) && (ob->data) &&
3251 !(ads_filterflag & ADS_FILTER_NOGPENCIL))
3252 {
3253 if (ob->type == OB_GREASE_PENCIL) {
3255 ac, &tmp_data, static_cast<GreasePencil *>(ob->data), filter_mode);
3256 }
3257 }
3258 }
3260
3261 /* if we collected some channels, add these to the new list... */
3262 if (tmp_items) {
3263 /* firstly add object expander if required */
3264 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3265 /* check if filtering by selection */
3266 /* XXX: double-check on this -
3267 * most of the time, a lot of tools need to filter out these channels! */
3268 if (ANIMCHANNEL_SELOK((base->flag & BASE_SELECTED))) {
3269 /* check if filtering by active status */
3270 if (ANIMCHANNEL_ACTIVEOK(ob)) {
3271 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, base, ANIMTYPE_OBJECT, ob, nullptr);
3272 }
3273 }
3274 }
3275
3276 /* now add the list of collected channels */
3277 BLI_movelisttolist(anim_data, &tmp_data);
3279 items += tmp_items;
3280 }
3281
3282 /* return the number of items added */
3283 return items;
3284}
3285
3287 bAnimContext *ac, ListBase *anim_data, Scene *sce, World *wo, eAnimFilter_Flags filter_mode)
3288{
3289 ListBase tmp_data = {nullptr, nullptr};
3290 size_t tmp_items = 0;
3291 size_t items = 0;
3292
3293 /* add world animation channels */
3295 /* animation data filtering */
3296 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)wo, filter_mode);
3297
3298 /* nodes */
3299 if ((wo->nodetree) && !(ac->ads->filterflag & ADS_FILTER_NONTREE)) {
3300 tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, (ID *)wo, wo->nodetree, filter_mode);
3301 }
3302 }
3304
3305 /* did we find anything? */
3306 if (tmp_items) {
3307 /* include data-expand widget first */
3308 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3309 /* check if filtering by active status */
3310 if (ANIMCHANNEL_ACTIVEOK(wo)) {
3311 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, wo, ANIMTYPE_DSWOR, sce, nullptr);
3312 }
3313 }
3314
3315 /* now add the list of collected channels */
3316 BLI_movelisttolist(anim_data, &tmp_data);
3318 items += tmp_items;
3319 }
3320
3321 /* return the number of items added to the list */
3322 return items;
3323}
3324
3326 ListBase *anim_data,
3327 Scene *sce,
3328 eAnimFilter_Flags filter_mode)
3329{
3330 ListBase tmp_data = {nullptr, nullptr};
3331 size_t tmp_items = 0;
3332 size_t items = 0;
3333
3334 AnimData *adt = sce->adt;
3336 short expanded = 1;
3337 void *cdata = nullptr;
3338
3339 /* determine the type of expander channels to use */
3340 /* this is the best way to do this for now... */
3342 sce, /* Some useless long comment to prevent wrapping by old clang-format versions... */
3343 {/* AnimData - no channel, but consider data */},
3344 {/* NLA - no channel, but consider data */},
3345 { /* Drivers */
3346 type = ANIMTYPE_FILLDRIVERS;
3347 cdata = adt;
3348 expanded = EXPANDED_DRVD(adt);
3349 },
3350 {/* NLA Strip Controls - no dedicated channel for now (XXX) */},
3351 { /* Keyframes from legacy Action. */
3352 type = ANIMTYPE_FILLACTD;
3353 cdata = adt->action;
3354 expanded = EXPANDED_ACTC(adt->action);
3355 },
3356 { /* Keyframes from layered Action. */
3358 cdata = adt->action;
3359 expanded = EXPANDED_ADT(adt);
3360 });
3361
3362 /* add scene-level animation channels */
3363 BEGIN_ANIMFILTER_SUBCHANNELS (expanded) {
3364 /* animation data filtering */
3365 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)sce, filter_mode);
3366 }
3368
3369 /* did we find anything? */
3370 if (tmp_items) {
3371 /* include anim-expand widget first */
3372 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3373 if (type != ANIMTYPE_NONE) {
3374 /* NOTE: active-status (and the associated checks) don't apply here... */
3375 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, cdata, type, sce, nullptr);
3376 }
3377 }
3378
3379 /* now add the list of collected channels */
3380 BLI_movelisttolist(anim_data, &tmp_data);
3382 items += tmp_items;
3383 }
3384
3385 /* return the number of items added to the list */
3386 return items;
3387}
3388
3390 ListBase *anim_data,
3391 Scene *sce,
3392 eAnimFilter_Flags filter_mode)
3393{
3394 ListBase tmp_data = {nullptr, nullptr};
3395 size_t tmp_items = 0;
3396 size_t items = 0;
3397
3398 /* filter data contained under object first */
3400 bNodeTree *ntree = sce->nodetree;
3401 bGPdata *gpd = sce->gpd;
3402 World *wo = sce->world;
3403
3404 /* Action, Drivers, or NLA for Scene */
3405 if ((ac->ads->filterflag & ADS_FILTER_NOSCE) == 0) {
3406 tmp_items += animdata_filter_ds_scene(ac, &tmp_data, sce, filter_mode);
3407 }
3408
3409 /* world */
3410 if ((wo) && !(ac->ads->filterflag & ADS_FILTER_NOWOR)) {
3411 tmp_items += animdata_filter_ds_world(ac, &tmp_data, sce, wo, filter_mode);
3412 }
3413
3414 /* nodetree */
3415 if ((ntree) && !(ac->ads->filterflag & ADS_FILTER_NONTREE)) {
3416 tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, (ID *)sce, ntree, filter_mode);
3417 }
3418
3419 /* line styles */
3420 if ((ac->ads->filterflag & ADS_FILTER_NOLINESTYLE) == 0) {
3421 tmp_items += animdata_filter_ds_linestyle(ac, &tmp_data, sce, filter_mode);
3422 }
3423
3424 /* grease pencil */
3425 if ((gpd) && !(ac->ads->filterflag & ADS_FILTER_NOGPENCIL)) {
3426 tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, gpd, filter_mode);
3427 }
3428
3429 /* TODO: one day, when sequencer becomes its own datatype,
3430 * perhaps it should be included here. */
3431 }
3433
3434 /* if we collected some channels, add these to the new list... */
3435 if (tmp_items) {
3436 /* firstly add object expander if required */
3437 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3438 /* check if filtering by selection */
3439 if (ANIMCHANNEL_SELOK((sce->flag & SCE_DS_SELECTED))) {
3440 /* NOTE: active-status doesn't matter for this! */
3441 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, sce, ANIMTYPE_SCENE, sce, nullptr);
3442 }
3443 }
3444
3445 /* now add the list of collected channels */
3446 BLI_movelisttolist(anim_data, &tmp_data);
3448 items += tmp_items;
3449 }
3450
3451 /* return the number of items added */
3452 return items;
3453}
3454
3456 ListBase *anim_data,
3457 MovieClip *clip,
3458 eAnimFilter_Flags filter_mode)
3459{
3460 ListBase tmp_data = {nullptr, nullptr};
3461 size_t tmp_items = 0;
3462 size_t items = 0;
3463 /* add world animation channels */
3465 /* animation data filtering */
3466 tmp_items += animfilter_block_data(ac, &tmp_data, (ID *)clip, filter_mode);
3467 }
3469 /* did we find anything? */
3470 if (tmp_items) {
3471 /* include data-expand widget first */
3472 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3473 /* check if filtering by active status */
3474 if (ANIMCHANNEL_ACTIVEOK(clip)) {
3475 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, clip, ANIMTYPE_DSMCLIP, clip, nullptr);
3476 }
3477 }
3478 /* now add the list of collected channels */
3479 BLI_movelisttolist(anim_data, &tmp_data);
3481 items += tmp_items;
3482 }
3483 /* return the number of items added to the list */
3484 return items;
3485}
3486
3488 ListBase *anim_data,
3489 const eAnimFilter_Flags filter_mode)
3490{
3491 size_t items = 0;
3492 LISTBASE_FOREACH (MovieClip *, clip, &ac->bmain->movieclips) {
3493 /* only show if gpd is used by something... */
3494 if (ID_REAL_USERS(clip) < 1) {
3495 continue;
3496 }
3497 items += animdata_filter_ds_movieclip(ac, anim_data, clip, filter_mode);
3498 }
3499 /* return the number of items added to the list */
3500 return items;
3501}
3502
3503/* Helper for animdata_filter_dopesheet() - For checking if an object should be included or not */
3505 Base *base,
3506 const eObjectMode object_mode,
3507 const eAnimFilter_Flags filter_mode)
3508{
3509 Object *ob = base->object;
3510
3511 if (base->object == nullptr) {
3512 return false;
3513 }
3514
3515 /* firstly, check if object can be included, by the following factors:
3516 * - if only visible, must check for layer and also viewport visibility
3517 * --> while tools may demand only visible, user setting takes priority
3518 * as user option controls whether sets of channels get included while
3519 * tool-flag takes into account collapsed/open channels too
3520 * - if only selected, must check if object is selected
3521 * - there must be animation data to edit (this is done recursively as we
3522 * try to add the channels)
3523 */
3524 if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ac->ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
3525 /* layer visibility - we check both object and base, since these may not be in sync yet */
3528 {
3529 return false;
3530 }
3531
3532 /* outliner restrict-flag */
3534 return false;
3535 }
3536 }
3537
3538 /* if only F-Curves with visible flags set can be shown, check that
3539 * data-block hasn't been set to invisible.
3540 */
3541 if (filter_mode & ANIMFILTER_CURVE_VISIBLE) {
3542 if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE)) {
3543 return false;
3544 }
3545 }
3546
3547 /* Pinned curves are visible regardless of selection flags. */
3548 if ((ob->adt) && (ob->adt->flag & ADT_CURVES_ALWAYS_VISIBLE)) {
3549 return true;
3550 }
3551
3552 /* Special case.
3553 * We don't do recursive checks for pin, but we need to deal with tricky
3554 * setup like animated camera lens without animated camera location.
3555 * Without such special handle here we wouldn't be able to bin such
3556 * camera data only animation to the editor.
3557 */
3558 if (ob->adt == nullptr && ob->data != nullptr) {
3559 AnimData *data_adt = BKE_animdata_from_id(static_cast<ID *>(ob->data));
3560 if (data_adt != nullptr && (data_adt->flag & ADT_CURVES_ALWAYS_VISIBLE)) {
3561 return true;
3562 }
3563 }
3564
3565 /* check selection and object type filters */
3566 if (ac->ads->filterflag & ADS_FILTER_ONLYSEL) {
3567 if (object_mode & OB_MODE_POSE) {
3568 /* When in pose-mode handle all pose-mode objects.
3569 * This avoids problems with pose-mode where objects may be unselected,
3570 * where a selected bone of an unselected object would be hidden. see: #81922. */
3571 if (!(base->object->mode & object_mode)) {
3572 return false;
3573 }
3574 }
3575 else {
3576 /* only selected should be shown (ignore active) */
3577 if (!(base->flag & BASE_SELECTED)) {
3578 return false;
3579 }
3580 }
3581 }
3582
3583 /* check if object belongs to the filtering group if option to filter
3584 * objects by the grouped status is on
3585 * - used to ease the process of doing multiple-character choreographies
3586 */
3587 if (ac->ads->filter_grp != nullptr) {
3589 return false;
3590 }
3591 }
3592
3593 /* no reason to exclude this object... */
3594 return true;
3595}
3596
3597/* Helper for animdata_filter_ds_sorted_bases() - Comparison callback for two Base pointers... */
3598static int ds_base_sorting_cmp(const void *base1_ptr, const void *base2_ptr)
3599{
3600 const Base *b1 = *((const Base **)base1_ptr);
3601 const Base *b2 = *((const Base **)base2_ptr);
3602
3603 return strcmp(b1->object->id.name + 2, b2->object->id.name + 2);
3604}
3605
3606/* Get a sorted list of all the bases - for inclusion in dopesheet (when drawing channels) */
3608 const Scene *scene,
3609 ViewLayer *view_layer,
3610 const eAnimFilter_Flags filter_mode,
3611 size_t *r_usable_bases)
3612{
3613 /* Create an array with space for all the bases, but only containing the usable ones */
3614 BKE_view_layer_synced_ensure(scene, view_layer);
3615 ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
3616 size_t tot_bases = BLI_listbase_count(object_bases);
3617 size_t num_bases = 0;
3618
3619 Base **sorted_bases = MEM_cnew_array<Base *>(tot_bases, "Dopesheet Usable Sorted Bases");
3620 LISTBASE_FOREACH (Base *, base, object_bases) {
3621 if (animdata_filter_base_is_ok(ac, base, OB_MODE_OBJECT, filter_mode)) {
3622 sorted_bases[num_bases++] = base;
3623 }
3624 }
3625
3626 /* Sort this list of pointers (based on the names) */
3627 qsort(sorted_bases, num_bases, sizeof(Base *), ds_base_sorting_cmp);
3628
3629 /* Return list of sorted bases */
3630 *r_usable_bases = num_bases;
3631 return sorted_bases;
3632}
3633
3634/* TODO: implement pinning...
3635 * (if and when pinning is done, what we need to do is to provide freeing mechanisms -
3636 * to protect against data that was deleted). */
3638 ListBase *anim_data,
3639 eAnimFilter_Flags filter_mode)
3640{
3641 bDopeSheet *ads = ac->ads;
3642 Scene *scene = (Scene *)ads->source;
3643 ViewLayer *view_layer = (ViewLayer *)ac->view_layer;
3644 size_t items = 0;
3645
3646 /* check that we do indeed have a scene */
3647 if ((ads->source == nullptr) || (GS(ads->source->name) != ID_SCE)) {
3648 printf("Dope Sheet Error: No scene!\n");
3649 if (G.debug & G_DEBUG) {
3650 printf("\tPointer = %p, Name = '%s'\n",
3651 (void *)ads->source,
3652 (ads->source) ? ads->source->name : nullptr);
3653 }
3654 return 0;
3655 }
3656
3657 /* augment the filter-flags with settings based on the dopesheet filterflags
3658 * so that some temp settings can get added automagically...
3659 */
3660 if (ac->ads->filterflag & ADS_FILTER_SELEDIT) {
3661 /* only selected F-Curves should get their keyframes considered for editability */
3662 filter_mode |= ANIMFILTER_SELEDIT;
3663 }
3664
3665 /* Cache files level animations (frame duration and such). */
3666 if (!(ac->ads->filterflag2 & ADS_FILTER_NOCACHEFILES) &&
3668 {
3669 LISTBASE_FOREACH (CacheFile *, cache_file, &ac->bmain->cachefiles) {
3670 items += animdata_filter_ds_cachefile(ac, anim_data, cache_file, filter_mode);
3671 }
3672 }
3673
3674 /* movie clip's animation */
3675 if (!(ac->ads->filterflag2 & ADS_FILTER_NOMOVIECLIPS) &&
3677 {
3678 items += animdata_filter_dopesheet_movieclips(ac, anim_data, filter_mode);
3679 }
3680
3681 /* Scene-linked animation - e.g. world, compositing nodes, scene anim
3682 * (including sequencer currently). */
3683 items += animdata_filter_dopesheet_scene(ac, anim_data, scene, filter_mode);
3684
3685 /* If filtering for channel drawing, we want the objects in alphabetical order,
3686 * to make it easier to predict where items are in the hierarchy
3687 * - This order only really matters
3688 * if we need to show all channels in the list (e.g. for drawing).
3689 * (XXX: What about lingering "active" flags? The order may now become unpredictable)
3690 * - Don't do this if this behavior has been turned off (i.e. due to it being too slow)
3691 * - Don't do this if there's just a single object
3692 */
3693 BKE_view_layer_synced_ensure(scene, view_layer);
3694 ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
3695 if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && !(ads->flag & ADS_FLAG_NO_DB_SORT) &&
3696 (object_bases->first != object_bases->last))
3697 {
3698 /* Filter list of bases (i.e. objects), sort them, then add their contents normally... */
3699 /* TODO: Cache the old sorted order - if the set of bases hasn't changed, don't re-sort... */
3700 Base **sorted_bases;
3701 size_t num_bases;
3702
3703 sorted_bases = animdata_filter_ds_sorted_bases(ac, scene, view_layer, filter_mode, &num_bases);
3704 if (sorted_bases) {
3705 /* Add the necessary channels for these bases... */
3706 for (size_t i = 0; i < num_bases; i++) {
3707 items += animdata_filter_dopesheet_ob(ac, anim_data, sorted_bases[i], filter_mode);
3708 }
3709
3710 /* TODO: store something to validate whether any changes are needed? */
3711
3712 /* free temporary data */
3713 MEM_freeN(sorted_bases);
3714 }
3715 }
3716 else {
3717 /* Filter and add contents of each base (i.e. object) without them sorting first
3718 * NOTE: This saves performance in cases where order doesn't matter
3719 */
3720 Object *obact = BKE_view_layer_active_object_get(view_layer);
3721 const eObjectMode object_mode = (obact != nullptr) ? eObjectMode(obact->mode) : OB_MODE_OBJECT;
3722 LISTBASE_FOREACH (Base *, base, object_bases) {
3723 if (animdata_filter_base_is_ok(ac, base, object_mode, filter_mode)) {
3724 /* since we're still here, this object should be usable */
3725 items += animdata_filter_dopesheet_ob(ac, anim_data, base, filter_mode);
3726 }
3727 }
3728 }
3729
3730 /* return the number of items in the list */
3731 return items;
3732}
3733
3734/* Summary track for DopeSheet/Action Editor
3735 * - return code is whether the summary lets the other channels get drawn
3736 */
3738 ListBase *anim_data,
3739 const eAnimFilter_Flags filter_mode,
3740 size_t *items)
3741{
3742 bDopeSheet *ads = nullptr;
3743
3744 /* get the DopeSheet information to use
3745 * - we should only need to deal with the DopeSheet/Action Editor,
3746 * since all the other Animation Editors won't have this concept
3747 * being applicable.
3748 */
3749 if ((ac && ac->sl) && (ac->spacetype == SPACE_ACTION)) {
3750 SpaceAction *saction = (SpaceAction *)ac->sl;
3751 ads = &saction->ads;
3752 }
3753 else {
3754 /* invalid space type - skip this summary channels */
3755 return 1;
3756 }
3757
3758 /* dopesheet summary
3759 * - only for drawing and/or selecting keyframes in channels, but not for real editing
3760 * - only useful for DopeSheet/Action/etc. editors where it is actually useful
3761 */
3762 if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && (ac->ads->filterflag & ADS_FILTER_SUMMARY)) {
3763 bAnimListElem *ale = make_new_animlistelem(ac->bmain, ac, ANIMTYPE_SUMMARY, nullptr, nullptr);
3764 if (ale) {
3765 BLI_addtail(anim_data, ale);
3766 (*items)++;
3767 }
3768
3769 /* If summary is collapsed, don't show other channels beneath this - this check is put inside
3770 * the summary check so that it doesn't interfere with normal operation.
3771 */
3772 if (ads->flag & ADS_FLAG_SUMMARY_COLLAPSED) {
3773 return 0;
3774 }
3775 }
3776
3777 /* the other channels beneath this can be shown */
3778 return 1;
3779}
3780
3781/* ......................... */
3782
3783/* filter data associated with a channel - usually for handling summary-channels in DopeSheet */
3785 ListBase *anim_data,
3786 bAnimListElem *channel,
3787 const eAnimFilter_Flags filter_mode)
3788{
3789 size_t items = 0;
3790
3791 /* data to filter depends on channel type */
3792 /* NOTE: only common channel-types have been handled for now. More can be added as necessary */
3793 switch (channel->type) {
3794 case ANIMTYPE_SUMMARY:
3795 items += animdata_filter_dopesheet(ac, anim_data, filter_mode);
3796 break;
3797
3798 case ANIMTYPE_SCENE:
3800 ac, anim_data, static_cast<Scene *>(channel->data), filter_mode);
3801 break;
3802
3803 case ANIMTYPE_OBJECT:
3805 ac, anim_data, static_cast<Base *>(channel->data), filter_mode);
3806 break;
3807
3810 ac, anim_data, static_cast<CacheFile *>(channel->data), filter_mode);
3811 break;
3812
3813 case ANIMTYPE_ANIMDATA:
3814 items += animfilter_block_data(ac, anim_data, channel->id, filter_mode);
3815 break;
3816
3817 default:
3818 printf("ERROR: Unsupported channel type (%d) in animdata_filter_animchan()\n",
3819 channel->type);
3820 break;
3821 }
3822
3823 return items;
3824}
3825
3826/* ----------- Cleanup API --------------- */
3827
3828/* Remove entries with invalid types in animation channel list */
3830{
3831 size_t items = 0;
3832
3833 /* only keep entries with valid types */
3834 LISTBASE_FOREACH_MUTABLE (bAnimListElem *, ale, anim_data) {
3835 if (ale->type == ANIMTYPE_NONE) {
3836 BLI_freelinkN(anim_data, ale);
3837 }
3838 else {
3839 items++;
3840 }
3841 }
3842
3843 return items;
3844}
3845
3846/* Remove duplicate entries in animation channel list */
3848{
3849 GSet *gs;
3850 size_t items = 0;
3851
3852 /* Build new hash-table to efficiently store and retrieve which entries have been
3853 * encountered already while searching. */
3854 gs = BLI_gset_ptr_new(__func__);
3855
3856 /* loop through items, removing them from the list if a similar item occurs already */
3857 LISTBASE_FOREACH_MUTABLE (bAnimListElem *, ale, anim_data) {
3858 /* check if hash has any record of an entry like this
3859 * - just use ale->data for now, though it would be nicer to involve
3860 * ale->type in combination too to capture corner cases
3861 * (where same data performs differently)
3862 */
3863 if (BLI_gset_add(gs, ale->data)) {
3864 /* this entry is 'unique' and can be kept */
3865 items++;
3866 }
3867 else {
3868 /* this entry isn't needed anymore */
3869 BLI_freelinkN(anim_data, ale);
3870 }
3871 }
3872
3873 /* free the hash... */
3874 BLI_gset_free(gs, nullptr);
3875
3876 /* return the number of items still in the list */
3877 return items;
3878}
3879
3880/* ----------- Public API --------------- */
3881
3883 ListBase *anim_data,
3884 const eAnimFilter_Flags filter_mode,
3885 void *data,
3886 const eAnimCont_Types datatype)
3887{
3888 if (!data || !anim_data) {
3889 return 0;
3890 }
3891
3892 size_t items = 0;
3893 switch (datatype) {
3894 /* Action-Editing Modes */
3895 case ANIMCONT_ACTION: /* 'Action Editor' */
3896 {
3897 Object *obact = ac->obact;
3898 SpaceAction *saction = (SpaceAction *)ac->sl;
3899 bDopeSheet *ads = (saction) ? &saction->ads : nullptr;
3900 BLI_assert(ads == ac->ads);
3901 UNUSED_VARS_NDEBUG(ads);
3902
3903 /* specially check for AnimData filter, see #36687. */
3904 /* TODO: see how this interacts with the new layered Actions. */
3905 if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
3906 /* all channels here are within the same AnimData block, hence this special case */
3907 if (LIKELY(obact->adt)) {
3908 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, obact->adt, ANIMTYPE_ANIMDATA, (ID *)obact, nullptr);
3909 }
3910 }
3911 else {
3912 /* The check for the DopeSheet summary is included here
3913 * since the summary works here too. */
3914 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
3916 obact->adt && data == obact->adt->action,
3917 "This code assumes the Action editor shows the Action of the active object");
3918
3919 animrig::Action &action = static_cast<bAction *>(data)->wrap();
3920 const animrig::slot_handle_t slot_handle = obact->adt->slot_handle;
3921 items += animfilter_action(ac, anim_data, action, slot_handle, filter_mode, (ID *)obact);
3922 }
3923 }
3924
3925 break;
3926 }
3927 case ANIMCONT_SHAPEKEY: /* 'ShapeKey Editor' */
3928 {
3929 Key *key = (Key *)data;
3930
3931 /* specially check for AnimData filter, see #36687. */
3932 if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
3933 /* all channels here are within the same AnimData block, hence this special case */
3934 if (LIKELY(key->adt)) {
3935 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, key->adt, ANIMTYPE_ANIMDATA, (ID *)key, nullptr);
3936 }
3937 }
3938 else {
3939 /* The check for the DopeSheet summary is included here
3940 * since the summary works here too. */
3941 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
3942 items = animdata_filter_shapekey(ac, anim_data, key, filter_mode);
3943 }
3944 }
3945
3946 break;
3947 }
3948
3949 /* Modes for Specialty Data Types (i.e. not keyframes) */
3950 case ANIMCONT_GPENCIL: {
3951 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
3952 items = animdata_filter_grease_pencil(ac, anim_data, filter_mode);
3953 }
3954 break;
3955 }
3956 case ANIMCONT_MASK: {
3957 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
3958 items = animdata_filter_mask(ac, anim_data, data, filter_mode);
3959 }
3960 break;
3961 }
3962
3963 /* DopeSheet Based Modes */
3964 case ANIMCONT_DOPESHEET: /* 'DopeSheet Editor' */
3965 {
3966 /* Due to code in `actedit_get_context()`, the equation below holds. The `data` pointer is no
3967 * longer used here, in favor of always passing `ac` down the call chain. The called code
3968 * can access it via `ac->ads`. Because the anim filtering code is quite complex, I (Sybren)
3969 * want to keep this assertion in place. */
3970 BLI_assert_msg(ac->ads == data, "ANIMCONT_DOPESHEET");
3971
3972 /* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
3973 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
3974 items += animdata_filter_dopesheet(ac, anim_data, filter_mode);
3975 }
3976 break;
3977 }
3978 case ANIMCONT_FCURVES: /* Graph Editor -> F-Curves/Animation Editing */
3979 case ANIMCONT_DRIVERS: /* Graph Editor -> Drivers Editing */
3980 case ANIMCONT_NLA: /* NLA Editor */
3981 {
3982 /* Due to code in `actedit_get_context()`, the equation below holds. The `data` pointer is no
3983 * longer used here, in favor of always passing `ac` down the call chain. The called code
3984 * can access it via `ac->ads`. Because the anim filtering code is quite complex, I (Sybren)
3985 * want to keep this assertion in place. */
3986 BLI_assert_msg(ac->ads == data, "ANIMCONT_FCURVES/DRIVERS/NLA");
3987
3988 /* all of these editors use the basic DopeSheet data for filtering options,
3989 * but don't have all the same features */
3990 items = animdata_filter_dopesheet(ac, anim_data, filter_mode);
3991 break;
3992 }
3993
3994 /* Timeline Mode - Basically the same as dopesheet,
3995 * except we only have the summary for now */
3996 case ANIMCONT_TIMELINE: {
3997 /* Due to code in `actedit_get_context()`, the equation below holds. The `data` pointer is no
3998 * longer used here, in favor of always passing `ac` down the call chain. The called code
3999 * can access it via `ac->ads`. Because the anim filtering code is quite complex, I (Sybren)
4000 * want to keep this assertion in place. */
4001 BLI_assert_msg(ac->ads == data, "ANIMCONT_TIMELINE");
4002
4003 /* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
4004 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
4005 items += animdata_filter_dopesheet(ac, anim_data, filter_mode);
4006 }
4007 break;
4008 }
4009
4010 /* Special/Internal Use */
4011 case ANIMCONT_CHANNEL: /* animation channel */
4012 {
4013 /* based on the channel type, filter relevant data for this */
4015 ac, anim_data, static_cast<bAnimListElem *>(data), filter_mode);
4016 break;
4017 }
4018
4019 case ANIMCONT_NONE:
4020 printf("ANIM_animdata_filter() - Invalid datatype argument ANIMCONT_NONE\n");
4021 break;
4022 }
4023
4024 /* remove any 'weedy' entries */
4025 items = animdata_filter_remove_invalid(anim_data);
4026
4027 /* remove duplicates (if required) */
4028 if (filter_mode & ANIMFILTER_NODUPLIS) {
4029 items = animdata_filter_remove_duplis(anim_data);
4030 }
4031
4032 return items;
4033}
4034
4035/* ************************************************************ */
Functions and classes to work with Actions.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
bool ANIM_bonecoll_is_visible_pchan(const bArmature *armature, const bPoseChannel *pchan)
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:89
bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
ScrArea * CTX_wm_area(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
SpaceLink * CTX_wm_space_data(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar)
#define DRIVER_TARGETS_LOOPER_END
@ G_DEBUG
Low-level operations for grease pencil.
std::optional< std::string > BKE_keyblock_curval_rnapath_get(const Key *key, const KeyBlock *kb)
Definition key.cc:1958
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1820
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
struct MaskLayer * BKE_mask_layer_active(struct Mask *mask)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
void void void BKE_modifiers_foreach_ID_link(Object *ob, IDWalkFunc walk, void *user_data)
#define NODE_GROUP
Definition BKE_node.hh:800
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
struct GSet GSet
Definition BLI_ghash.h:341
GSet * BLI_gset_ptr_new(const char *info)
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int bool bool bool size_t size_t size_t int BLI_string_max_possible_word_count(int str_len) ATTR_WARN_UNUSED_RESULT
Definition string.c:569
int char char * BLI_strncasestr(const char *s, const char *find, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int bool bool BLI_str_endswith(const char *__restrict str, const char *__restrict end) ATTR_NONNULL(1
bool bool int BLI_string_find_split_words(const char *str, size_t str_maxlen, char delim, int r_words[][2], int words_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
bool bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, size_t result_maxncpy)
Definition string.c:517
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:654
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
@ ID_TAG_DOIT
Definition DNA_ID.h:1003
#define ID_REAL_USERS(id)
Definition DNA_ID.h:637
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ ID_TE
@ ID_NT
@ ID_SCE
@ ID_AC
@ ID_OB
@ ID_PA
eDopeSheet_FilterFlag2
@ ADS_FILTER_DRIVER_FALLBACK_AS_ERROR
@ ADS_FILTER_NOMOVIECLIPS
@ ADS_FILTER_NOVOLUME
@ ADS_FILTER_NOHAIR
@ ADS_FILTER_NOCACHEFILES
@ ADS_FILTER_NOPOINTCLOUD
@ ADS_FILTER_ONLYSEL
@ ADS_FILTER_NOARM
@ ADS_FILTER_NLA_NOACT
@ ADS_FILTER_NOMAT
@ ADS_FILTER_NONTREE
@ ADS_FILTER_NOCAM
@ ADS_FILTER_NOSHAPEKEYS
@ ADS_FILTER_NOTEX
@ ADS_FILTER_ONLYNLA
@ ADS_FILTER_ONLY_ERRORS
@ ADS_FILTER_ALL_SLOTS
@ ADS_FILTER_ONLYDRIVERS
@ ADS_FILTER_SELEDIT
@ ADS_FILTER_NOSCE
@ ADS_FILTER_NOLAM
@ ADS_FILTER_NOMODIFIERS
@ ADS_FILTER_NOLINESTYLE
@ ADS_FILTER_SUMMARY
@ ADS_FILTER_NOGPENCIL
@ ADS_FILTER_NOOBJ
@ ADS_FILTER_NOCUR
@ ADS_FILTER_NOPART
@ ADS_FILTER_NOMESH
@ ADS_FILTER_NOWOR
@ ADS_FILTER_INCL_HIDDEN
@ ADS_FILTER_NOMBA
@ ADS_FILTER_NOLAT
@ AGRP_ACTIVE
@ AGRP_NOTVISIBLE
@ ADS_FLAG_SUMMARY_COLLAPSED
@ ADS_FLAG_INVERT_FILTER
@ ADS_FLAG_NO_DB_SORT
@ ADS_FLAG_FUZZY_NAMES
eAnimEdit_Context
@ SACTCONT_GPENCIL
@ SACTCONT_ACTION
@ SACTCONT_TIMELINE
@ SACTCONT_DOPESHEET
@ SACTCONT_SHAPEKEY
@ SACTCONT_MASK
@ SACTCONT_CACHEFILE
@ ADT_CURVES_ALWAYS_VISIBLE
@ ADT_CURVES_NOT_VISIBLE
@ ADT_NLA_SKEYS_COLLAPSED
@ DTAR_FLAG_FALLBACK_USED
@ DTAR_FLAG_INVALID
@ DRIVER_FLAG_INVALID
@ FCURVE_DISABLED
@ FCURVE_ACTIVE
@ FCURVE_SELECTED
@ FCURVE_PROTECTED
@ FCURVE_VISIBLE
@ NLATRACK_ACTIVE
@ BONE_SELECTED
@ BONE_HIDDEN_P
@ GP_LAYER_TREE_NODE_EXPANDED
@ GREASE_PENCIL_ANIM_CHANNEL_EXPANDED
@ KEY_RELATIVE
@ VIEW_LAYER_FREESTYLE
@ BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT
@ BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT
@ NODE_SELECT
eObjectMode
@ OB_MODE_POSE
@ OB_MODE_OBJECT
Object is a sort of wrapper for general info.
@ OB_HIDE_VIEWPORT
@ OB_SPEAKER
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_LAMP
@ OB_MESH
@ OB_POINTCLOUD
@ OB_VOLUME
@ OB_CURVES_LEGACY
@ OB_GPENCIL_LEGACY
@ OB_CURVES
#define BASE_SELECTED(v3d, base)
@ SCE_KEYS_NO_SELONLY
@ SCE_DS_SELECTED
eRegion_Type
eSpace_Type
@ SPACE_TEXT
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_CONSOLE
@ SPACE_OUTLINER
@ SPACE_STATUSBAR
@ SPACE_TOPBAR
@ SPACE_NODE
@ SPACE_SPREADSHEET
@ SPACE_USERPREF
@ SPACE_FILE
@ SPACE_PROPERTIES
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_EMPTY
@ SPACE_SCRIPT
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SPACE_INFO
eGraphEdit_Mode
@ SIPO_MODE_DRIVERS
@ SIPO_MODE_ANIMATION
@ USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS
#define EXPANDED_ADT(adt)
#define FILTER_SPK_OBJD(spk)
#define FILTER_CACHEFILE_OBJD(cf)
#define SEL_SHAPEKEY(kb)
eAnim_ChannelType
@ ANIMTYPE_DSSPK
@ ANIMTYPE_DSTEX
@ ANIMTYPE_SUMMARY
@ ANIMTYPE_DSNTREE
@ ANIMTYPE_NLACURVE
@ ANIMTYPE_SHAPEKEY
@ ANIMTYPE_DSMBALL
@ ANIMTYPE_DSCAM
@ ANIMTYPE_DSPOINTCLOUD
@ ANIMTYPE_FILLDRIVERS
@ ANIMTYPE_NONE
@ ANIMTYPE_DSPART
@ ANIMTYPE_DSLINESTYLE
@ ANIMTYPE_GROUP
@ ANIMTYPE_ACTION_SLOT
@ ANIMTYPE_SPECIALDATA__UNUSED
@ ANIMTYPE_GREASE_PENCIL_DATABLOCK
@ ANIMTYPE_DSCUR
@ ANIMTYPE_SCENE
@ ANIMTYPE_DSARM
@ ANIMTYPE_NLACONTROLS
@ ANIMTYPE_GPLAYER
@ ANIMTYPE_MASKDATABLOCK
@ ANIMTYPE_ANIMDATA
@ ANIMTYPE_MASKLAYER
@ ANIMTYPE_DSGPENCIL
@ ANIMTYPE_DSLAT
@ ANIMTYPE_NLAACTION
@ ANIMTYPE_DSMCLIP
@ ANIMTYPE_DSMAT
@ ANIMTYPE_NUM_TYPES
@ ANIMTYPE_DSCACHEFILE
@ ANIMTYPE_DSVOLUME
@ ANIMTYPE_FCURVE
@ ANIMTYPE_DSLAM
@ ANIMTYPE_PALETTE
@ ANIMTYPE_GPDATABLOCK
@ ANIMTYPE_GREASE_PENCIL_LAYER
@ ANIMTYPE_FILLACT_LAYERED
@ ANIMTYPE_FILLACTD
@ ANIMTYPE_OBJECT
@ ANIMTYPE_DSMESH
@ ANIMTYPE_GREASE_PENCIL_LAYER_GROUP
@ ANIMTYPE_NLATRACK
@ ANIMTYPE_DSWOR
@ ANIMTYPE_DSSKEY
@ ANIMTYPE_DSHAIR
#define EXPANDED_MCLIP(clip)
#define FILTER_NTREE_DATA(ntree)
#define FILTER_SKE_OBJD(key)
#define FILTER_MESH_OBJD(me)
#define EDITABLE_GPL(gpl)
#define SEL_AGRP(agrp)
#define EXPANDED_SCEC(sce)
#define SEL_MASKLAY(masklay)
#define SEL_GPL(gpl)
#define FILTER_ARM_OBJD(arm)
#define EDITABLE_AGRP(agrp)
#define EXPANDED_AGRP(ac, agrp)
#define FILTER_CAM_OBJD(ca)
@ ALE_GREASE_PENCIL_GROUP
@ ALE_SCE
@ ALE_GREASE_PENCIL_CEL
@ ALE_GREASE_PENCIL_DATA
@ ALE_NONE
@ ALE_GPFRAME
@ ALE_FCURVE
@ ALE_NLASTRIP
@ ALE_ALL
@ ALE_ACT
@ ALE_ACTION_LAYERED
@ ALE_OB
@ ALE_GROUP
@ ALE_ACTION_SLOT
@ ALE_MASKLAY
#define FILTER_LATTICE_OBJD(lt)
#define FILTER_CURVES_OBJD(ha)
#define EXPANDED_ACTC(actc)
#define FILTER_LAM_OBJD(la)
eAnimCont_Types
@ ANIMCONT_DRIVERS
@ ANIMCONT_FCURVES
@ ANIMCONT_NLA
@ ANIMCONT_MASK
@ ANIMCONT_SHAPEKEY
@ ANIMCONT_TIMELINE
@ ANIMCONT_DOPESHEET
@ ANIMCONT_ACTION
@ ANIMCONT_NONE
@ ANIMCONT_GPENCIL
@ ANIMCONT_CHANNEL
#define EXPANDED_OBJC(ob)
#define SEL_FCU(fcu)
#define EDITABLE_SHAPEKEY(kb)
#define EDITABLE_FCU(fcu)
#define EDITABLE_MASK(masklay)
#define FILTER_CUR_OBJD(cu)
#define FILTER_MAT_OBJD(ma)
#define EDITABLE_NLT(nlt)
#define FILTER_WOR_SCED(wo)
#define FILTER_MBALL_OBJD(mb)
#define FILTER_LS_SCED(linestyle)
#define EXPANDED_DRVD(adt)
#define EXPANDED_MASK(mask)
#define SEL_ANIMDATA(adt)
#define FILTER_POINTS_OBJD(pt)
#define EXPANDED_GPD(gpd)
#define FILTER_TEX_DATA(tex)
#define FILTER_VOLUME_OBJD(vo)
#define SEL_NLT(nlt)
eAnimFilter_Flags
@ ANIMFILTER_TMP_PEEK
@ ANIMFILTER_ACTIVE
@ ANIMFILTER_UNSEL
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_ANIMDATA
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_CURVE_VISIBLE
@ ANIMFILTER_SELEDIT
@ ANIMFILTER_LIST_VISIBLE
@ ANIMFILTER_LIST_CHANNELS
@ ANIMFILTER_NODUPLIS
@ ANIMFILTER_TMP_IGNORE_ONLYSEL
@ ANIMFILTER_FCURVESONLY
@ ANIMFILTER_SEL
@ ANIMFILTER_ACTGROUPED
#define FILTER_PART_OBJD(part)
Read Guarded memory(de)allocation.
#define MAX_MTEX
Definition Stroke.h:33
const bAnimChannelType * ANIM_channel_get_typeinfo(bAnimListElem *ale)
static size_t animdata_filter_ds_scene(bAnimContext *ac, ListBase *anim_data, Scene *sce, eAnimFilter_Flags filter_mode)
#define ANIMCHANNEL_NEW_CHANNEL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id)
static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data, Base *base, eAnimFilter_Flags filter_mode)
static Key * actedit_get_shapekeys(bAnimContext *ac)
static size_t animfilter_block_data(bAnimContext *ac, ListBase *anim_data, ID *id, const eAnimFilter_Flags filter_mode)
bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
static bool nlaedit_get_context(bAnimContext *ac, SpaceNla *snla)
static size_t animdata_filter_dopesheet_movieclips(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_grease_pencil_layers_data(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, const eAnimFilter_Flags filter_mode)
static bool animdata_filter_base_is_ok(bAnimContext *ac, Base *base, const eObjectMode object_mode, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_obanim(bAnimContext *ac, ListBase *anim_data, Object *ob, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_movieclip(bAnimContext *ac, ListBase *anim_data, MovieClip *clip, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_nodetree_group(bAnimContext *ac, ListBase *anim_data, ID *owner_id, bNodeTree *ntree, eAnimFilter_Flags filter_mode)
static bool name_matches_dopesheet_filter(const bDopeSheet *ads, const char *name)
static size_t animdata_filter_ds_particles(bAnimContext *ac, ListBase *anim_data, Object *ob, eAnimFilter_Flags filter_mode)
static size_t animfilter_fcurves_span(bAnimContext *ac, ListBase *anim_data, Span< FCurve * > fcurves, const animrig::slot_handle_t slot_handle, const eAnimFilter_Flags filter_mode, ID *animated_id, ID *fcurve_owner_id)
#define ANIMCHANNEL_SELOK(test_func)
static size_t animdata_filter_mask(bAnimContext *ac, ListBase *anim_data, void *, eAnimFilter_Flags filter_mode)
static bool fcurve_span_selection_matters(const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data, bGPdata *gpd, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_materials(bAnimContext *ac, ListBase *anim_data, Object *ob, const eAnimFilter_Flags filter_mode)
static size_t animfilter_nla_controls(bAnimContext *ac, ListBase *anim_data, AnimData *adt, eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_grease_pencil(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_grease_pencil_layer(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, blender::bke::greasepencil::Layer &layer, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_gpencil_layers_data_legacy(bAnimContext *ac, ListBase *anim_data, bGPdata *gpd, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_textures(bAnimContext *ac, ListBase *anim_data, ID *owner_id, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_modifiers(bAnimContext *ac, ListBase *anim_data, Object *ob, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_grease_pencil_layer_node_recursive(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, blender::bke::greasepencil::TreeNode &node, eAnimFilter_Flags filter_mode)
static size_t animfilter_action_slot(bAnimContext *ac, ListBase *anim_data, animrig::Action &action, animrig::Slot &slot, const eAnimFilter_Flags filter_mode, ID *animated_id)
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
static void key_data_from_adt(bAnimListElem &ale, AnimData *adt)
#define ANIMCHANNEL_ACTIVEOK(ale)
static size_t animdata_filter_ds_material(bAnimContext *ac, ListBase *anim_data, Material *ma, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_remove_invalid(ListBase *anim_data)
static size_t animdata_filter_ds_texture(bAnimContext *ac, ListBase *anim_data, Tex *tex, ID *owner_id, eAnimFilter_Flags filter_mode)
static short animdata_filter_dopesheet_summary(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, size_t *items)
bool ANIM_animdata_context_getdata(bAnimContext *ac)
static size_t animdata_filter_grease_pencil_data(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_shapekey(bAnimContext *ac, ListBase *anim_data, Key *key, const eAnimFilter_Flags filter_mode)
static size_t animfilter_fcurves(bAnimContext *ac, ListBase *anim_data, FCurve *first, eAnim_ChannelType fcurve_type, const eAnimFilter_Flags filter_mode, void *owner, ID *owner_id, ID *fcurve_owner_id)
static int ds_base_sorting_cmp(const void *base1_ptr, const void *base2_ptr)
static bool skip_fcurve_with_name(bAnimContext *ac, FCurve *fcu, eAnim_ChannelType channel_type, void *owner, ID *owner_id)
static size_t animdata_filter_ds_obdata(bAnimContext *ac, ListBase *anim_data, Object *ob, eAnimFilter_Flags filter_mode)
static bool fcurve_has_errors(bAnimContext *ac, const FCurve *fcu)
static void animfilter_modifier_idpoin_cb(void *afm_ptr, Object *ob, ID **idpoin, int)
static size_t animdata_filter_ds_nodetree(bAnimContext *ac, ListBase *anim_data, ID *owner_id, bNodeTree *ntree, const eAnimFilter_Flags filter_mode)
#define END_ANIMFILTER_SUBCHANNELS
static bAnimListElem * make_new_animlistelem(Main *bmain, void *data, const eAnim_ChannelType datatype, ID *owner_id, ID *fcurve_owner_id)
static size_t animfilter_act_group(bAnimContext *ac, ListBase *anim_data, bAction *act, animrig::slot_handle_t slot_handle, bActionGroup *agrp, eAnimFilter_Flags filter_mode, ID *owner_id)
static bool fcurve_span_must_be_selected(const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_world(bAnimContext *ac, ListBase *anim_data, Scene *sce, World *wo, eAnimFilter_Flags filter_mode)
static bool skip_fcurve_selected_data(bAnimContext *ac, FCurve *fcu, ID *owner_id, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_remove_duplis(ListBase *anim_data)
static size_t animfilter_action_slots(bAnimContext *ac, ListBase *anim_data, animrig::Action &action, const eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_animchan(bAnimContext *ac, ListBase *anim_data, bAnimListElem *channel, const eAnimFilter_Flags filter_mode)
static FCurve * animfilter_fcurve_next(bAnimContext *ac, FCurve *first, eAnim_ChannelType channel_type, const eAnimFilter_Flags filter_mode, void *owner, ID *owner_id)
static size_t animdata_filter_ds_cachefile(bAnimContext *ac, ListBase *anim_data, CacheFile *cache_file, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_keyanim(bAnimContext *ac, ListBase *anim_data, Object *ob, Key *key, eAnimFilter_Flags filter_mode)
#define BEGIN_ANIMFILTER_SUBCHANNELS(expanded_check)
static size_t animfilter_nla(bAnimContext *ac, ListBase *anim_data, AnimData *adt, const eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_ds_linestyle(bAnimContext *ac, ListBase *anim_data, Scene *sce, eAnimFilter_Flags filter_mode)
static bool graphedit_get_context(bAnimContext *ac, SpaceGraph *sipo)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, nlaKeysOk, legacyActionOk, layeredActionOk)
#define ANIMCHANNEL_NEW_CHANNEL_FULL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id, ale_statement)
static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
static bool ale_name_matches_dopesheet_filter(const bDopeSheet &ads, bAnimListElem &ale)
static Base ** animdata_filter_ds_sorted_bases(bAnimContext *ac, const Scene *scene, ViewLayer *view_layer, const eAnimFilter_Flags filter_mode, size_t *r_usable_bases)
static size_t animfilter_action(bAnimContext *ac, ListBase *anim_data, animrig::Action &action, const animrig::slot_handle_t slot_handle, const eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_dopesheet_scene(bAnimContext *ac, ListBase *anim_data, Scene *sce, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_mask_data(bAnimContext *ac, ListBase *anim_data, Mask *mask, const eAnimFilter_Flags filter_mode)
#define ANIMCHANNEL_SELEDITOK(test_func)
#define ANIM_CHAN_NAME_SIZE
ListBase * ED_context_get_markers(const bContext *C)
unsigned int U
Definition btGjkEpa3.h:78
blender::Span< const Slot * > slots() const
Slot * slot_for_handle(slot_handle_t handle)
blender::Span< const bActionGroup * > channel_groups() const
blender::Span< const FCurve * > fcurves() const
static constexpr slot_handle_t unassigned
#define printf
#define SELECT
FreestyleLineStyle linestyle
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define GS(x)
Definition iris.cc:202
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static ulong * next
#define G(x, y, z)
FCurve * fcurve_find_in_assigned_slot(AnimData &adt, FCurveDescriptor fcurve_descriptor)
ID * action_slot_get_id_best_guess(Main &bmain, Slot &slot, ID *primary_id)
decltype(::ActionSlot::handle) slot_handle_t
const animrig::ChannelBag * channelbag_for_action_slot(const Action &action, slot_handle_t slot_handle)
bNode * node_find_node_by_name(bNodeTree *ntree, const char *name)
Definition node.cc:2437
float wrap(float value, float max, float min)
Definition node_math.h:71
Sequence * SEQ_get_sequence_by_name(ListBase *seqbase, const char *name, bool recursive)
Editing * SEQ_editing_get(const Scene *scene)
Definition sequencer.cc:262
Sequence * SEQ_select_active_get(const Scene *scene)
bAction * action
int32_t slot_handle
ListBase drivers
ListBase nla_tracks
short flag
struct Object * object
struct AnimData * adt
struct AnimData * adt
struct AnimData * adt
ListBase * seqbasep
struct FCurve * next
bActionGroup * grp
char * rna_path
ChannelDriver * driver
struct AnimData * adt
GreasePencilLayerTreeNode base
GreasePencilLayerTreeGroup * root_group_ptr
struct AnimData * adt
Definition DNA_ID.h:413
int tag
Definition DNA_ID.h:434
char name[66]
Definition DNA_ID.h:425
struct AnimData * adt
char type
ListBase block
struct AnimData * adt
struct AnimData * adt
struct bNodeTree * nodetree
void * last
void * first
ListBase masks
Definition BKE_main.hh:243
ListBase movieclips
Definition BKE_main.hh:242
ListBase cachefiles
Definition BKE_main.hh:245
struct AnimData * adt
int masklay_act
struct AnimData * adt
ListBase strips
ListBase strips
struct NlaTrack * next
char name[64]
struct NlaTrack * prev
ListBase particlesystem
struct bPose * pose
ListBase modifiers
struct PartDeflect * pd
short visibility_flag
struct AnimData * adt
struct AnimData * adt
struct bNodeTree * nodetree
struct MovieClip * clip
struct bGPdata * gpd
ListBase view_layers
struct AnimData * adt
struct World * world
struct Mask * mask
struct bDopeSheet * ads
struct bDopeSheet * ads
struct AnimData * adt
struct AnimData * adt
struct bNodeTree * nodetree
struct AnimData * adt
struct ActionChannelBag * channel_bag
ListBase curves
ListBase groups
void(* name)(bAnimListElem *ale, char *name)
eAnimEdit_Context dopesheet_mode
eGraphEdit_Mode grapheditor_mode
SpaceLink * sl
ListBase * markers
eAnimCont_Types datatype
bDopeSheet * ads
eSpace_Type spacetype
ViewLayer * view_layer
ARegion * region
Depsgraph * depsgraph
Object * obact
eRegion_Type regiontype
ScrArea * area
int32_t slot_handle
AnimData * adt
eAnim_ChannelType type
eAnim_KeyType datatype
struct AnimData * adt
char searchstr[64]
struct Collection * filter_grp
struct AnimData * adt
ListBase nodes
struct AnimData * adt
struct Bone * bone
eAnimFilter_Flags filter_mode