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