Pose Sliding Tools - Custom Property Support + Other bugfixes
authorJoshua Leung <aligorith@gmail.com>
Sun, 13 Mar 2011 12:22:57 +0000 (12:22 +0000)
committerJoshua Leung <aligorith@gmail.com>
Sun, 13 Mar 2011 12:22:57 +0000 (12:22 +0000)
- 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
source/blender/blenkernel/intern/action.c
source/blender/editors/armature/armature_intern.h
source/blender/editors/armature/poseSlide.c
source/blender/editors/armature/poseUtils.c

index 59da97d8b09fd90adc61b51747fc189f8201bfc1..698f0f0fecf8459378c875b59f7c6b7e67f2a83f 100644 (file)
@@ -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 
index 1c0091cff744ffaea91461f11a3d83e504a9da7d..4c5b7f5fcafc994fc880f55c561f6ac3c54c6ddf 100644 (file)
@@ -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;
+                               }
+                       }
                }
        }
        
index 5110f04aa6bb1e44e1a42bfd9ccbf76dd9563a02..b72cf75d9c76129aff603bb3be5e04b55a229ccd 100644 (file)
@@ -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;
 
 /* ----------- */
index d3d430f43319009732661f64d60096b32b1653d8..302f23f702899a65139a5df6ff83fc9f3c70b844 100644 (file)
@@ -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;
        }
        
index f95f8836d71a1ba00f64a72d344fd47e5f1214d8..bae44eb6d5b5ea87c3d9823a6c4608805cee9d11 100644 (file)
@@ -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);
        }
 }