Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/animation')
-rw-r--r--source/blender/editors/animation/anim_motion_paths.c185
1 files changed, 159 insertions, 26 deletions
diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c
index 9d94b14250a..aff532f4d54 100644
--- a/source/blender/editors/animation/anim_motion_paths.c
+++ b/source/blender/editors/animation/anim_motion_paths.c
@@ -210,6 +210,126 @@ static bAnimVizSettings *animviz_target_settings_get(MPathTarget *mpt)
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, &current_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, &current_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
* - scene: current scene
* - ob: object whose flagged motionpaths should get calculated
@@ -223,36 +343,33 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
eAnimvizCalcRange range,
bool restore)
{
- /* sanity check */
+ /* Sanity check. */
if (ELEM(NULL, targets, targets->first)) {
return;
}
- /* Compute frame range to bake within.
- * TODO: this method could be improved...
- * 1) max range for standard baking
- * 2) minimum range for recalc baking (i.e. between keyframes, but how?) */
- int sfra = INT_MAX;
- int efra = INT_MIN;
-
- 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;
- }
-
- /* Limit frame range if we are updating just the current frame. */
- /* set frame values */
- int cfra = CFRA;
- if (range == ANIMVIZ_CALC_RANGE_CURRENT_FRAME) {
- if (cfra < sfra || cfra > efra) {
- return;
- }
- sfra = efra = cfra;
+ const int cfra = CFRA;
+ int sfra = INT_MAX, efra = INT_MIN;
+ switch (range) {
+ case ANIMVIZ_CALC_RANGE_CURRENT_FRAME:
+ motionpath_get_global_framerange(targets, &sfra, &efra);
+ if (sfra > efra) {
+ return;
+ }
+ if (cfra < sfra || cfra > efra) {
+ return;
+ }
+ 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
@@ -280,6 +397,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
/* build list of all keyframes in active action for object or pchan */
BLI_dlrbTree_init(&mpt->keys);
+ ListBase *fcurve_list = NULL;
if (adt) {
/* get pointer to animviz settings for each target */
bAnimVizSettings *avs = animviz_target_settings_get(mpt);
@@ -291,13 +409,28 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
bActionGroup *agrp = BKE_action_group_find_name(adt->action, mpt->pchan->name);
if (agrp) {
+ fcurve_list = &agrp->channels;
agroup_to_keylist(adt, agrp, &mpt->keys, 0);
}
}
else {
+ fcurve_list = &adt->action->curves;
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 */