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:
authorJoshua Leung <aligorith@gmail.com>2011-03-25 06:58:21 +0300
committerJoshua Leung <aligorith@gmail.com>2011-03-25 06:58:21 +0300
commit16e736b7db3ef3597e22f55e7b26416ee562a418 (patch)
tree7b4d0980e6c33c35aea5b375718add6f84dbb74e
parent0c03fa78c10f04283512f2a5a4557d86c72d6f29 (diff)
Graph Editor: Euler Filter ported from Py to C
Ported joeedh's Euler Filter code from Python to C so that this is more in line with the other Graph Editor tools - i.e. joeedh's version only worked on the active bone's curves, while standard tools could work with multiple bones/objects at the same time. To use this new version of this operator: 1) Select all the F-Curves for all 3 of the components (XYZ) for the euler rotations you wish to clean up. In the Graph Editor, they must be one after the other (i.e. you can't have "RotX, RotY, something else, RotZ") 2) Activate the operator from the Key menu in the Graph Editor In an old test file I have floating around, this method did not appear to be good enough to fix a very clear discontinuity in the middle of the action, so I'll test some additional methods too
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_operators/fcurve_euler_filter.py78
-rw-r--r--source/blender/editors/space_graph/graph_edit.c123
-rw-r--r--source/blender/editors/space_graph/graph_intern.h1
-rw-r--r--source/blender/editors/space_graph/graph_ops.c1
5 files changed, 108 insertions, 96 deletions
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index 599cd6c1889..2a42cfbacb8 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -25,7 +25,6 @@ if "bpy" in locals():
_modules = (
"add_mesh_torus",
"animsys_update",
- "fcurve_euler_filter",
"image",
"mesh",
"nla",
diff --git a/release/scripts/startup/bl_operators/fcurve_euler_filter.py b/release/scripts/startup/bl_operators/fcurve_euler_filter.py
deleted file mode 100644
index c7b249a1d0c..00000000000
--- a/release/scripts/startup/bl_operators/fcurve_euler_filter.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-# <pep8 compliant>
-
-import bpy
-
-
-def main(context):
- from math import pi
-
- def cleanupEulCurve(fcv):
- keys = []
-
- for k in fcv.keyframe_points:
- keys.append([k.handle_left.copy(), k.co.copy(), k.handle_right.copy()])
-
- for i in range(len(keys)):
- cur = keys[i]
- prev = keys[i - 1] if i > 0 else None
- next = keys[i + 1] if i < len(keys) - 1 else None
-
- if prev is None:
- continue
-
- th = pi
- if abs(prev[1][1] - cur[1][1]) >= th: # more than 180 degree jump
- fac = pi * 2.0
- if prev[1][1] > cur[1][1]:
- while abs(cur[1][1] - prev[1][1]) >= th: # < prev[1][1]:
- cur[0][1] += fac
- cur[1][1] += fac
- cur[2][1] += fac
- elif prev[1][1] < cur[1][1]:
- while abs(cur[1][1] - prev[1][1]) >= th:
- cur[0][1] -= fac
- cur[1][1] -= fac
- cur[2][1] -= fac
-
- for i in range(len(keys)):
- for x in range(2):
- fcv.keyframe_points[i].handle_left[x] = keys[i][0][x]
- fcv.keyframe_points[i].co[x] = keys[i][1][x]
- fcv.keyframe_points[i].handle_right[x] = keys[i][2][x]
-
- flist = bpy.context.active_object.animation_data.action.fcurves
- for f in flist:
- if f.select and f.data_path.endswith("rotation_euler"):
- cleanupEulCurve(f)
-
-
-class DiscontFilterOp(bpy.types.Operator):
- """Fixes the most common causes of gimbal lock in the fcurves of the active bone"""
- bl_idname = "graph.euler_filter"
- bl_label = "Filter out discontinuities in the active fcurves"
-
- @classmethod
- def poll(cls, context):
- return context.active_object != None
-
- def execute(self, context):
- main(context)
- return {'FINISHED'}
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index fdd43aa0566..deffc60019e 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -1501,12 +1501,13 @@ static int graphkeys_handletype_exec(bContext *C, wmOperator *op)
* of values to -180 degrees to 180 degrees.
*/
-#if 0 // XXX this is not ready for the primetime yet
-
/* set of three euler-rotation F-Curves */
typedef struct tEulerFilter {
+ struct tEulerFilter *next, *prev;
+
ID *id; /* ID-block which owns the channels */
- FCurve (*fcurves)[3]; /* 3 Pointers to F-Curves */
+ FCurve *(fcurves[3]); /* 3 Pointers to F-Curves */
+ char *rna_path; /* Pointer to one of the RNA Path's used by one of the F-Curves */
} tEulerFilter;
static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op)
@@ -1518,7 +1519,8 @@ static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op)
int filter;
ListBase eulers = {NULL, NULL};
- tEulerFilter *euf= NULL;
+ tEulerFilter *euf= NULL;
+ int groups=0, failed=0;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -1528,7 +1530,7 @@ static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op)
* 1) Sets of three related rotation curves are identified from the selected channels,
* and are stored as a single 'operation unit' for the next step
* 2) Each set of three F-Curves is processed for each keyframe, with the values being
- * processed according to one of several ways.
+ * processed as necessary
*/
/* step 1: extract only the rotation f-curves */
@@ -1542,45 +1544,134 @@ static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op)
* - only rotation curves
* - for pchan curves, make sure we're only using the euler curves
*/
- if (strstr(fcu->rna_path, "rotation_euler") == 0)
+ if (strstr(fcu->rna_path, "rotation_euler") == NULL)
continue;
+ else if (ELEM3(fcu->array_index, 0, 1, 2) == 0) {
+ BKE_reportf(op->reports, RPT_WARNING,
+ "Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)",
+ (ale->id)? ale->id->name:"<No ID>", fcu->rna_path, fcu->array_index);
+ continue;
+ }
- /* check if current set of 3-curves is suitable to add this curve to
- * - things like whether the current set of curves is 'full' should be checked later only
- * - first check if id-blocks are compatible
+ /* optimisation: assume that xyz curves will always be stored consecutively,
+ * so if the paths or the ID's don't match up, then a curve needs to be added
+ * to a new group
*/
- if ((euf) && (ale->id != euf->id)) {
- /* if the paths match, add this curve to the set of curves */
- // NOTE: simple string compare for now... could be a bit more fancy...
-
+ if ((euf) && (euf->id == ale->id) && (strcmp(euf->rna_path, fcu->rna_path)==0)) {
+ /* this should be fine to add to the existing group then */
+ euf->fcurves[fcu->array_index]= fcu;
}
else {
/* just add to a new block */
euf= MEM_callocN(sizeof(tEulerFilter), "tEulerFilter");
BLI_addtail(&eulers, euf);
+ groups++;
euf->id= ale->id;
+ euf->rna_path = fcu->rna_path; /* this should be safe, since we're only using it for a short time */
euf->fcurves[fcu->array_index]= fcu;
}
}
BLI_freelistN(&anim_data);
+ if (groups == 0) {
+ BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up");
+ return OPERATOR_CANCELLED;
+ }
+
/* step 2: go through each set of curves, processing the values at each keyframe
* - it is assumed that there must be a full set of keyframes at each keyframe position
*/
for (euf= eulers.first; euf; euf= euf->next) {
+ int f;
+
+ /* sanity check: ensure that there are enough F-Curves to work on in this group */
+ // TODO: also enforce assumption that there be a full set of keyframes at each position by ensuring that totvert counts are same?
+ if (ELEM3(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) {
+ /* report which components are missing */
+ BKE_reportf(op->reports, RPT_WARNING,
+ "Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'",
+ (euf->fcurves[0]==NULL)? "X":"",
+ (euf->fcurves[1]==NULL)? "Y":"",
+ (euf->fcurves[2]==NULL)? "Z":"",
+ euf->id->name, euf->rna_path);
+
+ /* keep track of number of failed sets, and carry on to next group */
+ failed++;
+ continue;
+ }
+ /* simple method: just treat any difference between keys of greater than 180 degrees as being a flip */
+ // FIXME: there are more complicated methods that will be needed to fix more cases than just some
+ for (f = 0; f < 3; f++) {
+ FCurve *fcu = euf->fcurves[f];
+ BezTriple *bezt, *prev=NULL;
+ unsigned int i;
+
+ /* skip if not enough vets to do a decent analysis of... */
+ if (fcu->totvert <= 2)
+ continue;
+
+ /* prev follows bezt, bezt = "current" point to be fixed */
+ for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, prev=bezt, bezt++) {
+ /* our method depends on determining a "difference" from the previous vert */
+ if (prev == NULL)
+ continue;
+
+ /* > 180 degree flip? */
+ if (fabs(prev->vec[1][1] - bezt->vec[1][1]) >= M_PI) {
+ /* 360 degrees to add/subtract frame value until difference is acceptably small that there's no more flip */
+ const double fac = 2.0 * M_PI;
+
+ if (prev->vec[1][1] > bezt->vec[1][1]) {
+ while (fabs(bezt->vec[1][1] - prev->vec[1][1]) >= M_PI) {
+ bezt->vec[0][1] += fac;
+ bezt->vec[1][1] += fac;
+ bezt->vec[2][1] += fac;
+ }
+ }
+ else /* if (prev->vec[1][1] < bezt->vec[1][1]) */ {
+ while (fabs(bezt->vec[1][1] - prev->vec[1][1]) >= M_PI) {
+ bezt->vec[0][1] -= fac;
+ bezt->vec[1][1] -= fac;
+ bezt->vec[2][1] -= fac;
+ }
+ }
+ }
+ }
+ }
}
BLI_freelistN(&eulers);
- return OPERATOR_FINISHED;
+ /* updates + finishing warnings */
+ if (failed == groups) {
+ BKE_report(op->reports, RPT_ERROR,
+ "No Euler Rotations could be corrected. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected.");
+ return OPERATOR_CANCELLED;
+ }
+ else {
+ if (failed) {
+ BKE_report(op->reports, RPT_ERROR,
+ "Some Euler Rotations couldn't be corrected due to missing/unselected/out-of-order F-Curves. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected.");
+ }
+
+ /* validate keyframes after editing */
+ ANIM_editkeyframes_refresh(&ac);
+
+ /* set notifier that keyframes have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
+
+ /* done at last */
+ return OPERATOR_FINISHED;
+ }
}
void GRAPH_OT_euler_filter (wmOperatorType *ot)
{
/* identifiers */
- ot->name= "Euler Filter";
+ ot->name= "Euler Discontinuity Filter";
ot->idname= "GRAPH_OT_euler_filter";
+ ot->description= "Fixes the most common causes of gimbal lock in the selected Euler Rotation F-Curves";
/* api callbacks */
ot->exec= graphkeys_euler_filter_exec;
@@ -1590,8 +1681,6 @@ void GRAPH_OT_euler_filter (wmOperatorType *ot)
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
-#endif // XXX this is not ready for the primetime yet
-
/* ***************** Jump to Selected Frames Operator *********************** */
/* snap current-frame indicator to 'average time' of selected keyframe */
diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h
index feb17827db7..2aefb7c64dd 100644
--- a/source/blender/editors/space_graph/graph_intern.h
+++ b/source/blender/editors/space_graph/graph_intern.h
@@ -111,6 +111,7 @@ void GRAPH_OT_sample(struct wmOperatorType *ot);
void GRAPH_OT_bake(struct wmOperatorType *ot);
void GRAPH_OT_sound_bake(struct wmOperatorType *ot);
void GRAPH_OT_smooth(struct wmOperatorType *ot);
+void GRAPH_OT_euler_filter(struct wmOperatorType *ot);
void GRAPH_OT_handle_type(struct wmOperatorType *ot);
void GRAPH_OT_interpolation_type(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index 674d2809188..e75fbeb8efc 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -255,6 +255,7 @@ void graphedit_operatortypes(void)
WM_operatortype_append(GRAPH_OT_sound_bake);
WM_operatortype_append(GRAPH_OT_smooth);
WM_operatortype_append(GRAPH_OT_clean);
+ WM_operatortype_append(GRAPH_OT_euler_filter);
WM_operatortype_append(GRAPH_OT_delete);
WM_operatortype_append(GRAPH_OT_duplicate);