|
Blender
V2.59
|
00001 /* 00002 * $Id: poseSlide.c 36297 2011-04-23 11:09:24Z aligorith $ 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software Foundation, 00018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 * 00020 * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung 00021 * This is a new part of Blender 00022 * 00023 * Contributor(s): Joshua Leung 00024 * 00025 * ***** END GPL LICENSE BLOCK ***** 00026 */ 00027 00033 #include <stdlib.h> 00034 #include <stdio.h> 00035 #include <stddef.h> 00036 #include <string.h> 00037 #include <math.h> 00038 #include <float.h> 00039 00040 #include "MEM_guardedalloc.h" 00041 00042 #include "BLI_math.h" 00043 #include "BLI_blenlib.h" 00044 #include "BLI_dynstr.h" 00045 #include "BLI_dlrbTree.h" 00046 #include "BLI_utildefines.h" 00047 00048 #include "DNA_anim_types.h" 00049 #include "DNA_armature_types.h" 00050 #include "DNA_object_types.h" 00051 #include "DNA_scene_types.h" 00052 00053 #include "BKE_fcurve.h" 00054 00055 #include "BKE_context.h" 00056 #include "BKE_report.h" 00057 00058 #include "RNA_access.h" 00059 #include "RNA_define.h" 00060 00061 #include "WM_api.h" 00062 #include "WM_types.h" 00063 00064 #include "ED_armature.h" 00065 #include "ED_keyframes_draw.h" 00066 #include "ED_markers.h" 00067 #include "ED_screen.h" 00068 00069 #include "armature_intern.h" 00070 00071 /* **************************************************** */ 00072 /* == POSE 'SLIDING' TOOLS == 00073 * 00074 * A) Push & Relax, Breakdowner 00075 * These tools provide the animator with various capabilities 00076 * for interactively controlling the spacing of poses, but also 00077 * for 'pushing' and/or 'relaxing' extremes as they see fit. 00078 * 00079 * B) Propagate 00080 * This tool copies elements of the selected pose to successive 00081 * keyframes, allowing the animator to go back and modify the poses 00082 * for some "static" pose controls, without having to repeatedly 00083 * doing a "next paste" dance. 00084 * 00085 * C) Pose Sculpting 00086 * This is yet to be implemented, but the idea here is to use 00087 * sculpting techniques to make it easier to pose rigs by allowing 00088 * rigs to be manipulated using a familiar paint-based interface. 00089 */ 00090 /* **************************************************** */ 00091 /* A) Push & Relax, Breakdowner */ 00092 00093 /* Temporary data shared between these operators */ 00094 typedef struct tPoseSlideOp { 00095 Scene *scene; /* current scene */ 00096 ARegion *ar; /* region that we're operating in (needed for */ 00097 Object *ob; /* active object that Pose Info comes from */ 00098 bArmature *arm; /* armature for pose */ 00099 00100 ListBase pfLinks; /* links between posechannels and f-curves */ 00101 DLRBT_Tree keys; /* binary tree for quicker searching for keyframes (when applicable) */ 00102 00103 int cframe; /* current frame number */ 00104 int prevFrame; /* frame before current frame (blend-from) */ 00105 int nextFrame; /* frame after current frame (blend-to) */ 00106 00107 int mode; /* sliding mode (ePoseSlide_Modes) */ 00108 int flag; // unused for now, but can later get used for storing runtime settings.... 00109 00110 float percentage; /* 0-1 value for determining the influence of whatever is relevant */ 00111 } tPoseSlideOp; 00112 00113 /* Pose Sliding Modes */ 00114 typedef enum ePoseSlide_Modes { 00115 POSESLIDE_PUSH = 0, /* exaggerate the pose... */ 00116 POSESLIDE_RELAX, /* soften the pose... */ 00117 POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */ 00118 } ePoseSlide_Modes; 00119 00120 /* ------------------------------------ */ 00121 00122 /* operator init */ 00123 static int pose_slide_init (bContext *C, wmOperator *op, short mode) 00124 { 00125 tPoseSlideOp *pso; 00126 bAction *act= NULL; 00127 00128 /* init slide-op data */ 00129 pso= op->customdata= MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp"); 00130 00131 /* get info from context */ 00132 pso->scene= CTX_data_scene(C); 00133 pso->ob= ED_object_pose_armature(CTX_data_active_object(C)); 00134 pso->arm= (pso->ob)? pso->ob->data : NULL; 00135 pso->ar= CTX_wm_region(C); /* only really needed when doing modal() */ 00136 00137 pso->cframe= pso->scene->r.cfra; 00138 pso->mode= mode; 00139 00140 /* set range info from property values - these may get overridden for the invoke() */ 00141 pso->percentage= RNA_float_get(op->ptr, "percentage"); 00142 pso->prevFrame= RNA_int_get(op->ptr, "prev_frame"); 00143 pso->nextFrame= RNA_int_get(op->ptr, "next_frame"); 00144 00145 /* check the settings from the context */ 00146 if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action)) 00147 return 0; 00148 else 00149 act= pso->ob->adt->action; 00150 00151 /* for each Pose-Channel which gets affected, get the F-Curves for that channel 00152 * and set the relevant transform flags... 00153 */ 00154 poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act); 00155 00156 /* set depsgraph flags */ 00157 /* make sure the lock is set OK, unlock can be accidentally saved? */ 00158 pso->ob->pose->flag |= POSE_LOCKED; 00159 pso->ob->pose->flag &= ~POSE_DO_UNLOCK; 00160 00161 /* do basic initialise of RB-BST used for finding keyframes, but leave the filling of it up 00162 * to the caller of this (usually only invoke() will do it, to make things more efficient). 00163 */ 00164 BLI_dlrbTree_init(&pso->keys); 00165 00166 /* return status is whether we've got all the data we were requested to get */ 00167 return 1; 00168 } 00169 00170 /* exiting the operator - free data */ 00171 static void pose_slide_exit(wmOperator *op) 00172 { 00173 tPoseSlideOp *pso= op->customdata; 00174 00175 /* if data exists, clear its data and exit */ 00176 if (pso) { 00177 /* free the temp pchan links and their data */ 00178 poseAnim_mapping_free(&pso->pfLinks); 00179 00180 /* free RB-BST for keyframes (if it contained data) */ 00181 BLI_dlrbTree_free(&pso->keys); 00182 00183 /* free data itself */ 00184 MEM_freeN(pso); 00185 } 00186 00187 /* cleanup */ 00188 op->customdata= NULL; 00189 } 00190 00191 /* ------------------------------------ */ 00192 00193 /* helper for apply() / reset() - refresh the data */ 00194 static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso) 00195 { 00196 /* wrapper around the generic version, allowing us to add some custom stuff later still */ 00197 poseAnim_mapping_refresh(C, pso->scene, pso->ob); 00198 } 00199 00200 /* helper for apply() - perform sliding for some value */ 00201 static void pose_slide_apply_val (tPoseSlideOp *pso, FCurve *fcu, float *val) 00202 { 00203 float cframe = (float)pso->cframe; 00204 float sVal, eVal; 00205 float w1, w2; 00206 00207 /* get keyframe values for endpoint poses to blend with */ 00208 /* previous/start */ 00209 sVal= evaluate_fcurve(fcu, (float)pso->prevFrame); 00210 /* next/end */ 00211 eVal= evaluate_fcurve(fcu, (float)pso->nextFrame); 00212 00213 /* calculate the relative weights of the endpoints */ 00214 if (pso->mode == POSESLIDE_BREAKDOWN) { 00215 /* get weights from the percentage control */ 00216 w1= pso->percentage; /* this must come second */ 00217 w2= 1.0f - w1; /* this must come first */ 00218 } 00219 else { 00220 /* - these weights are derived from the relative distance of these 00221 * poses from the current frame 00222 * - they then get normalised so that they only sum up to 1 00223 */ 00224 float wtot; 00225 00226 w1 = cframe - (float)pso->prevFrame; 00227 w2 = (float)pso->nextFrame - cframe; 00228 00229 wtot = w1 + w2; 00230 w1 = (w1/wtot); 00231 w2 = (w2/wtot); 00232 } 00233 00234 /* depending on the mode, calculate the new value 00235 * - in all of these, the start+end values are multiplied by w2 and w1 (respectively), 00236 * since multiplication in another order would decrease the value the current frame is closer to 00237 */ 00238 switch (pso->mode) { 00239 case POSESLIDE_PUSH: /* make the current pose more pronounced */ 00240 { 00241 /* perform a weighted average here, favouring the middle pose 00242 * - numerator should be larger than denominator to 'expand' the result 00243 * - perform this weighting a number of times given by the percentage... 00244 */ 00245 int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed 00246 00247 while (iters-- > 0) { 00248 (*val)= ( -((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; 00249 } 00250 } 00251 break; 00252 00253 case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ 00254 { 00255 /* perform a weighted average here, favouring the middle pose 00256 * - numerator should be smaller than denominator to 'relax' the result 00257 * - perform this weighting a number of times given by the percentage... 00258 */ 00259 int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed 00260 00261 while (iters-- > 0) { 00262 (*val)= ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f; 00263 } 00264 } 00265 break; 00266 00267 case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ 00268 { 00269 /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */ 00270 // TODO: make this use some kind of spline interpolation instead? 00271 (*val)= ((sVal * w2) + (eVal * w1)); 00272 } 00273 break; 00274 } 00275 } 00276 00277 /* helper for apply() - perform sliding for some 3-element vector */ 00278 static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[]) 00279 { 00280 LinkData *ld=NULL; 00281 char *path=NULL; 00282 00283 /* get the path to use... */ 00284 path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName); 00285 00286 /* using this path, find each matching F-Curve for the variables we're interested in */ 00287 while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) { 00288 FCurve *fcu= (FCurve *)ld->data; 00289 00290 /* just work on these channels one by one... there's no interaction between values */ 00291 pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]); 00292 } 00293 00294 /* free the temp path we got */ 00295 MEM_freeN(path); 00296 } 00297 00298 /* helper for apply() - perform sliding for custom properties */ 00299 static void pose_slide_apply_props (tPoseSlideOp *pso, tPChanFCurveLink *pfl) 00300 { 00301 PointerRNA ptr = {{NULL}}; 00302 LinkData *ld; 00303 int len = strlen(pfl->pchan_path); 00304 00305 /* setup pointer RNA for resolving paths */ 00306 RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr); 00307 00308 /* custom properties are just denoted using ["..."][etc.] after the end of the base path, 00309 * so just check for opening pair after the end of the path 00310 */ 00311 for (ld = pfl->fcurves.first; ld; ld = ld->next) { 00312 FCurve *fcu = (FCurve *)ld->data; 00313 char *bPtr, *pPtr; 00314 00315 if (fcu->rna_path == NULL) 00316 continue; 00317 00318 /* do we have a match? 00319 * - bPtr is the RNA Path with the standard part chopped off 00320 * - pPtr is the chunk of the path which is left over 00321 */ 00322 bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len; 00323 pPtr = strstr(bPtr, "[\""); /* dummy " for texteditor bugs */ 00324 00325 if (pPtr) { 00326 /* use RNA to try and get a handle on this property, then, assuming that it is just 00327 * numerical, try and grab the value as a float for temp editing before setting back 00328 */ 00329 PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr); 00330 00331 if (prop) { 00332 switch (RNA_property_type(prop)) { 00333 case PROP_FLOAT: 00334 { 00335 float tval = RNA_property_float_get(&ptr, prop); 00336 pose_slide_apply_val(pso, fcu, &tval); 00337 RNA_property_float_set(&ptr, prop, tval); 00338 } 00339 break; 00340 case PROP_BOOLEAN: 00341 case PROP_ENUM: 00342 case PROP_INT: 00343 { 00344 float tval = (float)RNA_property_int_get(&ptr, prop); 00345 pose_slide_apply_val(pso, fcu, &tval); 00346 RNA_property_int_set(&ptr, prop, (int)tval); 00347 } 00348 break; 00349 default: 00350 /* cannot handle */ 00351 //printf("Cannot Pose Slide non-numerical property\n"); 00352 break; 00353 } 00354 } 00355 } 00356 } 00357 } 00358 00359 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */ 00360 static void pose_slide_apply_quat (tPoseSlideOp *pso, tPChanFCurveLink *pfl) 00361 { 00362 FCurve *fcu_w=NULL, *fcu_x=NULL, *fcu_y=NULL, *fcu_z=NULL; 00363 bPoseChannel *pchan= pfl->pchan; 00364 LinkData *ld=NULL; 00365 char *path=NULL; 00366 float cframe; 00367 00368 /* get the path to use - this should be quaternion rotations only (needs care) */ 00369 path= BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion"); 00370 00371 /* get the current frame number */ 00372 cframe= (float)pso->cframe; 00373 00374 /* using this path, find each matching F-Curve for the variables we're interested in */ 00375 while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) { 00376 FCurve *fcu= (FCurve *)ld->data; 00377 00378 /* assign this F-Curve to one of the relevant pointers... */ 00379 switch (fcu->array_index) { 00380 case 3: /* z */ 00381 fcu_z= fcu; 00382 break; 00383 case 2: /* y */ 00384 fcu_y= fcu; 00385 break; 00386 case 1: /* x */ 00387 fcu_x= fcu; 00388 break; 00389 case 0: /* w */ 00390 fcu_w= fcu; 00391 break; 00392 } 00393 } 00394 00395 /* only if all channels exist, proceed */ 00396 if (fcu_w && fcu_x && fcu_y && fcu_z) { 00397 float quat_prev[4], quat_next[4]; 00398 00399 /* get 2 quats */ 00400 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame); 00401 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame); 00402 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame); 00403 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame); 00404 00405 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame); 00406 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame); 00407 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame); 00408 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame); 00409 00410 /* perform blending */ 00411 if (pso->mode == POSESLIDE_BREAKDOWN) { 00412 /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */ 00413 interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage); 00414 } 00415 else if (pso->mode == POSESLIDE_PUSH) { 00416 float quat_diff[4], quat_orig[4]; 00417 00418 /* calculate the delta transform from the previous to the current */ 00419 // TODO: investigate ways to favour one transform more? 00420 sub_qt_qtqt(quat_diff, pchan->quat, quat_prev); 00421 00422 /* make a copy of the original rotation */ 00423 QUATCOPY(quat_orig, pchan->quat); 00424 00425 /* increase the original by the delta transform, by an amount determined by percentage */ 00426 add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage); 00427 } 00428 else { 00429 float quat_interp[4], quat_orig[4]; 00430 int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed 00431 00432 /* perform this blending several times until a satisfactory result is reached */ 00433 while (iters-- > 0) { 00434 /* calculate the interpolation between the endpoints */ 00435 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe-pso->prevFrame) / (pso->nextFrame-pso->prevFrame) ); 00436 00437 /* make a copy of the original rotation */ 00438 QUATCOPY(quat_orig, pchan->quat); 00439 00440 /* tricky interpolations - blending between original and new */ 00441 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f); 00442 } 00443 } 00444 } 00445 00446 /* free the path now */ 00447 MEM_freeN(path); 00448 } 00449 00450 /* apply() - perform the pose sliding based on weighting various poses */ 00451 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) 00452 { 00453 tPChanFCurveLink *pfl; 00454 00455 /* sanitise the frame ranges */ 00456 if (pso->prevFrame == pso->nextFrame) { 00457 /* move out one step either side */ 00458 pso->prevFrame--; 00459 pso->nextFrame++; 00460 } 00461 00462 /* for each link, handle each set of transforms */ 00463 for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) { 00464 /* valid transforms for each PoseChannel should have been noted already 00465 * - sliding the pose should be a straightforward exercise for location+rotation, 00466 * but rotations get more complicated since we may want to use quaternion blending 00467 * for quaternions instead... 00468 */ 00469 bPoseChannel *pchan= pfl->pchan; 00470 00471 if (pchan->flag & POSE_LOC) { 00472 /* calculate these for the 'location' vector, and use location curves */ 00473 pose_slide_apply_vec3(pso, pfl, pchan->loc, "location"); 00474 } 00475 00476 if (pchan->flag & POSE_SIZE) { 00477 /* calculate these for the 'scale' vector, and use scale curves */ 00478 pose_slide_apply_vec3(pso, pfl, pchan->size, "scale"); 00479 } 00480 00481 if (pchan->flag & POSE_ROT) { 00482 /* everything depends on the rotation mode */ 00483 if (pchan->rotmode > 0) { 00484 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */ 00485 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler"); 00486 } 00487 else if (pchan->rotmode == ROT_MODE_AXISANGLE) { 00488 // TODO: need to figure out how to do this! 00489 } 00490 else { 00491 /* quaternions - use quaternion blending */ 00492 pose_slide_apply_quat(pso, pfl); 00493 } 00494 } 00495 00496 if (pfl->oldprops) { 00497 /* not strictly a transform, but contributes to the pose produced in many rigs */ 00498 pose_slide_apply_props(pso, pfl); 00499 } 00500 } 00501 00502 /* depsgraph updates + redraws */ 00503 pose_slide_refresh(C, pso); 00504 } 00505 00506 /* perform autokeyframing after changes were made + confirmed */ 00507 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso) 00508 { 00509 /* wrapper around the generic call */ 00510 poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe); 00511 } 00512 00513 /* reset changes made to current pose */ 00514 static void pose_slide_reset (tPoseSlideOp *pso) 00515 { 00516 /* wrapper around the generic call, so that custom stuff can be added later */ 00517 poseAnim_mapping_reset(&pso->pfLinks); 00518 } 00519 00520 /* ------------------------------------ */ 00521 00522 /* common code for invoke() methods */ 00523 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso) 00524 { 00525 tPChanFCurveLink *pfl; 00526 AnimData *adt= pso->ob->adt; 00527 wmWindow *win= CTX_wm_window(C); 00528 00529 /* for each link, add all its keyframes to the search tree */ 00530 for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) { 00531 LinkData *ld; 00532 00533 /* do this for each F-Curve */ 00534 for (ld= pfl->fcurves.first; ld; ld= ld->next) { 00535 FCurve *fcu= (FCurve *)ld->data; 00536 fcurve_to_keylist(adt, fcu, &pso->keys, NULL); 00537 } 00538 } 00539 00540 /* consolidate these keyframes, and figure out the nearest ones */ 00541 BLI_dlrbTree_linkedlist_sync(&pso->keys); 00542 00543 /* cancel if no keyframes found... */ 00544 if (pso->keys.root) { 00545 ActKeyColumn *ak; 00546 float cframe= (float)pso->cframe; 00547 00548 /* firstly, check if the current frame is a keyframe... */ 00549 ak= (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe); 00550 00551 if (ak == NULL) { 00552 /* current frame is not a keyframe, so search */ 00553 ActKeyColumn *pk= (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe); 00554 ActKeyColumn *nk= (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe); 00555 00556 /* new set the frames */ 00557 /* prev frame */ 00558 pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1); 00559 RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); 00560 /* next frame */ 00561 pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1); 00562 RNA_int_set(op->ptr, "next_frame", pso->nextFrame); 00563 } 00564 else { 00565 /* current frame itself is a keyframe, so just take keyframes on either side */ 00566 /* prev frame */ 00567 pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1); 00568 RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); 00569 /* next frame */ 00570 pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1); 00571 RNA_int_set(op->ptr, "next_frame", pso->nextFrame); 00572 } 00573 } 00574 else { 00575 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between."); 00576 pose_slide_exit(op); 00577 return OPERATOR_CANCELLED; 00578 } 00579 00580 /* initial apply for operator... */ 00581 // TODO: need to calculate percentage for initial round too... 00582 pose_slide_apply(C, pso); 00583 00584 /* depsgraph updates + redraws */ 00585 pose_slide_refresh(C, pso); 00586 00587 /* set cursor to indicate modal */ 00588 WM_cursor_modal(win, BC_EW_SCROLLCURSOR); 00589 00590 /* add a modal handler for this operator */ 00591 WM_event_add_modal_handler(C, op); 00592 return OPERATOR_RUNNING_MODAL; 00593 } 00594 00595 /* common code for modal() */ 00596 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt) 00597 { 00598 tPoseSlideOp *pso= op->customdata; 00599 wmWindow *win= CTX_wm_window(C); 00600 00601 switch (evt->type) { 00602 case LEFTMOUSE: /* confirm */ 00603 { 00604 /* return to normal cursor */ 00605 WM_cursor_restore(win); 00606 00607 /* insert keyframes as required... */ 00608 pose_slide_autoKeyframe(C, pso); 00609 pose_slide_exit(op); 00610 00611 /* done! */ 00612 return OPERATOR_FINISHED; 00613 } 00614 00615 case ESCKEY: /* cancel */ 00616 case RIGHTMOUSE: 00617 { 00618 /* return to normal cursor */ 00619 WM_cursor_restore(win); 00620 00621 /* reset transforms back to original state */ 00622 pose_slide_reset(pso); 00623 00624 /* depsgraph updates + redraws */ 00625 pose_slide_refresh(C, pso); 00626 00627 /* clean up temp data */ 00628 pose_slide_exit(op); 00629 00630 /* cancelled! */ 00631 return OPERATOR_CANCELLED; 00632 } 00633 00634 case MOUSEMOVE: /* calculate new position */ 00635 { 00636 /* calculate percentage based on position of mouse (we only use x-axis for now. 00637 * since this is more conveninent for users to do), and store new percentage value 00638 */ 00639 pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx); 00640 RNA_float_set(op->ptr, "percentage", pso->percentage); 00641 00642 /* reset transforms (to avoid accumulation errors) */ 00643 pose_slide_reset(pso); 00644 00645 /* apply... */ 00646 pose_slide_apply(C, pso); 00647 } 00648 break; 00649 00650 default: /* unhandled event (maybe it was some view manip? */ 00651 /* allow to pass through */ 00652 return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; 00653 } 00654 00655 /* still running... */ 00656 return OPERATOR_RUNNING_MODAL; 00657 } 00658 00659 /* common code for cancel() */ 00660 static int pose_slide_cancel (bContext *UNUSED(C), wmOperator *op) 00661 { 00662 /* cleanup and done */ 00663 pose_slide_exit(op); 00664 return OPERATOR_CANCELLED; 00665 } 00666 00667 /* common code for exec() methods */ 00668 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso) 00669 { 00670 /* settings should have been set up ok for applying, so just apply! */ 00671 pose_slide_apply(C, pso); 00672 00673 /* insert keyframes if needed */ 00674 pose_slide_autoKeyframe(C, pso); 00675 00676 /* cleanup and done */ 00677 pose_slide_exit(op); 00678 00679 return OPERATOR_FINISHED; 00680 } 00681 00682 /* common code for defining RNA properties */ 00683 static void pose_slide_opdef_properties (wmOperatorType *ot) 00684 { 00685 RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame.", 0, 50); 00686 RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame.", 0, 50); 00687 RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7); 00688 } 00689 00690 /* ------------------------------------ */ 00691 00692 /* invoke() - for 'push' mode */ 00693 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt)) 00694 { 00695 tPoseSlideOp *pso; 00696 00697 /* initialise data */ 00698 if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { 00699 pose_slide_exit(op); 00700 return OPERATOR_CANCELLED; 00701 } 00702 else 00703 pso= op->customdata; 00704 00705 /* do common setup work */ 00706 return pose_slide_invoke_common(C, op, pso); 00707 } 00708 00709 /* exec() - for push */ 00710 static int pose_slide_push_exec (bContext *C, wmOperator *op) 00711 { 00712 tPoseSlideOp *pso; 00713 00714 /* initialise data (from RNA-props) */ 00715 if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { 00716 pose_slide_exit(op); 00717 return OPERATOR_CANCELLED; 00718 } 00719 else 00720 pso= op->customdata; 00721 00722 /* do common exec work */ 00723 return pose_slide_exec_common(C, op, pso); 00724 } 00725 00726 void POSE_OT_push (wmOperatorType *ot) 00727 { 00728 /* identifiers */ 00729 ot->name= "Push Pose"; 00730 ot->idname= "POSE_OT_push"; 00731 ot->description= "Exaggerate the current pose"; 00732 00733 /* callbacks */ 00734 ot->exec= pose_slide_push_exec; 00735 ot->invoke= pose_slide_push_invoke; 00736 ot->modal= pose_slide_modal; 00737 ot->cancel= pose_slide_cancel; 00738 ot->poll= ED_operator_posemode; 00739 00740 /* flags */ 00741 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; 00742 00743 /* Properties */ 00744 pose_slide_opdef_properties(ot); 00745 } 00746 00747 /* ........................ */ 00748 00749 /* invoke() - for 'relax' mode */ 00750 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt)) 00751 { 00752 tPoseSlideOp *pso; 00753 00754 /* initialise data */ 00755 if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { 00756 pose_slide_exit(op); 00757 return OPERATOR_CANCELLED; 00758 } 00759 else 00760 pso= op->customdata; 00761 00762 /* do common setup work */ 00763 return pose_slide_invoke_common(C, op, pso); 00764 } 00765 00766 /* exec() - for relax */ 00767 static int pose_slide_relax_exec (bContext *C, wmOperator *op) 00768 { 00769 tPoseSlideOp *pso; 00770 00771 /* initialise data (from RNA-props) */ 00772 if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { 00773 pose_slide_exit(op); 00774 return OPERATOR_CANCELLED; 00775 } 00776 else 00777 pso= op->customdata; 00778 00779 /* do common exec work */ 00780 return pose_slide_exec_common(C, op, pso); 00781 } 00782 00783 void POSE_OT_relax (wmOperatorType *ot) 00784 { 00785 /* identifiers */ 00786 ot->name= "Relax Pose"; 00787 ot->idname= "POSE_OT_relax"; 00788 ot->description= "Make the current pose more similar to its surrounding ones"; 00789 00790 /* callbacks */ 00791 ot->exec= pose_slide_relax_exec; 00792 ot->invoke= pose_slide_relax_invoke; 00793 ot->modal= pose_slide_modal; 00794 ot->cancel= pose_slide_cancel; 00795 ot->poll= ED_operator_posemode; 00796 00797 /* flags */ 00798 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; 00799 00800 /* Properties */ 00801 pose_slide_opdef_properties(ot); 00802 } 00803 00804 /* ........................ */ 00805 00806 /* invoke() - for 'breakdown' mode */ 00807 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt)) 00808 { 00809 tPoseSlideOp *pso; 00810 00811 /* initialise data */ 00812 if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { 00813 pose_slide_exit(op); 00814 return OPERATOR_CANCELLED; 00815 } 00816 else 00817 pso= op->customdata; 00818 00819 /* do common setup work */ 00820 return pose_slide_invoke_common(C, op, pso); 00821 } 00822 00823 /* exec() - for breakdown */ 00824 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op) 00825 { 00826 tPoseSlideOp *pso; 00827 00828 /* initialise data (from RNA-props) */ 00829 if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { 00830 pose_slide_exit(op); 00831 return OPERATOR_CANCELLED; 00832 } 00833 else 00834 pso= op->customdata; 00835 00836 /* do common exec work */ 00837 return pose_slide_exec_common(C, op, pso); 00838 } 00839 00840 void POSE_OT_breakdown (wmOperatorType *ot) 00841 { 00842 /* identifiers */ 00843 ot->name= "Pose Breakdowner"; 00844 ot->idname= "POSE_OT_breakdown"; 00845 ot->description= "Create a suitable breakdown pose on the current frame"; 00846 00847 /* callbacks */ 00848 ot->exec= pose_slide_breakdown_exec; 00849 ot->invoke= pose_slide_breakdown_invoke; 00850 ot->modal= pose_slide_modal; 00851 ot->cancel= pose_slide_cancel; 00852 ot->poll= ED_operator_posemode; 00853 00854 /* flags */ 00855 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; 00856 00857 /* Properties */ 00858 pose_slide_opdef_properties(ot); 00859 } 00860 00861 /* **************************************************** */ 00862 /* B) Pose Propagate */ 00863 00864 /* "termination conditions" - i.e. when we stop */ 00865 typedef enum ePosePropagate_Termination { 00866 /* stop after the current hold ends */ 00867 POSE_PROPAGATE_SMART_HOLDS = 0, 00868 /* only do on the last keyframe */ 00869 POSE_PROPAGATE_LAST_KEY, 00870 /* stop after the next keyframe */ 00871 POSE_PROPAGATE_NEXT_KEY, 00872 /* stop after the specified frame */ 00873 POSE_PROPAGATE_BEFORE_FRAME, 00874 /* stop when we run out of keyframes */ 00875 POSE_PROPAGATE_BEFORE_END, 00876 00877 /* only do on the frames where markers are selected */ 00878 POSE_PROPAGATE_SELECTED_MARKERS 00879 } ePosePropagate_Termination; 00880 00881 /* termination data needed for some modes - assumes only one of these entries will be needed at a time */ 00882 typedef union tPosePropagate_ModeData { 00883 /* smart holds + before frame: frame number to stop on */ 00884 float end_frame; 00885 00886 /* selected markers: listbase for CfraElem's marking these frames */ 00887 ListBase sel_markers; 00888 } tPosePropagate_ModeData; 00889 00890 /* --------------------------------- */ 00891 00892 /* get frame on which the "hold" for the bone ends 00893 * XXX: this may not really work that well if a bone moves on some channels and not others 00894 * if this happens to be a major issue, scrap this, and just make this happen 00895 * independently per F-Curve 00896 */ 00897 static float pose_propagate_get_boneHoldEndFrame (Object *ob, tPChanFCurveLink *pfl, float startFrame) 00898 { 00899 DLRBT_Tree keys, blocks; 00900 ActKeyBlock *ab; 00901 00902 AnimData *adt= ob->adt; 00903 LinkData *ld; 00904 float endFrame = startFrame; 00905 00906 /* set up optimised data-structures for searching for relevant keyframes + holds */ 00907 BLI_dlrbTree_init(&keys); 00908 BLI_dlrbTree_init(&blocks); 00909 00910 for (ld = pfl->fcurves.first; ld; ld = ld->next) { 00911 FCurve *fcu = (FCurve *)ld->data; 00912 fcurve_to_keylist(adt, fcu, &keys, &blocks); 00913 } 00914 00915 BLI_dlrbTree_linkedlist_sync(&keys); 00916 BLI_dlrbTree_linkedlist_sync(&blocks); 00917 00918 /* find the long keyframe (i.e. hold), and hence obtain the endFrame value 00919 * - the best case would be one that starts on the frame itself 00920 */ 00921 ab = (ActKeyBlock *)BLI_dlrbTree_search_exact(&blocks, compare_ab_cfraPtr, &startFrame); 00922 00923 if (actkeyblock_is_valid(ab, &keys) == 0) { 00924 /* There are only two cases for no-exact match: 00925 * 1) the current frame is just before another key but not on a key itself 00926 * 2) the current frame is on a key, but that key doesn't link to the next 00927 * 00928 * If we've got the first case, then we can search for another block, 00929 * otherwise forget it, as we'd be overwriting some valid data. 00930 */ 00931 if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame) == NULL) { 00932 /* we've got case 1, so try the one after */ 00933 ab = (ActKeyBlock *)BLI_dlrbTree_search_next(&blocks, compare_ab_cfraPtr, &startFrame); 00934 00935 if (actkeyblock_is_valid(ab, &keys) == 0) { 00936 /* try the block before this frame then as last resort */ 00937 ab = (ActKeyBlock *)BLI_dlrbTree_search_prev(&blocks, compare_ab_cfraPtr, &startFrame); 00938 00939 /* whatever happens, stop searching now... */ 00940 if (actkeyblock_is_valid(ab, &keys) == 0) { 00941 /* restrict range to just the frame itself 00942 * i.e. everything is in motion, so no holds to safely overwrite 00943 */ 00944 ab = NULL; 00945 } 00946 } 00947 } 00948 else { 00949 /* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */ 00950 ab = NULL; 00951 } 00952 } 00953 00954 /* check if we can go any further than we've already gone */ 00955 if (ab) { 00956 /* go to next if it is also valid and meets "extension" criteria */ 00957 while (ab->next) { 00958 ActKeyBlock *abn = (ActKeyBlock *)ab->next; 00959 00960 /* must be valid */ 00961 if (actkeyblock_is_valid(abn, &keys) == 0) 00962 break; 00963 /* should start on the same frame that the last ended on */ 00964 if (ab->end != abn->start) 00965 break; 00966 /* should have the same number of curves */ 00967 if (ab->totcurve != abn->totcurve) 00968 break; 00969 /* should have the same value 00970 * XXX: this may be a bit fuzzy on larger data sets, so be careful 00971 */ 00972 if (ab->val != abn->val) 00973 break; 00974 00975 /* we can extend the bounds to the end of this "next" block now */ 00976 ab = abn; 00977 } 00978 00979 /* end frame can now take the value of the end of the block */ 00980 endFrame = ab->end; 00981 } 00982 00983 /* free temp memory */ 00984 BLI_dlrbTree_free(&keys); 00985 BLI_dlrbTree_free(&blocks); 00986 00987 /* return the end frame we've found */ 00988 return endFrame; 00989 } 00990 00991 /* get reference value from F-Curve using RNA */ 00992 static short pose_propagate_get_refVal (Object *ob, FCurve *fcu, float *value) 00993 { 00994 PointerRNA id_ptr, ptr; 00995 PropertyRNA *prop; 00996 short found= FALSE; 00997 00998 /* base pointer is always the object -> id_ptr */ 00999 RNA_id_pointer_create(&ob->id, &id_ptr); 01000 01001 /* resolve the property... */ 01002 if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop)) { 01003 if (RNA_property_array_check(&ptr, prop)) { 01004 /* array */ 01005 if (fcu->array_index < RNA_property_array_length(&ptr, prop)) { 01006 found= TRUE; 01007 switch (RNA_property_type(prop)) { 01008 case PROP_BOOLEAN: 01009 *value= (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index); 01010 break; 01011 case PROP_INT: 01012 *value= (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index); 01013 break; 01014 case PROP_FLOAT: 01015 *value= RNA_property_float_get_index(&ptr, prop, fcu->array_index); 01016 break; 01017 default: 01018 found= FALSE; 01019 break; 01020 } 01021 } 01022 } 01023 else { 01024 /* not an array */ 01025 found= TRUE; 01026 switch (RNA_property_type(prop)) { 01027 case PROP_BOOLEAN: 01028 *value= (float)RNA_property_boolean_get(&ptr, prop); 01029 break; 01030 case PROP_INT: 01031 *value= (float)RNA_property_int_get(&ptr, prop); 01032 break; 01033 case PROP_ENUM: 01034 *value= (float)RNA_property_enum_get(&ptr, prop); 01035 break; 01036 case PROP_FLOAT: 01037 *value= RNA_property_float_get(&ptr, prop); 01038 break; 01039 default: 01040 found= FALSE; 01041 break; 01042 } 01043 } 01044 } 01045 01046 return found; 01047 } 01048 01049 /* propagate just works along each F-Curve in turn */ 01050 static void pose_propagate_fcurve (wmOperator *op, Object *ob, FCurve *fcu, 01051 float startFrame, tPosePropagate_ModeData modeData) 01052 { 01053 const int mode = RNA_enum_get(op->ptr, "mode"); 01054 01055 BezTriple *bezt; 01056 float refVal = 0.0f; 01057 short keyExists; 01058 int i, match; 01059 short first=1; 01060 01061 /* skip if no keyframes to edit */ 01062 if ((fcu->bezt == NULL) || (fcu->totvert < 2)) 01063 return; 01064 01065 /* find the reference value from bones directly, which means that the user 01066 * doesn't need to firstly keyframe the pose (though this doesn't mean that 01067 * they can't either) 01068 */ 01069 if( !pose_propagate_get_refVal(ob, fcu, &refVal)) 01070 return; 01071 01072 /* find the first keyframe to start propagating from 01073 * - if there's a keyframe on the current frame, we probably want to save this value there too 01074 * since it may be as of yet unkeyed 01075 * - if starting before the starting frame, don't touch the key, as it may have had some valid 01076 * values 01077 */ 01078 match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists); 01079 01080 if (fcu->bezt[match].vec[1][0] < startFrame) 01081 i = match + 1; 01082 else 01083 i = match; 01084 01085 for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) { 01086 /* additional termination conditions based on the operator 'mode' property go here... */ 01087 if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) { 01088 /* stop if keyframe is outside the accepted range */ 01089 if (bezt->vec[1][0] > modeData.end_frame) 01090 break; 01091 } 01092 else if (mode == POSE_PROPAGATE_NEXT_KEY) { 01093 /* stop after the first keyframe has been processed */ 01094 if (first == 0) 01095 break; 01096 } 01097 else if (mode == POSE_PROPAGATE_LAST_KEY) { 01098 /* only affect this frame if it will be the last one */ 01099 if (i != (fcu->totvert-1)) 01100 continue; 01101 } 01102 else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { 01103 /* only allow if there's a marker on this frame */ 01104 CfraElem *ce = NULL; 01105 01106 /* stop on matching marker if there is one */ 01107 for (ce = modeData.sel_markers.first; ce; ce = ce->next) { 01108 if (ce->cfra == (int)(floor(bezt->vec[1][0] + 0.5f))) 01109 break; 01110 } 01111 01112 /* skip this keyframe if no marker */ 01113 if (ce == NULL) 01114 continue; 01115 } 01116 01117 /* just flatten handles, since values will now be the same either side... */ 01118 // TODO: perhaps a fade-out modulation of the value is required here (optional once again)? 01119 bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal; 01120 01121 /* select keyframe to indicate that it's been changed */ 01122 bezt->f2 |= SELECT; 01123 first = 0; 01124 } 01125 } 01126 01127 /* --------------------------------- */ 01128 01129 static int pose_propagate_exec (bContext *C, wmOperator *op) 01130 { 01131 Scene *scene = CTX_data_scene(C); 01132 Object *ob= ED_object_pose_armature(CTX_data_active_object(C)); 01133 bAction *act= (ob && ob->adt)? ob->adt->action : NULL; 01134 01135 ListBase pflinks = {NULL, NULL}; 01136 tPChanFCurveLink *pfl; 01137 01138 tPosePropagate_ModeData modeData; 01139 const int mode = RNA_enum_get(op->ptr, "mode"); 01140 01141 /* sanity checks */ 01142 if (ob == NULL) { 01143 BKE_report(op->reports, RPT_ERROR, "No object to propagate poses for"); 01144 return OPERATOR_CANCELLED; 01145 } 01146 if (act == NULL) { 01147 BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to"); 01148 return OPERATOR_CANCELLED; 01149 } 01150 01151 /* isolate F-Curves related to the selected bones */ 01152 poseAnim_mapping_get(C, &pflinks, ob, act); 01153 01154 /* mode-specific data preprocessing (requiring no access to curves) */ 01155 if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { 01156 /* get a list of selected markers */ 01157 ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT); 01158 } 01159 else { 01160 /* assume everything else wants endFrame */ 01161 modeData.end_frame = RNA_float_get(op->ptr, "end_frame"); 01162 } 01163 01164 /* for each bone, perform the copying required */ 01165 for (pfl = pflinks.first; pfl; pfl = pfl->next) { 01166 LinkData *ld; 01167 01168 /* mode-specific data preprocessing (requiring access to all curves) */ 01169 if (mode == POSE_PROPAGATE_SMART_HOLDS) { 01170 /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting 01171 * from the keyframe that occurs after the current frame 01172 */ 01173 modeData.end_frame = pose_propagate_get_boneHoldEndFrame(ob, pfl, (float)CFRA); 01174 } 01175 01176 /* go through propagating pose to keyframes, curve by curve */ 01177 for (ld = pfl->fcurves.first; ld; ld= ld->next) 01178 pose_propagate_fcurve(op, ob, (FCurve *)ld->data, (float)CFRA, modeData); 01179 } 01180 01181 /* free temp data */ 01182 poseAnim_mapping_free(&pflinks); 01183 01184 if (mode == POSE_PROPAGATE_SELECTED_MARKERS) 01185 BLI_freelistN(&modeData.sel_markers); 01186 01187 /* updates + notifiers */ 01188 poseAnim_mapping_refresh(C, scene, ob); 01189 01190 return OPERATOR_FINISHED; 01191 } 01192 01193 /* --------------------------------- */ 01194 01195 void POSE_OT_propagate (wmOperatorType *ot) 01196 { 01197 static EnumPropertyItem terminate_items[]= { 01198 {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behaviour)"}, 01199 {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", "Propagate pose to first keyframe following the current frame only"}, 01200 {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", "Propagate pose to the last keyframe only (i.e. making action cyclic)"}, 01201 {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"}, 01202 {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", "Propagate pose to all keyframes from current frame until no more are found"}, 01203 {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"}, 01204 {0, NULL, 0, NULL, NULL}}; 01205 01206 /* identifiers */ 01207 ot->name= "Propagate Pose"; 01208 ot->idname= "POSE_OT_propagate"; 01209 ot->description= "Copy selected aspects of the current pose to subsequent poses already keyframed"; 01210 01211 /* callbacks */ 01212 ot->exec= pose_propagate_exec; 01213 ot->poll= ED_operator_posemode; // XXX: needs selected bones! 01214 01215 /* flag */ 01216 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; 01217 01218 /* properties */ 01219 // TODO: add "fade out" control for tapering off amount of propagation as time goes by? 01220 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"); 01221 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); 01222 } 01223 01224 /* **************************************************** */