NLA SoC: Influence/Time properties for strips can now be animated
authorJoshua Leung <aligorith@gmail.com>
Wed, 8 Jul 2009 05:00:10 +0000 (05:00 +0000)
committerJoshua Leung <aligorith@gmail.com>
Wed, 8 Jul 2009 05:00:10 +0000 (05:00 +0000)
* These settings can now be edited + keyframed (using IKEY over the button only for now... other cases will fail)

* Reshuffled some of the keyframing code to make this sort of thing easier to do. Also, restored corrections for NLA-mapping when inserting/removing keyframes.

TODOS:
* animation editors don't show these keyframes yet
* the buttons don't change colour yet to reflect this state. How to do this efficiently?
* allow keyframing of these in more places
* more robust UI handling for this.

source/blender/blenkernel/BKE_nla.h
source/blender/blenkernel/intern/nla.c
source/blender/editors/animation/keyframing.c
source/blender/editors/include/ED_keyframing.h
source/blender/editors/space_nla/nla_buttons.c
source/blender/makesrna/intern/rna_nla.c

index 5cb967c9c5997a6030de54d6744c7f60f46ed1aa..7fdff7e41f7f25b9db23f401e773b3e835f2394b 100644 (file)
@@ -80,8 +80,12 @@ short BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip);
 /* ............ */
 
 struct NlaStrip *BKE_nlastrip_find_active(struct NlaTrack *nlt);
+
 short BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max);
 
+void BKE_nlastrip_validate_fcurves(struct NlaStrip *strip);
+
+/* ............ */
 
 void BKE_nla_action_pushdown(struct AnimData *adt);
 
index 511623f46fc68904d67f66934ea2abe11a31d153..217444d16d216d503af056620a89bc72b0c091ba 100644 (file)
@@ -1049,6 +1049,58 @@ short nlastrip_is_first (AnimData *adt, NlaStrip *strip)
        /* should be first now */
        return 1;
 }
+
+/* Validate the NLA-Strips 'control' F-Curves based on the flags set*/
+void BKE_nlastrip_validate_fcurves (NlaStrip *strip) 
+{
+       FCurve *fcu;
+       
+       /* sanity checks */
+       if (strip == NULL)
+               return;
+       
+       /* if controlling influence... */
+       if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
+               /* try to get F-Curve */
+               fcu= list_find_fcurve(&strip->fcurves, "influence", 0);
+               
+               /* add one if not found */
+               if (fcu == NULL) {
+                       /* make new F-Curve */
+                       fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve");
+                       BLI_addtail(&strip->fcurves, fcu);
+                       
+                       /* set default flags */
+                       fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+                       
+                       /* store path - make copy, and store that */
+                       fcu->rna_path= BLI_strdupn("influence", 9);
+                       
+                       // TODO: insert a few keyframes to ensure default behaviour?
+               }
+       }
+       
+       /* if controlling time... */
+       if (strip->flag & NLASTRIP_FLAG_USR_TIME) {
+               /* try to get F-Curve */
+               fcu= list_find_fcurve(&strip->fcurves, "strip_time", 0);
+               
+               /* add one if not found */
+               if (fcu == NULL) {
+                       /* make new F-Curve */
+                       fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve");
+                       BLI_addtail(&strip->fcurves, fcu);
+                       
+                       /* set default flags */
+                       fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+                       
+                       /* store path - make copy, and store that */
+                       fcu->rna_path= BLI_strdupn("strip_time", 10);
+                       
+                       // TODO: insert a few keyframes to ensure default behaviour?
+               }
+       }
+}
  
 /* Tools ------------------------------------------- */
 
index 331e2d0894e70e86f46c03fe7017558db0608216..ac195f42f0300b73243aa9e93beee168a512edab 100644 (file)
@@ -54,6 +54,7 @@
 #include "BKE_action.h"
 #include "BKE_constraint.h"
 #include "BKE_fcurve.h"
+#include "BKE_nla.h"
 #include "BKE_global.h"
 #include "BKE_utildefines.h"
 #include "BKE_context.h"
@@ -722,9 +723,119 @@ static float visualkey_get_value (PointerRNA *ptr, PropertyRNA *prop, int array_
 
 /* ------------------------- Insert Key API ------------------------- */
 
+/* Secondary Keyframing API call: 
+ *     Use this when validation of necessary animation data is not necessary, since an RNA-pointer to the necessary
+ *     data being keyframed, and a pointer to the F-Curve to use have both been provided.
+ *
+ *     The flag argument is used for special settings that alter the behaviour of
+ *     the keyframe insertion. These include the 'visual' keyframing modes, quick refresh,
+ *     and extra keyframe filtering.
+ */
+short insert_keyframe_direct (PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, float cfra, short flag)
+{
+       float curval= 0.0f;
+       
+       /* no F-Curve to add keyframe to? */
+       if (fcu == NULL) {
+               printf("ERROR: no F-Curve to add keyframes to \n");
+               return 0;
+       }
+       
+       /* if no property given yet, try to validate from F-Curve info */
+       if ((ptr.id.data == NULL) && (ptr.data==NULL)) {
+               printf("ERROR: no RNA-pointer available to retrieve values for keyframing from\n");
+               return 0;
+       }
+       if (prop == NULL) {
+               PointerRNA tmp_ptr;
+               
+               /* try to get property we should be affecting */
+               if ((RNA_path_resolve(&ptr, fcu->rna_path, &tmp_ptr, &prop) == 0) || (prop == NULL)) {
+                       /* property not found... */
+                       char *idname= (ptr.id.data) ? ((ID *)ptr.id.data)->name : "<No ID-Pointer>";
+                       
+                       printf("Insert Key: Could not insert keyframe, as RNA Path is invalid for the given ID (ID = %s, Path = %s)\n", idname, fcu->rna_path);
+                       return 0;
+               }
+               else {
+                       /* property found, so overwrite 'ptr' to make later code easier */
+                       ptr= tmp_ptr;
+               }
+       }
+       
+       /* set additional flags for the F-Curve (i.e. only integer values) */
+       fcu->flag &= ~(FCURVE_INT_VALUES|FCURVE_DISCRETE_VALUES);
+       switch (RNA_property_type(prop)) {
+               case PROP_FLOAT:
+                       /* do nothing */
+                       break;
+               case PROP_INT:
+                       /* do integer (only 'whole' numbers) interpolation between all points */
+                       fcu->flag |= FCURVE_INT_VALUES;
+                       break;
+               default:
+                       /* do 'discrete' (i.e. enum, boolean values which cannot take any intermediate
+                        * values at all) interpolation between all points
+                        *      - however, we must also ensure that evaluated values are only integers still
+                        */
+                       fcu->flag |= (FCURVE_DISCRETE_VALUES|FCURVE_INT_VALUES);
+                       break;
+       }
+       
+       /* obtain value to give keyframe */
+       if ( (flag & INSERTKEY_MATRIX) && 
+                (visualkey_can_use(&ptr, prop)) ) 
+       {
+               /* visual-keying is only available for object and pchan datablocks, as 
+                * it works by keyframing using a value extracted from the final matrix 
+                * instead of using the kt system to extract a value.
+                */
+               curval= visualkey_get_value(&ptr, prop, fcu->array_index);
+       }
+       else {
+               /* read value from system */
+               curval= setting_get_rna_value(&ptr, prop, fcu->array_index);
+       }
+       
+       /* only insert keyframes where they are needed */
+       if (flag & INSERTKEY_NEEDED) {
+               short insert_mode;
+               
+               /* check whether this curve really needs a new keyframe */
+               insert_mode= new_key_needed(fcu, cfra, curval);
+               
+               /* insert new keyframe at current frame */
+               if (insert_mode)
+                       insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST));
+               
+               /* delete keyframe immediately before/after newly added */
+               switch (insert_mode) {
+                       case KEYNEEDED_DELPREV:
+                               delete_fcurve_key(fcu, fcu->totvert-2, 1);
+                               break;
+                       case KEYNEEDED_DELNEXT:
+                               delete_fcurve_key(fcu, 1, 1);
+                               break;
+               }
+               
+               /* only return success if keyframe added */
+               if (insert_mode)
+                       return 1;
+       }
+       else {
+               /* just insert keyframe */
+               insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST));
+               
+               /* return success */
+               return 1;
+       }
+       
+       /* failed */
+       return 0;
+}
+
 /* Main Keyframing API call:
- *     Use this when validation of necessary animation data isn't necessary as it
- *     already exists. It will insert a keyframe using the current value being keyframed.
+ *     Use this when validation of necessary animation data is necessary, since it may not exist yet.
  *     
  *     The flag argument is used for special settings that alter the behaviour of
  *     the keyframe insertion. These include the 'visual' keyframing modes, quick refresh,
@@ -744,102 +855,31 @@ short insert_keyframe (ID *id, bAction *act, const char group[], const char rna_
        }
        
        /* get F-Curve - if no action is provided, keyframe to the default one attached to this ID-block */
-       if (act == NULL)
+       if (act == NULL) {
+               AnimData *adt= BKE_animdata_from_id(id);
+               
+               /* get action to add F-Curve+keyframe to */
                act= verify_adt_action(id, 1);
+               
+               /* apply NLA-mapping to frame to use (if applicable) */
+               cfra= BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
+       }
        fcu= verify_fcurve(act, group, rna_path, array_index, 1);
        
-       /* only continue if we have an F-Curve to add keyframe to */
-       if (fcu) {
-               float curval= 0.0f;
+       /* apply special time tweaking */
+               // XXX check on this stuff...
+       if (GS(id->name) == ID_OB) {
+               //Object *ob= (Object *)id;
                
-               /* set additional flags for the F-Curve (i.e. only integer values) */
-               fcu->flag &= ~(FCURVE_INT_VALUES|FCURVE_DISCRETE_VALUES);
-               switch (RNA_property_type(prop)) {
-                       case PROP_FLOAT:
-                               /* do nothing */
-                               break;
-                       case PROP_INT:
-                               /* do integer (only 'whole' numbers) interpolation between all points */
-                               fcu->flag |= FCURVE_INT_VALUES;
-                               break;
-                       default:
-                               /* do 'discrete' (i.e. enum, boolean values which cannot take any intermediate
-                                * values at all) interpolation between all points
-                                *      - however, we must also ensure that evaluated values are only integers still
-                                */
-                               fcu->flag |= (FCURVE_DISCRETE_VALUES|FCURVE_INT_VALUES);
-                               break;
-               }
-               
-               /* apply special time tweaking */
-                       // XXX check on this stuff...
-               if (GS(id->name) == ID_OB) {
-                       //Object *ob= (Object *)id;
-                       
-                       /* apply NLA-scaling (if applicable) */
-                       //cfra= get_action_frame(ob, cfra);
-                       
-                       /* ancient time-offset cruft */
-                       //if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) {
-                       //      /* actually frametofloat calc again! */
-                       //      cfra-= give_timeoffset(ob)*scene->r.framelen;
-                       //}
-               }
-               
-               /* obtain value to give keyframe */
-               if ( (flag & INSERTKEY_MATRIX) && 
-                        (visualkey_can_use(&ptr, prop)) ) 
-               {
-                       /* visual-keying is only available for object and pchan datablocks, as 
-                        * it works by keyframing using a value extracted from the final matrix 
-                        * instead of using the kt system to extract a value.
-                        */
-                       curval= visualkey_get_value(&ptr, prop, array_index);
-               }
-               else {
-                       /* read value from system */
-                       curval= setting_get_rna_value(&ptr, prop, array_index);
-               }
-               
-               /* only insert keyframes where they are needed */
-               if (flag & INSERTKEY_NEEDED) {
-                       short insert_mode;
-                       
-                       /* check whether this curve really needs a new keyframe */
-                       insert_mode= new_key_needed(fcu, cfra, curval);
-                       
-                       /* insert new keyframe at current frame */
-                       if (insert_mode)
-                               insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST));
-                       
-                       /* delete keyframe immediately before/after newly added */
-                       switch (insert_mode) {
-                               case KEYNEEDED_DELPREV:
-                                       delete_fcurve_key(fcu, fcu->totvert-2, 1);
-                                       break;
-                               case KEYNEEDED_DELNEXT:
-                                       delete_fcurve_key(fcu, 1, 1);
-                                       break;
-                       }
-                       
-                       /* only return success if keyframe added */
-                       if (insert_mode)
-                               return 1;
-               }
-               else {
-                       /* just insert keyframe */
-                       insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST));
-                       
-                       /* return success */
-                       return 1;
-               }
+               /* ancient time-offset cruft */
+               //if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) {
+               //      /* actually frametofloat calc again! */
+               //      cfra-= give_timeoffset(ob)*scene->r.framelen;
+               //}
        }
        
-       /* no F-Curve to add keyframes to */
-       printf("ERROR: no F-Curve to add keyframes to \n");
-       
-       /* return failure */
-       return 0;
+       /* insert keyframe */
+       return insert_keyframe_direct(ptr, prop, fcu, cfra, flag);
 }
 
 /* ************************************************** */
@@ -864,6 +904,9 @@ short delete_keyframe (ID *id, bAction *act, const char group[], const char rna_
                /* if no action is provided, use the default one attached to this ID-block */
                AnimData *adt= BKE_animdata_from_id(id);
                act= adt->action;
+               
+               /* apply NLA-mapping to frame to use (if applicable) */
+               cfra= BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); 
        }
        /* we don't check the validity of the path here yet, but it should be ok... */
        fcu= verify_fcurve(act, group, rna_path, array_index, 0);
@@ -877,9 +920,6 @@ short delete_keyframe (ID *id, bAction *act, const char group[], const char rna_
                if (GS(id->name) == ID_OB) {
                        //Object *ob= (Object *)id;
                        
-                       /* apply NLA-scaling (if applicable) */
-                       //      cfra= get_action_frame(ob, cfra);
-                       
                        /* ancient time-offset cruft */
                        //if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) {
                        //      /* actually frametofloat calc again! */
@@ -1266,6 +1306,13 @@ static int insert_key_button_exec (bContext *C, wmOperator *op)
                        
                        MEM_freeN(path);
                }
+               else if (ptr.type == &RNA_NlaStrip) {
+                       /* handle special vars for NLA-strips */
+                       NlaStrip *strip= (NlaStrip *)ptr.data;
+                       FCurve *fcu= list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), 0);
+                       
+                       success+= insert_keyframe_direct(ptr, prop, fcu, cfra, 0);
+               }
                else {
                        if (G.f & G_DEBUG)
                                printf("Button Insert-Key: no path to property \n");
index 9d063910aa9b73d3f3e304f87789042d75c22973..ffebb42ce99231657c49abce368addcd33e65390 100644 (file)
@@ -43,6 +43,9 @@ struct bConstraint;
 struct bContext;
 struct wmOperatorType;
 
+struct PointerRNA;
+struct PropertyRNA;
+
 /* ************ Keyframing Management **************** */
 
 /* Get (or add relevant data to be able to do so) the Active Action for the given 
@@ -69,6 +72,16 @@ int insert_bezt_fcurve(struct FCurve *fcu, struct BezTriple *bezt);
  */
 void insert_vert_fcurve(struct FCurve *fcu, float x, float y, short flag);
 
+/* -------- */
+
+/* Secondary Keyframing API calls: 
+ *     Use this to insert a keyframe using the current value being keyframed, in the 
+ *     nominated F-Curve (no creation of animation data performed). Returns success.
+ */
+short insert_keyframe_direct(struct PointerRNA ptr, struct PropertyRNA *prop, struct FCurve *fcu, float cfra, short flag);
+
+
+
 /* -------- */
 
 /* Main Keyframing API calls: 
index 38ac59cbc9e1e43d845236b0b30e3e0c7d281b9d..d09cc6a1e5395ee12c678be8284f5ccaa240b531 100644 (file)
@@ -271,7 +271,7 @@ static void nla_panel_actclip(const bContext *C, Panel *pa)
                uiItemR(row, NULL, ICON_ACTION, &strip_ptr, "action", 0, 0, 0);
                
        /* action extents */
-       // XXX custom names were used here... probably not necessary in future?
+       // XXX custom names were used here (to avoid the prefixes)... probably not necessary in future?
        column= uiLayoutColumn(layout, 1);
                uiItemL(column, "Action Extents:", 0);
                uiItemR(column, "Start Frame", 0, &strip_ptr, "action_start_frame", 0, 0, 0);
@@ -289,7 +289,7 @@ static void nla_panel_evaluation(const bContext *C, Panel *pa)
 {
        PointerRNA strip_ptr;
        uiLayout *layout= pa->layout;
-       uiLayout *column;
+       uiLayout *column, *subcolumn;
        uiBlock *block;
 
        /* check context and also validity of pointer */
@@ -300,14 +300,19 @@ static void nla_panel_evaluation(const bContext *C, Panel *pa)
        uiBlockSetHandleFunc(block, do_nla_region_buttons, NULL);
                
        column= uiLayoutColumn(layout, 1);
-       uiLayoutSetEnabled(column, 0);          // XXX for now, don't allow user to edit
                uiItemR(column, NULL, 0, &strip_ptr, "animated_influence", 0, 0, 0);
-               uiItemR(column, NULL, 0, &strip_ptr, "influence", 0, 0, 0);
                
+               subcolumn= uiLayoutColumn(column, 1);
+               uiLayoutSetEnabled(subcolumn, RNA_boolean_get(&strip_ptr, "animated_influence"));       
+                       uiItemR(subcolumn, NULL, 0, &strip_ptr, "influence", 0, 0, 0);
+               
+       
        column= uiLayoutColumn(layout, 1);
-       uiLayoutSetEnabled(column, 0);          // XXX for now, don't allow user to edit
                uiItemR(column, NULL, 0, &strip_ptr, "animated_time", 0, 0, 0);
-               uiItemR(column, NULL, 0, &strip_ptr, "strip_time", 0, 0, 0);
+               
+               subcolumn= uiLayoutColumn(column, 1);
+               uiLayoutSetEnabled(subcolumn, RNA_boolean_get(&strip_ptr, "animated_time"));
+                       uiItemR(subcolumn, NULL, 0, &strip_ptr, "strip_time", 0, 0, 0);
 }
 
 /* F-Modifiers for active NLA-Strip */
index b0149d29e69e06b43f87fe6e4bb4381b4ad2e7d0..219feaad09bbd5df7e30b2e8a6017fd4fd9957c2 100644 (file)
@@ -40,6 +40,9 @@
 #include <stdio.h>
 #include <math.h>
 
+/* needed for some of the validation stuff... */
+#include "BKE_nla.h"
+
 /* temp constant defined for these funcs only... */
 #define NLASTRIP_MIN_LEN_THRESH        0.1f
 
@@ -196,6 +199,32 @@ static void rna_NlaStrip_action_end_frame_set(PointerRNA *ptr, float value)
        data->actend= value;
 }
 
+static void rna_NlaStrip_animated_influence_set(PointerRNA *ptr, int value)
+{
+       NlaStrip *data= (NlaStrip*)ptr->data;
+       
+       if (value) {
+               /* set the flag, then make sure a curve for this exists */
+               data->flag |= NLASTRIP_FLAG_USR_INFLUENCE;
+               BKE_nlastrip_validate_fcurves(data);
+       }
+       else
+               data->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE;
+}
+
+static void rna_NlaStrip_animated_time_set(PointerRNA *ptr, int value)
+{
+       NlaStrip *data= (NlaStrip*)ptr->data;
+       
+       if (value) {
+               /* set the flag, then make sure a curve for this exists */
+               data->flag |= NLASTRIP_FLAG_USR_TIME;
+               BKE_nlastrip_validate_fcurves(data);
+       }
+       else
+               data->flag &= ~NLASTRIP_FLAG_USR_TIME;
+}
+
 #else
 
 void rna_def_nlastrip(BlenderRNA *brna)
@@ -321,14 +350,15 @@ void rna_def_nlastrip(BlenderRNA *brna)
        prop= RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_NONE);
        RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate.");
        
+               // TODO: should the animated_influence/time settings be animatable themselves?
        prop= RNA_def_property(srna, "animated_influence", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_clear_flag(prop, PROP_EDITABLE); // XXX for now, not editable
        RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_INFLUENCE);
+       RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_influence_set");
        RNA_def_property_ui_text(prop, "Animated Influence", "Influence setting is controlled by an F-Curve rather than automatically determined.");
        
        prop= RNA_def_property(srna, "animated_time", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_clear_flag(prop, PROP_EDITABLE); // XXX for now, not editable
-       RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_INFLUENCE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME);
+       RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_time_set");
        RNA_def_property_ui_text(prop, "Animated Strip Time", "Strip time is controlled by an F-Curve rather than automatically determined.");
        
        /* settings */