Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/animation/anim_motion_paths.c
| Show All 30 Lines | |||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "BKE_animsys.h" | #include "BKE_animsys.h" | ||||
| #include "BKE_action.h" | #include "BKE_action.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "DEG_depsgraph_build.h" | |||||
| #include "DEG_depsgraph_query.h" | #include "DEG_depsgraph_query.h" | ||||
| #include "GPU_batch.h" | #include "GPU_batch.h" | ||||
| #include "GPU_vertex_buffer.h" | #include "GPU_vertex_buffer.h" | ||||
| #include "ED_anim_api.h" | #include "ED_anim_api.h" | ||||
| #include "ED_keyframes_draw.h" | #include "ED_keyframes_draw.h" | ||||
| Show All 15 Lines | typedef struct MPathTarget { | ||||
| /* "Evaluated" Copies (these come from the background COW copy | /* "Evaluated" Copies (these come from the background COW copy | ||||
| * that provide all the coordinates we want to save off). */ | * that provide all the coordinates we want to save off). */ | ||||
| Object *ob_eval; /* evaluated object */ | Object *ob_eval; /* evaluated object */ | ||||
| } MPathTarget; | } MPathTarget; | ||||
| /* ........ */ | /* ........ */ | ||||
| /* update scene for current frame */ | |||||
| static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph) | |||||
| { | |||||
| BKE_scene_graph_update_for_newframe(depsgraph, bmain); | |||||
| } | |||||
| Depsgraph *animviz_depsgraph_build(Main *bmain, | |||||
| Scene *scene, | |||||
| ViewLayer *view_layer, | |||||
| ListBase *targets) | |||||
| { | |||||
| /* Allocate dependency graph. */ | |||||
| Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); | |||||
| /* Make a flat array of IDs for the DEG API. */ | |||||
| const int num_ids = BLI_listbase_count(targets); | |||||
| ID **ids = MEM_malloc_arrayN(sizeof(ID *), num_ids, "animviz IDS"); | |||||
| int current_id_index = 0; | |||||
| for (MPathTarget *mpt = targets->first; mpt != NULL; mpt = mpt->next) { | |||||
| ids[current_id_index++] = &mpt->ob->id; | |||||
| } | |||||
| /* Build graph from all requested IDs. */ | |||||
| DEG_graph_build_from_ids(depsgraph, bmain, scene, view_layer, ids, num_ids); | |||||
| MEM_freeN(ids); | |||||
| /* Update once so we can access pointers of evaluated animation data. */ | |||||
| motionpaths_calc_update_scene(bmain, depsgraph); | |||||
| return depsgraph; | |||||
| } | |||||
| /* get list of motion paths to be baked for the given object | /* get list of motion paths to be baked for the given object | ||||
| * - assumes the given list is ready to be used | * - assumes the given list is ready to be used | ||||
| */ | */ | ||||
| /* TODO: it would be nice in future to be able to update objects dependent on these bones too? */ | /* TODO: it would be nice in future to be able to update objects dependent on these bones too? */ | ||||
| void animviz_get_object_motionpaths(Object *ob, ListBase *targets) | void animviz_get_object_motionpaths(Object *ob, ListBase *targets) | ||||
| { | { | ||||
| MPathTarget *mpt; | MPathTarget *mpt; | ||||
| Show All 23 Lines | for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { | ||||
| mpt->pchan = pchan; | mpt->pchan = pchan; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* ........ */ | /* ........ */ | ||||
| /* update scene for current frame */ | |||||
| static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph) | |||||
| { | |||||
| /* Do all updates | |||||
| * - if this is too slow, resort to using a more efficient way | |||||
| * that doesn't force complete update, but for now, this is the | |||||
| * most accurate way! | |||||
| * | |||||
| * TODO(segey): Bring back partial updates, which became impossible | |||||
| * with the new depsgraph due to unsorted nature of bases. | |||||
| * | |||||
| * TODO(sergey): Use evaluation context dedicated to motion paths. | |||||
| */ | |||||
| BKE_scene_graph_update_for_newframe(depsgraph, bmain); | |||||
| } | |||||
| /* ........ */ | |||||
| /* perform baking for the targets on the current frame */ | /* perform baking for the targets on the current frame */ | ||||
| static void motionpaths_calc_bake_targets(ListBase *targets, int cframe) | static void motionpaths_calc_bake_targets(ListBase *targets, int cframe) | ||||
| { | { | ||||
| MPathTarget *mpt; | MPathTarget *mpt; | ||||
| /* for each target, check if it can be baked on the current frame */ | /* for each target, check if it can be baked on the current frame */ | ||||
| for (mpt = targets->first; mpt; mpt = mpt->next) { | for (mpt = targets->first; mpt; mpt = mpt->next) { | ||||
| bMotionPath *mpath = mpt->mpath; | bMotionPath *mpath = mpt->mpath; | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | if (mpath_eval && mpath_eval->length == mpath->length) { | ||||
| GPU_VERTBUF_DISCARD_SAFE(mpath_eval->points_vbo); | GPU_VERTBUF_DISCARD_SAFE(mpath_eval->points_vbo); | ||||
| GPU_BATCH_DISCARD_SAFE(mpath_eval->batch_line); | GPU_BATCH_DISCARD_SAFE(mpath_eval->batch_line); | ||||
| GPU_BATCH_DISCARD_SAFE(mpath_eval->batch_points); | GPU_BATCH_DISCARD_SAFE(mpath_eval->batch_points); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* Get pointer to animviz settings for the given target. */ | |||||
| static bAnimVizSettings *animviz_target_settings_get(MPathTarget *mpt) | |||||
| { | |||||
| if (mpt->pchan != NULL) { | |||||
| return &mpt->ob->pose->avs; | |||||
| } | |||||
| return &mpt->ob->avs; | |||||
| } | |||||
| static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int *r_efra) | |||||
| { | |||||
| *r_sfra = INT_MAX; | |||||
| *r_efra = INT_MIN; | |||||
| for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { | |||||
| *r_sfra = min_ii(*r_sfra, mpt->mpath->start_frame); | |||||
| *r_efra = max_ii(*r_efra, mpt->mpath->end_frame); | |||||
| } | |||||
| } | |||||
| static int motionpath_get_prev_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) | |||||
| { | |||||
| /* If the current frame is outside of the configured motion path range we ignore update of this | |||||
| * motion path by using invalid frame range where start frame is above the end frame. */ | |||||
| if (current_frame <= mpt->mpath->start_frame) { | |||||
| return INT_MAX; | |||||
| } | |||||
| float current_frame_float = current_frame; | |||||
| DLRBT_Node *node = BLI_dlrbTree_search_prev(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); | |||||
| if (node == NULL) { | |||||
| return mpt->mpath->start_frame; | |||||
| } | |||||
| ActKeyColumn *key_data = (ActKeyColumn *)node; | |||||
| return key_data->cfra; | |||||
| } | |||||
| static int motionpath_get_prev_prev_keyframe(MPathTarget *mpt, | |||||
| DLRBT_Tree *fcu_keys, | |||||
| int current_frame) | |||||
| { | |||||
| int frame = motionpath_get_prev_keyframe(mpt, fcu_keys, current_frame); | |||||
| return motionpath_get_prev_keyframe(mpt, fcu_keys, frame); | |||||
| } | |||||
| static int motionpath_get_next_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) | |||||
| { | |||||
| /* If the current frame is outside of the configured motion path range we ignore update of this | |||||
| * motion path by using invalid frame range where start frame is above the end frame. */ | |||||
| if (current_frame >= mpt->mpath->end_frame) { | |||||
| return INT_MIN; | |||||
| } | |||||
| float current_frame_float = current_frame; | |||||
| DLRBT_Node *node = BLI_dlrbTree_search_next(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); | |||||
| if (node == NULL) { | |||||
| return mpt->mpath->end_frame; | |||||
| } | |||||
| ActKeyColumn *key_data = (ActKeyColumn *)node; | |||||
| return key_data->cfra; | |||||
| } | |||||
| static int motionpath_get_next_next_keyframe(MPathTarget *mpt, | |||||
| DLRBT_Tree *fcu_keys, | |||||
| int current_frame) | |||||
| { | |||||
| int frame = motionpath_get_next_keyframe(mpt, fcu_keys, current_frame); | |||||
| return motionpath_get_next_keyframe(mpt, fcu_keys, frame); | |||||
| } | |||||
| static bool motionpath_check_can_use_keyframe_range(MPathTarget *UNUSED(mpt), | |||||
| AnimData *adt, | |||||
| ListBase *fcurve_list) | |||||
| { | |||||
| if (adt == NULL || fcurve_list == NULL) { | |||||
| return false; | |||||
| } | |||||
| /* NOTE: We might needed to do a full frame range update if there is a specific setup of NLA | |||||
| * or drivers or modifiers on the f-curves. */ | |||||
| return true; | |||||
| } | |||||
| static void motionpath_calculate_update_range(MPathTarget *mpt, | |||||
| AnimData *adt, | |||||
| ListBase *fcurve_list, | |||||
| int current_frame, | |||||
| int *r_sfra, | |||||
| int *r_efra) | |||||
| { | |||||
| /* Similar to the case when there is only a single keyframe: need to update en entire range to | |||||
| * a constant value. */ | |||||
| if (!motionpath_check_can_use_keyframe_range(mpt, adt, fcurve_list)) { | |||||
| *r_sfra = mpt->mpath->start_frame; | |||||
| *r_efra = mpt->mpath->end_frame; | |||||
| return; | |||||
| } | |||||
| *r_sfra = INT_MAX; | |||||
| *r_efra = INT_MIN; | |||||
| /* NOTE: Iterate over individual f-curves, and check their keyframes individually and pick a | |||||
| * widest range from them. This is because it's possible to have more narrow keyframe on a | |||||
| * channel which wasn't edited. | |||||
| * Could be optimized further by storing some flags about which channels has been modified so | |||||
| * we ignore all others (which can potentially make an update range unnecessary wide). */ | |||||
| for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) { | |||||
| DLRBT_Tree fcu_keys; | |||||
| BLI_dlrbTree_init(&fcu_keys); | |||||
| fcurve_to_keylist(adt, fcu, &fcu_keys, 0); | |||||
| int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, current_frame); | |||||
| int fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, current_frame); | |||||
| /* Extend range furher, since accelleration compensation propagates even further away. */ | |||||
| if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { | |||||
| fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, fcu_sfra); | |||||
| fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, fcu_efra); | |||||
| } | |||||
| if (fcu_sfra <= fcu_efra) { | |||||
| *r_sfra = min_ii(*r_sfra, fcu_sfra); | |||||
| *r_efra = max_ii(*r_efra, fcu_efra); | |||||
| } | |||||
| BLI_dlrbTree_free(&fcu_keys); | |||||
| } | |||||
| } | |||||
| /* Perform baking of the given object's and/or its bones' transforms to motion paths | /* Perform baking of the given object's and/or its bones' transforms to motion paths | ||||
| * - scene: current scene | * - scene: current scene | ||||
| * - ob: object whose flagged motionpaths should get calculated | * - ob: object whose flagged motionpaths should get calculated | ||||
| * - recalc: whether we need to | * - recalc: whether we need to | ||||
| */ | */ | ||||
| /* TODO: include reports pointer? */ | /* TODO: include reports pointer? */ | ||||
| void animviz_calc_motionpaths(Depsgraph *depsgraph, | void animviz_calc_motionpaths(Depsgraph *depsgraph, | ||||
| Main *bmain, | Main *bmain, | ||||
| Scene *scene, | Scene *scene, | ||||
| ListBase *targets, | ListBase *targets, | ||||
| bool restore, | eAnimvizCalcRange range, | ||||
| bool current_frame_only) | bool restore) | ||||
| { | { | ||||
| /* sanity check */ | /* Sanity check. */ | ||||
| if (ELEM(NULL, targets, targets->first)) { | if (ELEM(NULL, targets, targets->first)) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Compute frame range to bake within. | const int cfra = CFRA; | ||||
| * TODO: this method could be improved... | int sfra = INT_MAX, efra = INT_MIN; | ||||
| * 1) max range for standard baking | switch (range) { | ||||
| * 2) minimum range for recalc baking (i.e. between keyframes, but how?) */ | case ANIMVIZ_CALC_RANGE_CURRENT_FRAME: | ||||
| int sfra = INT_MAX; | motionpath_get_global_framerange(targets, &sfra, &efra); | ||||
| int efra = INT_MIN; | if (sfra > efra) { | ||||
| for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { | |||||
| /* try to increase area to do (only as much as needed) */ | |||||
| sfra = MIN2(sfra, mpt->mpath->start_frame); | |||||
| efra = MAX2(efra, mpt->mpath->end_frame); | |||||
| } | |||||
| if (efra <= sfra) { | |||||
| return; | return; | ||||
| } | } | ||||
| /* Limit frame range if we are updating just the current frame. */ | |||||
| /* set frame values */ | |||||
| int cfra = CFRA; | |||||
| if (current_frame_only) { | |||||
| if (cfra < sfra || cfra > efra) { | if (cfra < sfra || cfra > efra) { | ||||
| return; | return; | ||||
| } | } | ||||
| sfra = efra = cfra; | sfra = efra = cfra; | ||||
| break; | |||||
| case ANIMVIZ_CALC_RANGE_CHANGED: | |||||
| /* Nothing to do here, will be handled later when iterating through the targets. */ | |||||
| break; | |||||
| case ANIMVIZ_CALC_RANGE_FULL: | |||||
| motionpath_get_global_framerange(targets, &sfra, &efra); | |||||
| if (sfra > efra) { | |||||
| return; | |||||
| } | |||||
| break; | |||||
| } | } | ||||
| /* get copies of objects/bones to get the calculated results from | /* get copies of objects/bones to get the calculated results from | ||||
| * (for copy-on-write evaluation), so that we actually get some results | * (for copy-on-write evaluation), so that we actually get some results | ||||
| */ | */ | ||||
| /* TODO: Create a copy of background depsgraph that only contain these entities, | /* TODO: Create a copy of background depsgraph that only contain these entities, | ||||
| * and only evaluates them. | * and only evaluates them. | ||||
| Show All 11 Lines | void animviz_calc_motionpaths(Depsgraph *depsgraph, | ||||
| for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { | for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { | ||||
| mpt->ob_eval = DEG_get_evaluated_object(depsgraph, mpt->ob); | mpt->ob_eval = DEG_get_evaluated_object(depsgraph, mpt->ob); | ||||
| AnimData *adt = BKE_animdata_from_id(&mpt->ob_eval->id); | AnimData *adt = BKE_animdata_from_id(&mpt->ob_eval->id); | ||||
| /* build list of all keyframes in active action for object or pchan */ | /* build list of all keyframes in active action for object or pchan */ | ||||
| BLI_dlrbTree_init(&mpt->keys); | BLI_dlrbTree_init(&mpt->keys); | ||||
| ListBase *fcurve_list = NULL; | |||||
| if (adt) { | if (adt) { | ||||
| bAnimVizSettings *avs; | |||||
| /* get pointer to animviz settings for each target */ | /* get pointer to animviz settings for each target */ | ||||
| if (mpt->pchan) { | bAnimVizSettings *avs = animviz_target_settings_get(mpt); | ||||
| avs = &mpt->ob->pose->avs; | |||||
| } | |||||
| else { | |||||
| avs = &mpt->ob->avs; | |||||
| } | |||||
| /* it is assumed that keyframes for bones are all grouped in a single group | /* it is assumed that keyframes for bones are all grouped in a single group | ||||
| * unless an option is set to always use the whole action | * unless an option is set to always use the whole action | ||||
| */ | */ | ||||
| if ((mpt->pchan) && (avs->path_viewflag & MOTIONPATH_VIEW_KFACT) == 0) { | if ((mpt->pchan) && (avs->path_viewflag & MOTIONPATH_VIEW_KFACT) == 0) { | ||||
| bActionGroup *agrp = BKE_action_group_find_name(adt->action, mpt->pchan->name); | bActionGroup *agrp = BKE_action_group_find_name(adt->action, mpt->pchan->name); | ||||
| if (agrp) { | if (agrp) { | ||||
| fcurve_list = &agrp->channels; | |||||
| agroup_to_keylist(adt, agrp, &mpt->keys, 0); | agroup_to_keylist(adt, agrp, &mpt->keys, 0); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| fcurve_list = &adt->action->curves; | |||||
| action_to_keylist(adt, adt->action, &mpt->keys, 0); | action_to_keylist(adt, adt->action, &mpt->keys, 0); | ||||
| } | } | ||||
| } | } | ||||
| if (range == ANIMVIZ_CALC_RANGE_CHANGED) { | |||||
| int mpt_sfra, mpt_efra; | |||||
| motionpath_calculate_update_range(mpt, adt, fcurve_list, cfra, &mpt_sfra, &mpt_efra); | |||||
| if (mpt_sfra <= mpt_efra) { | |||||
| sfra = min_ii(sfra, mpt_sfra); | |||||
| efra = max_ii(efra, mpt_efra); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (sfra > efra) { | |||||
| return; | |||||
| } | } | ||||
| /* calculate path over requested range */ | /* calculate path over requested range */ | ||||
| CLOG_INFO(&LOG, | CLOG_INFO(&LOG, | ||||
| 1, | 1, | ||||
| "Calculating MotionPaths between frames %d - %d (%d frames)", | "Calculating MotionPaths between frames %d - %d (%d frames)", | ||||
| sfra, | sfra, | ||||
| efra, | efra, | ||||
| efra - sfra + 1); | efra - sfra + 1); | ||||
| for (CFRA = sfra; CFRA <= efra; CFRA++) { | for (CFRA = sfra; CFRA <= efra; CFRA++) { | ||||
| if (current_frame_only) { | if (range == ANIMVIZ_CALC_RANGE_CURRENT_FRAME) { | ||||
| /* For current frame, only update tagged. */ | /* For current frame, only update tagged. */ | ||||
| BKE_scene_graph_update_tagged(depsgraph, bmain); | BKE_scene_graph_update_tagged(depsgraph, bmain); | ||||
| } | } | ||||
| else { | else { | ||||
| /* Update relevant data for new frame. */ | /* Update relevant data for new frame. */ | ||||
| motionpaths_calc_update_scene(bmain, depsgraph); | motionpaths_calc_update_scene(bmain, depsgraph); | ||||
| } | } | ||||
| /* perform baking for targets */ | /* perform baking for targets */ | ||||
| motionpaths_calc_bake_targets(targets, CFRA); | motionpaths_calc_bake_targets(targets, CFRA); | ||||
| } | } | ||||
| /* reset original environment */ | /* reset original environment */ | ||||
| /* NOTE: We don't always need to reevaluate the main scene, as the depsgraph | /* NOTE: We don't always need to reevaluate the main scene, as the depsgraph | ||||
| * may be a temporary one that works on a subset of the data. | * may be a temporary one that works on a subset of the data. | ||||
| * We always have to restore the current frame though. */ | * We always have to restore the current frame though. */ | ||||
| CFRA = cfra; | CFRA = cfra; | ||||
| if (!current_frame_only && restore) { | if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) { | ||||
| motionpaths_calc_update_scene(bmain, depsgraph); | motionpaths_calc_update_scene(bmain, depsgraph); | ||||
| } | } | ||||
| if (is_active_depsgraph) { | if (is_active_depsgraph) { | ||||
| DEG_make_active(depsgraph); | DEG_make_active(depsgraph); | ||||
| } | } | ||||
| /* clear recalc flags from targets */ | /* clear recalc flags from targets */ | ||||
| for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { | for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { | ||||
| bAnimVizSettings *avs; | |||||
| bMotionPath *mpath = mpt->mpath; | bMotionPath *mpath = mpt->mpath; | ||||
| /* get pointer to animviz settings for each target */ | /* get pointer to animviz settings for each target */ | ||||
| if (mpt->pchan) { | bAnimVizSettings *avs = animviz_target_settings_get(mpt); | ||||
| avs = &mpt->ob->pose->avs; | |||||
| } | |||||
| else { | |||||
| avs = &mpt->ob->avs; | |||||
| } | |||||
| /* clear the flag requesting recalculation of targets */ | /* clear the flag requesting recalculation of targets */ | ||||
| avs->recalc &= ~ANIMVIZ_RECALC_PATHS; | avs->recalc &= ~ANIMVIZ_RECALC_PATHS; | ||||
| /* Clean temp data */ | /* Clean temp data */ | ||||
| BLI_dlrbTree_free(&mpt->keys); | BLI_dlrbTree_free(&mpt->keys); | ||||
| /* Free previous batches to force update. */ | /* Free previous batches to force update. */ | ||||
| GPU_VERTBUF_DISCARD_SAFE(mpath->points_vbo); | GPU_VERTBUF_DISCARD_SAFE(mpath->points_vbo); | ||||
| GPU_BATCH_DISCARD_SAFE(mpath->batch_line); | GPU_BATCH_DISCARD_SAFE(mpath->batch_line); | ||||
| GPU_BATCH_DISCARD_SAFE(mpath->batch_points); | GPU_BATCH_DISCARD_SAFE(mpath->batch_points); | ||||
| } | } | ||||
| } | } | ||||