diff options
Diffstat (limited to 'source/blender/editors/animation/keyframes_general.c')
-rw-r--r-- | source/blender/editors/animation/keyframes_general.c | 364 |
1 files changed, 304 insertions, 60 deletions
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 5073cfe08b8..aea8c2407b4 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -34,6 +34,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_utildefines.h" #include "DNA_anim_types.h" #include "DNA_object_types.h" @@ -42,6 +43,12 @@ #include "BKE_fcurve.h" #include "BKE_utildefines.h" +#include "BKE_report.h" +#include "BKE_library.h" +#include "BKE_global.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" #include "ED_anim_api.h" #include "ED_keyframing.h" @@ -82,6 +89,12 @@ void delete_fcurve_key(FCurve *fcu, int index, short do_recalc) /* Delete this keyframe */ memmove(&fcu->bezt[index], &fcu->bezt[index+1], sizeof(BezTriple)*(fcu->totvert-index-1)); fcu->totvert--; + + if (fcu->totvert == 0) { + if (fcu->bezt) + MEM_freeN(fcu->bezt); + fcu->bezt= NULL; + } /* recalc handles - only if it won't cause problems */ if (do_recalc) @@ -106,11 +119,18 @@ void delete_fcurve_keys(FCurve *fcu) } /* Free the array of BezTriples if there are not keyframes */ - if (fcu->totvert == 0) { - if (fcu->bezt) - MEM_freeN(fcu->bezt); - fcu->bezt= NULL; - } + if(fcu->totvert == 0) + clear_fcurve_keys(fcu); +} + + +void clear_fcurve_keys(FCurve *fcu) +{ + if (fcu->bezt) + MEM_freeN(fcu->bezt); + fcu->bezt= NULL; + + fcu->totvert= 0; } /* ---------------- */ @@ -366,7 +386,7 @@ void sample_fcurve (FCurve *fcu) int sfra, range; int i, n, nIndex; - if(fcu->bezt==NULL) /* ignore baked */ + if (fcu->bezt==NULL) /* ignore baked */ return; /* find selected keyframes... once pair has been found, add keyframes */ @@ -433,8 +453,10 @@ void sample_fcurve (FCurve *fcu) */ /* globals for copy/paste data (like for other copy/paste buffers) */ -ListBase animcopybuf = {NULL, NULL}; +static ListBase animcopybuf = {NULL, NULL}; static float animcopy_firstframe= 999999999.0f; +static float animcopy_lastframe= -999999999.0f; +static float animcopy_cfra= 0.0; /* datatype for use in copy/paste buffer */ typedef struct tAnimCopybufItem { @@ -447,6 +469,8 @@ typedef struct tAnimCopybufItem { int totvert; /* number of keyframes stored for this channel */ BezTriple *bezt; /* keyframes in buffer */ + + short id_type; /* Result of GS(id->name)*/ } tAnimCopybufItem; @@ -475,6 +499,7 @@ void free_anim_copybuf (void) /* restore initial state */ animcopybuf.first= animcopybuf.last= NULL; animcopy_firstframe= 999999999.0f; + animcopy_lastframe= -999999999.0f; } /* ------------------- */ @@ -483,6 +508,7 @@ void free_anim_copybuf (void) short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data) { bAnimListElem *ale; + Scene *scene= ac->scene; /* clear buffer first */ free_anim_copybuf(); @@ -491,7 +517,7 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data) for (ale= anim_data->first; ale; ale= ale->next) { FCurve *fcu= (FCurve *)ale->key_data; tAnimCopybufItem *aci; - BezTriple *bezt, *newbuf; + BezTriple *bezt, *nbezt, *newbuf; int i; /* firstly, check if F-Curve has any selected keyframes @@ -504,6 +530,7 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data) /* init copybuf item info */ aci= MEM_callocN(sizeof(tAnimCopybufItem), "AnimCopybufItem"); aci->id= ale->id; + aci->id_type= GS(ale->id->name); aci->grp= fcu->grp; aci->rna_path= MEM_dupallocN(fcu->rna_path); aci->array_index= fcu->array_index; @@ -521,7 +548,11 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data) memcpy(newbuf, aci->bezt, sizeof(BezTriple)*(aci->totvert)); /* copy current beztriple across too */ - *(newbuf + aci->totvert)= *bezt; + nbezt= &newbuf[aci->totvert]; + *nbezt= *bezt; + + /* ensure copy buffer is selected so pasted keys are selected */ + BEZ_SEL(nbezt); /* free old array and set the new */ if (aci->bezt) MEM_freeN(aci->bezt); @@ -531,6 +562,8 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data) /* check if this is the earliest frame encountered so far */ if (bezt->vec[1][0] < animcopy_firstframe) animcopy_firstframe= bezt->vec[1][0]; + if (bezt->vec[1][0] > animcopy_lastframe) + animcopy_lastframe= bezt->vec[1][0]; } } @@ -539,76 +572,287 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data) /* check if anything ended up in the buffer */ if (ELEM(NULL, animcopybuf.first, animcopybuf.last)) return -1; - + + /* incase 'relative' paste method is used */ + animcopy_cfra= CFRA; + /* everything went fine */ return 0; } +/* ------------------- */ + +/* most strict method: exact matches only */ +static tAnimCopybufItem *pastebuf_match_path_full(FCurve *fcu, const short from_single, const short to_simple) +{ + tAnimCopybufItem *aci; + + for (aci= animcopybuf.first; aci; aci= aci->next) { + /* check that paths exist */ + if (to_simple || (aci->rna_path && fcu->rna_path)) { + if (to_simple || (strcmp(aci->rna_path, fcu->rna_path) == 0)) { + if ((from_single) || (aci->array_index == fcu->array_index)) + break; + } + } + } + + return aci; +} + +/* medium match strictness: path match only (i.e. ignore ID) */ +static tAnimCopybufItem *pastebuf_match_path_property(FCurve *fcu, const short from_single, const short UNUSED(to_simple)) +{ + tAnimCopybufItem *aci; + + for (aci= animcopybuf.first; aci; aci= aci->next) { + /* check that paths exist */ + if (aci->rna_path && fcu->rna_path) { + /* find the property of the fcurve and compare against the end of the tAnimCopybufItem + * more involved since it needs to to path lookups. + * This is not 100% reliable since the user could be editing the curves on a path that wont + * resolve, or a bone could be renamed after copying for eg. but in normal copy & paste + * this should work out ok. + */ + if (BLI_findindex(which_libbase(G.main, aci->id_type), aci->id) == -1) { + /* pedantic but the ID could have been removed, and beats crashing! */ + printf("paste_animedit_keys: error ID has been removed!\n"); + } + else { + PointerRNA id_ptr, rptr; + PropertyRNA *prop; + + RNA_id_pointer_create(aci->id, &id_ptr); + RNA_path_resolve(&id_ptr, aci->rna_path, &rptr, &prop); + + if (prop) { + const char *identifier= RNA_property_identifier(prop); + int len_id = strlen(identifier); + int len_path = strlen(fcu->rna_path); + if (len_id <= len_path) { + /* note, paths which end with "] will fail with this test - Animated ID Props */ + if (strcmp(identifier, fcu->rna_path + (len_path-len_id))==0) { + if ((from_single) || (aci->array_index == fcu->array_index)) + break; + } + } + } + else { + printf("paste_animedit_keys: failed to resolve path id:%s, '%s'!\n", aci->id->name, aci->rna_path); + } + } + } + } + + return aci; +} + +/* least strict matching heuristic: indices only */ +static tAnimCopybufItem *pastebuf_match_index_only(FCurve *fcu, const short from_single, const short UNUSED(to_simple)) +{ + tAnimCopybufItem *aci; + + for (aci= animcopybuf.first; aci; aci= aci->next) { + /* check that paths exist */ + if ((from_single) || (aci->array_index == fcu->array_index)) { + break; + } + } + + return aci; +} + +/* ................ */ + +/* helper for paste_animedit_keys() - performs the actual pasting */ +static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode) +{ + BezTriple *bezt; + int i; + + /* First de-select existing FCuvre */ + for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) { + bezt->f2 &= ~SELECT; + } + + /* mix mode with existing data */ + switch (merge_mode) { + case KEYFRAME_PASTE_MERGE_MIX: + /* do-nothing */ + break; + + case KEYFRAME_PASTE_MERGE_OVER: + /* remove all keys */ + clear_fcurve_keys(fcu); + break; + + case KEYFRAME_PASTE_MERGE_OVER_RANGE: + case KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL: + { + float f_min; + float f_max; + + if (merge_mode==KEYFRAME_PASTE_MERGE_OVER_RANGE) { + f_min= aci->bezt[0].vec[1][0] + offset; + f_max= aci->bezt[aci->totvert-1].vec[1][0] + offset; + } + else { /* Entire Range */ + f_min= animcopy_firstframe + offset; + f_max= animcopy_lastframe + offset; + } + + /* remove keys in range */ + if (f_min < f_max) { + /* select verts in range for removal */ + for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) { + if ((f_min < bezt[0].vec[1][0]) && (bezt[0].vec[1][0] < f_max)) { + bezt->f2 |= SELECT; + } + } + + /* remove frames in the range */ + delete_fcurve_keys(fcu); + } + break; + } + } + + /* just start pasting, with the the first keyframe on the current frame, and so on */ + for (i=0, bezt=aci->bezt; i < aci->totvert; i++, bezt++) { + /* temporarily apply offset to src beztriple while copying */ + bezt->vec[0][0] += offset; + bezt->vec[1][0] += offset; + bezt->vec[2][0] += offset; + + /* insert the keyframe + * NOTE: no special flags here for now + */ + insert_bezt_fcurve(fcu, bezt, 0); + + /* un-apply offset from src beztriple after copying */ + bezt->vec[0][0] -= offset; + bezt->vec[1][0] -= offset; + bezt->vec[2][0] -= offset; + } + + /* recalculate F-Curve's handles? */ + calchandles_fcurve(fcu); +} + +/* ------------------- */ + +EnumPropertyItem keyframe_paste_offset_items[] = { + {KEYFRAME_PASTE_OFFSET_CFRA_START, "START", 0, "Frame Start", "Paste keys starting at current frame"}, + {KEYFRAME_PASTE_OFFSET_CFRA_END, "END", 0, "Frame End", "Paste keys ending at current frame"}, + {KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE, "RELATIVE", 0, "Frame Relative", "Paste keys relative to the current frame when copying"}, + {KEYFRAME_PASTE_OFFSET_NONE, "NONE", 0, "No Offset", "Paste keys from original time"}, + {0, NULL, 0, NULL, NULL}}; + +EnumPropertyItem keyframe_paste_merge_items[] = { + {KEYFRAME_PASTE_MERGE_MIX, "MIX", 0, "Mix", "Overlay existing with new keys"}, + {KEYFRAME_PASTE_MERGE_OVER, "OVER_ALL", 0, "Overwrite All", "Replace all keys"}, + {KEYFRAME_PASTE_MERGE_OVER_RANGE, "OVER_RANGE", 0, "Overwrite Range", "Overwrite keys in pasted range"}, + {KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL, "OVER_RANGE_ALL", 0, "Overwrite Entire Range", "Overwrite keys in pasted range, using the range of all copied keys."}, + {0, NULL, 0, NULL, NULL}}; + + /* This function pastes data from the keyframes copy/paste buffer */ -short paste_animedit_keys (bAnimContext *ac, ListBase *anim_data) +short paste_animedit_keys (bAnimContext *ac, ListBase *anim_data, + const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode) { bAnimListElem *ale; + const Scene *scene= (ac->scene); - const float offset = (float)(CFRA - animcopy_firstframe); - short no_name= 0; + const short from_single= (animcopybuf.first == animcopybuf.last); + const short to_simple= (anim_data->first == anim_data->last); + + float offset = 0.0f; + int pass; + /* check if buffer is empty */ - if (ELEM(NULL, animcopybuf.first, animcopybuf.last)) { - //error("No data in buffer to paste"); + if (animcopybuf.first == NULL) { + BKE_report(ac->reports, RPT_WARNING, "No data in buffer to paste"); + return -1; + } + + if (anim_data->first == NULL) { + BKE_report(ac->reports, RPT_WARNING, "No FCurves to paste into"); return -1; } - /* check if single channel in buffer (disregard names if so) */ - if (animcopybuf.first == animcopybuf.last) - no_name= 1; - /* from selected channels */ - for (ale= anim_data->first; ale; ale= ale->next) { - FCurve *fcu = (FCurve *)ale->data; /* destination F-Curve */ - tAnimCopybufItem *aci= NULL; - BezTriple *bezt; - int i; + /* mathods of offset */ + switch(offset_mode) { + case KEYFRAME_PASTE_OFFSET_CFRA_START: + offset= (float)(CFRA - animcopy_firstframe); + break; + case KEYFRAME_PASTE_OFFSET_CFRA_END: + offset= (float)(CFRA - animcopy_lastframe); + break; + case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: + offset= (float)(CFRA - animcopy_cfra); + break; + case KEYFRAME_PASTE_OFFSET_NONE: + offset= 0.0f; + break; + } + + if (from_single && to_simple) { + /* 1:1 match, no tricky checking, just paste */ + FCurve *fcu; + tAnimCopybufItem *aci; + + ale= anim_data->first; + fcu= (FCurve *)ale->data; /* destination F-Curve */ + aci= animcopybuf.first; - /* find buffer item to paste from - * - if names don't matter (i.e. only 1 channel in buffer), don't check id/group - * - if names do matter, only check if id-type is ok for now (group check is not that important) - * - most importantly, rna-paths should match (array indices are unimportant for now) + paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode); + } + else { + /* from selected channels + * This "passes" system aims to try to find "matching" channels to paste keyframes + * into with increasingly loose matching heuristics. The process finishes when at least + * one F-Curve has been pasted into. */ - // TODO: the matching algorithm here is pathetic! - for (aci= animcopybuf.first; aci; aci= aci->next) { - /* check that paths exist */ - if (aci->rna_path && fcu->rna_path) { - // FIXME: this breaks for bone names! - if (strcmp(aci->rna_path, fcu->rna_path) == 0) { - /* should be a match unless there's more than one of these */ - if ((no_name) || (aci->array_index == fcu->array_index)) + for (pass= 0; pass < 3; pass++) { + unsigned int totmatch= 0; + + for (ale= anim_data->first; ale; ale= ale->next) { + /* find buffer item to paste from + * - if names don't matter (i.e. only 1 channel in buffer), don't check id/group + * - if names do matter, only check if id-type is ok for now (group check is not that important) + * - most importantly, rna-paths should match (array indices are unimportant for now) + */ + FCurve *fcu = (FCurve *)ale->data; /* destination F-Curve */ + tAnimCopybufItem *aci= NULL; + + switch (pass) { + case 0: + /* most strict, must be exact path match data_path & index */ + aci= pastebuf_match_path_full(fcu, from_single, to_simple); + break; + + case 1: + /* less strict, just compare property names */ + aci= pastebuf_match_path_property(fcu, from_single, to_simple); + break; + + case 2: + /* Comparing properties gave no results, so just do index comparisons */ + aci= pastebuf_match_index_only(fcu, from_single, to_simple); break; } - } - } - - - /* copy the relevant data from the matching buffer curve */ - if (aci) { - /* just start pasting, with the the first keyframe on the current frame, and so on */ - for (i=0, bezt=aci->bezt; i < aci->totvert; i++, bezt++) { - /* temporarily apply offset to src beztriple while copying */ - bezt->vec[0][0] += offset; - bezt->vec[1][0] += offset; - bezt->vec[2][0] += offset; - /* insert the keyframe - * NOTE: no special flags here for now - */ - insert_bezt_fcurve(fcu, bezt, 0); - - /* un-apply offset from src beztriple after copying */ - bezt->vec[0][0] -= offset; - bezt->vec[1][0] -= offset; - bezt->vec[2][0] -= offset; + /* copy the relevant data from the matching buffer curve */ + if (aci) { + totmatch++; + paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode); + } } - /* recalculate F-Curve's handles? */ - calchandles_fcurve(fcu); + /* dont continue if some fcurves were pasted */ + if (totmatch) + break; } } |