Blender V5.0
anim_motion_paths.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include <cstdlib>
12
13#include "BLI_listbase.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_matrix.hh"
17#include "BLI_math_vector.h"
18
19#include "DNA_anim_types.h"
20#include "DNA_armature_types.h"
21#include "DNA_scene_types.h"
22
23#include "BKE_action.hh"
24#include "BKE_anim_data.hh"
25#include "BKE_main.hh"
26#include "BKE_scene.hh"
27
28#include "DEG_depsgraph.hh"
31
32#include "GPU_batch.hh"
33#include "GPU_vertex_buffer.hh"
34
35#include "ED_anim_api.hh"
37
38#include "ANIM_action.hh"
39#include "ANIM_action_legacy.hh"
41
42#include "CLG_log.h"
43
44static CLG_LogRef LOG = {"anim.motion_paths"};
45
46/* Motion path needing to be baked (mpt). */
48 bMotionPath *mpath; /* Motion path in question. */
49
50 AnimKeylist *keylist; /* Temp, to know where the keyframes are. */
51
52 /* Original (Source Objects) */
53 Object *ob; /* Source Object */
54 bPoseChannel *pchan; /* Source pose-channel (if applicable). */
55
56 /* "Evaluated" Copies (these come from the background evaluated copy
57 * that provide all the coordinates we want to save off). */
58 Object *ob_eval; /* Evaluated Object. */
59};
60
61/* ........ */
62
63/* Update scene for current frame. */
68
69Depsgraph *animviz_depsgraph_build(Main *bmain,
70 Scene *scene,
71 ViewLayer *view_layer,
73{
74 /* Allocate dependency graph. */
75 Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT);
76
77 /* Make a flat array of IDs for the DEG API. */
78 blender::Array<ID *> ids(targets.size());
79 int current_id_index = 0;
80 for (const MPathTarget *mpt : targets) {
81 ids[current_id_index++] = &mpt->ob->id;
82 }
83
84 /* Build graph from all requested IDs. */
86
87 /* Update once so we can access pointers of evaluated animation data. */
89 return depsgraph;
90}
91
93{
94 /* TODO: it would be nice in future to be able to update objects dependent on these bones too? */
95
96 MPathTarget *mpt;
97
98 /* Object itself first. */
99 if ((ob->avs.recalc & ANIMVIZ_RECALC_PATHS) && (ob->mpath)) {
100 /* New target for object. */
101 mpt = MEM_callocN<MPathTarget>("MPathTarget Ob");
102 mpt->mpath = ob->mpath;
103 mpt->ob = ob;
104
105 r_targets.append(mpt);
106 }
107
108 /* Bones. */
109 if ((ob->pose) && (ob->pose->avs.recalc & ANIMVIZ_RECALC_PATHS)) {
110 bArmature *arm = static_cast<bArmature *>(ob->data);
111 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
112 if ((pchan->bone) && ANIM_bonecoll_is_visible_pchan(arm, pchan) && (pchan->mpath)) {
113 /* New target for bone. */
114 mpt = MEM_callocN<MPathTarget>("MPathTarget PoseBone");
115 mpt->mpath = pchan->mpath;
116 mpt->ob = ob;
117 mpt->pchan = pchan;
118 r_targets.append(mpt);
119 }
120 }
121 }
122}
123
125{
126 for (MPathTarget *mpt : targets) {
127 MEM_freeN(mpt);
128 }
129 targets.clear_and_shrink();
130}
131
132/* ........ */
133
134/* Perform baking for the targets on the current frame. */
136 int cframe,
137 Depsgraph *depsgraph,
138 Object *camera)
139{
140 using namespace blender;
141 /* For each target, check if it can be baked on the current frame. */
142 for (const MPathTarget *mpt : targets) {
143 bMotionPath *mpath = mpt->mpath;
144
145 /* Current frame must be within the range the cache works for.
146 * - is inclusive of the first frame, but not the last otherwise we get buffer overruns.
147 */
148 if ((cframe < mpath->start_frame) || (cframe >= mpath->end_frame)) {
149 continue;
150 }
151
152 /* Get the relevant cache vert to write to. */
153 bMotionPathVert *mpv = mpath->points + (cframe - mpath->start_frame);
154
155 Object *ob_eval = mpt->ob_eval;
156
157 /* Lookup evaluated pose channel, here because the depsgraph
158 * evaluation can change them so they are not cached in mpt. */
159 bPoseChannel *pchan_eval = nullptr;
160 if (mpt->pchan) {
161 pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, mpt->pchan->name);
162 }
163
164 /* Pose-channel or object path baking? */
165 if (pchan_eval) {
166 /* Heads or tails. */
167 if (mpath->flag & MOTIONPATH_FLAG_BHEAD) {
168 copy_v3_v3(mpv->co, pchan_eval->pose_head);
169 }
170 else {
171 copy_v3_v3(mpv->co, pchan_eval->pose_tail);
172 }
173
174 /* Result must be in world-space. */
175 mul_m4_v3(ob_eval->object_to_world().ptr(), mpv->co);
176 }
177 else {
178 /* World-space object location. */
179 copy_v3_v3(mpv->co, ob_eval->object_to_world().location());
180 }
181
182 if (mpath->flag & MOTIONPATH_FLAG_BAKE_CAMERA && camera) {
183 Object *cam_eval = DEG_get_evaluated(depsgraph, camera);
184 /* Convert point to camera space. */
185 float3 co_camera_space = math::transform_point(cam_eval->world_to_object(), float3(mpv->co));
186 copy_v3_v3(mpv->co, co_camera_space);
187 }
188
189 float mframe = float(cframe);
190
191 /* Tag if it's a keyframe. */
192 if (ED_keylist_find_exact(mpt->keylist, mframe)) {
194 }
195 else {
197 }
198
199 /* Incremental update on evaluated object if possible, for fast updating
200 * while dragging in transform. */
201 bMotionPath *mpath_eval = nullptr;
202 if (mpt->pchan) {
203 mpath_eval = (pchan_eval) ? pchan_eval->mpath : nullptr;
204 }
205 else {
206 mpath_eval = ob_eval->mpath;
207 }
208
209 if (mpath_eval && mpath_eval->length == mpath->length) {
210 bMotionPathVert *mpv_eval = mpath_eval->points + (cframe - mpath_eval->start_frame);
211 *mpv_eval = *mpv;
212
216 }
217 }
218}
219
220/* Get pointer to animviz settings for the given target. */
222{
223 if (mpt->pchan != nullptr) {
224 return &mpt->ob->pose->avs;
225 }
226 return &mpt->ob->avs;
227}
228
230 int *r_sfra,
231 int *r_efra)
232{
233 *r_sfra = INT_MAX;
234 *r_efra = INT_MIN;
235 for (const MPathTarget *mpt : targets) {
236 *r_sfra = min_ii(*r_sfra, mpt->mpath->start_frame);
237 *r_efra = max_ii(*r_efra, mpt->mpath->end_frame);
238 }
239}
240
241static int motionpath_get_prev_keyframe(MPathTarget *mpt, AnimKeylist *keylist, int current_frame)
242{
243 /* TODO(jbakker): Remove complexity, key-lists are ordered. */
244
245 if (current_frame <= mpt->mpath->start_frame) {
246 return mpt->mpath->start_frame;
247 }
248
249 float current_frame_float = current_frame;
250 const ActKeyColumn *ak = ED_keylist_find_prev(keylist, current_frame_float);
251 if (ak == nullptr) {
252 return mpt->mpath->start_frame;
253 }
254
255 return ak->cfra;
256}
257
259 AnimKeylist *keylist,
260 int current_frame)
261{
262 int frame = motionpath_get_prev_keyframe(mpt, keylist, current_frame);
263 return motionpath_get_prev_keyframe(mpt, keylist, frame);
264}
265
266static int motionpath_get_next_keyframe(MPathTarget *mpt, AnimKeylist *keylist, int current_frame)
267{
268 if (current_frame >= mpt->mpath->end_frame) {
269 return mpt->mpath->end_frame;
270 }
271
272 float current_frame_float = current_frame;
273 const ActKeyColumn *ak = ED_keylist_find_next(keylist, current_frame_float);
274 if (ak == nullptr) {
275 return mpt->mpath->end_frame;
276 }
277
278 return ak->cfra;
279}
280
282 AnimKeylist *keylist,
283 int current_frame)
284{
285 int frame = motionpath_get_next_keyframe(mpt, keylist, current_frame);
286 return motionpath_get_next_keyframe(mpt, keylist, frame);
287}
288
290 AnimData *adt,
292{
293 if (adt == nullptr || fcurves.is_empty()) {
294 return false;
295 }
296 /* NOTE: We might needed to do a full frame range update if there is a specific setup of NLA
297 * or drivers or modifiers on the f-curves. */
298 return true;
299}
300
302 AnimData *adt,
304 int current_frame,
305 int *r_sfra,
306 int *r_efra)
307{
308 *r_sfra = INT_MAX;
309 *r_efra = INT_MIN;
310
311 /* If the current frame is outside of the configured motion path range we ignore update of this
312 * motion path by using invalid frame range where start frame is above the end frame. */
313 if (current_frame < mpt->mpath->start_frame || current_frame > mpt->mpath->end_frame) {
314 return;
315 }
316
317 /* Similar to the case when there is only a single keyframe: need to update en entire range to
318 * a constant value. */
319 if (!motionpath_check_can_use_keyframe_range(mpt, adt, fcurves)) {
320 *r_sfra = mpt->mpath->start_frame;
321 *r_efra = mpt->mpath->end_frame;
322 return;
323 }
324
325 /* NOTE: Iterate over individual f-curves, and check their keyframes individually and pick a
326 * widest range from them. This is because it's possible to have more narrow keyframe on a
327 * channel which wasn't edited.
328 * Could be optimized further by storing some flags about which channels has been modified so
329 * we ignore all others (which can potentially make an update range unnecessary wide). */
330 for (FCurve *fcu : fcurves) {
331 AnimKeylist *keylist = ED_keylist_create();
332 fcurve_to_keylist(adt, fcu, keylist, 0, {-FLT_MAX, FLT_MAX}, true);
334
335 int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, current_frame);
336 int fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, current_frame);
337
338 /* Extend range further, since acceleration compensation propagates even further away. */
339 if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) {
340 fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, fcu_sfra);
341 fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, fcu_efra);
342 }
343
344 if (fcu_sfra <= fcu_efra) {
345 *r_sfra = min_ii(*r_sfra, fcu_sfra);
346 *r_efra = max_ii(*r_efra, fcu_efra);
347 }
348
349 ED_keylist_free(keylist);
350 }
351}
352
354{
355 for (MPathTarget *mpt : targets) {
356 ED_keylist_free(mpt->keylist);
357 }
358}
359
361{
362 bAnimVizSettings *avs = ob->mode == OB_MODE_POSE ? &ob->pose->avs : &ob->avs;
363
365 /* Don't touch manually-determined ranges. */
366 return;
367 }
368
369 const bool has_action = ob->adt && ob->adt->action;
370 if (avs->path_range == MOTIONPATH_RANGE_SCENE || !has_action ||
372 {
373 /* Default to the scene (preview) range if there is no animation data to
374 * find selected keys in. */
375 avs->path_sf = PSFRA;
376 avs->path_ef = PEFRA;
377 return;
378 }
379
380 AnimKeylist *keylist = ED_keylist_create();
382 fcurve_to_keylist(ob->adt, fcu, keylist, 0, {-FLT_MAX, FLT_MAX}, true);
383 }
384
385 blender::Bounds<float> frame_range;
386 switch (avs->path_range) {
388 if (ED_keylist_selected_keys_frame_range(keylist, &frame_range)) {
389 break;
390 }
391 ATTR_FALLTHROUGH; /* Fall through if there were no selected keys found. */
393 ED_keylist_all_keys_frame_range(keylist, &frame_range);
394 break;
397 BLI_assert_msg(false, "This should not happen, function should have exited earlier.");
398 };
399
400 avs->path_sf = frame_range.min;
401 avs->path_ef = frame_range.max;
402
403 ED_keylist_free(keylist);
404}
405
407 Main *bmain,
408 Scene *scene,
410 eAnimvizCalcRange range,
411 bool restore)
412{
413 /* TODO: include reports pointer? */
414 using namespace blender::animrig;
415
416 if (targets.is_empty()) {
417 return;
418 }
419
420 const int cfra = scene->r.cfra;
421 int sfra = INT_MAX, efra = INT_MIN;
422 switch (range) {
424 motionpath_get_global_framerange(targets, &sfra, &efra);
425 if (sfra > efra) {
426 return;
427 }
428 if (cfra < sfra || cfra > efra) {
429 return;
430 }
431 sfra = efra = cfra;
432 break;
434 /* Nothing to do here, will be handled later when iterating through the targets. */
435 break;
437 motionpath_get_global_framerange(targets, &sfra, &efra);
438 if (sfra > efra) {
439 return;
440 }
441 break;
442 }
443
444 /* Get copies of objects/bones to get the calculated results from
445 * (for copy-on-evaluation), so that we actually get some results.
446 */
447
448 /* TODO: Create a copy of background depsgraph that only contain these entities,
449 * and only evaluates them.
450 *
451 * For until that is done we force dependency graph to not be active, so we don't lose unkeyed
452 * changes during updating the motion path.
453 * This still doesn't include unkeyed changes to the path itself, but allows to have updates in
454 * an environment when auto-keying and pose paste is used. */
455
456 const bool is_active_depsgraph = DEG_is_active(depsgraph);
457 if (is_active_depsgraph) {
459 }
460
461 for (MPathTarget *mpt : targets) {
462 mpt->ob_eval = DEG_get_evaluated(depsgraph, mpt->ob);
463
464 AnimData *adt = BKE_animdata_from_id(&mpt->ob_eval->id);
465
466 /* Build list of all keyframes in active action for object or pchan. */
467 mpt->keylist = ED_keylist_create();
468
470 if (adt && adt->action) {
471 /* Get pointer to animviz settings for each target. */
473
474 /* It is assumed that keyframes for bones are all grouped in a single group
475 * unless an option is set to always use the whole action.
476 */
477 if ((mpt->pchan) && (avs->path_viewflag & MOTIONPATH_VIEW_KFACT) == 0) {
478 Action &action = adt->action->wrap();
479 bActionGroup *agrp = nullptr;
480 if (action.is_action_layered()) {
482 agrp = cbag ? cbag->channel_group_find(mpt->pchan->name) : nullptr;
483 }
484 else {
485 agrp = BKE_action_group_find_name(adt->action, mpt->pchan->name);
486 }
487
488 if (agrp) {
490 action_group_to_keylist(adt, agrp, mpt->keylist, 0, {-FLT_MAX, FLT_MAX});
491 }
492 }
493 else {
494 Action &action = adt->action->wrap();
495 if (action.is_action_layered()) {
497 channelbag_for_action_slot(action, adt->slot_handle)->fcurves());
498 }
499 else {
501 }
502 action_to_keylist(adt, adt->action, mpt->keylist, 0, {-FLT_MAX, FLT_MAX});
503 }
504 }
506
507 if (range == ANIMVIZ_CALC_RANGE_CHANGED) {
508 int mpt_sfra, mpt_efra;
509 motionpath_calculate_update_range(mpt, adt, fcurves, cfra, &mpt_sfra, &mpt_efra);
510 if (mpt_sfra <= mpt_efra) {
511 sfra = min_ii(sfra, mpt_sfra);
512 efra = max_ii(efra, mpt_efra);
513 }
514 }
515 }
516
517 if (sfra > efra) {
519 return;
520 }
521
522 /* Calculate path over requested range. */
523 CLOG_INFO(&LOG,
524 "Calculating MotionPaths between frames %d - %d (%d frames)",
525 sfra,
526 efra,
527 efra - sfra + 1);
528 for (scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) {
530 /* For current frame, only update tagged. */
532 }
533 else {
534 /* Update relevant data for new frame. */
536 }
537
538 /* Perform baking for targets. */
539 motionpaths_calc_bake_targets(targets, scene->r.cfra, depsgraph, scene->camera);
540 }
541
542 /* Reset original environment. */
543 /* NOTE: We don't always need to reevaluate the main scene, as the depsgraph
544 * may be a temporary one that works on a subset of the data.
545 * We always have to restore the current frame though. */
546 scene->r.cfra = cfra;
547 if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) {
549 }
550
551 if (is_active_depsgraph) {
553 }
554
555 /* Clear recalc flags from targets. */
556 for (MPathTarget *mpt : targets) {
557 bMotionPath *mpath = mpt->mpath;
558
559 /* Get pointer to animviz settings for each target. */
561
562 /* Clear the flag requesting recalculation of targets. */
564
565 /* Clean temp data. */
566 ED_keylist_free(mpt->keylist);
567
568 /* Free previous batches to force update. */
572 }
573}
Functions and classes to work with Actions.
Functions for backward compatibility with the legacy Action API.
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)
bActionGroup * BKE_action_group_find_name(bAction *act, const char name[])
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2621
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2700
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define ATTR_FALLTHROUGH
#define LISTBASE_FOREACH(type, var, list)
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])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define CLOG_INFO(clg_ref,...)
Definition CLG_log.h:190
@ DAG_EVAL_VIEWPORT
Depsgraph * DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
Definition depsgraph.cc:278
bool DEG_is_active(const Depsgraph *depsgraph)
Definition depsgraph.cc:323
void DEG_make_inactive(Depsgraph *depsgraph)
Definition depsgraph.cc:343
void DEG_make_active(Depsgraph *depsgraph)
Definition depsgraph.cc:336
void DEG_graph_build_from_ids(Depsgraph *graph, blender::Span< ID * > ids)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ MOTIONPATH_VERT_KEY
@ MOTIONPATH_VIEW_KFACT
@ MOTIONPATH_RANGE_KEYS_ALL
@ MOTIONPATH_RANGE_KEYS_SELECTED
@ MOTIONPATH_RANGE_SCENE
@ MOTIONPATH_RANGE_MANUAL
@ ANIMVIZ_RECALC_PATHS
@ MOTIONPATH_FLAG_BAKE_CAMERA
@ MOTIONPATH_FLAG_BHEAD
@ FCURVE_SMOOTH_NONE
@ OB_MODE_POSE
#define PSFRA
#define PEFRA
eAnimvizCalcRange
@ ANIMVIZ_CALC_RANGE_FULL
@ ANIMVIZ_CALC_RANGE_CURRENT_FRAME
@ ANIMVIZ_CALC_RANGE_CHANGED
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:197
#define GPU_VERTBUF_DISCARD_SAFE(verts)
Read Guarded memory(de)allocation.
Depsgraph * animviz_depsgraph_build(Main *bmain, Scene *scene, ViewLayer *view_layer, blender::Span< MPathTarget * > targets)
static void motionpaths_calc_bake_targets(blender::Span< MPathTarget * > targets, int cframe, Depsgraph *depsgraph, Object *camera)
static void motionpath_get_global_framerange(blender::Span< MPathTarget * > targets, int *r_sfra, int *r_efra)
static void motionpath_calculate_update_range(MPathTarget *mpt, AnimData *adt, blender::Vector< FCurve * > fcurves, int current_frame, int *r_sfra, int *r_efra)
static int motionpath_get_prev_prev_keyframe(MPathTarget *mpt, AnimKeylist *keylist, int current_frame)
static int motionpath_get_prev_keyframe(MPathTarget *mpt, AnimKeylist *keylist, int current_frame)
static bool motionpath_check_can_use_keyframe_range(MPathTarget *, AnimData *adt, blender::Span< FCurve * > fcurves)
void animviz_free_motionpath_targets(blender::Vector< MPathTarget * > &targets)
static int motionpath_get_next_next_keyframe(MPathTarget *mpt, AnimKeylist *keylist, int current_frame)
void animviz_motionpath_compute_range(Object *ob, Scene *scene)
static void motionpaths_calc_update_scene(Depsgraph *depsgraph)
static void motionpath_free_free_tree_data(blender::MutableSpan< MPathTarget * > targets)
static int motionpath_get_next_keyframe(MPathTarget *mpt, AnimKeylist *keylist, int current_frame)
static bAnimVizSettings * animviz_target_settings_get(const MPathTarget *mpt)
void animviz_calc_motionpaths(Depsgraph *depsgraph, Main *bmain, Scene *scene, blender::MutableSpan< MPathTarget * > targets, eAnimvizCalcRange range, bool restore)
void animviz_build_motionpath_targets(Object *ob, blender::Vector< MPathTarget * > &r_targets)
BPy_StructRNA * depsgraph
constexpr bool is_empty() const
Definition BLI_span.hh:509
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
void append(const T &value)
void clear_and_shrink()
const bActionGroup * channel_group_find(StringRef name) const
nullptr float
const ActKeyColumn * ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag, blender::float2 range, const bool use_nla_remapping)
void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist)
bool ED_keylist_all_keys_frame_range(const AnimKeylist *keylist, Bounds< float > *r_frame_range)
AnimKeylist * ED_keylist_create()
void ED_keylist_free(AnimKeylist *keylist)
bool ED_keylist_selected_keys_frame_range(const AnimKeylist *keylist, Bounds< float > *r_frame_range)
const ActKeyColumn * ED_keylist_find_next(const AnimKeylist *keylist, const float cfra)
const ActKeyColumn * ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra)
void action_to_keylist(AnimData *adt, bAction *dna_action, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
void action_group_to_keylist(AnimData *adt, bActionGroup *agrp, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
#define LOG(level)
Definition log.h:97
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
Vector< FCurve * > fcurves_for_assigned_action(AnimData *adt)
bool assigned_action_has_keyframes(AnimData *adt)
const animrig::Channelbag * channelbag_for_action_slot(const Action &action, slot_handle_t slot_handle)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
Vector< T * > listbase_to_vector(ListBase &list)
#define FLT_MAX
Definition stdcycles.h:14
bAction * action
int32_t slot_handle
AnimKeylist * keylist
bPoseChannel * pchan
bMotionPath * mpath
struct bPose * pose
bMotionPath * mpath
struct AnimData * adt
bAnimVizSettings avs
struct RenderData r
struct Object * camera
ListBase curves
GPUBatchHandle * batch_line
bMotionPathVert * points
GPUVertBufHandle * points_vbo
GPUBatchHandle * batch_points
bMotionPath * mpath
ListBase chanbase
bAnimVizSettings avs