From 2144b23f514005958e5ec962528599d6623bf15b Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Sun, 13 Mar 2011 12:22:57 +0000 Subject: Pose Sliding Tools - Custom Property Support + Other bugfixes - Custom properties are now affected by the Pose Sliding tools too. This is now more important to support, given that modern rigs use these a lot for facial expressions/posing. By and large, this should work fine, though discrete integer values may experience a bit of trouble - Fixed potential bugs with the code which detects which F-Curves are relevant to a PoseBone's transforms (+ custom props). This was prone to being tricked by certain setups if the names of the bones contained some of the keywords these were searching for. - Shuffled some code around: moved bulk of logic out of vec3 case into new function for single-value, since it was really doing per axis already --- source/blender/blenkernel/BKE_action.h | 8 +- source/blender/blenkernel/intern/action.c | 29 +++- source/blender/editors/armature/armature_intern.h | 13 +- source/blender/editors/armature/poseSlide.c | 200 ++++++++++++++-------- source/blender/editors/armature/poseUtils.c | 67 ++++---- 5 files changed, 196 insertions(+), 121 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index 59da97d8b09..698f0f0fecf 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -82,9 +82,15 @@ typedef enum eAction_TransformFlags { ACT_TRANS_ROT = (1<<1), /* scaling */ ACT_TRANS_SCALE = (1<<2), + + /* strictly not a transform, but custom properties are also + * quite often used in modern rigs + */ + ACT_TRANS_PROP = (1<<3), /* all flags */ - ACT_TRANS_ALL = (ACT_TRANS_LOC|ACT_TRANS_ROT|ACT_TRANS_SCALE), + ACT_TRANS_ONLY = (ACT_TRANS_LOC|ACT_TRANS_ROT|ACT_TRANS_SCALE), + ACT_TRANS_ALL = (ACT_TRANS_ONLY|ACT_TRANS_PROP) } eAction_TransformFlags; /* Return flags indicating which transforms the given object/posechannel has diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 1c0091cff74..4c5b7f5fcaf 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -973,6 +973,11 @@ short action_get_item_transforms (bAction *act, Object *ob, bPoseChannel *pchan, bPtr= strstr(fcu->rna_path, basePath); if (bPtr) { + /* we must add len(basePath) bytes to the match so that we are at the end of the + * base path so that we don't get false positives with these strings in the names + */ + bPtr += strlen(basePath); + /* step 2: check for some property with transforms * - to speed things up, only check for the ones not yet found * unless we're getting the curves too @@ -981,8 +986,8 @@ short action_get_item_transforms (bAction *act, Object *ob, bPoseChannel *pchan, * - once a match has been found, the curve cannot possibly be any other one */ if ((curves) || (flags & ACT_TRANS_LOC) == 0) { - pPtr= strstr(fcu->rna_path, "location"); - if ((pPtr) && (pPtr >= bPtr)) { + pPtr= strstr(bPtr, "location"); + if (pPtr) { flags |= ACT_TRANS_LOC; if (curves) @@ -992,8 +997,8 @@ short action_get_item_transforms (bAction *act, Object *ob, bPoseChannel *pchan, } if ((curves) || (flags & ACT_TRANS_SCALE) == 0) { - pPtr= strstr(fcu->rna_path, "scale"); - if ((pPtr) && (pPtr >= bPtr)) { + pPtr= strstr(bPtr, "scale"); + if (pPtr) { flags |= ACT_TRANS_SCALE; if (curves) @@ -1003,8 +1008,8 @@ short action_get_item_transforms (bAction *act, Object *ob, bPoseChannel *pchan, } if ((curves) || (flags & ACT_TRANS_ROT) == 0) { - pPtr= strstr(fcu->rna_path, "rotation"); - if ((pPtr) && (pPtr >= bPtr)) { + pPtr= strstr(bPtr, "rotation"); + if (pPtr) { flags |= ACT_TRANS_ROT; if (curves) @@ -1012,6 +1017,18 @@ short action_get_item_transforms (bAction *act, Object *ob, bPoseChannel *pchan, continue; } } + + if ((curves) || (flags & ACT_TRANS_PROP) == 0) { + /* custom properties only */ + pPtr= strstr(bPtr, "[\""); /* extra '"' comment here to keep my texteditor functionlist working :) */ + if (pPtr) { + flags |= ACT_TRANS_PROP; + + if (curves) + BLI_addtail(curves, BLI_genericNodeN(fcu)); + continue; + } + } } } diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 5110f04aa6b..b72cf75d9c7 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -150,16 +150,19 @@ void SKETCH_OT_select(struct wmOperatorType *ot); typedef struct tPChanFCurveLink { struct tPChanFCurveLink *next, *prev; - ListBase fcurves; /* F-Curves for this PoseChannel (wrapped with LinkData) */ - struct bPoseChannel *pchan; /* Pose Channel which data is attached to */ + ListBase fcurves; /* F-Curves for this PoseChannel (wrapped with LinkData) */ + struct bPoseChannel *pchan; /* Pose Channel which data is attached to */ - char *pchan_path; /* RNA Path to this Pose Channel (needs to be freed when we're done) */ + char *pchan_path; /* RNA Path to this Pose Channel (needs to be freed when we're done) */ - // TODO: need to include axis-angle here at some stage - float oldloc[3]; /* transform values at start of operator (to be restored before each modal step) */ + float oldloc[3]; /* transform values at start of operator (to be restored before each modal step) */ float oldrot[3]; float oldscale[3]; float oldquat[4]; + float oldangle; + float oldaxis[3]; + + struct IDProperty *oldprops; /* copy of custom properties at start of operator (to be restored before each modal step) */ } tPChanFCurveLink; /* ----------- */ diff --git a/source/blender/editors/armature/poseSlide.c b/source/blender/editors/armature/poseSlide.c index d3d430f4331..302f23f7028 100644 --- a/source/blender/editors/armature/poseSlide.c +++ b/source/blender/editors/armature/poseSlide.c @@ -192,102 +192,146 @@ static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso) poseAnim_mapping_refresh(C, pso->scene, pso->ob); } +/* helper for apply() - perform sliding for some value */ +static void pose_slider_apply_val (tPoseSlideOp *pso, FCurve *fcu, float *val) +{ + float cframe = (float)pso->cframe; + float sVal, eVal; + float w1, w2; + + /* get keyframe values for endpoint poses to blend with */ + /* previous/start */ + sVal= evaluate_fcurve(fcu, (float)pso->prevFrame); + /* next/end */ + eVal= evaluate_fcurve(fcu, (float)pso->nextFrame); + + /* calculate the relative weights of the endpoints */ + if (pso->mode == POSESLIDE_BREAKDOWN) { + /* get weights from the percentage control */ + w1= pso->percentage; /* this must come second */ + w2= 1.0f - w1; /* this must come first */ + } + else { + /* - these weights are derived from the relative distance of these + * poses from the current frame + * - they then get normalised so that they only sum up to 1 + */ + float wtot; + + w1 = cframe - (float)pso->prevFrame; + w2 = (float)pso->nextFrame - cframe; + + wtot = w1 + w2; + w1 = (w1/wtot); + w2 = (w2/wtot); + } + + /* depending on the mode, calculate the new value + * - in all of these, the start+end values are multiplied by w2 and w1 (respectively), + * since multiplication in another order would decrease the value the current frame is closer to + */ + switch (pso->mode) { + case POSESLIDE_PUSH: /* make the current pose more pronounced */ + { + /* perform a weighted average here, favouring the middle pose + * - numerator should be larger than denominator to 'expand' the result + * - perform this weighting a number of times given by the percentage... + */ + int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed + + while (iters-- > 0) { + (*val)= ( -((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; + } + } + break; + + case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ + { + /* perform a weighted average here, favouring the middle pose + * - numerator should be smaller than denominator to 'relax' the result + * - perform this weighting a number of times given by the percentage... + */ + int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed + + while (iters-- > 0) { + (*val)= ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f; + } + } + break; + + case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ + { + /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */ + // TODO: make this use some kind of spline interpolation instead? + (*val)= ((sVal * w2) + (eVal * w1)); + } + break; + } +} + /* helper for apply() - perform sliding for some 3-element vector */ -static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char *propName) +static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[]) { LinkData *ld=NULL; char *path=NULL; - float cframe; /* get the path to use... */ path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName); - /* get the current frame number */ - cframe= (float)pso->cframe; - /* using this path, find each matching F-Curve for the variables we're interested in */ while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) { FCurve *fcu= (FCurve *)ld->data; - float sVal, eVal; - float w1, w2; - int ch; - /* get keyframe values for endpoint poses to blend with */ - /* previous/start */ - sVal= evaluate_fcurve(fcu, (float)pso->prevFrame); - /* next/end */ - eVal= evaluate_fcurve(fcu, (float)pso->nextFrame); + /* just work on these channels one by one... there's no interaction between values */ + pose_slider_apply_val(pso, fcu, &vec[fcu->array_index]); + } + + /* free the temp path we got */ + MEM_freeN(path); +} + +/* helper for apply() - perform sliding for custom properties */ +static void pose_slide_apply_props (tPoseSlideOp *pso, tPChanFCurveLink *pfl) +{ + PointerRNA ptr = {{NULL}}; + LinkData *ld; + int len = strlen(pfl->pchan_path); + + /* setup pointer RNA for resolving paths */ + RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr); + + /* custom properties are just denoted using ["..."][etc.] after the end of the base path, + * so just check for opening pair after the end of the path + */ + for (ld = pfl->fcurves.first; ld; ld = ld->next) { + FCurve *fcu = (FCurve *)ld->data; + char *bPtr, *pPtr; + + if (fcu->rna_path == NULL) + continue; - /* get channel index */ - ch= fcu->array_index; + /* do we have a match? + * - bPtr is the RNA Path with the standard part chopped off + * - pPtr is the chunk of the path which is left over + */ + bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len; + pPtr = strstr(bPtr, "[\""); /* dummy " for texteditor bugs */ - /* calculate the relative weights of the endpoints */ - if (pso->mode == POSESLIDE_BREAKDOWN) { - /* get weights from the percentage control */ - w1= pso->percentage; /* this must come second */ - w2= 1.0f - w1; /* this must come first */ - } - else { - /* - these weights are derived from the relative distance of these - * poses from the current frame - * - they then get normalised so that they only sum up to 1 + if (pPtr) { + /* use RNA to try and get a handle on this property, then, assuming that it is just + * numerical, try and grab the value as a float for temp editing before setting back */ - float wtot; - - w1 = cframe - (float)pso->prevFrame; - w2 = (float)pso->nextFrame - cframe; + PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr); - wtot = w1 + w2; - w1 = (w1/wtot); - w2 = (w2/wtot); - } - - /* depending on the mode, calculate the new value - * - in all of these, the start+end values are multiplied by w2 and w1 (respectively), - * since multiplication in another order would decrease the value the current frame is closer to - */ - switch (pso->mode) { - case POSESLIDE_PUSH: /* make the current pose more pronounced */ - { - /* perform a weighted average here, favouring the middle pose - * - numerator should be larger than denominator to 'expand' the result - * - perform this weighting a number of times given by the percentage... - */ - int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed + if (prop) { + float tval = RNA_property_float_get(&ptr, prop); - while (iters-- > 0) { - vec[ch]= ( -((sVal * w2) + (eVal * w1)) + (vec[ch] * 6.0f) ) / 5.0f; - } - } - break; - - case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ - { - /* perform a weighted average here, favouring the middle pose - * - numerator should be smaller than denominator to 'relax' the result - * - perform this weighting a number of times given by the percentage... - */ - int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed - - while (iters-- > 0) { - vec[ch]= ( ((sVal * w2) + (eVal * w1)) + (vec[ch] * 5.0f) ) / 6.0f; - } - } - break; + pose_slider_apply_val(pso, fcu, &tval); - case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ - { - /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */ - // TODO: make this use some kind of spline interpolation instead? - vec[ch]= ((sVal * w2) + (eVal * w1)); + RNA_property_float_set(&ptr, prop, tval); } - break; } - } - - /* free the temp path we got */ - MEM_freeN(path); } /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */ @@ -426,6 +470,11 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) pose_slide_apply_quat(pso, pfl); } } + + if (pfl->oldprops) { + /* not strictly a transform, but contributes to the pose produced in many rigs */ + pose_slide_apply_props(pso, pfl); + } } /* depsgraph updates + redraws */ @@ -502,6 +551,7 @@ static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp * } else { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between."); + pose_slide_exit(op); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/armature/poseUtils.c b/source/blender/editors/armature/poseUtils.c index f95f8836d71..bae44eb6d5b 100644 --- a/source/blender/editors/armature/poseUtils.c +++ b/source/blender/editors/armature/poseUtils.c @@ -53,6 +53,7 @@ #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_depsgraph.h" +#include "BKE_idprop.h" #include "BKE_context.h" @@ -112,11 +113,16 @@ static void fcurves_to_pchan_links_get (ListBase *pfLinks, Object *ob, bAction * pchan->flag |= POSE_SIZE; /* store current transforms */ - // TODO: store axis-angle too? VECCOPY(pfl->oldloc, pchan->loc); VECCOPY(pfl->oldrot, pchan->eul); VECCOPY(pfl->oldscale, pchan->size); QUATCOPY(pfl->oldquat, pchan->quat); + VECCOPY(pfl->oldaxis, pchan->rotAxis); + pfl->oldangle = pchan->rotAngle; + + /* make copy of custom properties */ + if (transFlags & ACT_TRANS_PROP) + pfl->oldprops = IDP_CopyProperty(pchan->prop); } } @@ -144,6 +150,12 @@ void poseAnim_mapping_free (ListBase *pfLinks) for (pfl= pfLinks->first; pfl; pfl= pfln) { pfln= pfl->next; + /* free custom properties */ + if (pfl->oldprops) { + IDP_FreeProperty(pfl->oldprops); + MEM_freeN(pfl->oldprops); + } + /* free list of F-Curve reference links */ BLI_freelistN(&pfl->fcurves); @@ -185,59 +197,46 @@ void poseAnim_mapping_reset (ListBase *pfLinks) bPoseChannel *pchan= pfl->pchan; /* just copy all the values over regardless of whether they changed or not */ - // TODO; include axis-angle here too? VECCOPY(pchan->loc, pfl->oldloc); VECCOPY(pchan->eul, pfl->oldrot); VECCOPY(pchan->size, pfl->oldscale); QUATCOPY(pchan->quat, pfl->oldquat); + VECCOPY(pchan->rotAxis, pfl->oldaxis); + pchan->rotAngle = pfl->oldangle; + + /* just overwrite values of properties from the stored copies (there should be some) */ + if (pfl->oldprops) + IDP_SyncGroupValues(pfl->pchan->prop, pfl->oldprops); } } /* perform autokeyframing after changes were made + confirmed */ void poseAnim_mapping_autoKeyframe (bContext *C, Scene *scene, Object *ob, ListBase *pfLinks, float cframe) { - static short keyingsets_need_init = 1; - static KeyingSet *ks_loc = NULL; - static KeyingSet *ks_rot = NULL; - static KeyingSet *ks_scale = NULL; - - /* get keyingsets the first time this is run? - * NOTE: it should be safe to store these static, since they're currently builtin ones - * but maybe later this may change, in which case this code needs to be revised! - */ - if (keyingsets_need_init) { - ks_loc= ANIM_builtin_keyingset_get_named(NULL, "Location"); - ks_rot= ANIM_builtin_keyingset_get_named(NULL, "Rotation"); - ks_scale= ANIM_builtin_keyingset_get_named(NULL, "Scaling"); - - keyingsets_need_init = 0; - } - /* insert keyframes as necessary if autokeyframing */ if (autokeyframe_cfra_can_key(scene, &ob->id)) { + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, "Whole Character"); + ListBase dsources = {NULL, NULL}; tPChanFCurveLink *pfl; - /* iterate over each pose-channel affected, applying the changes */ + /* iterate over each pose-channel affected, tagging bones to be keyed */ + /* XXX: here we already have the information about what transforms exist, though + * it might be easier to just overwrite all using normal mechanisms + */ for (pfl= pfLinks->first; pfl; pfl= pfl->next) { - ListBase dsources = {NULL, NULL}; bPoseChannel *pchan= pfl->pchan; - /* add datasource override for the PoseChannel so KeyingSet will do right thing */ + /* add datasource override for the PoseChannel, to be used later */ ANIM_relative_keyingset_add_source(&dsources, &ob->id, &RNA_PoseBone, pchan); - /* insert keyframes - * - these keyingsets here use dsources, since we need to specify exactly which keyframes get affected - */ - if (pchan->flag & POSE_LOC) - ANIM_apply_keyingset(C, &dsources, NULL, ks_loc, MODIFYKEY_MODE_INSERT, cframe); - if (pchan->flag & POSE_ROT) - ANIM_apply_keyingset(C, &dsources, NULL, ks_rot, MODIFYKEY_MODE_INSERT, cframe); - if (pchan->flag & POSE_SIZE) - ANIM_apply_keyingset(C, &dsources, NULL, ks_scale, MODIFYKEY_MODE_INSERT, cframe); - - /* free the temp info */ - BLI_freelistN(&dsources); + /* clear any unkeyed tags */ + if (pchan->bone) + pchan->bone->flag &= ~BONE_UNKEYED; } + + /* insert keyframes for all relevant bones in one go */ + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + BLI_freelistN(&dsources); } } -- cgit v1.2.3