[Bf-blender-cvs] [d78efe0fbe3] temp-angavrilov: Animation: support filtering for curves that have cycle issues.

Alexander Gavrilov noreply at git.blender.org
Sun Nov 21 11:08:52 CET 2021


Commit: d78efe0fbe3d49c8ca5666344def6c9faef130cd
Author: Alexander Gavrilov
Date:   Mon May 3 17:27:53 2021 +0300
Branches: temp-angavrilov
https://developer.blender.org/rBd78efe0fbe3d49c8ca5666344def6c9faef130cd

Animation: support filtering for curves that have cycle issues.

It is possible to have curves with cyclic extrapolation that
have a mismatch in their end keyframes, causing a jump.

Also, since the looping behavior is defined per curve rather than at
action level, it is possible for curve loop periods to get out of
sync with each other. This commit adds an option to compare curves
against the manual frame range specified in the action, and treat
any mismatches as errors for the purpose of F-Curve filtering.

When enabled, the check verifies that end values of cyclic curves
match, curves within a cyclic action have valid cyclic extrapolation,
and the action period evenly divides by the curve period (since
a curve looping at e.g. half of the action period length still
repeats in sync with the action).

Ref: https://developer.blender.org/D11803

===================================================================

M	release/scripts/startup/bl_ui/space_dopesheet.py
M	source/blender/blenkernel/BKE_fcurve.h
M	source/blender/blenkernel/intern/fcurve.c
M	source/blender/editors/animation/anim_filter.c
M	source/blender/makesdna/DNA_action_types.h
M	source/blender/makesrna/intern/rna_action.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index 3c90e0c96cc..4d1c61af5be 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -79,6 +79,12 @@ class DopesheetFilterPopoverBase:
         else:  # graph and dopesheet editors - F-Curves and drivers only
             col.prop(dopesheet, "show_only_errors", icon='NONE')
 
+            col.separator()
+
+            col2 = col.column(align=True)
+            col2.active = dopesheet.show_only_errors
+            col2.prop(dopesheet, "show_cycle_errors")
+
     # Name/Membership Filters
     # XXX: Perhaps these should just stay in the headers (exclusively)?
     @classmethod
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index f494c2e30cc..96a14bd744a 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -294,7 +294,7 @@ typedef enum eFCU_Cycle_Type {
   FCU_CYCLE_OFFSET,
 } eFCU_Cycle_Type;
 
-eFCU_Cycle_Type BKE_fcurve_get_cycle_type(struct FCurve *fcu);
+eFCU_Cycle_Type BKE_fcurve_get_cycle_type(const struct FCurve *fcu);
 
 /* Recompute handles to neatly subdivide the prev-next range at bezt. */
 bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index bbf61c51bfb..9da94c84610 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -1236,7 +1236,7 @@ void fcurve_samples_to_keyframes(FCurve *fcu, const int start, const int end)
  */
 
 /* Checks if the F-Curve has a Cycles modifier, and returns the type of the cycle behavior. */
-eFCU_Cycle_Type BKE_fcurve_get_cycle_type(FCurve *fcu)
+eFCU_Cycle_Type BKE_fcurve_get_cycle_type(const FCurve *fcu)
 {
   FModifier *fcm = fcu->modifiers.first;
 
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index e1d046428a8..90c079954ee 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -79,6 +79,7 @@
 #include "BLI_alloca.h"
 #include "BLI_blenlib.h"
 #include "BLI_ghash.h"
+#include "BLI_math.h"
 #include "BLI_string.h"
 #include "BLI_utildefines.h"
 
@@ -1225,6 +1226,53 @@ static bool skip_fcurve_with_name(
   return true;
 }
 
+/* Check if the F-Curve doesn't cycle properly based on action settings. */
+static bool fcurve_has_cycle_errors(const FCurve *fcu, const bAction *act)
+{
+  /* Check if the curve is cyclic. */
+  const eFCU_Cycle_Type cycle_type = BKE_fcurve_get_cycle_type(fcu);
+
+  if (cycle_type == FCU_CYCLE_NONE) {
+    /* Curves in a cyclic action should be cyclic; in an ordinary action either way is fine. */
+    return BKE_action_is_cyclic(act);
+  }
+
+  /* Check if the curve has enough points. */
+  if (fcu->totvert < 2 || !fcu->bezt) {
+    return true;
+  }
+
+  /* Check that it has a nonzero period length. */
+  const BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1];
+
+  const float curve_period = last->vec[1][0] - first->vec[1][0];
+
+  if (curve_period < 0.1f) {
+    return true;
+  }
+
+  /* Check that the action period is divisible by the curve period. */
+  if (BKE_action_is_cyclic(act)) {
+    const float action_period = act->frame_end - act->frame_start;
+    const float gap = action_period - roundf(action_period / curve_period) * curve_period;
+
+    if (fabsf(gap) > 1e-3f) {
+      return true;
+    }
+  }
+
+  /* In case of a perfect cycle, check that the start and end values match. */
+  if (cycle_type == FCU_CYCLE_PERFECT) {
+    const float magnitude = max_fff(fabsf(first->vec[1][1]), fabsf(last->vec[1][1]), 0.01f);
+
+    if (fabsf(first->vec[1][1] - last->vec[1][1]) > magnitude * 1e-4f) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 /**
  * Check if F-Curve has errors and/or is disabled
  *
@@ -1271,6 +1319,7 @@ static FCurve *animfilter_fcurve_next(bDopeSheet *ads,
                                       FCurve *first,
                                       eAnim_ChannelType channel_type,
                                       int filter_mode,
+                                      bAction *act,
                                       void *owner,
                                       ID *owner_id)
 {
@@ -1322,7 +1371,9 @@ static FCurve *animfilter_fcurve_next(bDopeSheet *ads,
             /* error-based filtering... */
             if ((ads) && (ads->filterflag & ADS_FILTER_ONLY_ERRORS)) {
               /* skip if no errors... */
-              if (fcurve_has_errors(fcu) == false) {
+              if (fcurve_has_errors(fcu) == false &&
+                  !((ads->filterflag & ADS_FILTER_CYCLE_ERRORS) &&
+                    fcurve_has_cycle_errors(fcu, act))) {
                 continue;
               }
             }
@@ -1344,6 +1395,7 @@ static size_t animfilter_fcurves(ListBase *anim_data,
                                  FCurve *first,
                                  eAnim_ChannelType fcurve_type,
                                  int filter_mode,
+                                 bAction *act,
                                  void *owner,
                                  ID *owner_id,
                                  ID *fcurve_owner_id)
@@ -1364,7 +1416,7 @@ static size_t animfilter_fcurves(ListBase *anim_data,
    *    Back to step 2 :)
    */
   for (fcu = first;
-       ((fcu = animfilter_fcurve_next(ads, fcu, fcurve_type, filter_mode, owner, owner_id)));
+       ((fcu = animfilter_fcurve_next(ads, fcu, fcurve_type, filter_mode, act, owner, owner_id)));
        fcu = fcu->next) {
     if (UNLIKELY(fcurve_type == ANIMTYPE_NLACURVE)) {
       /* NLA Control Curve - Basically the same as normal F-Curves,
@@ -1439,11 +1491,18 @@ static size_t animfilter_act_group(bAnimContext *ac,
         if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) {
           /* get first F-Curve which can be used here */
           FCurve *first_fcu = animfilter_fcurve_next(
-              ads, agrp->channels.first, ANIMTYPE_FCURVE, filter_mode, agrp, owner_id);
+              ads, agrp->channels.first, ANIMTYPE_FCURVE, filter_mode, act, agrp, owner_id);
 
           /* filter list, starting from this F-Curve */
-          tmp_items += animfilter_fcurves(
-              &tmp_data, ads, first_fcu, ANIMTYPE_FCURVE, filter_mode, agrp, owner_id, &act->id);
+          tmp_items += animfilter_fcurves(&tmp_data,
+                                          ads,
+                                          first_fcu,
+                                          ANIMTYPE_FCURVE,
+                                          filter_mode,
+                                          act,
+                                          agrp,
+                                          owner_id,
+                                          &act->id);
         }
       }
     }
@@ -1508,7 +1567,7 @@ static size_t animfilter_action(bAnimContext *ac,
   if (!(filter_mode & ANIMFILTER_ACTGROUPED)) {
     FCurve *firstfcu = (lastchan) ? (lastchan->next) : (act->curves.first);
     items += animfilter_fcurves(
-        anim_data, ads, firstfcu, ANIMTYPE_FCURVE, filter_mode, NULL, owner_id, &act->id);
+        anim_data, ads, firstfcu, ANIMTYPE_FCURVE, filter_mode, act, NULL, owner_id, &act->id);
   }
 
   /* return the number of items added to the list */
@@ -1641,6 +1700,7 @@ static size_t animfilter_nla_controls(
                                         strip->fcurves.first,
                                         ANIMTYPE_NLACURVE,
                                         filter_mode,
+                                        NULL,
                                         strip,
                                         owner_id,
                                         owner_id);
@@ -1697,8 +1757,15 @@ static size_t animfilter_block_data(
           items += animfilter_nla(ac, anim_data, ads, adt, filter_mode, id);
         },
         { /* Drivers */
-          items += animfilter_fcurves(
-              anim_data, ads, adt->drivers.first, ANIMTYPE_FCURVE, filter_mode, NULL, id, id);
+          items += animfilter_fcurves(anim_data,
+                                      ads,
+                                      adt->drivers.first,
+                                      ANIMTYPE_FCURVE,
+                                      filter_mode,
+                                      NULL,
+                                      NULL,
+                                      id,
+                                      id);
         },
         { /* NLA Control Keyframes */
           items += animfilter_nla_controls(anim_data, ads, adt, filter_mode, id);
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 8e7551e1703..d47e90277a6 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -781,6 +781,8 @@ typedef enum eDopeSheet_FilterFlag {
   ADS_FILTER_INCL_HIDDEN = (1 << 26),
   /** show only F-Curves which are disabled/have errors - for debugging drivers */
   ADS_FILTER_ONLY_ERRORS = (1 << 28),
+  /** treat cyclic extrapolation issues as errors for the previous option */
+  ADS_FILTER_CYCLE_ERRORS = (1 << 29),
 
 #if 0
   /** combination filters (some only used at runtime) */
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index 96e37dfebbb..2916855e2ff 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -428,6 +428,14 @@ static void rna_def_dopesheet(BlenderRNA *brna)
   RNA_def_property_ui_icon(prop, ICON_ERROR, 0);
   RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
 
+  prop = RNA_def_property(srna, "show_cycle_errors", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_boolean_sdna(prop, NULL, "filterflag", ADS_FILTER_CYCLE_ERRORS);
+  RNA_def_property_ui_text(prop,
+                           "Show Cycle Errors",
+                           "Treat cyclic extrapolation issues as erro

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list