diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index f59cbd9c1d4..6505b1deea3 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -54,6 +54,8 @@ #include "ED_space_api.h" #include "ED_screen.h" +#include "BIF_transform.h" + #include "WM_api.h" #include "WM_types.h" @@ -191,7 +193,7 @@ static void nla_keymap_main (wmWindowManager *wm, ListBase *keymap) WM_keymap_add_item(keymap, "NLAEDIT_OT_tweakmode_exit", TABKEY, KM_PRESS, 0, 0); /* transform system */ - //transform_keymap_for_space(wm, keymap, SPACE_NLA); + transform_keymap_for_space(wm, keymap, SPACE_NLA); } /* --------------- */ diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 674de81a9f5..fa93d2a143d 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -319,6 +319,9 @@ static void viewRedrawForce(bContext *C, TransInfo *t) else ED_area_tag_redraw(t->sa); } + else if (t->spacetype == SPACE_NLA) { + ED_area_tag_redraw(t->sa); // XXX this should use a notifier instead! + } else if(t->spacetype == SPACE_NODE) { //ED_area_tag_redraw(t->sa); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index ee767fada58..c0a57a85033 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -161,6 +161,13 @@ typedef struct TransDataSeq { } TransDataSeq; +/* for NLA transform (stored in td->extra pointer) */ +typedef struct TransDataNla { + struct NlaStrip *strip; /* NLA-strip that handle belongs to */ + float val; /* value for the handle that the transform tools write to */ + int handle; /* handle-index, 0 for start, 1 for end */ +} TransDataNla; + typedef struct TransData { float dist; /* Distance needed to affect element (for Proportionnal Editing) */ float rdist; /* Distance to the nearest element (for Proportionnal Editing) */ diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 6c7aa1ee49d..96f62f1e18e 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -2559,7 +2559,140 @@ int clipUVTransform(TransInfo *t, float *vec, int resize) return (clipx || clipy); } -/* ********************* ACTION/NLA EDITOR ****************** */ +/* ********************* ANIMATION EDITORS (GENERAL) ************************* */ + +/* This function tests if a point is on the "mouse" side of the cursor/frame-marking */ +static short FrameOnMouseSide(char side, float frame, float cframe) +{ + /* both sides, so it doesn't matter */ + if (side == 'B') return 1; + + /* only on the named side */ + if (side == 'R') + return (frame >= cframe) ? 1 : 0; + else + return (frame <= cframe) ? 1 : 0; +} + +/* ********************* NLA EDITOR ************************* */ + +static void createTransNlaData(bContext *C, TransInfo *t) +{ + Scene *scene= CTX_data_scene(C); + TransData *td = NULL; + TransDataNla *tdn = NULL; + + bAnimContext ac; + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + int count=0; + char side; + + /* determine what type of data we are operating on */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return; + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* which side of the current frame should be allowed */ + if (t->mode == TFM_TIME_EXTEND) { + /* only side on which mouse is gets transformed */ + float xmouse, ymouse; + + UI_view2d_region_to_view(&ac.ar->v2d, t->imval[0], t->imval[1], &xmouse, &ymouse); + side = (xmouse > CFRA) ? 'R' : 'L'; // XXX use t->frame_side + } + else { + /* normal transform - both sides of current frame are considered */ + side = 'B'; + } + + /* loop 1: count how many strips are selected (consider each strip as 2 points) */ + for (ale= anim_data.first; ale; ale= ale->next) { + /* only if a real NLA-track */ + if (ale->type == ANIMTYPE_NLATRACK) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* only consider selected strips */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + // TODO: we can make strips have handles later on... + if (strip->flag & NLASTRIP_FLAG_SELECT) { + if (FrameOnMouseSide(side, strip->start, (float)CFRA)) count++; + if (FrameOnMouseSide(side, strip->end, (float)CFRA)) count++; + } + } + } + } + + /* stop if trying to build list if nothing selected */ + if (count == 0) { + /* cleanup temp list */ + BLI_freelistN(&anim_data); + return; + } + + /* allocate memory for data */ + t->total= count; + + t->data= MEM_callocN(t->total*sizeof(TransData), "TransData(NLA Editor)"); + td= t->data; + t->customData= MEM_callocN(t->total*sizeof(TransDataNla), "TransDataNla (NLA Editor)"); + tdn= t->customData; + + /* loop 2: build transdata array */ + for (ale= anim_data.first; ale; ale= ale->next) { + /* only if a real NLA-track */ + if (ale->type == ANIMTYPE_NLATRACK) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* only consider selected strips */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + // TODO: we can make strips have handles later on... + if (strip->flag & NLASTRIP_FLAG_SELECT) { + if (FrameOnMouseSide(side, strip->start, (float)CFRA)) + { + /* init the 'extra' data for NLA strip handles first */ + tdn->strip= strip; + tdn->val= strip->start; + tdn->handle= 0; + + /* now, link the transform data up to this data */ + td->val= &tdn->val; + td->ival= tdn->val; + td->extra= tdn; + td++; + tdn++; + } + if (FrameOnMouseSide(side, strip->end, (float)CFRA)) + { + /* init the 'extra' data for NLA strip handles first */ + tdn->strip= strip; + tdn->val= strip->end; + tdn->handle= 1; + + /* now, link the transform data up to this data */ + td->val= &tdn->val; + td->ival= tdn->val; + td->extra= tdn; + td++; + tdn++; + } + } + } + } + } + + /* cleanup temp list */ + BLI_freelistN(&anim_data); +} + +/* ********************* ACTION EDITOR ****************** */ /* Called by special_aftertrans_update to make sure selected gp-frames replace * any other gp-frames which may reside on that frame (that are not selected). @@ -2744,19 +2877,6 @@ static void posttrans_action_clean (bAnimContext *ac, bAction *act) /* ----------------------------- */ -/* This function tests if a point is on the "mouse" side of the cursor/frame-marking */ -static short FrameOnMouseSide(char side, float frame, float cframe) -{ - /* both sides, so it doesn't matter */ - if (side == 'B') return 1; - - /* only on the named side */ - if (side == 'R') - return (frame >= cframe) ? 1 : 0; - else - return (frame <= cframe) ? 1 : 0; -} - /* fully select selected beztriples, but only include if it's on the right side of cfra */ static int count_fcurve_keys(FCurve *fcu, char side, float cfra) { @@ -3044,8 +3164,6 @@ static void createTransActionData(bContext *C, TransInfo *t) /* ********************* GRAPH EDITOR ************************* */ - - /* Helper function for createTransGraphEditData, which is reponsible for associating * source data with transform data */ @@ -3502,7 +3620,6 @@ void flushTransGraphData(TransInfo *t) } } - /* **************** IpoKey stuff, for Object TransData ********** */ /* while transforming */ @@ -4604,6 +4721,29 @@ void special_aftertrans_update(TransInfo *t) /* make sure all F-Curves are set correctly */ ANIM_editkeyframes_refresh(&ac); } + else if (t->spacetype == SPACE_NLA) { + SpaceNla *snla= (SpaceNla *)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; + + // 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 // if (t->mode==TFM_BONESIZE || t->mode==TFM_BONE_ENVELOPE) @@ -4682,24 +4822,6 @@ void special_aftertrans_update(TransInfo *t) } } -#if 0 // TRANSFORM_FIX_ME - else if (t->spacetype == SPACE_NLA) { - recalc_all_ipos(); // bad - synchronize_action_strips(); - - /* cleanup */ - for (base=t->scene->base.first; base; base=base->next) - base->flag &= ~(BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA); - - /* after transform, remove duplicate keyframes on a frame that resulted from transform */ - if ( (G.snla->flag & SNLA_NOTRANSKEYCULL)==0 && - ((cancelled == 0) || (duplicate)) ) - { - posttrans_nla_clean(t); - } - } -#endif - clear_trans_object_base_flags(t); #if 0 // TRANSFORM_FIX_ME @@ -4932,8 +5054,7 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_NLA) { t->flag |= T_POINTS|T_2D_EDIT; - // TRANSFORM_FIX_ME - //createTransNlaData(C, t); + createTransNlaData(C, t); } else if (t->spacetype == SPACE_SEQ) { t->flag |= T_POINTS|T_2D_EDIT; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 171665c9282..4c9592fb27a 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -102,6 +102,8 @@ #include "BLI_editVert.h" #include "BLI_rand.h" +#include "RNA_access.h" + #include "WM_types.h" #include "UI_resources.h" @@ -299,61 +301,6 @@ void recalcData(TransInfo *t) DAG_object_flush_update(G.scene, OBACT, OB_RECALC_OB|OB_RECALC_DATA); } } - } - else if (t->spacetype == SPACE_NLA) { - if (G.snla->lock) { - for (base=G.scene->base.first; base; base=base->next) { - if (base->flag & BA_HAS_RECALC_OB) - base->object->recalc |= OB_RECALC_OB; - if (base->flag & BA_HAS_RECALC_DATA) - base->object->recalc |= OB_RECALC_DATA; - - if (base->object->recalc) - base->object->ctime= -1234567.0f; // eveil! - - /* recalculate scale of selected nla-strips */ - if (base->object->nlastrips.first) { - Object *bob= base->object; - bActionStrip *strip; - - for (strip= bob->nlastrips.first; strip; strip= strip->next) { - if (strip->flag & ACTSTRIP_SELECT) { - float actlen= strip->actend - strip->actstart; - float len= strip->end - strip->start; - - strip->scale= len / (actlen * strip->repeat); - } - } - } - } - - DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); - } - else { - for (base=G.scene->base.first; base; base=base->next) { - /* recalculate scale of selected nla-strips */ - if (base->object && base->object->nlastrips.first) { - Object *bob= base->object; - bActionStrip *strip; - - for (strip= bob->nlastrips.first; strip; strip= strip->next) { - if (strip->flag & ACTSTRIP_SELECT) { - float actlen= strip->actend - strip->actstart; - float len= strip->end - strip->start; - - /* prevent 'negative' scaling */ - if (len < 0) { - SWAP(float, strip->start, strip->end); - len= fabs(len); - } - - /* calculate new scale */ - strip->scale= len / (actlen * strip->repeat); - } - } - } - } - } } #endif if (t->obedit) { @@ -422,6 +369,47 @@ void recalcData(TransInfo *t) } } + else if (t->spacetype == SPACE_NLA) { + TransData *td= t->data; + int i; + + /* for each point we've captured, look at its 'extra' data, which is basically a wrapper around the strip + * it is for + some extra storage for the values that get set, and use RNA to set this value (performing validation + * work so that we don't need to repeat it here) + */ + for (i = 0; i < t->total; i++, td++) + { + if (td->extra) + { + TransDataNla *tdn= td->extra; + NlaStrip *strip= tdn->strip; + + /* if we're just cancelling (i.e. the user aborted the transform), + * just restore the data by directly overwriting the values with the original + * ones (i.e. don't go through RNA), as we get some artifacts... + */ + if (t->state == TRANS_CANCEL) { + /* write the value set by the transform tools to the appropriate property using RNA */ + if (tdn->handle) + strip->end= tdn->val; + else + strip->start= tdn->val; + } + else { + PointerRNA strip_ptr; + + /* make RNA-pointer */ + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); + + /* write the value set by the transform tools to the appropriate property using RNA */ + if (tdn->handle) + RNA_float_set(&strip_ptr, "end_frame", tdn->val); + else + RNA_float_set(&strip_ptr, "start_frame", tdn->val); + } + } + } + } else if (t->obedit) { if ELEM(t->obedit->type, OB_CURVE, OB_SURF) { Curve *cu= t->obedit->data; @@ -866,7 +854,7 @@ void postTrans (TransInfo *t) if(sima->flag & SI_LIVE_UNWRAP) ED_uvedit_live_unwrap_end(t->state == TRANS_CANCEL); } - else if(t->spacetype==SPACE_ACTION) { + else if(ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA)) { if (t->customData) MEM_freeN(t->customData); } diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 769001b30a8..f7ddd18452e 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -607,6 +607,19 @@ void transform_keymap_for_space(struct wmWindowManager *wm, struct ListBase *key km = WM_keymap_add_item(keymap, "TFM_OT_resize", SKEY, KM_PRESS, 0, 0); break; + case SPACE_NLA: + km= WM_keymap_add_item(keymap, "TFM_OT_transform", GKEY, KM_PRESS, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TIME_TRANSLATE); + + km= WM_keymap_add_item(keymap, "TFM_OT_transform", EVT_TWEAK_S, KM_ANY, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TIME_TRANSLATE); + + km= WM_keymap_add_item(keymap, "TFM_OT_transform", EKEY, KM_PRESS, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TIME_EXTEND); + + km= WM_keymap_add_item(keymap, "TFM_OT_transform", SKEY, KM_PRESS, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TIME_SCALE); + break; case SPACE_NODE: km= WM_keymap_add_item(keymap, "TFM_OT_translation", GKEY, KM_PRESS, 0, 0); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 0a7d17adc09..b55b4e431fb 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -43,7 +43,18 @@ static void rna_NlaStrip_start_frame_set(PointerRNA *ptr, float value) { NlaStrip *data= (NlaStrip*)ptr->data; - CLAMP(value, 1, data->end); + + /* clamp value to lie within valid limits + * - cannot start past the end of the strip + some flexibility threshold + * - cannot start before the previous strip (if present) ends + * - minimum frame is 1.0f (this can be changed) + */ + if (data->prev) { + CLAMP(value, data->prev->end, data->end-0.1f); + } + else { + CLAMP(value, 1, data->end); + } data->start= value; } @@ -52,8 +63,17 @@ static void rna_NlaStrip_end_frame_set(PointerRNA *ptr, float value) NlaStrip *data= (NlaStrip*)ptr->data; float len, actlen; - /* clamp value to lie within valid limits - must not have zero or negative length strip */ - CLAMP(value, data->start+0.1f, MAXFRAME); + /* clamp value to lie within valid limits + * - must not have zero or negative length strip, so cannot start before the first frame + * + some minimum-strip-length threshold + * - cannot end later than the start of the next strip (if present) + */ + if (data->next) { + CLAMP(value, data->start+0.1f, data->next->start); + } + else { + CLAMP(value, data->start+0.1f, MAXFRAME); + } data->end= value; /* calculate the lengths the strip and its action (if applicable) */