NLA - Auto-Blending + 'Smarter' Extend Behaviour
authorJoshua Leung <aligorith@gmail.com>
Fri, 24 Jul 2009 06:08:03 +0000 (06:08 +0000)
committerJoshua Leung <aligorith@gmail.com>
Fri, 24 Jul 2009 06:08:03 +0000 (06:08 +0000)
* Auto-blending (blend in/out values get determined based on 'overlaps' of strips) now occurs after transforming strips. Where islands (continuous chains of strips) occur, only the ones on the ends of the islands will get or contribute to auto-blending.

* Extend modes (other than 'nothing') now get automatically determined (after transforms) so that moving strips will no-longer cause problems.

* Added a new option to hide influence curves in NLA. Also, made the line widths for these curves narrower, since the old setting was too ugly.

* Pose copy/paste buffer now gets freed on exit

source/blender/blenkernel/BKE_nla.h
source/blender/blenkernel/intern/nla.c
source/blender/editors/space_nla/nla_draw.c
source/blender/editors/space_nla/nla_header.c
source/blender/editors/transform/transform_conversions.c
source/blender/makesdna/DNA_space_types.h
source/blender/makesrna/intern/rna_space.c
source/blender/windowmanager/intern/wm_init_exit.c

index bb5a2782663179b5d1287d0b54b045614c26fb2a..241a8fd7b59e58facacbfbfe1609e8b3abeb1455 100644 (file)
@@ -91,6 +91,8 @@ short BKE_nlatrack_has_animated_strips(struct NlaTrack *nlt);
 short BKE_nlatracks_have_animated_strips(ListBase *tracks);
 void BKE_nlastrip_validate_fcurves(struct NlaStrip *strip);
 
+void BKE_nla_validate_state(struct AnimData *adt);
+
 /* ............ */
 
 void BKE_nla_action_pushdown(struct AnimData *adt);
index 457df9be7a9c5a4c519e49f938a4729789d78af0..1871ec006f4e3e3864ac7e8de88d1960c5e99b51 100644 (file)
@@ -375,7 +375,7 @@ static float nlastrip_get_frame_actionclip (NlaStrip *strip, float cframe, short
                        return strip->actend - (repeatsNum * actlength * scale) 
                                        - (fmod(cframe - strip->start, actlength*scale) / scale);
                }
-               else {
+               else /* if (mode == NLATIME_CONVERT_EVAL) */{
                        if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) {
                                /* this case prevents the motion snapping back to the first frame at the end of the strip 
                                 * by catching the case where repeats is a whole number, which means that the end of the strip
@@ -1229,13 +1229,52 @@ void BKE_nlastrip_validate_name (AnimData *adt, NlaStrip *strip)
 
 /* ---- */
 
+/* Get strips which overlap the given one at the start/end of its range 
+ *     - strip: strip that we're finding overlaps for
+ *     - track: nla-track that the overlapping strips should be found from
+ *     - start, end: frames for the offending endpoints
+ */
+static void nlastrip_get_endpoint_overlaps (NlaStrip *strip, NlaTrack *track, float **start, float **end)
+{
+       NlaStrip *nls;
+       
+       /* find strips that overlap over the start/end of the given strip,
+        * but which don't cover the entire length 
+        */
+       // TODO: this scheme could get quite slow for doing this on many strips...
+       for (nls= track->strips.first; nls; nls= nls->next) {
+               /* check if strip overlaps (extends over or exactly on) the entire range of the strip we're validating */
+               if ((nls->start <= strip->start) && (nls->end >= strip->end)) {
+                       *start= NULL;
+                       *end= NULL;
+                       return;
+               }
+               
+               /* check if strip doesn't even occur anywhere near... */
+               if (nls->end < strip->start)
+                       continue; /* skip checking this strip... not worthy of mention */
+               if (nls->start > strip->end)
+                       return; /* the range we're after has already passed */
+                       
+               /* if this strip is not part of an island of continuous strips, it can be used
+                *      - this check needs to be done for each end of the strip we try and use...
+                */
+               if ((nls->next == NULL) || IS_EQ(nls->next->start, nls->end)==0) {
+                       if ((nls->end > strip->start) && (nls->end < strip->end))
+                               *start= &nls->end;
+               }
+               if ((nls->prev == NULL) || IS_EQ(nls->prev->end, nls->start)==0) {
+                       if ((nls->start < strip->end) && (nls->start > strip->start))
+                               *end= &nls->start;
+               }
+       }
+}
+
 /* Determine auto-blending for the given strip */
 void BKE_nlastrip_validate_autoblends (NlaTrack *nlt, NlaStrip *nls)
 {
-       NlaTrack *track;
-       NlaStrip *strip;
-       //float *ps=NULL, *pe=NULL;
-       //float *ns=NULL, *ne=NULL;
+       float *ps=NULL, *pe=NULL;
+       float *ns=NULL, *ne=NULL;
        
        /* sanity checks */
        if ELEM(NULL, nls, nlt)
@@ -1246,40 +1285,74 @@ void BKE_nlastrip_validate_autoblends (NlaTrack *nlt, NlaStrip *nls)
                return;
        
        /* get test ranges */
-       if (nlt->prev) {
-               /* find strips that overlap over the start/end of the given strip,
-                * but which don't cover the entire length 
-                */
-               track= nlt->prev;
-               for (strip= track->strips.first; strip; strip= strip->next) {
-                       
-               }
+       if (nlt->prev)
+               nlastrip_get_endpoint_overlaps(nls, nlt->prev, &ps, &pe);
+       if (nlt->next)
+               nlastrip_get_endpoint_overlaps(nls, nlt->next, &ns, &ne);
+               
+       /* set overlaps for this strip 
+        *      - don't use the values obtained though if the end in question 
+        *        is directly followed/preceeded by another strip, forming an 
+        *        'island' of continuous strips
+        */
+       if ( (ps || ns) && ((nls->prev == NULL) || IS_EQ(nls->prev->end, nls->start)==0) ) 
+       {
+               /* start overlaps - pick the largest overlap */
+               if ( ((ps && ns) && (*ps > *ns)) || (ps) )
+                       nls->blendin= *ps - nls->start;
+               else
+                       nls->blendin= *ns - nls->start;
        }
-       if (nlt->next) {
-               /* find strips that overlap over the start/end of the given strip,
-                * but which don't cover the entire length 
-                */
-               track= nlt->next;
-               for (strip= track->strips.first; strip; strip= strip->next) {
-                       
-               }
+       else /* no overlap allowed/needed */
+               nls->blendin= 0.0f;
+               
+       if ( (pe || ne) && ((nls->next == NULL) || IS_EQ(nls->next->start, nls->end)==0) ) 
+       {
+               /* end overlaps - pick the largest overlap */
+               if ( ((pe && ne) && (*pe > *ne)) || (pe) )
+                       nls->blendout= nls->end - *pe;
+               else
+                       nls->blendout= nls->end - *ne;
        }
+       else /* no overlap allowed/needed */
+               nls->blendout= 0.0f;
 }
 
 /* Ensure that auto-blending and other settings are set correctly */
 void BKE_nla_validate_state (AnimData *adt)
 {
-       NlaStrip *strip;
+       NlaStrip *strip, *fstrip=NULL;
        NlaTrack *nlt;
        
        /* sanity checks */
        if ELEM(NULL, adt, adt->nla_tracks.first)
                return;
                
-       /* adjust blending values for auto-blending */
+       /* adjust blending values for auto-blending, and also do an initial pass to find the earliest strip */
        for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
                for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       /* auto-blending first */
                        BKE_nlastrip_validate_autoblends(nlt, strip);
+                       
+                       /* extend mode - find first strip */
+                       if ((fstrip == NULL) || (strip->start < fstrip->start))
+                               fstrip= strip;
+               }
+       }
+       
+       /* second pass over the strips to adjust the extend-mode to fix any problems */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       /* apart from 'nothing' option which user has to explicitly choose, we don't really know if 
+                        * we should be overwriting the extend setting (but assume that's what the user wanted)
+                        */
+                       // TODO: 1 solution is to tie this in with auto-blending...
+                       if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) {
+                               if (strip == fstrip)
+                                       strip->extendmode= NLASTRIP_EXTEND_HOLD;
+                               else
+                                       strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD;
+                       }
                }
        }
 }
index 9e60651081f0ea54e4055018c663abcf89d51e88..9aa7118733414a53536fc76fe8390dc8cf9b4db8 100644 (file)
@@ -252,10 +252,9 @@ static void nla_draw_strip_curves (NlaStrip *strip, View2D *v2d, float yminc, fl
        // XXX nasty hacked color for now... which looks quite bad too...
        glColor3f(0.7f, 0.7f, 0.7f);
        
-       /* draw with AA'd line, 2 units thick (it's either 1 or 2 px) */
+       /* draw with AA'd line */
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_BLEND);
-       glLineWidth(2.0f);
        
        /* influence -------------------------- */
        if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
@@ -302,11 +301,10 @@ static void nla_draw_strip_curves (NlaStrip *strip, View2D *v2d, float yminc, fl
        /* turn off AA'd lines */
        glDisable(GL_LINE_SMOOTH);
        glDisable(GL_BLEND);
-       glLineWidth(1.0f);
 }
 
 /* main call for drawing a single NLA-strip */
-static void nla_draw_strip (AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc)
+static void nla_draw_strip (SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc)
 {
        float color[3];
        
@@ -373,8 +371,11 @@ static void nla_draw_strip (AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2
        gl_round_box_shade(GL_POLYGON, strip->start, yminc, strip->end, ymaxc, 0.0, 0.5, 0.1);
        
        
-       /* draw strip's control 'curves' */
-       nla_draw_strip_curves(strip, v2d, yminc, ymaxc);
+       /* draw strip's control 'curves'
+        *      - only if user hasn't hidden them...
+        */
+       if ((snla->flag & SNLA_NOSTRIPCURVES) == 0)
+               nla_draw_strip_curves(strip, v2d, yminc, ymaxc);
        
        /* draw strip outline 
         *      - color used here is to indicate active vs non-active
@@ -527,7 +528,7 @@ void draw_nla_main_data (bAnimContext *ac, SpaceNla *snla, ARegion *ar)
                                        for (strip=nlt->strips.first, index=1; strip; strip=strip->next, index++) {
                                                if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
                                                        /* draw the visualisation of the strip */
-                                                       nla_draw_strip(adt, nlt, strip, v2d, yminc, ymaxc);
+                                                       nla_draw_strip(snla, adt, nlt, strip, v2d, yminc, ymaxc);
                                                        
                                                        /* add the text for this strip to the cache */
                                                        nla_draw_strip_text(nlt, strip, index, v2d, yminc, ymaxc);
index e997a096bce8e19bbcc115de4aed19798294988c..f159f440759acd7385d04d5a2b11fd14f2fb4d84 100644 (file)
@@ -21,7 +21,7 @@
  * All rights reserved.
  *
  * 
- * Contributor(s): Blender Foundation
+ * Contributor(s): Blender Foundation, Joshua Leung
  *
  * ***** END GPL LICENSE BLOCK *****
  */
@@ -100,7 +100,9 @@ static void nla_viewmenu(bContext *C, uiLayout *layout, void *arg_unused)
                uiItemO(layout, "Show Frames", 0, "ANIM_OT_time_toggle");
        else
                uiItemO(layout, "Show Seconds", 0, "ANIM_OT_time_toggle");
-
+       
+       uiItemR(layout, NULL, 0, &spaceptr, "show_strip_curves", 0, 0, 0);
+       
        uiItemS(layout);
        
        uiItemS(layout);
index 3d643a2dec1c3253bf8090204ec8b2458197f8de..4c91ca9af4aa9996622f44a3b174a60c8a41db79 100644 (file)
@@ -4645,34 +4645,34 @@ void special_aftertrans_update(TransInfo *t)
                SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first;
                Scene *scene;
                bAnimContext ac;
-
+               
                /* initialise relevant anim-context 'context' data from TransInfo data */
                        /* NOTE: sync this with the code in ANIM_animdata_get_context() */
                memset(&ac, 0, sizeof(bAnimContext));
-
+               
                scene= ac.scene= t->scene;
                ob= ac.obact= OBACT;
                ac.sa= t->sa;
                ac.ar= t->ar;
                ac.spacetype= (t->sa)? t->sa->spacetype : 0;
                ac.regiontype= (t->ar)? t->ar->regiontype : 0;
-
+               
                if (ANIM_animdata_context_getdata(&ac) == 0)
                        return;
-
+               
                if (ac.datatype == ANIMCONT_DOPESHEET) {
                        ListBase anim_data = {NULL, NULL};
                        bAnimListElem *ale;
                        short filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY);
-
+                       
                        /* get channels to work on */
                        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
+                       
                        /* these should all be ipo-blocks */
                        for (ale= anim_data.first; ale; ale= ale->next) {
                                AnimData *adt= ANIM_nla_mapping_get(&ac, ale);
                                FCurve *fcu= (FCurve *)ale->key_data;
-
+                               
                                if ( (saction->flag & SACTION_NOTRANSKEYCULL)==0 &&
                                     ((cancelled == 0) || (duplicate)) )
                                {
@@ -4685,7 +4685,7 @@ void special_aftertrans_update(TransInfo *t)
                                                posttrans_fcurve_clean(fcu);
                                }
                        }
-
+                       
                        /* free temp memory */
                        BLI_freelistN(&anim_data);
                }
@@ -4694,13 +4694,13 @@ void special_aftertrans_update(TransInfo *t)
                        // fixme... some of this stuff is not good
                        if (ob) {
                                ob->ctime= -1234567.0f;
-
+                               
                                if (ob->pose || ob_get_key(ob))
                                        DAG_object_flush_update(scene, ob, OB_RECALC);
                                else
                                        DAG_object_flush_update(scene, ob, OB_RECALC_OB);
                        }
-
+                       
                        /* Do curve cleanups? */
                        if ( (saction->flag & SACTION_NOTRANSKEYCULL)==0 &&
                             ((cancelled == 0) || (duplicate)) )
@@ -4712,7 +4712,7 @@ void special_aftertrans_update(TransInfo *t)
 #if 0 // XXX old animation system
                        /* fix up the Ipocurves and redraw stuff */
                        Key *key= (Key *)ac.data;
-
+                       
                        if (key->ipo) {
                                if ( (saction->flag & SACTION_NOTRANSKEYCULL)==0 &&
                                     ((cancelled == 0) || (duplicate)) )
@@ -4721,7 +4721,7 @@ void special_aftertrans_update(TransInfo *t)
                                }
                        }
 #endif // XXX old animation system
-
+                       
                        DAG_object_flush_update(scene, OBACT, OB_RECALC_DATA);
                }
 #if 0 // XXX future of this is still not clear
@@ -4731,23 +4731,23 @@ void special_aftertrans_update(TransInfo *t)
                        {
                                bScreen *sc= (bScreen *)ac.data;
                                ScrArea *sa;
-
+                               
                                /* BAD... we need to loop over all screen areas for current screen...
                                 *      - sync this with actdata_filter_gpencil() in editaction.c
                                 */
                                for (sa= sc->areabase.first; sa; sa= sa->next) {
                                        bGPdata *gpd= gpencil_data_getactive(sa);
-
+                                       
                                        if (gpd)
                                                posttrans_gpd_clean(gpd);
                                }
                        }
                }
 #endif // XXX future of this is still not clear
-
+               
                /* make sure all F-Curves are set correctly */
                ANIM_editkeyframes_refresh(&ac);
-
+               
                /* clear flag that was set for time-slide drawing */
                saction->flag &= ~SACTION_MOVING;
        }
@@ -4755,35 +4755,35 @@ void special_aftertrans_update(TransInfo *t)
                SpaceIpo *sipo= (SpaceIpo *)t->sa->spacedata.first;
                Scene *scene;
                bAnimContext ac;
-
+               
                /* initialise relevant anim-context 'context' data from TransInfo data */
                        /* NOTE: sync this with the code in ANIM_animdata_get_context() */
                memset(&ac, 0, sizeof(bAnimContext));
-
+               
                scene= ac.scene= t->scene;
                ob= ac.obact= OBACT;
                ac.sa= t->sa;
                ac.ar= t->ar;
                ac.spacetype= (t->sa)? t->sa->spacetype : 0;
                ac.regiontype= (t->ar)? t->ar->regiontype : 0;
-
+               
                if (ANIM_animdata_context_getdata(&ac) == 0)
                        return;
-
+               
                if (ac.datatype)
                {
                        ListBase anim_data = {NULL, NULL};
                        bAnimListElem *ale;
                        short filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_CURVEVISIBLE);
-
+                       
                        /* get channels to work on */
                        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
+                       
                        /* these should all be ipo-blocks */
                        for (ale= anim_data.first; ale; ale= ale->next) {
                                AnimData *adt= ANIM_nla_mapping_get(&ac, ale);
                                FCurve *fcu= (FCurve *)ale->key_data;
-
+                               
                                if ( (sipo->flag & SIPO_NOTRANSKEYCULL)==0 &&
                                     ((cancelled == 0) || (duplicate)) )
                                {
@@ -4796,58 +4796,74 @@ void special_aftertrans_update(TransInfo *t)
                                                posttrans_fcurve_clean(fcu);
                                }
                        }
-
+                       
                        /* free temp memory */
                        BLI_freelistN(&anim_data);
                }
-
+               
                /* make sure all F-Curves are set correctly */
                ANIM_editkeyframes_refresh(&ac);
        }
        else if (t->spacetype == SPACE_NLA) {
                Scene *scene;
                bAnimContext ac;
-
+               
                /* initialise relevant anim-context 'context' data from TransInfo data */
                /* NOTE: sync this with the code in ANIM_animdata_get_context() */
                memset(&ac, 0, sizeof(bAnimContext));
-
+               
                scene= ac.scene= t->scene;
                ob= ac.obact= OBACT;
                ac.sa= t->sa;
                ac.ar= t->ar;
                ac.spacetype= (t->sa)? t->sa->spacetype : 0;
                ac.regiontype= (t->ar)? t->ar->regiontype : 0;
-
+               
                if (ANIM_animdata_context_getdata(&ac) == 0)
                        return;
-
+                       
                if (ac.datatype)
                {
                        ListBase anim_data = {NULL, NULL};
                        bAnimListElem *ale;
-                       short filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NLATRACKS);
-
-                       /* get channels to work on */
-                       ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
-                       for (ale= anim_data.first; ale; ale= ale->next) {
-                               NlaTrack *nlt= (NlaTrack *)ale->data;
-
-                               /* make sure strips are in order again */
-                               BKE_nlatrack_sort_strips(nlt);
-
-                               /* remove the temp metas */
-                               BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
+                       
+                       /* firstly, make the strips normal again */
+                       {
+                               short filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NLATRACKS);
+                               
+                               /* get channels to work on */
+                               ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+                               
+                               for (ale= anim_data.first; ale; ale= ale->next) {
+                                       NlaTrack *nlt= (NlaTrack *)ale->data;
+                                       
+                                       /* make sure strips are in order again */
+                                       BKE_nlatrack_sort_strips(nlt);
+                                       
+                                       /* remove the temp metas */
+                                       BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
+                               }
+                               
+                               /* free temp memory */
+                               BLI_freelistN(&anim_data);
+                       }
+                       
+                       /* perform after-transfrom validation */
+                       {
+                               short filter= (ANIMFILTER_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FOREDIT);
+                               
+                               /* get blocks to work on */
+                               ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+                               
+                               for (ale= anim_data.first; ale; ale= ale->next) {
+                                       /* performing auto-blending, extend-mode validation, etc. */
+                                       BKE_nla_validate_state(ale->data);
+                               }
+                               
+                               /* free temp memory */
+                               BLI_freelistN(&anim_data);
                        }
-
-                       /* free temp memory */
-                       BLI_freelistN(&anim_data);
                }
-
-               // XXX check on the calls below... we need some of these sanity checks
-               //synchronize_action_strips();
-               //ANIM_editkeyframes_refresh(&ac);
        }
        else if (t->obedit) {
                // TRANSFORM_FIX_ME
index 34a5efbdf5f88be12ecd542d01d821ea7cb23f54..4760fd00bf2cbf514b46ea0a880355814c1eb41e 100644 (file)
@@ -811,6 +811,7 @@ enum {
 #define SNLA_DRAWTIME          (1<<2)
 #define SNLA_NOTRANSKEYCULL    (1<<3)
 #define SNLA_NODRAWCFRANUM     (1<<4)
+#define SNLA_NOSTRIPCURVES     (1<<5)
 
 /* time->flag */
        /* show timing in frames instead of in seconds */
index cf14d18ac98ee07baa70128d5bdbf32605333e84..bf475d4904814ca8ac7b1f7927ec8136cfab91ab 100644 (file)
@@ -1060,6 +1060,10 @@ static void rna_def_space_nla(BlenderRNA *brna)
        RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SNLA_NODRAWCFRANUM);
        RNA_def_property_ui_text(prop, "Show Frame Number Indicator", "Show frame number beside the current frame indicator line.");
        
+       prop= RNA_def_property(srna, "show_strip_curves", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SNLA_NOSTRIPCURVES);
+       RNA_def_property_ui_text(prop, "Show Control Curves", "Show influence curves on strips.");
+       
        /* editing */
        // TODO... autosnap, dopesheet?
 }
index fd102b663d0a1ddfb41b0b09a3bd029f1a9ccceb..599844f10202e94abda1c340e65b9ad0ae0f2f75 100644 (file)
@@ -178,6 +178,7 @@ extern wchar_t *copybufinfo;
 
        // XXX copy/paste buffer stuff...
 extern void free_anim_copybuf(); 
+extern void free_posebuf(); 
 
 /* called in creator.c even... tsk, split this! */
 void WM_exit(bContext *C)
@@ -231,6 +232,7 @@ void WM_exit(bContext *C)
        free_blender();                         /* blender.c, does entire library and spacetypes */
 //     free_matcopybuf();
        free_anim_copybuf();
+       free_posebuf();
 //     free_vertexpaint();
 //     free_imagepaint();