NLA - Auto-Blending + 'Smarter' Extend Behaviour

* 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
This commit is contained in:
Joshua Leung 2009-07-24 06:08:03 +00:00
parent 57727fc145
commit 0f4fd4f5b1
8 changed files with 182 additions and 81 deletions

View 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);

View 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;
}
}
}
}

View 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);

View 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);

View 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

View 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 */

View 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?
}

View 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();