Blender  V3.3
anim_motion_paths.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "MEM_guardedalloc.h"
8 
9 #include <stdlib.h>
10 
11 #include "BLI_dlrbTree.h"
12 #include "BLI_listbase.h"
13 #include "BLI_math.h"
14 
15 #include "DNA_anim_types.h"
16 #include "DNA_armature_types.h"
17 #include "DNA_scene_types.h"
18 
19 #include "BKE_action.h"
20 #include "BKE_anim_data.h"
21 #include "BKE_main.h"
22 #include "BKE_scene.h"
23 
24 #include "DEG_depsgraph.h"
25 #include "DEG_depsgraph_build.h"
26 #include "DEG_depsgraph_query.h"
27 
28 #include "GPU_batch.h"
29 #include "GPU_vertex_buffer.h"
30 
31 #include "ED_anim_api.h"
32 #include "ED_keyframes_keylist.h"
33 
34 #include "CLG_log.h"
35 
36 static CLG_LogRef LOG = {"ed.anim.motion_paths"};
37 
38 /* Motion path needing to be baked (mpt) */
39 typedef struct MPathTarget {
40  struct MPathTarget *next, *prev;
41 
42  bMotionPath *mpath; /* motion path in question */
43 
44  struct AnimKeylist *keylist; /* temp, to know where the keyframes are */
45 
46  /* Original (Source Objects) */
47  Object *ob; /* source object */
48  bPoseChannel *pchan; /* source posechannel (if applicable) */
49 
50  /* "Evaluated" Copies (these come from the background COW copy
51  * that provide all the coordinates we want to save off). */
52  Object *ob_eval; /* evaluated object */
54 
55 /* ........ */
56 
57 /* update scene for current frame */
59 {
61 }
62 
64  Scene *scene,
65  ViewLayer *view_layer,
66  ListBase *targets)
67 {
68  /* Allocate dependency graph. */
70 
71  /* Make a flat array of IDs for the DEG API. */
72  const int num_ids = BLI_listbase_count(targets);
73  ID **ids = MEM_malloc_arrayN(num_ids, sizeof(ID *), "animviz IDS");
74  int current_id_index = 0;
75  for (MPathTarget *mpt = targets->first; mpt != NULL; mpt = mpt->next) {
76  ids[current_id_index++] = &mpt->ob->id;
77  }
78 
79  /* Build graph from all requested IDs. */
80  DEG_graph_build_from_ids(depsgraph, ids, num_ids);
81  MEM_freeN(ids);
82 
83  /* Update once so we can access pointers of evaluated animation data. */
85  return depsgraph;
86 }
87 
89 {
90  /* TODO: it would be nice in future to be able to update objects dependent on these bones too? */
91 
92  MPathTarget *mpt;
93 
94  /* object itself first */
95  if ((ob->avs.recalc & ANIMVIZ_RECALC_PATHS) && (ob->mpath)) {
96  /* new target for object */
97  mpt = MEM_callocN(sizeof(MPathTarget), "MPathTarget Ob");
98  BLI_addtail(targets, mpt);
99 
100  mpt->mpath = ob->mpath;
101  mpt->ob = ob;
102  }
103 
104  /* bones */
105  if ((ob->pose) && (ob->pose->avs.recalc & ANIMVIZ_RECALC_PATHS)) {
106  bArmature *arm = ob->data;
107  bPoseChannel *pchan;
108 
109  for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
110  if ((pchan->bone) && (arm->layer & pchan->bone->layer) && (pchan->mpath)) {
111  /* new target for bone */
112  mpt = MEM_callocN(sizeof(MPathTarget), "MPathTarget PoseBone");
113  BLI_addtail(targets, mpt);
114 
115  mpt->mpath = pchan->mpath;
116  mpt->ob = ob;
117  mpt->pchan = pchan;
118  }
119  }
120  }
121 }
122 
123 /* ........ */
124 
125 /* perform baking for the targets on the current frame */
126 static void motionpaths_calc_bake_targets(ListBase *targets, int cframe)
127 {
128  MPathTarget *mpt;
129 
130  /* for each target, check if it can be baked on the current frame */
131  for (mpt = targets->first; mpt; mpt = mpt->next) {
132  bMotionPath *mpath = mpt->mpath;
133 
134  /* current frame must be within the range the cache works for
135  * - is inclusive of the first frame, but not the last otherwise we get buffer overruns
136  */
137  if ((cframe < mpath->start_frame) || (cframe >= mpath->end_frame)) {
138  continue;
139  }
140 
141  /* get the relevant cache vert to write to */
142  bMotionPathVert *mpv = mpath->points + (cframe - mpath->start_frame);
143 
144  Object *ob_eval = mpt->ob_eval;
145 
146  /* Lookup evaluated pose channel, here because the depsgraph
147  * evaluation can change them so they are not cached in mpt. */
148  bPoseChannel *pchan_eval = NULL;
149  if (mpt->pchan) {
150  pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, mpt->pchan->name);
151  }
152 
153  /* pose-channel or object path baking? */
154  if (pchan_eval) {
155  /* heads or tails */
156  if (mpath->flag & MOTIONPATH_FLAG_BHEAD) {
157  copy_v3_v3(mpv->co, pchan_eval->pose_head);
158  }
159  else {
160  copy_v3_v3(mpv->co, pchan_eval->pose_tail);
161  }
162 
163  /* Result must be in world-space. */
164  mul_m4_v3(ob_eval->obmat, mpv->co);
165  }
166  else {
167  /* World-space object location. */
168  copy_v3_v3(mpv->co, ob_eval->obmat[3]);
169  }
170 
171  float mframe = (float)(cframe);
172 
173  /* Tag if it's a keyframe */
174  if (ED_keylist_find_exact(mpt->keylist, mframe)) {
175  mpv->flag |= MOTIONPATH_VERT_KEY;
176  }
177  else {
178  mpv->flag &= ~MOTIONPATH_VERT_KEY;
179  }
180 
181  /* Incremental update on evaluated object if possible, for fast updating
182  * while dragging in transform. */
183  bMotionPath *mpath_eval = NULL;
184  if (mpt->pchan) {
185  mpath_eval = (pchan_eval) ? pchan_eval->mpath : NULL;
186  }
187  else {
188  mpath_eval = ob_eval->mpath;
189  }
190 
191  if (mpath_eval && mpath_eval->length == mpath->length) {
192  bMotionPathVert *mpv_eval = mpath_eval->points + (cframe - mpath_eval->start_frame);
193  *mpv_eval = *mpv;
194 
196  GPU_BATCH_DISCARD_SAFE(mpath_eval->batch_line);
198  }
199  }
200 }
201 
202 /* Get pointer to animviz settings for the given target. */
204 {
205  if (mpt->pchan != NULL) {
206  return &mpt->ob->pose->avs;
207  }
208  return &mpt->ob->avs;
209 }
210 
211 static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int *r_efra)
212 {
213  *r_sfra = INT_MAX;
214  *r_efra = INT_MIN;
215  LISTBASE_FOREACH (MPathTarget *, mpt, targets) {
216  *r_sfra = min_ii(*r_sfra, mpt->mpath->start_frame);
217  *r_efra = max_ii(*r_efra, mpt->mpath->end_frame);
218  }
219 }
220 
221 /* TODO(jbakker): Remove complexity, keylists are ordered. */
223  struct AnimKeylist *keylist,
224  int current_frame)
225 {
226  if (current_frame <= mpt->mpath->start_frame) {
227  return mpt->mpath->start_frame;
228  }
229 
230  float current_frame_float = current_frame;
231  const ActKeyColumn *ak = ED_keylist_find_prev(keylist, current_frame_float);
232  if (ak == NULL) {
233  return mpt->mpath->start_frame;
234  }
235 
236  return ak->cfra;
237 }
238 
240  struct AnimKeylist *keylist,
241  int current_frame)
242 {
243  int frame = motionpath_get_prev_keyframe(mpt, keylist, current_frame);
244  return motionpath_get_prev_keyframe(mpt, keylist, frame);
245 }
246 
248  struct AnimKeylist *keylist,
249  int current_frame)
250 {
251  if (current_frame >= mpt->mpath->end_frame) {
252  return mpt->mpath->end_frame;
253  }
254 
255  float current_frame_float = current_frame;
256  const ActKeyColumn *ak = ED_keylist_find_next(keylist, current_frame_float);
257  if (ak == NULL) {
258  return mpt->mpath->end_frame;
259  }
260 
261  return ak->cfra;
262 }
263 
265  struct AnimKeylist *keylist,
266  int current_frame)
267 {
268  int frame = motionpath_get_next_keyframe(mpt, keylist, current_frame);
269  return motionpath_get_next_keyframe(mpt, keylist, frame);
270 }
271 
273  AnimData *adt,
274  ListBase *fcurve_list)
275 {
276  if (adt == NULL || fcurve_list == NULL) {
277  return false;
278  }
279  /* NOTE: We might needed to do a full frame range update if there is a specific setup of NLA
280  * or drivers or modifiers on the f-curves. */
281  return true;
282 }
283 
285  AnimData *adt,
286  ListBase *fcurve_list,
287  int current_frame,
288  int *r_sfra,
289  int *r_efra)
290 {
291  *r_sfra = INT_MAX;
292  *r_efra = INT_MIN;
293 
294  /* If the current frame is outside of the configured motion path range we ignore update of this
295  * motion path by using invalid frame range where start frame is above the end frame. */
296  if (current_frame < mpt->mpath->start_frame || current_frame > mpt->mpath->end_frame) {
297  return;
298  }
299 
300  /* Similar to the case when there is only a single keyframe: need to update en entire range to
301  * a constant value. */
302  if (!motionpath_check_can_use_keyframe_range(mpt, adt, fcurve_list)) {
303  *r_sfra = mpt->mpath->start_frame;
304  *r_efra = mpt->mpath->end_frame;
305  return;
306  }
307 
308  /* NOTE: Iterate over individual f-curves, and check their keyframes individually and pick a
309  * widest range from them. This is because it's possible to have more narrow keyframe on a
310  * channel which wasn't edited.
311  * Could be optimized further by storing some flags about which channels has been modified so
312  * we ignore all others (which can potentially make an update range unnecessary wide). */
313  for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) {
314  struct AnimKeylist *keylist = ED_keylist_create();
315  fcurve_to_keylist(adt, fcu, keylist, 0);
317 
318  int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, current_frame);
319  int fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, current_frame);
320 
321  /* Extend range further, since acceleration compensation propagates even further away. */
322  if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) {
323  fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, fcu_sfra);
324  fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, fcu_efra);
325  }
326 
327  if (fcu_sfra <= fcu_efra) {
328  *r_sfra = min_ii(*r_sfra, fcu_sfra);
329  *r_efra = max_ii(*r_efra, fcu_efra);
330  }
331 
332  ED_keylist_free(keylist);
333  }
334 }
335 
337 {
338  LISTBASE_FOREACH (MPathTarget *, mpt, targets) {
339  ED_keylist_free(mpt->keylist);
340  }
341 }
342 
344 {
345  bAnimVizSettings *avs = ob->mode == OB_MODE_POSE ? &ob->pose->avs : &ob->avs;
346 
347  const bool has_action = ob->adt && ob->adt->action;
348  if (avs->path_range == MOTIONPATH_RANGE_SCENE || !has_action ||
350  avs->path_sf = PSFRA;
351  avs->path_ef = PEFRA;
352  return;
353  }
354 
355  struct AnimKeylist *keylist = ED_keylist_create();
356  LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->action->curves) {
357  fcurve_to_keylist(ob->adt, fcu, keylist, 0);
358  }
359 
360  Range2f frame_range;
361  switch (avs->path_range) {
363  if (ED_keylist_selected_keys_frame_range(keylist, &frame_range)) {
364  break;
365  }
366  ATTR_FALLTHROUGH; // Fall through if there were no selected keys found.
368  ED_keylist_all_keys_frame_range(keylist, &frame_range);
369  break;
371  BLI_assert_msg(false, "This should not happen, function should have exited earlier.");
372  };
373 
374  avs->path_sf = frame_range.min;
375  avs->path_ef = frame_range.max;
376 
377  ED_keylist_free(keylist);
378 }
379 
381  Main *bmain,
382  Scene *scene,
383  ListBase *targets,
384  eAnimvizCalcRange range,
385  bool restore)
386 {
387  /* TODO: include reports pointer? */
388 
389  /* Sanity check. */
390  if (ELEM(NULL, targets, targets->first)) {
391  return;
392  }
393 
394  const int cfra = scene->r.cfra;
395  int sfra = INT_MAX, efra = INT_MIN;
396  switch (range) {
398  motionpath_get_global_framerange(targets, &sfra, &efra);
399  if (sfra > efra) {
400  return;
401  }
402  if (cfra < sfra || cfra > efra) {
403  return;
404  }
405  sfra = efra = cfra;
406  break;
408  /* Nothing to do here, will be handled later when iterating through the targets. */
409  break;
411  motionpath_get_global_framerange(targets, &sfra, &efra);
412  if (sfra > efra) {
413  return;
414  }
415  break;
416  }
417 
418  /* get copies of objects/bones to get the calculated results from
419  * (for copy-on-write evaluation), so that we actually get some results
420  */
421 
422  /* TODO: Create a copy of background depsgraph that only contain these entities,
423  * and only evaluates them.
424  *
425  * For until that is done we force dependency graph to not be active, so we don't lose unkeyed
426  * changes during updating the motion path.
427  * This still doesn't include unkeyed changes to the path itself, but allows to have updates in
428  * an environment when auto-keying and pose paste is used. */
429 
430  const bool is_active_depsgraph = DEG_is_active(depsgraph);
431  if (is_active_depsgraph) {
433  }
434 
435  LISTBASE_FOREACH (MPathTarget *, mpt, targets) {
436  mpt->ob_eval = DEG_get_evaluated_object(depsgraph, mpt->ob);
437 
438  AnimData *adt = BKE_animdata_from_id(&mpt->ob_eval->id);
439 
440  /* build list of all keyframes in active action for object or pchan */
441  mpt->keylist = ED_keylist_create();
442 
443  ListBase *fcurve_list = NULL;
444  if (adt) {
445  /* get pointer to animviz settings for each target */
447 
448  /* it is assumed that keyframes for bones are all grouped in a single group
449  * unless an option is set to always use the whole action
450  */
451  if ((mpt->pchan) && (avs->path_viewflag & MOTIONPATH_VIEW_KFACT) == 0) {
452  bActionGroup *agrp = BKE_action_group_find_name(adt->action, mpt->pchan->name);
453 
454  if (agrp) {
455  fcurve_list = &agrp->channels;
456  agroup_to_keylist(adt, agrp, mpt->keylist, 0);
457  }
458  }
459  else {
460  fcurve_list = &adt->action->curves;
461  action_to_keylist(adt, adt->action, mpt->keylist, 0);
462  }
463  }
465 
466  if (range == ANIMVIZ_CALC_RANGE_CHANGED) {
467  int mpt_sfra, mpt_efra;
468  motionpath_calculate_update_range(mpt, adt, fcurve_list, cfra, &mpt_sfra, &mpt_efra);
469  if (mpt_sfra <= mpt_efra) {
470  sfra = min_ii(sfra, mpt_sfra);
471  efra = max_ii(efra, mpt_efra);
472  }
473  }
474  }
475 
476  if (sfra > efra) {
478  return;
479  }
480 
481  /* calculate path over requested range */
482  CLOG_INFO(&LOG,
483  1,
484  "Calculating MotionPaths between frames %d - %d (%d frames)",
485  sfra,
486  efra,
487  efra - sfra + 1);
488  for (scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) {
489  if (range == ANIMVIZ_CALC_RANGE_CURRENT_FRAME) {
490  /* For current frame, only update tagged. */
492  }
493  else {
494  /* Update relevant data for new frame. */
496  }
497 
498  /* perform baking for targets */
500  }
501 
502  /* reset original environment */
503  /* NOTE: We don't always need to reevaluate the main scene, as the depsgraph
504  * may be a temporary one that works on a subset of the data.
505  * We always have to restore the current frame though. */
506  scene->r.cfra = cfra;
507  if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) {
509  }
510 
511  if (is_active_depsgraph) {
513  }
514 
515  /* clear recalc flags from targets */
516  LISTBASE_FOREACH (MPathTarget *, mpt, targets) {
517  bMotionPath *mpath = mpt->mpath;
518 
519  /* get pointer to animviz settings for each target */
521 
522  /* clear the flag requesting recalculation of targets */
523  avs->recalc &= ~ANIMVIZ_RECALC_PATHS;
524 
525  /* Clean temp data */
526  ED_keylist_free(mpt->keylist);
527 
528  /* Free previous batches to force update. */
532  }
533 }
typedef float(TangentPoint)[2]
Blender kernel action and pose functionality.
struct bPoseChannel * BKE_pose_channel_find_name(const struct bPose *pose, const char *name)
struct bActionGroup * BKE_action_group_find_name(struct bAction *act, const char name[])
Definition: action.c:582
struct AnimData * BKE_animdata_from_id(const struct ID *id)
void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bmain)
Definition: scene.cc:2648
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph)
Definition: scene.cc:2728
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define UNUSED(x)
#define ELEM(...)
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
Depsgraph * DEG_graph_new(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, eEvaluationMode mode)
Definition: depsgraph.cc:267
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
bool DEG_is_active(const struct Depsgraph *depsgraph)
Definition: depsgraph.cc:312
void DEG_make_active(struct Depsgraph *depsgraph)
Definition: depsgraph.cc:325
@ DAG_EVAL_VIEWPORT
Definition: DEG_depsgraph.h:45
void DEG_make_inactive(struct Depsgraph *depsgraph)
Definition: depsgraph.cc:332
void DEG_graph_build_from_ids(struct Depsgraph *graph, struct ID **ids, int num_ids)
struct Object * DEG_get_evaluated_object(const struct Depsgraph *depsgraph, struct Object *object)
@ MOTIONPATH_VERT_KEY
@ MOTIONPATH_VIEW_KFACT
@ MOTIONPATH_RANGE_KEYS_ALL
@ MOTIONPATH_RANGE_KEYS_SELECTED
@ MOTIONPATH_RANGE_SCENE
@ ANIMVIZ_RECALC_PATHS
@ MOTIONPATH_FLAG_BHEAD
@ FCURVE_SMOOTH_NONE
@ OB_MODE_POSE
#define PSFRA
#define PEFRA
eAnimvizCalcRange
Definition: ED_anim_api.h:1088
@ ANIMVIZ_CALC_RANGE_FULL
Definition: ED_anim_api.h:1096
@ ANIMVIZ_CALC_RANGE_CURRENT_FRAME
Definition: ED_anim_api.h:1090
@ ANIMVIZ_CALC_RANGE_CHANGED
Definition: ED_anim_api.h:1093
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition: GPU_batch.h:216
#define GPU_VERTBUF_DISCARD_SAFE(verts)
Read Guarded memory(de)allocation.
static int motionpath_get_prev_keyframe(MPathTarget *mpt, struct AnimKeylist *keylist, int current_frame)
static void motionpaths_calc_bake_targets(ListBase *targets, int cframe)
static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int *r_efra)
static int motionpath_get_prev_prev_keyframe(MPathTarget *mpt, struct AnimKeylist *keylist, int current_frame)
void animviz_calc_motionpaths(Depsgraph *depsgraph, Main *bmain, Scene *scene, ListBase *targets, eAnimvizCalcRange range, bool restore)
void animviz_get_object_motionpaths(Object *ob, ListBase *targets)
Depsgraph * animviz_depsgraph_build(Main *bmain, Scene *scene, ViewLayer *view_layer, ListBase *targets)
void animviz_motionpath_compute_range(Object *ob, Scene *scene)
static int motionpath_get_next_next_keyframe(MPathTarget *mpt, struct AnimKeylist *keylist, int current_frame)
static bool motionpath_check_can_use_keyframe_range(MPathTarget *UNUSED(mpt), AnimData *adt, ListBase *fcurve_list)
static void motionpath_calculate_update_range(MPathTarget *mpt, AnimData *adt, ListBase *fcurve_list, int current_frame, int *r_sfra, int *r_efra)
static void motionpaths_calc_update_scene(struct Depsgraph *depsgraph)
static CLG_LogRef LOG
struct MPathTarget MPathTarget
static void motionpath_free_free_tree_data(ListBase *targets)
static bAnimVizSettings * animviz_target_settings_get(MPathTarget *mpt)
static int motionpath_get_next_keyframe(MPathTarget *mpt, struct AnimKeylist *keylist, int current_frame)
Scene scene
const Depsgraph * depsgraph
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag)
AnimKeylist * ED_keylist_create()
bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_next(const AnimKeylist *keylist, const float cfra)
void ED_keylist_free(AnimKeylist *keylist)
bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag)
void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, AnimKeylist *keylist, const int saction_flag)
const ActKeyColumn * ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
const ActKeyColumn * ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra)
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
bAction * action
Definition: DNA_ID.h:368
void * first
Definition: DNA_listBase.h:31
struct AnimKeylist * keylist
bPoseChannel * pchan
struct MPathTarget * next
bMotionPath * mpath
Object * ob_eval
struct MPathTarget * prev
Definition: BKE_main.h:121
struct bPose * pose
bMotionPath * mpath
float obmat[4][4]
struct AnimData * adt
void * data
bAnimVizSettings avs
float min
Definition: BLI_range.h:14
float max
Definition: BLI_range.h:15
struct RenderData r
ListBase curves
unsigned int layer
struct GPUVertBuf * points_vbo
bMotionPathVert * points
struct GPUBatch * batch_points
struct GPUBatch * batch_line
struct Bone * bone
bMotionPath * mpath
float pose_head[3]
float pose_tail[3]
struct bPoseChannel * next
ListBase chanbase
bAnimVizSettings avs