2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
19 * This is a new part of Blender
21 * Contributor(s): Joshua Leung
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/armature/pose_slide.c
30 #include "MEM_guardedalloc.h"
33 #include "BLI_blenlib.h"
34 #include "BLI_dlrbTree.h"
36 #include "DNA_anim_types.h"
37 #include "DNA_armature_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
41 #include "BKE_fcurve.h"
43 #include "BKE_context.h"
44 #include "BKE_object.h"
45 #include "BKE_report.h"
47 #include "RNA_access.h"
48 #include "RNA_define.h"
53 #include "ED_armature.h"
54 #include "ED_keyframes_draw.h"
55 #include "ED_markers.h"
56 #include "ED_screen.h"
58 #include "armature_intern.h"
60 /* **************************************************** */
61 /* == POSE 'SLIDING' TOOLS ==
63 * A) Push & Relax, Breakdowner
64 * These tools provide the animator with various capabilities
65 * for interactively controlling the spacing of poses, but also
66 * for 'pushing' and/or 'relaxing' extremes as they see fit.
69 * This tool copies elements of the selected pose to successive
70 * keyframes, allowing the animator to go back and modify the poses
71 * for some "static" pose controls, without having to repeatedly
72 * doing a "next paste" dance.
75 * This is yet to be implemented, but the idea here is to use
76 * sculpting techniques to make it easier to pose rigs by allowing
77 * rigs to be manipulated using a familiar paint-based interface.
79 /* **************************************************** */
80 /* A) Push & Relax, Breakdowner */
82 /* Temporary data shared between these operators */
83 typedef struct tPoseSlideOp {
84 Scene *scene; /* current scene */
85 ScrArea *sa; /* area that we're operating in (needed for modal()) */
86 ARegion *ar; /* region that we're operating in (needed for modal()) */
87 Object *ob; /* active object that Pose Info comes from */
88 bArmature *arm; /* armature for pose */
90 ListBase pfLinks; /* links between posechannels and f-curves */
91 DLRBT_Tree keys; /* binary tree for quicker searching for keyframes (when applicable) */
93 int cframe; /* current frame number */
94 int prevFrame; /* frame before current frame (blend-from) */
95 int nextFrame; /* frame after current frame (blend-to) */
97 int mode; /* sliding mode (ePoseSlide_Modes) */
98 int flag; /* unused for now, but can later get used for storing runtime settings.... */
100 float percentage; /* 0-1 value for determining the influence of whatever is relevant */
103 /* Pose Sliding Modes */
104 typedef enum ePoseSlide_Modes {
105 POSESLIDE_PUSH = 0, /* exaggerate the pose... */
106 POSESLIDE_RELAX, /* soften the pose... */
107 POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */
110 /* ------------------------------------ */
113 static int pose_slide_init(bContext *C, wmOperator *op, short mode)
118 /* init slide-op data */
119 pso = op->customdata = MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
121 /* get info from context */
122 pso->scene = CTX_data_scene(C);
123 pso->ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
124 pso->arm = (pso->ob) ? pso->ob->data : NULL;
125 pso->sa = CTX_wm_area(C); /* only really needed when doing modal() */
126 pso->ar = CTX_wm_region(C); /* only really needed when doing modal() */
128 pso->cframe = pso->scene->r.cfra;
131 /* set range info from property values - these may get overridden for the invoke() */
132 pso->percentage = RNA_float_get(op->ptr, "percentage");
133 pso->prevFrame = RNA_int_get(op->ptr, "prev_frame");
134 pso->nextFrame = RNA_int_get(op->ptr, "next_frame");
136 /* check the settings from the context */
137 if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
140 act = pso->ob->adt->action;
142 /* for each Pose-Channel which gets affected, get the F-Curves for that channel
143 * and set the relevant transform flags...
145 poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act);
147 /* set depsgraph flags */
148 /* make sure the lock is set OK, unlock can be accidentally saved? */
149 pso->ob->pose->flag |= POSE_LOCKED;
150 pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
152 /* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
153 * to the caller of this (usually only invoke() will do it, to make things more efficient).
155 BLI_dlrbTree_init(&pso->keys);
157 /* return status is whether we've got all the data we were requested to get */
161 /* exiting the operator - free data */
162 static void pose_slide_exit(wmOperator *op)
164 tPoseSlideOp *pso = op->customdata;
166 /* if data exists, clear its data and exit */
168 /* free the temp pchan links and their data */
169 poseAnim_mapping_free(&pso->pfLinks);
171 /* free RB-BST for keyframes (if it contained data) */
172 BLI_dlrbTree_free(&pso->keys);
174 /* free data itself */
179 op->customdata = NULL;
182 /* ------------------------------------ */
184 /* helper for apply() / reset() - refresh the data */
185 static void pose_slide_refresh(bContext *C, tPoseSlideOp *pso)
187 /* wrapper around the generic version, allowing us to add some custom stuff later still */
188 poseAnim_mapping_refresh(C, pso->scene, pso->ob);
191 /* helper for apply() - perform sliding for some value */
192 static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, float *val)
194 float cframe = (float)pso->cframe;
198 /* get keyframe values for endpoint poses to blend with */
200 sVal = evaluate_fcurve(fcu, (float)pso->prevFrame);
202 eVal = evaluate_fcurve(fcu, (float)pso->nextFrame);
204 /* calculate the relative weights of the endpoints */
205 if (pso->mode == POSESLIDE_BREAKDOWN) {
206 /* get weights from the percentage control */
207 w1 = pso->percentage; /* this must come second */
208 w2 = 1.0f - w1; /* this must come first */
211 /* - these weights are derived from the relative distance of these
212 * poses from the current frame
213 * - they then get normalized so that they only sum up to 1
217 w1 = cframe - (float)pso->prevFrame;
218 w2 = (float)pso->nextFrame - cframe;
225 /* depending on the mode, calculate the new value
226 * - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
227 * since multiplication in another order would decrease the value the current frame is closer to
230 case POSESLIDE_PUSH: /* make the current pose more pronounced */
232 /* perform a weighted average here, favoring the middle pose
233 * - numerator should be larger than denominator to 'expand' the result
234 * - perform this weighting a number of times given by the percentage...
236 int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */
238 while (iters-- > 0) {
239 (*val) = (-((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f;
243 case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
245 /* perform a weighted average here, favoring the middle pose
246 * - numerator should be smaller than denominator to 'relax' the result
247 * - perform this weighting a number of times given by the percentage...
249 int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */
251 while (iters-- > 0) {
252 (*val) = ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f;
256 case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
258 /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
259 /* TODO: make this use some kind of spline interpolation instead? */
260 (*val) = ((sVal * w2) + (eVal * w1));
266 /* helper for apply() - perform sliding for some 3-element vector */
267 static void pose_slide_apply_vec3(tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[])
272 /* get the path to use... */
273 path = BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
275 /* using this path, find each matching F-Curve for the variables we're interested in */
276 while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
277 FCurve *fcu = (FCurve *)ld->data;
279 /* just work on these channels one by one... there's no interaction between values */
280 BLI_assert(fcu->array_index < 3);
281 pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]);
284 /* free the temp path we got */
288 /* helper for apply() - perform sliding for custom properties */
289 static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
291 PointerRNA ptr = {{NULL}};
293 int len = strlen(pfl->pchan_path);
295 /* setup pointer RNA for resolving paths */
296 RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
298 /* custom properties are just denoted using ["..."][etc.] after the end of the base path,
299 * so just check for opening pair after the end of the path
301 for (ld = pfl->fcurves.first; ld; ld = ld->next) {
302 FCurve *fcu = (FCurve *)ld->data;
305 if (fcu->rna_path == NULL)
308 /* do we have a match?
309 * - bPtr is the RNA Path with the standard part chopped off
310 * - pPtr is the chunk of the path which is left over
312 bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
313 pPtr = strstr(bPtr, "[\""); /* dummy " for texteditor bugs */
316 /* use RNA to try and get a handle on this property, then, assuming that it is just
317 * numerical, try and grab the value as a float for temp editing before setting back
319 PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
322 switch (RNA_property_type(prop)) {
325 float tval = RNA_property_float_get(&ptr, prop);
326 pose_slide_apply_val(pso, fcu, &tval);
327 RNA_property_float_set(&ptr, prop, tval);
334 float tval = (float)RNA_property_int_get(&ptr, prop);
335 pose_slide_apply_val(pso, fcu, &tval);
336 RNA_property_int_set(&ptr, prop, (int)tval);
341 //printf("Cannot Pose Slide non-numerical property\n");
349 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
350 static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
352 FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL;
353 bPoseChannel *pchan = pfl->pchan;
358 /* get the path to use - this should be quaternion rotations only (needs care) */
359 path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
361 /* get the current frame number */
362 cframe = (float)pso->cframe;
364 /* using this path, find each matching F-Curve for the variables we're interested in */
365 while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
366 FCurve *fcu = (FCurve *)ld->data;
368 /* assign this F-Curve to one of the relevant pointers... */
369 switch (fcu->array_index) {
385 /* only if all channels exist, proceed */
386 if (fcu_w && fcu_x && fcu_y && fcu_z) {
387 float quat_prev[4], quat_next[4];
390 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame);
391 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame);
392 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame);
393 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame);
395 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame);
396 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame);
397 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame);
398 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame);
400 /* perform blending */
401 if (pso->mode == POSESLIDE_BREAKDOWN) {
402 /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
403 interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
405 else if (pso->mode == POSESLIDE_PUSH) {
406 float quat_diff[4], quat_orig[4];
408 /* calculate the delta transform from the previous to the current */
409 /* TODO: investigate ways to favour one transform more? */
410 sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
412 /* make a copy of the original rotation */
413 copy_qt_qt(quat_orig, pchan->quat);
415 /* increase the original by the delta transform, by an amount determined by percentage */
416 add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
419 float quat_interp[4], quat_orig[4];
420 int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */
422 /* perform this blending several times until a satisfactory result is reached */
423 while (iters-- > 0) {
424 /* calculate the interpolation between the endpoints */
425 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe - pso->prevFrame) / (pso->nextFrame - pso->prevFrame));
427 /* make a copy of the original rotation */
428 copy_qt_qt(quat_orig, pchan->quat);
430 /* tricky interpolations - blending between original and new */
431 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f / 6.0f);
436 /* free the path now */
440 /* apply() - perform the pose sliding based on weighting various poses */
441 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
443 tPChanFCurveLink *pfl;
445 /* sanitise the frame ranges */
446 if (pso->prevFrame == pso->nextFrame) {
447 /* move out one step either side */
452 /* for each link, handle each set of transforms */
453 for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
454 /* valid transforms for each PoseChannel should have been noted already
455 * - sliding the pose should be a straightforward exercise for location+rotation,
456 * but rotations get more complicated since we may want to use quaternion blending
457 * for quaternions instead...
459 bPoseChannel *pchan = pfl->pchan;
461 if (pchan->flag & POSE_LOC) {
462 /* calculate these for the 'location' vector, and use location curves */
463 pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
466 if (pchan->flag & POSE_SIZE) {
467 /* calculate these for the 'scale' vector, and use scale curves */
468 pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
471 if (pchan->flag & POSE_ROT) {
472 /* everything depends on the rotation mode */
473 if (pchan->rotmode > 0) {
474 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
475 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
477 else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
478 /* TODO: need to figure out how to do this! */
481 /* quaternions - use quaternion blending */
482 pose_slide_apply_quat(pso, pfl);
487 /* not strictly a transform, but contributes to the pose produced in many rigs */
488 pose_slide_apply_props(pso, pfl);
492 /* depsgraph updates + redraws */
493 pose_slide_refresh(C, pso);
496 /* perform autokeyframing after changes were made + confirmed */
497 static void pose_slide_autoKeyframe(bContext *C, tPoseSlideOp *pso)
499 /* wrapper around the generic call */
500 poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe);
503 /* reset changes made to current pose */
504 static void pose_slide_reset(tPoseSlideOp *pso)
506 /* wrapper around the generic call, so that custom stuff can be added later */
507 poseAnim_mapping_reset(&pso->pfLinks);
510 /* ------------------------------------ */
512 /* draw percentage indicator in header */
513 static void pose_slide_draw_status(tPoseSlideOp *pso)
520 strcpy(mode_str, "Push Pose");
522 case POSESLIDE_RELAX:
523 strcpy(mode_str, "Relax Pose");
525 case POSESLIDE_BREAKDOWN:
526 strcpy(mode_str, "Breakdown");
531 strcpy(mode_str, "Sliding-Tool");
535 BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(pso->percentage * 100.0f));
536 ED_area_headerprint(pso->sa, status_str);
539 /* common code for invoke() methods */
540 static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
542 tPChanFCurveLink *pfl;
543 AnimData *adt = pso->ob->adt;
544 wmWindow *win = CTX_wm_window(C);
546 /* for each link, add all its keyframes to the search tree */
547 for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
550 /* do this for each F-Curve */
551 for (ld = pfl->fcurves.first; ld; ld = ld->next) {
552 FCurve *fcu = (FCurve *)ld->data;
553 fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
557 /* consolidate these keyframes, and figure out the nearest ones */
558 BLI_dlrbTree_linkedlist_sync(&pso->keys);
560 /* cancel if no keyframes found... */
561 if (pso->keys.root) {
563 float cframe = (float)pso->cframe;
565 /* firstly, check if the current frame is a keyframe... */
566 ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
569 /* current frame is not a keyframe, so search */
570 ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
571 ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
573 /* new set the frames */
575 pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1);
576 RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
578 pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1);
579 RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
582 /* current frame itself is a keyframe, so just take keyframes on either side */
584 pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1);
585 RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
587 pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1);
588 RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
592 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between");
594 return OPERATOR_CANCELLED;
597 /* initial apply for operator... */
598 /* TODO: need to calculate percentage for initial round too... */
599 pose_slide_apply(C, pso);
601 /* depsgraph updates + redraws */
602 pose_slide_refresh(C, pso);
604 /* set cursor to indicate modal */
605 WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR);
608 pose_slide_draw_status(pso);
610 /* add a modal handler for this operator */
611 WM_event_add_modal_handler(C, op);
612 return OPERATOR_RUNNING_MODAL;
615 /* common code for modal() */
616 static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
618 tPoseSlideOp *pso = op->customdata;
619 wmWindow *win = CTX_wm_window(C);
621 switch (event->type) {
622 case LEFTMOUSE: /* confirm */
625 /* return to normal cursor and header status */
626 ED_area_headerprint(pso->sa, NULL);
627 WM_cursor_modal_restore(win);
629 /* insert keyframes as required... */
630 pose_slide_autoKeyframe(C, pso);
634 return OPERATOR_FINISHED;
637 case ESCKEY: /* cancel */
640 /* return to normal cursor and header status */
641 ED_area_headerprint(pso->sa, NULL);
642 WM_cursor_modal_restore(win);
644 /* reset transforms back to original state */
645 pose_slide_reset(pso);
647 /* depsgraph updates + redraws */
648 pose_slide_refresh(C, pso);
650 /* clean up temp data */
654 return OPERATOR_CANCELLED;
657 case MOUSEMOVE: /* calculate new position */
659 /* calculate percentage based on position of mouse (we only use x-axis for now.
660 * since this is more convenient for users to do), and store new percentage value
662 pso->percentage = (event->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
663 RNA_float_set(op->ptr, "percentage", pso->percentage);
665 /* update percentage indicator in header */
666 pose_slide_draw_status(pso);
668 /* reset transforms (to avoid accumulation errors) */
669 pose_slide_reset(pso);
672 pose_slide_apply(C, pso);
675 default: /* unhandled event (maybe it was some view manip? */
676 /* allow to pass through */
677 return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
680 /* still running... */
681 return OPERATOR_RUNNING_MODAL;
684 /* common code for cancel() */
685 static int pose_slide_cancel(bContext *UNUSED(C), wmOperator *op)
687 /* cleanup and done */
689 return OPERATOR_CANCELLED;
692 /* common code for exec() methods */
693 static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
695 /* settings should have been set up ok for applying, so just apply! */
696 pose_slide_apply(C, pso);
698 /* insert keyframes if needed */
699 pose_slide_autoKeyframe(C, pso);
701 /* cleanup and done */
704 return OPERATOR_FINISHED;
707 /* common code for defining RNA properties */
708 static void pose_slide_opdef_properties(wmOperatorType *ot)
710 RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame", 0, 50);
711 RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame", 0, 50);
712 RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
715 /* ------------------------------------ */
717 /* invoke() - for 'push' mode */
718 static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
722 /* initialize data */
723 if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
725 return OPERATOR_CANCELLED;
728 pso = op->customdata;
730 /* do common setup work */
731 return pose_slide_invoke_common(C, op, pso);
734 /* exec() - for push */
735 static int pose_slide_push_exec(bContext *C, wmOperator *op)
739 /* initialize data (from RNA-props) */
740 if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
742 return OPERATOR_CANCELLED;
745 pso = op->customdata;
747 /* do common exec work */
748 return pose_slide_exec_common(C, op, pso);
751 void POSE_OT_push(wmOperatorType *ot)
754 ot->name = "Push Pose";
755 ot->idname = "POSE_OT_push";
756 ot->description = "Exaggerate the current pose";
759 ot->exec = pose_slide_push_exec;
760 ot->invoke = pose_slide_push_invoke;
761 ot->modal = pose_slide_modal;
762 ot->cancel = pose_slide_cancel;
763 ot->poll = ED_operator_posemode;
766 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
769 pose_slide_opdef_properties(ot);
772 /* ........................ */
774 /* invoke() - for 'relax' mode */
775 static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
779 /* initialize data */
780 if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
782 return OPERATOR_CANCELLED;
785 pso = op->customdata;
787 /* do common setup work */
788 return pose_slide_invoke_common(C, op, pso);
791 /* exec() - for relax */
792 static int pose_slide_relax_exec(bContext *C, wmOperator *op)
796 /* initialize data (from RNA-props) */
797 if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
799 return OPERATOR_CANCELLED;
802 pso = op->customdata;
804 /* do common exec work */
805 return pose_slide_exec_common(C, op, pso);
808 void POSE_OT_relax(wmOperatorType *ot)
811 ot->name = "Relax Pose";
812 ot->idname = "POSE_OT_relax";
813 ot->description = "Make the current pose more similar to its surrounding ones";
816 ot->exec = pose_slide_relax_exec;
817 ot->invoke = pose_slide_relax_invoke;
818 ot->modal = pose_slide_modal;
819 ot->cancel = pose_slide_cancel;
820 ot->poll = ED_operator_posemode;
823 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
826 pose_slide_opdef_properties(ot);
829 /* ........................ */
831 /* invoke() - for 'breakdown' mode */
832 static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
836 /* initialize data */
837 if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
839 return OPERATOR_CANCELLED;
842 pso = op->customdata;
844 /* do common setup work */
845 return pose_slide_invoke_common(C, op, pso);
848 /* exec() - for breakdown */
849 static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
853 /* initialize data (from RNA-props) */
854 if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
856 return OPERATOR_CANCELLED;
859 pso = op->customdata;
861 /* do common exec work */
862 return pose_slide_exec_common(C, op, pso);
865 void POSE_OT_breakdown(wmOperatorType *ot)
868 ot->name = "Pose Breakdowner";
869 ot->idname = "POSE_OT_breakdown";
870 ot->description = "Create a suitable breakdown pose on the current frame";
873 ot->exec = pose_slide_breakdown_exec;
874 ot->invoke = pose_slide_breakdown_invoke;
875 ot->modal = pose_slide_modal;
876 ot->cancel = pose_slide_cancel;
877 ot->poll = ED_operator_posemode;
880 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
883 pose_slide_opdef_properties(ot);
886 /* **************************************************** */
887 /* B) Pose Propagate */
889 /* "termination conditions" - i.e. when we stop */
890 typedef enum ePosePropagate_Termination {
891 /* stop after the current hold ends */
892 POSE_PROPAGATE_SMART_HOLDS = 0,
893 /* only do on the last keyframe */
894 POSE_PROPAGATE_LAST_KEY,
895 /* stop after the next keyframe */
896 POSE_PROPAGATE_NEXT_KEY,
897 /* stop after the specified frame */
898 POSE_PROPAGATE_BEFORE_FRAME,
899 /* stop when we run out of keyframes */
900 POSE_PROPAGATE_BEFORE_END,
902 /* only do on the frames where markers are selected */
903 POSE_PROPAGATE_SELECTED_MARKERS
904 } ePosePropagate_Termination;
906 /* termination data needed for some modes - assumes only one of these entries will be needed at a time */
907 typedef union tPosePropagate_ModeData {
908 /* smart holds + before frame: frame number to stop on */
911 /* selected markers: listbase for CfraElem's marking these frames */
912 ListBase sel_markers;
913 } tPosePropagate_ModeData;
915 /* --------------------------------- */
917 /* get frame on which the "hold" for the bone ends
918 * XXX: this may not really work that well if a bone moves on some channels and not others
919 * if this happens to be a major issue, scrap this, and just make this happen
920 * independently per F-Curve
922 static float pose_propagate_get_boneHoldEndFrame(Object *ob, tPChanFCurveLink *pfl, float startFrame)
924 DLRBT_Tree keys, blocks;
927 AnimData *adt = ob->adt;
929 float endFrame = startFrame;
931 /* set up optimized data-structures for searching for relevant keyframes + holds */
932 BLI_dlrbTree_init(&keys);
933 BLI_dlrbTree_init(&blocks);
935 for (ld = pfl->fcurves.first; ld; ld = ld->next) {
936 FCurve *fcu = (FCurve *)ld->data;
937 fcurve_to_keylist(adt, fcu, &keys, &blocks);
940 BLI_dlrbTree_linkedlist_sync(&keys);
941 BLI_dlrbTree_linkedlist_sync(&blocks);
943 /* find the long keyframe (i.e. hold), and hence obtain the endFrame value
944 * - the best case would be one that starts on the frame itself
946 ab = (ActKeyBlock *)BLI_dlrbTree_search_exact(&blocks, compare_ab_cfraPtr, &startFrame);
948 if (actkeyblock_is_valid(ab, &keys) == 0) {
949 /* There are only two cases for no-exact match:
950 * 1) the current frame is just before another key but not on a key itself
951 * 2) the current frame is on a key, but that key doesn't link to the next
953 * If we've got the first case, then we can search for another block,
954 * otherwise forget it, as we'd be overwriting some valid data.
956 if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame) == NULL) {
957 /* we've got case 1, so try the one after */
958 ab = (ActKeyBlock *)BLI_dlrbTree_search_next(&blocks, compare_ab_cfraPtr, &startFrame);
960 if (actkeyblock_is_valid(ab, &keys) == 0) {
961 /* try the block before this frame then as last resort */
962 ab = (ActKeyBlock *)BLI_dlrbTree_search_prev(&blocks, compare_ab_cfraPtr, &startFrame);
964 /* whatever happens, stop searching now... */
965 if (actkeyblock_is_valid(ab, &keys) == 0) {
966 /* restrict range to just the frame itself
967 * i.e. everything is in motion, so no holds to safely overwrite
974 /* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */
979 /* check if we can go any further than we've already gone */
981 /* go to next if it is also valid and meets "extension" criteria */
983 ActKeyBlock *abn = (ActKeyBlock *)ab->next;
986 if (actkeyblock_is_valid(abn, &keys) == 0)
988 /* should start on the same frame that the last ended on */
989 if (ab->end != abn->start)
991 /* should have the same number of curves */
992 if (ab->totcurve != abn->totcurve)
994 /* should have the same value
995 * XXX: this may be a bit fuzzy on larger data sets, so be careful
997 if (ab->val != abn->val)
1000 /* we can extend the bounds to the end of this "next" block now */
1004 /* end frame can now take the value of the end of the block */
1008 /* free temp memory */
1009 BLI_dlrbTree_free(&keys);
1010 BLI_dlrbTree_free(&blocks);
1012 /* return the end frame we've found */
1016 /* get reference value from F-Curve using RNA */
1017 static short pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
1019 PointerRNA id_ptr, ptr;
1021 short found = FALSE;
1023 /* base pointer is always the object -> id_ptr */
1024 RNA_id_pointer_create(&ob->id, &id_ptr);
1026 /* resolve the property... */
1027 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
1028 if (RNA_property_array_check(prop)) {
1030 if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
1032 switch (RNA_property_type(prop)) {
1034 *value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
1037 *value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
1040 *value = RNA_property_float_get_index(&ptr, prop, fcu->array_index);
1051 switch (RNA_property_type(prop)) {
1053 *value = (float)RNA_property_boolean_get(&ptr, prop);
1056 *value = (float)RNA_property_int_get(&ptr, prop);
1059 *value = (float)RNA_property_enum_get(&ptr, prop);
1062 *value = RNA_property_float_get(&ptr, prop);
1074 /* propagate just works along each F-Curve in turn */
1075 static void pose_propagate_fcurve(wmOperator *op, Object *ob, FCurve *fcu,
1076 float startFrame, tPosePropagate_ModeData modeData)
1078 const int mode = RNA_enum_get(op->ptr, "mode");
1081 float refVal = 0.0f;
1086 /* skip if no keyframes to edit */
1087 if ((fcu->bezt == NULL) || (fcu->totvert < 2))
1090 /* find the reference value from bones directly, which means that the user
1091 * doesn't need to firstly keyframe the pose (though this doesn't mean that
1092 * they can't either)
1094 if (!pose_propagate_get_refVal(ob, fcu, &refVal))
1097 /* find the first keyframe to start propagating from
1098 * - if there's a keyframe on the current frame, we probably want to save this value there too
1099 * since it may be as of yet unkeyed
1100 * - if starting before the starting frame, don't touch the key, as it may have had some valid
1103 match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
1105 if (fcu->bezt[match].vec[1][0] < startFrame)
1110 for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
1111 /* additional termination conditions based on the operator 'mode' property go here... */
1112 if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
1113 /* stop if keyframe is outside the accepted range */
1114 if (bezt->vec[1][0] > modeData.end_frame)
1117 else if (mode == POSE_PROPAGATE_NEXT_KEY) {
1118 /* stop after the first keyframe has been processed */
1122 else if (mode == POSE_PROPAGATE_LAST_KEY) {
1123 /* only affect this frame if it will be the last one */
1124 if (i != (fcu->totvert - 1))
1127 else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1128 /* only allow if there's a marker on this frame */
1129 CfraElem *ce = NULL;
1131 /* stop on matching marker if there is one */
1132 for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
1133 if (ce->cfra == (int)(floor(bezt->vec[1][0] + 0.5f)))
1137 /* skip this keyframe if no marker */
1142 /* just flatten handles, since values will now be the same either side... */
1143 /* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */
1144 bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
1146 /* select keyframe to indicate that it's been changed */
1152 /* --------------------------------- */
1154 static int pose_propagate_exec(bContext *C, wmOperator *op)
1156 Scene *scene = CTX_data_scene(C);
1157 Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
1158 bAction *act = (ob && ob->adt) ? ob->adt->action : NULL;
1160 ListBase pflinks = {NULL, NULL};
1161 tPChanFCurveLink *pfl;
1163 tPosePropagate_ModeData modeData;
1164 const int mode = RNA_enum_get(op->ptr, "mode");
1168 BKE_report(op->reports, RPT_ERROR, "No object to propagate poses for");
1169 return OPERATOR_CANCELLED;
1172 BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to");
1173 return OPERATOR_CANCELLED;
1176 /* isolate F-Curves related to the selected bones */
1177 poseAnim_mapping_get(C, &pflinks, ob, act);
1179 /* mode-specific data preprocessing (requiring no access to curves) */
1180 if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1181 /* get a list of selected markers */
1182 ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
1185 /* assume everything else wants endFrame */
1186 modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
1189 /* for each bone, perform the copying required */
1190 for (pfl = pflinks.first; pfl; pfl = pfl->next) {
1193 /* mode-specific data preprocessing (requiring access to all curves) */
1194 if (mode == POSE_PROPAGATE_SMART_HOLDS) {
1195 /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
1196 * from the keyframe that occurs after the current frame
1198 modeData.end_frame = pose_propagate_get_boneHoldEndFrame(ob, pfl, (float)CFRA);
1201 /* go through propagating pose to keyframes, curve by curve */
1202 for (ld = pfl->fcurves.first; ld; ld = ld->next)
1203 pose_propagate_fcurve(op, ob, (FCurve *)ld->data, (float)CFRA, modeData);
1206 /* free temp data */
1207 poseAnim_mapping_free(&pflinks);
1209 if (mode == POSE_PROPAGATE_SELECTED_MARKERS)
1210 BLI_freelistN(&modeData.sel_markers);
1212 /* updates + notifiers */
1213 poseAnim_mapping_refresh(C, scene, ob);
1215 return OPERATOR_FINISHED;
1218 /* --------------------------------- */
1220 void POSE_OT_propagate(wmOperatorType *ot)
1222 static EnumPropertyItem terminate_items[] = {
1223 {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behavior)"},
1224 {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", "Propagate pose to first keyframe following the current frame only"},
1225 {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", "Propagate pose to the last keyframe only (i.e. making action cyclic)"},
1226 {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"},
1227 {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", "Propagate pose to all keyframes from current frame until no more are found"},
1228 {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"},
1229 {0, NULL, 0, NULL, NULL}};
1232 ot->name = "Propagate Pose";
1233 ot->idname = "POSE_OT_propagate";
1234 ot->description = "Copy selected aspects of the current pose to subsequent poses already keyframed";
1237 ot->exec = pose_propagate_exec;
1238 ot->poll = ED_operator_posemode; /* XXX: needs selected bones! */
1241 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1244 /* TODO: add "fade out" control for tapering off amount of propagation as time goes by? */
1245 ot->prop = RNA_def_enum(ot->srna, "mode", terminate_items, POSE_PROPAGATE_SMART_HOLDS, "Terminate Mode", "Method used to determine when to stop propagating pose to keyframes");
1246 RNA_def_float(ot->srna, "end_frame", 250.0, FLT_MIN, FLT_MAX, "End Frame", "Frame to stop propagating frames to (for 'Before Frame' mode)", 1.0, 250.0);
1249 /* **************************************************** */