From 87c05f7836e5b646e0053bd23f138bc36ab18773 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Tue, 23 Dec 2008 11:02:39 +0000 Subject: 2.5 Action Editor - Big WIP Commit * Brought back backend for editing keyframes IPO/IPO-Curves. Did some refactoring work here that will still have to be verified when operators using them are added. * Animation channel filtering code now returns the number of channels filtered (for Action Editor to set totrect of channels - TODO still!) * View2D - made function to check if mouse is in View2D scrollers an API function * Renamed keyframe related files. The old names were too clumsy. * Started porting click-select operators for Action Editor. These don't work currently, as the events are being stolen by the markers. This needs to be fixed ASAP. --- source/blender/editors/animation/anim_draw.c | 35 + source/blender/editors/animation/anim_filter.c | 207 +- .../editors/animation/anim_keyframes_draw.c | 704 ------ source/blender/editors/animation/anim_keyframing.c | 2287 -------------------- source/blender/editors/animation/keyframes_draw.c | 704 ++++++ source/blender/editors/animation/keyframes_edit.c | 785 +++++++ source/blender/editors/animation/keyframing.c | 2287 ++++++++++++++++++++ source/blender/editors/include/ED_anim_api.h | 11 +- source/blender/editors/include/ED_keyframes_edit.h | 104 + source/blender/editors/include/ED_markers.h | 4 +- source/blender/editors/include/UI_view2d.h | 4 + source/blender/editors/interface/view2d.c | 29 + source/blender/editors/interface/view2d_ops.c | 34 +- source/blender/editors/space_action/SConscript | 2 +- .../blender/editors/space_action/action_intern.h | 8 + source/blender/editors/space_action/action_ops.c | 91 + .../blender/editors/space_action/action_select.c | 551 +++++ source/blender/editors/space_action/space_action.c | 19 +- 18 files changed, 4775 insertions(+), 3091 deletions(-) delete mode 100644 source/blender/editors/animation/anim_keyframes_draw.c delete mode 100644 source/blender/editors/animation/anim_keyframing.c create mode 100644 source/blender/editors/animation/keyframes_draw.c create mode 100644 source/blender/editors/animation/keyframes_edit.c create mode 100644 source/blender/editors/animation/keyframing.c create mode 100644 source/blender/editors/include/ED_keyframes_edit.h create mode 100644 source/blender/editors/space_action/action_ops.c create mode 100644 source/blender/editors/space_action/action_select.c (limited to 'source') diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 3765337f990..f89a1eb96b8 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -254,4 +254,39 @@ void ANIM_nla_mapping_draw(gla2DDrawInfo *di, Object *ob, short restore) } } +/* Apply/Unapply NLA mapping to all keyframes in the nominated IPO block + * - restore = whether to map points back to ipo-time + * - only_keys = whether to only adjust the location of the center point of beztriples + */ +// was called actstrip_map_ipo_keys() +void ANIM_nla_mapping_apply(Object *ob, Ipo *ipo, short restore, short only_keys) +{ + IpoCurve *icu; + BezTriple *bezt; + int a; + + if (ipo==NULL) return; + + /* loop through all ipo curves, adjusting the times of the selected keys */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + for (a=0, bezt=icu->bezt; atotvert; a++, bezt++) { + /* are the times being adjusted for editing, or has editing finished */ + if (restore) { + if (only_keys == 0) { + bezt->vec[0][0]= get_action_frame(ob, bezt->vec[0][0]); + bezt->vec[2][0]= get_action_frame(ob, bezt->vec[2][0]); + } + bezt->vec[1][0]= get_action_frame(ob, bezt->vec[1][0]); + } + else { + if (only_keys == 0) { + bezt->vec[0][0]= get_action_frame_inv(ob, bezt->vec[0][0]); + bezt->vec[2][0]= get_action_frame_inv(ob, bezt->vec[2][0]); + } + bezt->vec[1][0]= get_action_frame_inv(ob, bezt->vec[1][0]); + } + } + } +} + /* *************************************************** */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index d4b9f9fff93..d1f41047475 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -490,12 +490,13 @@ bAnimListElem *make_new_animlistelem (void *data, short datatype, void *owner, s /* ----------------------------------------- */ -static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel *achan, int filter_mode, void *owner, short ownertype) +static int animdata_filter_actionchannel (ListBase *anim_data, bActionChannel *achan, int filter_mode, void *owner, short ownertype) { bAnimListElem *ale = NULL; bConstraintChannel *conchan; IpoCurve *icu; short owned= (owner && ownertype)? 1 : 0; + int items = 0; /* only work with this channel and its subchannels if it is visible */ if (!(filter_mode & ANIMFILTER_VISIBLE) || VISIBLE_ACHAN(achan)) { @@ -510,19 +511,20 @@ static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } } else { /* for insert key... this check could be improved */ - return; + //return; // FIXME... } /* check if expanded - if not, continue on to next animion channel */ if (EXPANDED_ACHAN(achan) == 0 && (filter_mode & ANIMFILTER_ONLYICU)==0) { /* only exit if we don't need to include constraint channels for group-channel keyframes */ if ( !(filter_mode & ANIMFILTER_IPOKEYS) || (achan->grp == NULL) || (EXPANDED_AGRP(achan->grp)==0) ) - return; + return items; } /* ipo channels */ @@ -534,6 +536,7 @@ static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } @@ -546,6 +549,7 @@ static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } } @@ -562,6 +566,7 @@ static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } @@ -574,12 +579,12 @@ static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel /* check if this conchan should only be included if it is selected */ if (!(filter_mode & ANIMFILTER_SEL) || SEL_CONCHAN(conchan)) { if (filter_mode & ANIMFILTER_IPOKEYS) { - if (ale) BLI_addtail(anim_data, ale); ale= make_new_animlistelem(conchan, ANIMTYPE_CONCHAN2, achan, ANIMTYPE_ACHAN); if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } else { @@ -588,6 +593,7 @@ static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } } @@ -597,14 +603,18 @@ static void animdata_filter_animionchannel (ListBase *anim_data, bActionChannel } } } + + /* return the number of items added to the list */ + return items; } -static void animdata_filter_action (ListBase *anim_data, bAction *act, int filter_mode, void *owner, short ownertype) +static int animdata_filter_action (ListBase *anim_data, bAction *act, int filter_mode, void *owner, short ownertype) { bAnimListElem *ale=NULL; bActionGroup *agrp; bActionChannel *achan, *lastchan=NULL; short owned= (owner && ownertype) ? 1 : 0; + int items = 0; /* loop over groups */ for (agrp= act->groups.first; agrp; agrp= agrp->next) { @@ -616,6 +626,7 @@ static void animdata_filter_action (ListBase *anim_data, bAction *act, int filte if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } } @@ -642,7 +653,7 @@ static void animdata_filter_action (ListBase *anim_data, bAction *act, int filte { if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) { for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) { - animdata_filter_animionchannel(anim_data, achan, filter_mode, owner, ownertype); + items += animdata_filter_actionchannel(anim_data, achan, filter_mode, owner, ownertype); } /* remove group from filtered list if last element is group @@ -653,6 +664,7 @@ static void animdata_filter_action (ListBase *anim_data, bAction *act, int filte (ale->data == agrp) && (agrp->channels.first) ) { BLI_freelinkN(anim_data, ale); + items--; } } } @@ -661,19 +673,22 @@ static void animdata_filter_action (ListBase *anim_data, bAction *act, int filte /* loop over un-grouped animion channels (only if we're not only considering those channels in the animive group) */ if (!(filter_mode & ANIMFILTER_ACTGROUPED)) { - for (achan=(lastchan)?lastchan->next:act->chanbase.first; achan; achan=achan->next) { - animdata_filter_animionchannel(anim_data, achan, filter_mode, owner, ownertype); + for (achan=(lastchan)?(lastchan->next):(act->chanbase.first); achan; achan=achan->next) { + items += animdata_filter_actionchannel(anim_data, achan, filter_mode, owner, ownertype); } } + + /* return the number of items added to the list */ + return items; } -static void animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_mode, void *owner, short ownertype) +static int animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_mode, void *owner, short ownertype) { bAnimListElem *ale; KeyBlock *kb; IpoCurve *icu; short owned= (owner && ownertype)? 1 : 0; - int i; + int i, items=0; /* are we filtering for display or editing */ if (filter_mode & ANIMFILTER_FORDRAWING) { @@ -705,6 +720,7 @@ static void animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_ if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } else { @@ -715,6 +731,7 @@ static void animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_ if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } else { @@ -723,21 +740,26 @@ static void animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_ if (ale) { if (owned) ale->id= owner; BLI_addtail(anim_data, ale); + items++; } } } } } + + /* return the number of items added to the list */ + return items; } #if 0 // FIXME: switch this to use the bDopeSheet... -static void animdata_filter_gpencil (ListBase *anim_data, bScreen *sc, int filter_mode) +static int animdata_filter_gpencil (ListBase *anim_data, bScreen *sc, int filter_mode) { bAnimListElem *ale; ScrArea *sa, *curarea; bGPdata *gpd; bGPDlayer *gpl; + int items = 0; /* check if filtering types are appropriate */ if ( !(filter_mode & (ANIMFILTER_IPOKEYS|ANIMFILTER_ONLYICU|ANIMFILTER_ACTGROUPED)) ) @@ -762,7 +784,10 @@ static void animdata_filter_gpencil (ListBase *anim_data, bScreen *sc, int filte if ((filter_mode & ANIMFILTER_FORDRAWING) && (gpd->layers.first)) { /* add to list */ ale= make_new_animlistelem(gpd, ANIMTYPE_GPDATABLOCK, sa, ANIMTYPE_SPECIALDATA); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* only add layers if they will be visible (if drawing channels) */ @@ -775,26 +800,36 @@ static void animdata_filter_gpencil (ListBase *anim_data, bScreen *sc, int filte if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_GPL(gpl)) { /* add to list */ ale= make_new_animlistelem(gpl, ANIMTYPE_GPLAYER, gpd, ANIMTYPE_GPDATABLOCK); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } } } } } } + + /* return the number of items added to the list */ + return items; } #endif -static void animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) +static int animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) { bAnimListElem *ale=NULL; Object *ob= base->object; IpoCurve *icu; + int items = 0; /* include materials-expand widget? */ if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & (ANIMFILTER_IPOKEYS|ANIMFILTER_ONLYICU))) { ale= make_new_animlistelem(ob, ANIMTYPE_FILLMATD, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add materials? */ @@ -812,7 +847,10 @@ static void animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads // hmm... do we need to store the index of this material in the array anywhere? if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) { ale= make_new_animlistelem(ma, ANIMTYPE_DSMAT, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add material's ipo-curve channels? */ @@ -828,25 +866,33 @@ static void animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads /* make owner the material not object, so that indent is not just object level */ ale->id= (ID *)ma; BLI_addtail(anim_data, ale); + items++; } } } } } } + + /* return the number of items added to the list */ + return items; } -static void animdata_filter_dopesheet_cam (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) +static int animdata_filter_dopesheet_cam (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) { bAnimListElem *ale=NULL; Object *ob= base->object; Camera *ca= (Camera *)ob->data; IpoCurve *icu; + int items = 0; /* include camera-expand widget? */ if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) { ale= make_new_animlistelem(ca, ANIMTYPE_DSCAM, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add camera ipo-curve channels? */ @@ -861,24 +907,32 @@ static void animdata_filter_dopesheet_cam (ListBase *anim_data, bDopeSheet *ads, if (ale) { /* make owner the material not object, so that indent is not just object level */ ale->id= (ID *)ca; - BLI_addtail(anim_data, ale); + BLI_addtail(anim_data, ale); + items++; } } } } + + /* return the number of items added to the list */ + return items; } -static void animdata_filter_dopesheet_lamp (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) +static int animdata_filter_dopesheet_lamp (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) { bAnimListElem *ale=NULL; Object *ob= base->object; Lamp *la= (Lamp *)ob->data; IpoCurve *icu; + int items = 0; /* include lamp-expand widget? */ if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) { ale= make_new_animlistelem(la, ANIMTYPE_DSLAM, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add lamp ipo-curve channels? */ @@ -893,24 +947,32 @@ static void animdata_filter_dopesheet_lamp (ListBase *anim_data, bDopeSheet *ads if (ale) { /* make owner the material not object, so that indent is not just object level */ ale->id= (ID *)la; - BLI_addtail(anim_data, ale); + BLI_addtail(anim_data, ale); + items++; } } } } + + /* return the number of items added to the list */ + return items; } -static void animdata_filter_dopesheet_curve (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) +static int animdata_filter_dopesheet_curve (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) { bAnimListElem *ale=NULL; Object *ob= base->object; Curve *cu= (Curve *)ob->data; IpoCurve *icu; + int items = 0; /* include curve-expand widget? */ if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) { ale= make_new_animlistelem(cu, ANIMTYPE_DSCUR, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add curve ipo-curve channels? */ @@ -925,40 +987,51 @@ static void animdata_filter_dopesheet_curve (ListBase *anim_data, bDopeSheet *ad if (ale) { /* make owner the material not object, so that indent is not just object level */ ale->id= (ID *)cu; - BLI_addtail(anim_data, ale); + BLI_addtail(anim_data, ale); + items++; } } } } + + /* return the number of items added to the list */ + return items; } -static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) +static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) { bAnimListElem *ale=NULL; Scene *sce= (Scene *)ads->source; Object *ob= base->object; Key *key= ob_get_key(ob); IpoCurve *icu; + int items = 0; /* add this object as a channel first */ if (!(filter_mode & ANIMFILTER_ONLYICU) && !(filter_mode & ANIMFILTER_IPOKEYS)) { /* check if filtering by selection */ if ( !(filter_mode & ANIMFILTER_SEL) || ((base->flag & SELECT) || (base == sce->basact)) ) { ale= make_new_animlistelem(base, ANIMTYPE_OBJECT, NULL, ANIMTYPE_NONE); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } } /* if collapsed, don't go any further (unless adding keyframes only) */ if ( (EXPANDED_OBJC(ob) == 0) && !(filter_mode & (ANIMFILTER_IPOKEYS|ANIMFILTER_ONLYICU)) ) - return; + return items; /* IPO? */ if ((ob->ipo) && !(ads->filterflag & ADS_FILTER_NOIPOS)) { /* include ipo-expand widget? */ if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) { ale= make_new_animlistelem(ob, ANIMTYPE_FILLIPOD, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add ipo-curve channels? */ @@ -970,7 +1043,10 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, /* only if selected (if checking for selection) */ if ( !(filter_mode & ANIMFILTER_SEL) || (SEL_ICU(icu)) ) { ale= make_new_animlistelem(icu, ANIMTYPE_ICU, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } } } @@ -984,13 +1060,14 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, if (ale) { ale->id= (ID *)ob; // err.... is this a good idea? BLI_addtail(anim_data, ale); + items++; } } /* add ipo-curve channels? */ if (EXPANDED_ACTC(ob->action) || !(filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_FORDRAWING))) { // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?) - animdata_filter_action(anim_data, ob->action, filter_mode, ob, ANIMTYPE_OBJECT); + items += animdata_filter_action(anim_data, ob->action, filter_mode, ob, ANIMTYPE_OBJECT); } } @@ -999,18 +1076,21 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, /* include shapekey-expand widget? */ if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & (ANIMFILTER_IPOKEYS|ANIMFILTER_ONLYICU))) { ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add channels */ if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_IPOKEYS) || (filter_mode & ANIMFILTER_ONLYICU)) { - animdata_filter_shapekey (anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT); + items += animdata_filter_shapekey (anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT); } } /* Materials? */ if ((ob->totcol) && !(ads->filterflag & ADS_FILTER_NOMAT)) - animdata_filter_dopesheet_mats(anim_data, ads, base, filter_mode); + items += animdata_filter_dopesheet_mats(anim_data, ads, base, filter_mode); /* Object Data */ switch (ob->type) { @@ -1018,21 +1098,21 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, { Camera *ca= (Camera *)ob->data; if ((ca->ipo) && !(ads->filterflag & ADS_FILTER_NOCAM)) - animdata_filter_dopesheet_cam(anim_data, ads, base, filter_mode); + items += animdata_filter_dopesheet_cam(anim_data, ads, base, filter_mode); } break; case OB_LAMP: /* ---------- Lamp ----------- */ { Lamp *la= (Lamp *)ob->data; if ((la->ipo) && !(ads->filterflag & ADS_FILTER_NOLAM)) - animdata_filter_dopesheet_lamp(anim_data, ads, base, filter_mode); + items += animdata_filter_dopesheet_lamp(anim_data, ads, base, filter_mode); } break; case OB_CURVE: /* ------- Curve ---------- */ { Curve *cu= (Curve *)ob->data; if ((cu->ipo) && !(ads->filterflag & ADS_FILTER_NOCUR)) - animdata_filter_dopesheet_curve(anim_data, ads, base, filter_mode); + items += animdata_filter_dopesheet_curve(anim_data, ads, base, filter_mode); } break; } @@ -1046,7 +1126,10 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, && !(filter_mode & ANIMFILTER_IPOKEYS) ) { ale= make_new_animlistelem(ob, ANIMTYPE_FILLCOND, base, ANIMTYPE_OBJECT); - if (ale) BLI_addtail(anim_data, ale); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } } /* add constraint channels? */ @@ -1062,6 +1145,7 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, if (ale) { ale->id= (ID *)ob; BLI_addtail(anim_data, ale); + items++; } } else { @@ -1069,6 +1153,7 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, if (ale) { ale->id= (ID *)ob; BLI_addtail(anim_data, ale); + items++; } } } @@ -1076,18 +1161,22 @@ static void animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, } } } + + /* return the number of items added to the list */ + return items; } // TODO: implement pinning... (if and when pinning is done, what we need to do is to provide freeing mechanisms - to protect against data that was deleted) -static void animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int filter_mode) +static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int filter_mode) { Scene *sce= (Scene *)ads->source; Base *base; + int items = 0; /* check that we do indeed have a scene */ if ((ads->source == NULL) || (GS(ads->source->name)!=ID_SCE)) { printf("DopeSheet Error: Not scene! \n"); - return; + return 0; } /* loop over all bases in the scene */ @@ -1199,21 +1288,27 @@ static void animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int } /* since we're still here, this object should be usable */ - animdata_filter_dopesheet_ob(anim_data, ads, base, filter_mode); + items += animdata_filter_dopesheet_ob(anim_data, ads, base, filter_mode); } } + + /* return the number of items in the list */ + return items; } /* ----------- Public API --------------- */ -/* This function filters the active data source to leave only the desired - * data types. 'Public' api call. +/* This function filters the active data source to leave only animation channels suitable for + * usage by the caller. It will return the length of the list + * * *act_data: is a pointer to a ListBase, to which the filtered animation channels * will be placed for use. * filter_mode: how should the data be filtered - bitmapping accessed flags */ -void ANIM_animdata_filter (ListBase *anim_data, int filter_mode, void *data, short datatype) +int ANIM_animdata_filter (ListBase *anim_data, int filter_mode, void *data, short datatype) { + int items = 0; + /* only filter data if there's somewhere to put it */ if (data && anim_data) { bAnimListElem *ale, *next; @@ -1221,20 +1316,21 @@ void ANIM_animdata_filter (ListBase *anim_data, int filter_mode, void *data, sho /* firstly filter the data */ switch (datatype) { case ANIMCONT_ACTION: - animdata_filter_action(anim_data, data, filter_mode, NULL, ANIMTYPE_NONE); + items= animdata_filter_action(anim_data, data, filter_mode, NULL, ANIMTYPE_NONE); break; case ANIMCONT_SHAPEKEY: - animdata_filter_shapekey(anim_data, data, filter_mode, NULL, ANIMTYPE_NONE); + items= animdata_filter_shapekey(anim_data, data, filter_mode, NULL, ANIMTYPE_NONE); break; case ANIMCONT_GPENCIL: - //animdata_filter_gpencil(anim_data, data, filter_mode); + //items= animdata_filter_gpencil(anim_data, data, filter_mode); break; case ANIMCONT_DOPESHEET: - animdata_filter_dopesheet(anim_data, data, filter_mode); + items= animdata_filter_dopesheet(anim_data, data, filter_mode); break; case ANIMCONT_IPO: // FIXME: this will be used for showing a single IPO-block (not too useful from animator perspective though!) + //items= 0; break; } @@ -1243,17 +1339,26 @@ void ANIM_animdata_filter (ListBase *anim_data, int filter_mode, void *data, sho for (ale= anim_data->first; ale; ale= next) { next= ale->next; - if (ale->type == ANIMTYPE_NONE) + if (ale->type == ANIMTYPE_NONE) { + items--; BLI_freelinkN(anim_data, ale); + } if (filter_mode & ANIMFILTER_IPOKEYS) { - if (ale->datatype != ALE_IPO) + if (ale->datatype != ALE_IPO) { + items--; BLI_freelinkN(anim_data, ale); - else if (ale->key_data == NULL) + } + else if (ale->key_data == NULL) { + items--; BLI_freelinkN(anim_data, ale); + } } } } + + /* return the number of items in the list */ + return items; } /* ************************************************************ */ diff --git a/source/blender/editors/animation/anim_keyframes_draw.c b/source/blender/editors/animation/anim_keyframes_draw.c deleted file mode 100644 index b72b2bd27fa..00000000000 --- a/source/blender/editors/animation/anim_keyframes_draw.c +++ /dev/null @@ -1,704 +0,0 @@ -/** - * $Id: drawaction.c 17746 2008-12-08 11:19:44Z aligorith $ - * - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): Joshua Leung - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/* System includes ----------------------------------------------------- */ - -#include -#include -#include -#include - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_arithb.h" - -/* Types --------------------------------------------------------------- */ - -#include "DNA_listBase.h" -#include "DNA_action_types.h" -#include "DNA_armature_types.h" -#include "DNA_camera_types.h" -#include "DNA_curve_types.h" -#include "DNA_ipo_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" -#include "DNA_scene_types.h" -#include "DNA_space_types.h" -#include "DNA_constraint_types.h" -#include "DNA_key_types.h" -#include "DNA_lamp_types.h" -#include "DNA_material_types.h" -#include "DNA_userdef_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_windowmanager_types.h" - -#include "BKE_action.h" -#include "BKE_depsgraph.h" -#include "BKE_ipo.h" -#include "BKE_key.h" -#include "BKE_material.h" -#include "BKE_object.h" -#include "BKE_global.h" // XXX remove me! -#include "BKE_context.h" -#include "BKE_utildefines.h" - -/* Everything from source (BIF, BDR, BSE) ------------------------------ */ - -#include "BIF_gl.h" -#include "BIF_glutil.h" - -#include "UI_interface.h" -#include "UI_interface_icons.h" -#include "UI_resources.h" -#include "UI_text.h" -#include "UI_view2d.h" - -#include "ED_anim_api.h" -#include "ED_keyframing.h" -#include "ED_keyframes_draw.h" -#include "ED_screen.h" -#include "ED_space_api.h" - - -#if 0 // XXX old includes for reference only - #include "BIF_editaction.h" - #include "BIF_editkey.h" - #include "BIF_editnla.h" - #include "BIF_drawgpencil.h" - #include "BIF_keyframing.h" - #include "BIF_language.h" - #include "BIF_space.h" - - #include "BDR_editcurve.h" - #include "BDR_gpencil.h" - - #include "BSE_drawnla.h" - #include "BSE_drawipo.h" - #include "BSE_drawview.h" - #include "BSE_editaction_types.h" - #include "BSE_editipo.h" - #include "BSE_headerbuttons.h" - #include "BSE_time.h" - #include "BSE_view.h" -#endif // XXX old defines for reference only - -/* *************************** Keyframe Drawing *************************** */ - -static void add_bezt_to_keycolumnslist(ListBase *keys, BezTriple *bezt) -{ - /* The equivilant of add_to_cfra_elem except this version - * makes ActKeyColumns - one of the two datatypes required - * for action editor drawing. - */ - ActKeyColumn *ak, *akn; - - if (ELEM(NULL, keys, bezt)) return; - - /* try to any existing key to replace, or where to insert after */ - for (ak= keys->last; ak; ak= ak->prev) { - /* do because of double keys */ - if (ak->cfra == bezt->vec[1][0]) { - /* set selection status and 'touched' status */ - if (BEZSELECTED(bezt)) ak->sel = SELECT; - ak->modified += 1; - - return; - } - else if (ak->cfra < bezt->vec[1][0]) break; - } - - /* add new block */ - akn= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); - if (ak) BLI_insertlinkafter(keys, ak, akn); - else BLI_addtail(keys, akn); - - akn->cfra= bezt->vec[1][0]; - akn->modified += 1; - - // TODO: handle type = bezt->h1 or bezt->h2 - akn->handle_type= 0; - - if (BEZSELECTED(bezt)) - akn->sel = SELECT; - else - akn->sel = 0; -} - -static void add_bezt_to_keyblockslist(ListBase *blocks, IpoCurve *icu, int index) -{ - /* The equivilant of add_to_cfra_elem except this version - * makes ActKeyBlocks - one of the two datatypes required - * for action editor drawing. - */ - ActKeyBlock *ab, *abn; - BezTriple *beztn=NULL, *prev=NULL; - BezTriple *bezt; - int v; - - /* get beztriples */ - beztn= (icu->bezt + index); - - /* we need to go through all beztriples, as they may not be in order (i.e. during transform) */ - for (v=0, bezt=icu->bezt; vtotvert; v++, bezt++) { - /* skip if beztriple is current */ - if (v != index) { - /* check if beztriple is immediately before */ - if (beztn->vec[1][0] > bezt->vec[1][0]) { - /* check if closer than previous was */ - if (prev) { - if (prev->vec[1][0] < bezt->vec[1][0]) - prev= bezt; - } - else { - prev= bezt; - } - } - } - } - - /* check if block needed - same value(s)? - * -> firstly, handles must have same central value as each other - * -> secondly, handles which control that section of the curve must be constant - */ - if ((!prev) || (!beztn)) return; - if (IS_EQ(beztn->vec[1][1], prev->vec[1][1])==0) return; - if (IS_EQ(beztn->vec[1][1], beztn->vec[0][1])==0) return; - if (IS_EQ(prev->vec[1][1], prev->vec[2][1])==0) return; - - /* try to find a keyblock that starts on the previous beztriple - * Note: we can't search from end to try to optimise this as it causes errors there's - * an A ___ B |---| B situation - */ - // FIXME: here there is a bug where we are trying to get the summary for the following channels - // A|--------------|A ______________ B|--------------|B - // A|------------------------------------------------|A - // A|----|A|---|A|-----------------------------------|A - for (ab= blocks->first; ab; ab= ab->next) { - /* check if alter existing block or add new block */ - if (ab->start == prev->vec[1][0]) { - /* set selection status and 'touched' status */ - if (BEZSELECTED(beztn)) ab->sel = SELECT; - ab->modified += 1; - - return; - } - else if (ab->start < prev->vec[1][0]) break; - } - - /* add new block */ - abn= MEM_callocN(sizeof(ActKeyBlock), "ActKeyBlock"); - if (ab) BLI_insertlinkbefore(blocks, ab, abn); - else BLI_addtail(blocks, abn); - - abn->start= prev->vec[1][0]; - abn->end= beztn->vec[1][0]; - abn->val= beztn->vec[1][1]; - - if (BEZSELECTED(prev) || BEZSELECTED(beztn)) - abn->sel = SELECT; - else - abn->sel = 0; - abn->modified = 1; -} - -/* helper function - find actkeycolumn that occurs on cframe */ -static ActKeyColumn *cfra_find_actkeycolumn (ListBase *keys, float cframe) -{ - ActKeyColumn *ak, *ak2; - - if (keys==NULL) - return NULL; - - /* search from both ends at the same time, and stop if we find match or if both ends meet */ - for (ak=keys->first, ak2=keys->last; ak && ak2; ak=ak->next, ak2=ak2->prev) { - /* return whichever end encounters the frame */ - if (ak->cfra == cframe) - return ak; - if (ak2->cfra == cframe) - return ak2; - - /* no matches on either end, so return NULL */ - if (ak == ak2) - return NULL; - } - - return NULL; -} - -#if 0 // disabled, as some intel cards have problems with this -/* Draw a simple diamond shape with a filled in center (in screen space) */ -static void draw_key_but(int x, int y, short w, short h, int sel) -{ - int xmin= x, ymin= y; - int xmax= x+w-1, ymax= y+h-1; - int xc= (xmin+xmax)/2, yc= (ymin+ymax)/2; - - /* interior - hardcoded colors (for selected and unselected only) */ - if (sel) glColor3ub(0xF1, 0xCA, 0x13); - else glColor3ub(0xE9, 0xE9, 0xE9); - - glBegin(GL_QUADS); - glVertex2i(xc, ymin); - glVertex2i(xmax, yc); - glVertex2i(xc, ymax); - glVertex2i(xmin, yc); - glEnd(); - - - /* outline */ - glColor3ub(0, 0, 0); - - glBegin(GL_LINE_LOOP); - glVertex2i(xc, ymin); - glVertex2i(xmax, yc); - glVertex2i(xc, ymax); - glVertex2i(xmin, yc); - glEnd(); -} -#endif - -static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, float ypos) -{ - ActKeyColumn *ak; - ActKeyBlock *ab; - - glEnable(GL_BLEND); - - /* draw keyblocks */ - if (blocks) { - for (ab= blocks->first; ab; ab= ab->next) { - short startCurves, endCurves, totCurves; - - /* find out how many curves occur at each keyframe */ - ak= cfra_find_actkeycolumn(keys, ab->start); - startCurves = (ak)? ak->totcurve: 0; - - ak= cfra_find_actkeycolumn(keys, ab->end); - endCurves = (ak)? ak->totcurve: 0; - - /* only draw keyblock if it appears in at all of the keyframes at lowest end */ - if (!startCurves && !endCurves) - continue; - else - totCurves = (startCurves>endCurves)? endCurves: startCurves; - - if (ab->totcurve >= totCurves) { - int sc_xa, sc_xb, sc_ya, sc_yb; - - /* get co-ordinates of block */ - gla2DDrawTranslatePt(di, ab->start, ypos, &sc_xa, &sc_ya); - gla2DDrawTranslatePt(di, ab->end, ypos, &sc_xb, &sc_yb); - - /* draw block */ - if (ab->sel) - UI_ThemeColor4(TH_STRIP_SELECT); - else - UI_ThemeColor4(TH_STRIP); - glRectf((float)sc_xa, (float)sc_ya-3, (float)sc_xb, (float)sc_yb+5); - } - } - } - - /* draw keys */ - if (keys) { - for (ak= keys->first; ak; ak= ak->next) { - int sc_x, sc_y; - - /* get co-ordinate to draw at */ - gla2DDrawTranslatePt(di, ak->cfra, ypos, &sc_x, &sc_y); - - /* draw using icons - old way which is slower but more proven */ - if (ak->sel & SELECT) UI_icon_draw_aspect((float)sc_x-7, (float)sc_y-6, ICON_SPACE2, 1.0f); - else UI_icon_draw_aspect((float)sc_x-7, (float)sc_y-6, ICON_SPACE3, 1.0f); - - /* draw using OpenGL - slightly uglier but faster */ - // NOTE: disabled for now, as some intel cards seem to have problems with this - //draw_key_but(sc_x-5, sc_y-4, 11, 11, (ak->sel & SELECT)); - } - } - - glDisable(GL_BLEND); -} - -/* *************************** Channel Drawing Funcs *************************** */ - -void draw_object_channel(gla2DDrawInfo *di, ActKeysInc *aki, Object *ob, float ypos) -{ - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; - - ob_to_keylist(ob, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); - - BLI_freelistN(&keys); - BLI_freelistN(&blocks); -} - -void draw_ipo_channel(gla2DDrawInfo *di, ActKeysInc *aki, Ipo *ipo, float ypos) -{ - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; - - ipo_to_keylist(ipo, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); - - BLI_freelistN(&keys); - BLI_freelistN(&blocks); -} - -void draw_icu_channel(gla2DDrawInfo *di, ActKeysInc *aki, IpoCurve *icu, float ypos) -{ - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; - - icu_to_keylist(icu, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); - - BLI_freelistN(&keys); - BLI_freelistN(&blocks); -} - -void draw_agroup_channel(gla2DDrawInfo *di, ActKeysInc *aki, bActionGroup *agrp, float ypos) -{ - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; - - agroup_to_keylist(agrp, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); - - BLI_freelistN(&keys); - BLI_freelistN(&blocks); -} - -void draw_action_channel(gla2DDrawInfo *di, ActKeysInc *aki, bAction *act, float ypos) -{ - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; - - action_to_keylist(act, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); - - BLI_freelistN(&keys); - BLI_freelistN(&blocks); -} - -void draw_gpl_channel(gla2DDrawInfo *di, ActKeysInc *aki, bGPDlayer *gpl, float ypos) -{ - ListBase keys = {0, 0}; - - gpl_to_keylist(gpl, &keys, NULL, aki); - draw_keylist(di, &keys, NULL, ypos); - BLI_freelistN(&keys); -} - -/* *************************** Keyframe List Conversions *************************** */ - -void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - bConstraintChannel *conchan; - Key *key= ob_get_key(ob); - - if (ob) { - bDopeSheet *ads= (aki)? (aki->ads) : NULL; - int filterflag; - - /* get filterflag */ - if (ads) - filterflag= ads->filterflag; - else if ((aki) && (aki->actmode == -1)) /* only set like this by NLA */ - filterflag= ADS_FILTER_NLADUMMY; - else - filterflag= 0; - - /* Add object keyframes */ - if ((ob->ipo) && !(filterflag & ADS_FILTER_NOIPOS)) - ipo_to_keylist(ob->ipo, keys, blocks, aki); - - /* Add action keyframes */ - if ((ob->action) && !(filterflag & ADS_FILTER_NOACTS)) - action_nlascaled_to_keylist(ob, ob->action, keys, blocks, aki); - - /* Add shapekey keyframes (only if dopesheet allows, if it is available) */ - if ((key && key->ipo) && !(filterflag & ADS_FILTER_NOSHAPEKEYS)) - ipo_to_keylist(key->ipo, keys, blocks, aki); - - /* Add material keyframes (only if dopesheet allows, if it is available) */ - if ((ob->totcol) && !(filterflag & ADS_FILTER_NOMAT)) { - short a; - - for (a=0; atotcol; a++) { - Material *ma= give_current_material(ob, a); - - if (ELEM(NULL, ma, ma->ipo) == 0) - ipo_to_keylist(ma->ipo, keys, blocks, aki); - } - } - - /* Add object data keyframes */ - switch (ob->type) { - case OB_CAMERA: /* ------- Camera ------------ */ - { - Camera *ca= (Camera *)ob->data; - if ((ca->ipo) && !(ads->filterflag & ADS_FILTER_NOCAM)) - ipo_to_keylist(ca->ipo, keys, blocks, aki); - } - break; - case OB_LAMP: /* ---------- Lamp ----------- */ - { - Lamp *la= (Lamp *)ob->data; - if ((la->ipo) && !(ads->filterflag & ADS_FILTER_NOLAM)) - ipo_to_keylist(la->ipo, keys, blocks, aki); - } - break; - case OB_CURVE: /* ------- Curve ---------- */ - { - Curve *cu= (Curve *)ob->data; - if ((cu->ipo) && !(ads->filterflag & ADS_FILTER_NOCUR)) - ipo_to_keylist(cu->ipo, keys, blocks, aki); - } - break; - } - - /* Add constraint keyframes */ - if (!(filterflag & ADS_FILTER_NOCONSTRAINTS)) { - for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next) { - if (conchan->ipo) - ipo_to_keylist(conchan->ipo, keys, blocks, aki); - } - } - } -} - -static short bezt_in_aki_range (ActKeysInc *aki, BezTriple *bezt) -{ - /* when aki == NULL, we don't care about range */ - if (aki == NULL) - return 1; - - /* if start and end are both 0, then don't care about range */ - if (IS_EQ(aki->start, 0) && IS_EQ(aki->end, 0)) - return 1; - - /* if nla-scaling is in effect, apply appropriate scaling adjustments */ -#if 0 // XXX this was from some buggy code... do not port for now - if (aki->ob) { - float frame= get_action_frame_inv(aki->ob, bezt->vec[1][0]); - return IN_RANGE(frame, aki->start, aki->end); - } - else { - /* check if in range */ - return IN_RANGE(bezt->vec[1][0], aki->start, aki->end); - } -#endif // XXX this was from some buggy code... do not port for now - return 1; -} - -void icu_to_keylist(IpoCurve *icu, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - BezTriple *bezt; - ActKeyColumn *ak, *ak2; - ActKeyBlock *ab, *ab2; - int v; - - if (icu && icu->totvert) { - /* loop through beztriples, making ActKeys and ActKeyBlocks */ - bezt= icu->bezt; - - for (v=0; vtotvert; v++, bezt++) { - /* only if keyframe is in range (optimisation) */ - if (bezt_in_aki_range(aki, bezt)) { - add_bezt_to_keycolumnslist(keys, bezt); - if (blocks) add_bezt_to_keyblockslist(blocks, icu, v); - } - } - - /* update the number of curves that elements have appeared in */ - if (keys) { - for (ak=keys->first, ak2=keys->last; ak && ak2; ak=ak->next, ak2=ak2->prev) { - if (ak->modified) { - ak->modified = 0; - ak->totcurve += 1; - } - - if (ak == ak2) - break; - - if (ak2->modified) { - ak2->modified = 0; - ak2->totcurve += 1; - } - } - } - if (blocks) { - for (ab=blocks->first, ab2=blocks->last; ab && ab2; ab=ab->next, ab2=ab2->prev) { - if (ab->modified) { - ab->modified = 0; - ab->totcurve += 1; - } - - if (ab == ab2) - break; - - if (ab2->modified) { - ab2->modified = 0; - ab2->totcurve += 1; - } - } - } - } -} - -void ipo_to_keylist(Ipo *ipo, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - IpoCurve *icu; - - if (ipo) { - for (icu= ipo->curve.first; icu; icu= icu->next) - icu_to_keylist(icu, keys, blocks, aki); - } -} - -void agroup_to_keylist(bActionGroup *agrp, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - bActionChannel *achan; - bConstraintChannel *conchan; - - if (agrp) { - /* loop through action channels */ - for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) { - if (VISIBLE_ACHAN(achan)) { - /* firstly, add keys from action channel's ipo block */ - if (achan->ipo) - ipo_to_keylist(achan->ipo, keys, blocks, aki); - - /* then, add keys from constraint channels */ - for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { - if (conchan->ipo) - ipo_to_keylist(conchan->ipo, keys, blocks, aki); - } - } - } - } -} - -void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - bActionChannel *achan; - bConstraintChannel *conchan; - - if (act) { - /* loop through action channels */ - for (achan= act->chanbase.first; achan; achan= achan->next) { - /* firstly, add keys from action channel's ipo block */ - if (achan->ipo) - ipo_to_keylist(achan->ipo, keys, blocks, aki); - - /* then, add keys from constraint channels */ - for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { - if (conchan->ipo) - ipo_to_keylist(conchan->ipo, keys, blocks, aki); - } - } - } -} - -void action_nlascaled_to_keylist(Object *ob, bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - bActionChannel *achan; - bConstraintChannel *conchan; - Object *oldob= NULL; - - /* although apply and clearing NLA-scaling pre-post creating keylist does impact on performance, - * the effects should be fairly minimal, as we're already going through the keyframes multiple times - * already for blocks too... - */ - if (act) { - /* if 'aki' is provided, store it's current ob to restore later as it might not be the same */ - if (aki) { - oldob= aki->ob; - aki->ob= ob; - } - - /* loop through action channels */ - for (achan= act->chanbase.first; achan; achan= achan->next) { - /* firstly, add keys from action channel's ipo block - * - scaling correction only does times for center-points, so should be faster - */ - if (achan->ipo) { - //actstrip_map_ipo_keys(ob, achan->ipo, 0, 1); // XXX - ipo_to_keylist(achan->ipo, keys, blocks, aki); - //actstrip_map_ipo_keys(ob, achan->ipo, 1, 1); // XXX - } - - /* then, add keys from constraint channels - * - scaling correction only does times for center-points, so should be faster - */ - for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { - if (conchan->ipo) { - //actstrip_map_ipo_keys(ob, conchan->ipo, 0, 1); // XXX - ipo_to_keylist(conchan->ipo, keys, blocks, aki); - //actstrip_map_ipo_keys(ob, conchan->ipo, 1, 1); // XXX - } - } - } - - /* if 'aki' is provided, restore ob */ - if (aki) - aki->ob= oldob; - } -} - -void gpl_to_keylist(bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - bGPDframe *gpf; - ActKeyColumn *ak; - - if (gpl && keys) { - /* loop over frames, converting directly to 'keyframes' (should be in order too) */ - for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { - ak= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); - BLI_addtail(keys, ak); - - ak->cfra= (float)gpf->framenum; - ak->modified = 1; - ak->handle_type= 0; - - if (gpf->flag & GP_FRAME_SELECT) - ak->sel = SELECT; - else - ak->sel = 0; - } - } -} - diff --git a/source/blender/editors/animation/anim_keyframing.c b/source/blender/editors/animation/anim_keyframing.c deleted file mode 100644 index a86cf3719d3..00000000000 --- a/source/blender/editors/animation/anim_keyframing.c +++ /dev/null @@ -1,2287 +0,0 @@ -/** - * $Id: keyframing.c 17745 2008-12-08 09:16:09Z aligorith $ - * - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The Original Code is Copyright (C) 2008, Blender Foundation - * This is a new part of Blender (with some old code) - * - * Contributor(s): Joshua Leung - * - * ***** END GPL LICENSE BLOCK ***** - */ - - - -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "MEM_guardedalloc.h" - -#include "BLI_arithb.h" -#include "BLI_blenlib.h" -#include "BLI_dynstr.h" - -#include "DNA_listBase.h" -#include "DNA_ID.h" -#include "DNA_action_types.h" -#include "DNA_armature_types.h" -#include "DNA_camera_types.h" -#include "DNA_constraint_types.h" -#include "DNA_curve_types.h" -#include "DNA_ipo_types.h" -#include "DNA_key_types.h" -#include "DNA_lamp_types.h" -#include "DNA_object_types.h" -#include "DNA_material_types.h" -#include "DNA_screen_types.h" -#include "DNA_scene_types.h" -#include "DNA_space_types.h" -#include "DNA_texture_types.h" -#include "DNA_userdef_types.h" -#include "DNA_vec_types.h" -#include "DNA_view3d_types.h" -#include "DNA_world_types.h" - -#include "BKE_global.h" -#include "BKE_utildefines.h" -#include "BKE_blender.h" -#include "BKE_main.h" // XXX not needed old cruft? -#include "BKE_action.h" -#include "BKE_armature.h" -#include "BKE_constraint.h" -#include "BKE_curve.h" -#include "BKE_depsgraph.h" -#include "BKE_ipo.h" -#include "BKE_key.h" -#include "BKE_object.h" -#include "BKE_material.h" - -#include "ED_keyframing.h" - -#if 0 // XXX resolve these old dependencies! -#include "BIF_butspace.h" -#include "BIF_editaction.h" -#include "BIF_editkey.h" -#include "BIF_interface.h" -#include "BIF_mywindow.h" -#include "BIF_poseobject.h" -#include "BIF_screen.h" -#include "BIF_space.h" -#include "BIF_toolbox.h" -#include "BIF_toets.h" - -#include "BSE_editipo.h" -#include "BSE_node.h" -#include "BSE_time.h" -#include "BSE_view.h" - -#include "blendef.h" - -#include "PIL_time.h" /* sleep */ -#include "mydevice.h" -#endif // XXX resolve these old dependencies! - - - -/* ************************************************** */ -/* LOCAL TYPES AND DEFINES */ - -/* ----------- Common KeyData Sources ------------ */ - -/* temporary struct to gather data combos to keyframe */ -typedef struct bCommonKeySrc { - struct bCommonKeySrc *next, *prev; - - /* general data/destination-source settings */ - ID *id; /* id-block this comes from */ - char *actname; /* name of action channel */ - char *constname; /* name of constraint channel */ - - /* general destination source settings */ - Ipo *ipo; /* ipo-block that id-block has (optional) */ - bAction *act; /* action-block that id-block has (optional) */ - - /* pose-level settings */ - bPoseChannel *pchan; /* pose channel */ - - /* buttons-window settings */ - int map; /* offset to apply to certain adrcodes */ -} bCommonKeySrc; - -/* -------------- Keying Sets ------------------- */ - -/* storage for iterator for looping over keyingset channels */ -typedef struct bKS_AdrcodeGetter { - struct bKeyingSet *ks; /* keyingset this applies to */ - struct bCommonKeySrc *cks; /* data to insert/delete keyframes... */ - - short index; /* index of current channel to resume from */ - short tot; /* index after which we start returning from some special collection */ -} bKS_AdrcodeGetter; - -/* flags to look out for in keyingset channels... */ -#define KAG_CHAN_EXTEND (-1) - - -/* keying set - a set of channels that will be keyframed together */ -// TODO: move this to a header to allow custom sets someday? -typedef struct bKeyingSet { - /* callback func to consider if keyingset should be included - * (by default, if this is undefined, item will be shown) - */ - short (*include_cb)(struct bKeyingSet *, const char *); - - char name[48]; /* name of keyingset */ - int blocktype; /* blocktype that all channels belong to */ // in future, this may be eliminated - short flag; /* flags to use when setting keyframes */ - - short chan_num; /* number of channels to insert keyframe in */ - short adrcodes[32]; /* adrcodes for channels to insert keys for (ideally would be variable-len, but limit of 32 will suffice) */ -} bKeyingSet; - -/* keying set context - an array of keying sets and the number of them */ -typedef struct bKeyingContext { - bKeyingSet *keyingsets; /* array containing the keyingsets of interest */ - bKeyingSet *lastused; /* item that was chosen last time*/ - int tot; /* number of keyingsets in */ -} bKeyingContext; - -/* ************************************************** */ -/* KEYFRAME INSERTION */ - -/* -------------- BezTriple Insertion -------------------- */ - -/* threshold for inserting keyframes - threshold here should be good enough for now, but should become userpref */ -#define BEZT_INSERT_THRESH 0.00001 - -/* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_icu) - * Returns the index to insert at (data already at that index will be offset if replace is 0) - */ -static int binarysearch_bezt_index (BezTriple array[], float frame, int arraylen, short *replace) -{ - int start=0, end=arraylen; - int loopbreaker= 0, maxloop= arraylen * 2; - - /* initialise replace-flag first */ - *replace= 0; - - /* sneaky optimisations (don't go through searching process if...): - * - keyframe to be added is to be added out of current bounds - * - keyframe to be added would replace one of the existing ones on bounds - */ - if ((arraylen <= 0) || (array == NULL)) { - printf("Warning: binarysearch_bezt_index() encountered invalid array \n"); - return 0; - } - else { - /* check whether to add before/after/on */ - float framenum; - - /* 'First' Keyframe (when only one keyframe, this case is used) */ - framenum= array[0].vec[1][0]; - if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { - *replace = 1; - return 0; - } - else if (frame < framenum) - return 0; - - /* 'Last' Keyframe */ - framenum= array[(arraylen-1)].vec[1][0]; - if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { - *replace= 1; - return (arraylen - 1); - } - else if (frame > framenum) - return arraylen; - } - - - /* most of the time, this loop is just to find where to put it - * 'loopbreaker' is just here to prevent infinite loops - */ - for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { - /* compute and get midpoint */ - int mid = (start + end) / 2; - float midfra= array[mid].vec[1][0]; - - /* check if exactly equal to midpoint */ - if (IS_EQT(frame, midfra, BEZT_INSERT_THRESH)) { - *replace = 1; - return mid; - } - - /* repeat in upper/lower half */ - if (frame > midfra) - start= mid + 1; - else if (frame < midfra) - end= mid - 1; - } - - /* print error if loop-limit exceeded */ - if (loopbreaker == (maxloop-1)) { - printf("Error: binarysearch_bezt_index() was taking too long \n"); - - // include debug info - printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen); - } - - /* not found, so return where to place it */ - return start; -} - -/* This function adds a given BezTriple to an IPO-Curve. It will allocate - * memory for the array if needed, and will insert the BezTriple into a - * suitable place in chronological order. - * - * NOTE: any recalculate of the IPO-Curve that needs to be done will need to - * be done by the caller. - */ -int insert_bezt_icu (IpoCurve *icu, BezTriple *bezt) -{ - BezTriple *newb; - int i= 0; - - if (icu->bezt) { - short replace = -1; - i = binarysearch_bezt_index(icu->bezt, bezt->vec[1][0], icu->totvert, &replace); - - if (replace) { - /* sanity check: 'i' may in rare cases exceed arraylen */ - // FIXME: do not overwrite handletype if just replacing...? - if ((i >= 0) && (i < icu->totvert)) - *(icu->bezt + i) = *bezt; - } - else { - /* add new */ - newb= MEM_callocN((icu->totvert+1)*sizeof(BezTriple), "beztriple"); - - /* add the beztriples that should occur before the beztriple to be pasted (originally in ei->icu) */ - if (i > 0) - memcpy(newb, icu->bezt, i*sizeof(BezTriple)); - - /* add beztriple to paste at index i */ - *(newb + i)= *bezt; - - /* add the beztriples that occur after the beztriple to be pasted (originally in icu) */ - if (i < icu->totvert) - memcpy(newb+i+1, icu->bezt+i, (icu->totvert-i)*sizeof(BezTriple)); - - /* replace (+ free) old with new */ - MEM_freeN(icu->bezt); - icu->bezt= newb; - - icu->totvert++; - } - } - else { - icu->bezt= MEM_callocN(sizeof(BezTriple), "beztriple"); - *(icu->bezt)= *bezt; - icu->totvert= 1; - } - - - /* we need to return the index, so that some tools which do post-processing can - * detect where we added the BezTriple in the array - */ - return i; -} - -/* This function is a wrapper for insert_bezt_icu, and should be used when - * adding a new keyframe to a curve, when the keyframe doesn't exist anywhere - * else yet. - * - * 'fast' - is only for the python API where importing BVH's would take an extreamly long time. - */ -void insert_vert_icu (IpoCurve *icu, float x, float y, short fast) -{ - BezTriple beztr; - int a, h1, h2; - - /* set all three points, for nicer start position */ - memset(&beztr, 0, sizeof(BezTriple)); - beztr.vec[0][0]= x; - beztr.vec[0][1]= y; - beztr.vec[1][0]= x; - beztr.vec[1][1]= y; - beztr.vec[2][0]= x; - beztr.vec[2][1]= y; - beztr.hide= IPO_BEZ; - beztr.f1= beztr.f2= beztr.f3= SELECT; - beztr.h1= beztr.h2= HD_AUTO; - - /* add temp beztriple to keyframes */ - a= insert_bezt_icu(icu, &beztr); - - /* what if 'a' is a negative index? - * for now, just exit to prevent any segfaults - */ - if (a < 0) return; - - /* don't recalculate handles if fast is set - * - this is a hack to make importers faster - * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) - */ - if (!fast) calchandles_ipocurve(icu); - - /* set handletype and interpolation */ - if (icu->totvert > 2) { - BezTriple *bezt= (icu->bezt + a); - - /* set handles (autohandles by default) */ - h1= h2= HD_AUTO; - - if (a > 0) h1= (bezt-1)->h2; - if (a < icu->totvert-1) h2= (bezt+1)->h1; - - bezt->h1= h1; - bezt->h2= h2; - - /* set interpolation (if curve is using IPO_MIXED, then take from previous) */ - if (icu->ipo == IPO_MIXED) { - if (a > 0) bezt->ipo= (bezt-1)->ipo; - else if (a < icu->totvert-1) bezt->ipo= (bezt+1)->ipo; - } - else - bezt->ipo= icu->ipo; - - /* don't recalculate handles if fast is set - * - this is a hack to make importers faster - * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) - */ - if (!fast) calchandles_ipocurve(icu); - } - else { - BezTriple *bezt= (icu->bezt + a); - - /* set interpolation directly from ipo-curve */ - bezt->ipo= icu->ipo; - } -} - -#if 0 // XXX code to clean up - -/* ------------------- Get Data ------------------------ */ - -/* Get pointer to use to get values from */ -// FIXME: this should not be possible with Data-API -static void *get_context_ipo_poin (ID *id, int blocktype, char *actname, char *constname, IpoCurve *icu, int *vartype) -{ - switch (blocktype) { - case ID_PO: /* posechannel */ - if (GS(id->name)==ID_OB) { - Object *ob= (Object *)id; - bPoseChannel *pchan= get_pose_channel(ob->pose, actname); - - if (pchan) { - if (ELEM3(icu->adrcode, AC_EUL_X, AC_EUL_Y, AC_EUL_Z)) - *vartype= IPO_FLOAT_DEGR; - else - *vartype= IPO_FLOAT; - return get_pchan_ipo_poin(pchan, icu->adrcode); - } - } - break; - - case ID_CO: /* constraint */ - if ((GS(id->name)==ID_OB) && (constname && constname[0])) { - Object *ob= (Object *)id; - bConstraint *con; - - /* assume that we only want the influence (as only used for Constraint Channels) */ - if ((ob->ipoflag & OB_ACTION_OB) && !strcmp(actname, "Object")) { - for (con= ob->constraints.first; con; con= con->next) { - if (strcmp(constname, con->name)==0) { - *vartype= IPO_FLOAT; - return &con->enforce; - } - } - } - else if (ob->pose) { - bPoseChannel *pchan= get_pose_channel(ob->pose, actname); - - if (pchan) { - for (con= pchan->constraints.first; con; con= con->next) { - if (strcmp(constname, con->name)==0) { - *vartype= IPO_FLOAT; - return &con->enforce; - } - } - } - } - } - break; - - case ID_OB: /* object */ - /* hack: layer channels for object need to be keyed WITHOUT localview flag... - * tsk... tsk... why must we just dump bitflags upon users :/ - */ - if ((GS(id->name)==ID_OB) && (icu->adrcode==OB_LAY)) { - Object *ob= (Object *)id; - static int layer = 0; - - /* init layer to be the object's layer var, then remove local view from it */ - layer = ob->lay; - layer &= 0xFFFFFF; - *vartype= IPO_INT_BIT; - - /* return pointer to this static var - * - assumes that this pointer won't be stored for use later, so may not be threadsafe - * if multiple keyframe calls are made, but that is unlikely to happen in the near future - */ - return (void *)(&layer); - } - /* no break here for other ob channel-types - as they can be done normally */ - - default: /* normal data-source */ - return get_ipo_poin(id, icu, vartype); - } - - /* not valid... */ - return NULL; -} - - -/* -------------- 'Smarter' Keyframing Functions -------------------- */ -/* return codes for new_key_needed */ -enum { - KEYNEEDED_DONTADD = 0, - KEYNEEDED_JUSTADD, - KEYNEEDED_DELPREV, - KEYNEEDED_DELNEXT -} eKeyNeededStatus; - -/* This helper function determines whether a new keyframe is needed */ -/* Cases where keyframes should not be added: - * 1. Keyframe to be added bewteen two keyframes with similar values - * 2. Keyframe to be added on frame where two keyframes are already situated - * 3. Keyframe lies at point that intersects the linear line between two keyframes - */ -static short new_key_needed (IpoCurve *icu, float cFrame, float nValue) -{ - BezTriple *bezt=NULL, *prev=NULL; - int totCount, i; - float valA = 0.0f, valB = 0.0f; - - /* safety checking */ - if (icu == NULL) return KEYNEEDED_JUSTADD; - totCount= icu->totvert; - if (totCount == 0) return KEYNEEDED_JUSTADD; - - /* loop through checking if any are the same */ - bezt= icu->bezt; - for (i=0; ivec[1][0]; - beztVal= bezt->vec[1][1]; - - if (prev) { - /* there is a keyframe before the one currently being examined */ - - /* get previous time+value */ - prevPosi= prev->vec[1][0]; - prevVal= prev->vec[1][1]; - - /* keyframe to be added at point where there are already two similar points? */ - if (IS_EQ(prevPosi, cFrame) && IS_EQ(beztPosi, cFrame) && IS_EQ(beztPosi, prevPosi)) { - return KEYNEEDED_DONTADD; - } - - /* keyframe between prev+current points ? */ - if ((prevPosi <= cFrame) && (cFrame <= beztPosi)) { - /* is the value of keyframe to be added the same as keyframes on either side ? */ - if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) { - return KEYNEEDED_DONTADD; - } - else { - float realVal; - - /* get real value of curve at that point */ - realVal= eval_icu(icu, cFrame); - - /* compare whether it's the same as proposed */ - if (IS_EQ(realVal, nValue)) - return KEYNEEDED_DONTADD; - else - return KEYNEEDED_JUSTADD; - } - } - - /* new keyframe before prev beztriple? */ - if (cFrame < prevPosi) { - /* A new keyframe will be added. However, whether the previous beztriple - * stays around or not depends on whether the values of previous/current - * beztriples and new keyframe are the same. - */ - if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) - return KEYNEEDED_DELNEXT; - else - return KEYNEEDED_JUSTADD; - } - } - else { - /* just add a keyframe if there's only one keyframe - * and the new one occurs before the exisiting one does. - */ - if ((cFrame < beztPosi) && (totCount==1)) - return KEYNEEDED_JUSTADD; - } - - /* continue. frame to do not yet passed (or other conditions not met) */ - if (i < (totCount-1)) { - prev= bezt; - bezt++; - } - else - break; - } - - /* Frame in which to add a new-keyframe occurs after all other keys - * -> If there are at least two existing keyframes, then if the values of the - * last two keyframes and the new-keyframe match, the last existing keyframe - * gets deleted as it is no longer required. - * -> Otherwise, a keyframe is just added. 1.0 is added so that fake-2nd-to-last - * keyframe is not equal to last keyframe. - */ - bezt= (icu->bezt + (icu->totvert - 1)); - valA= bezt->vec[1][1]; - - if (prev) - valB= prev->vec[1][1]; - else - valB= bezt->vec[1][1] + 1.0f; - - if (IS_EQ(valA, nValue) && IS_EQ(valA, valB)) - return KEYNEEDED_DELPREV; - else - return KEYNEEDED_JUSTADD; -} - -/* ------------------ 'Visual' Keyframing Functions ------------------ */ - -/* internal status codes for visualkey_can_use */ -enum { - VISUALKEY_NONE = 0, - VISUALKEY_LOC, - VISUALKEY_ROT -}; - -/* This helper function determines if visual-keyframing should be used when - * inserting keyframes for the given channel. As visual-keyframing only works - * on Object and Pose-Channel blocks, this should only get called for those - * blocktypes, when using "standard" keying but 'Visual Keying' option in Auto-Keying - * settings is on. - */ -static short visualkey_can_use (ID *id, int blocktype, char *actname, char *constname, int adrcode) -{ - Object *ob= NULL; - bConstraint *con= NULL; - short searchtype= VISUALKEY_NONE; - - /* validate data */ - if ((id == NULL) || (GS(id->name)!=ID_OB) || !(ELEM(blocktype, ID_OB, ID_PO))) - return 0; - - /* get first constraint and determine type of keyframe constraints to check for*/ - ob= (Object *)id; - - if (blocktype == ID_OB) { - con= ob->constraints.first; - - if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) - searchtype= VISUALKEY_LOC; - else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) - searchtype= VISUALKEY_ROT; - } - else if (blocktype == ID_PO) { - bPoseChannel *pchan= get_pose_channel(ob->pose, actname); - con= pchan->constraints.first; - - if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) - searchtype= VISUALKEY_LOC; - else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) - searchtype= VISUALKEY_ROT; - } - - /* only search if a searchtype and initial constraint are available */ - if (searchtype && con) { - for (; con; con= con->next) { - /* only consider constraint if it is not disabled, and has influence */ - if (con->flag & CONSTRAINT_DISABLE) continue; - if (con->enforce == 0.0f) continue; - - /* some constraints may alter these transforms */ - switch (con->type) { - /* multi-transform constraints */ - case CONSTRAINT_TYPE_CHILDOF: - return 1; - case CONSTRAINT_TYPE_TRANSFORM: - return 1; - case CONSTRAINT_TYPE_FOLLOWPATH: - return 1; - case CONSTRAINT_TYPE_KINEMATIC: - return 1; - - /* single-transform constraits */ - case CONSTRAINT_TYPE_TRACKTO: - if (searchtype==VISUALKEY_ROT) return 1; - break; - case CONSTRAINT_TYPE_ROTLIMIT: - if (searchtype==VISUALKEY_ROT) return 1; - break; - case CONSTRAINT_TYPE_LOCLIMIT: - if (searchtype==VISUALKEY_LOC) return 1; - break; - case CONSTRAINT_TYPE_ROTLIKE: - if (searchtype==VISUALKEY_ROT) return 1; - break; - case CONSTRAINT_TYPE_DISTLIMIT: - if (searchtype==VISUALKEY_LOC) return 1; - break; - case CONSTRAINT_TYPE_LOCLIKE: - if (searchtype==VISUALKEY_LOC) return 1; - break; - case CONSTRAINT_TYPE_LOCKTRACK: - if (searchtype==VISUALKEY_ROT) return 1; - break; - case CONSTRAINT_TYPE_MINMAX: - if (searchtype==VISUALKEY_LOC) return 1; - break; - - default: - break; - } - } - } - - /* when some condition is met, this function returns, so here it can be 0 */ - return 0; -} - -/* This helper function extracts the value to use for visual-keyframing - * In the event that it is not possible to perform visual keying, try to fall-back - * to using the poin method. Assumes that all data it has been passed is valid. - */ -static float visualkey_get_value (ID *id, int blocktype, char *actname, char *constname, int adrcode, IpoCurve *icu) -{ - Object *ob; - void *poin = NULL; - int index, vartype; - - /* validate situtation */ - if ((id==NULL) || (GS(id->name)!=ID_OB) || (ELEM(blocktype, ID_OB, ID_PO)==0)) - return 0.0f; - - /* get object */ - ob= (Object *)id; - - /* only valid for objects or posechannels */ - if (blocktype == ID_OB) { - /* parented objects are not supported, as the effects of the parent - * are included in the matrix, which kindof beats the point - */ - if (ob->parent == NULL) { - /* only Location or Rotation keyframes are supported now */ - if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) { - /* assumes that OB_LOC_Z > OB_LOC_Y > OB_LOC_X */ - index= adrcode - OB_LOC_X; - - return ob->obmat[3][index]; - } - else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) { - float eul[3]; - - /* assumes that OB_ROT_Z > OB_ROT_Y > OB_ROT_X */ - index= adrcode - OB_ROT_X; - - Mat4ToEul(ob->obmat, eul); - return eul[index]*(5.72958f); - } - } - } - else if (blocktype == ID_PO) { - bPoseChannel *pchan; - float tmat[4][4]; - - /* get data to use */ - pchan= get_pose_channel(ob->pose, actname); - - /* Although it is not strictly required for this particular space conversion, - * arg1 must not be null, as there is a null check for the other conversions to - * be safe. Therefore, the active object is passed here, and in many cases, this - * will be what owns the pose-channel that is getting this anyway. - */ - Mat4CpyMat4(tmat, pchan->pose_mat); - constraint_mat_convertspace(ob, pchan, tmat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL); - - /* Loc, Rot/Quat keyframes are supported... */ - if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) { - /* assumes that AC_LOC_Z > AC_LOC_Y > AC_LOC_X */ - index= adrcode - AC_LOC_X; - - /* only use for non-connected bones */ - if ((pchan->bone->parent) && !(pchan->bone->flag & BONE_CONNECTED)) - return tmat[3][index]; - else if (pchan->bone->parent == NULL) - return tmat[3][index]; - } - else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) { - float trimat[3][3], quat[4]; - - /* assumes that AC_QUAT_Z > AC_QUAT_Y > AC_QUAT_X > AC_QUAT_W */ - index= adrcode - AC_QUAT_W; - - Mat3CpyMat4(trimat, tmat); - Mat3ToQuat_is_ok(trimat, quat); - - return quat[index]; - } - } - - /* as the function hasn't returned yet, try reading from poin */ - poin= get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); - if (poin) - return read_ipo_poin(poin, vartype); - else - return 0.0; -} - -/* ------------------------- Insert Key API ------------------------- */ - -/* 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. - * - * 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 insertkey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) -{ - IpoCurve *icu; - - /* get ipo-curve */ - //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 1); // XXX this call needs to be in blenkernel - - /* only continue if we have an ipo-curve to add keyframe to */ - if (icu) { - float cfra = frame_to_float(CFRA); - float curval= 0.0f; - - /* apply special time tweaking */ - if (GS(id->name) == ID_OB) { - Object *ob= (Object *)id; - - /* apply NLA-scaling (if applicable) */ - if (actname && actname[0]) - 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)*G.scene->r.framelen; - } - } - - /* obtain value to give keyframe */ - if ( (flag & INSERTKEY_MATRIX) && - (visualkey_can_use(id, blocktype, actname, constname, adrcode)) ) - { - /* 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(id, blocktype, actname, constname, adrcode, icu); - } - else { - void *poin; - int vartype; - - /* get pointer to data to read from */ - poin = get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); - if (poin == NULL) { - printf("Insert Key: No pointer to variable obtained \n"); - return 0; - } - - /* use kt's read_poin function to extract value (kt->read_poin should - * exist in all cases, but it never hurts to check) - */ - curval= read_ipo_poin(poin, vartype); - } - - /* 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(icu, cfra, curval); - - /* insert new keyframe at current frame */ - if (insert_mode) - insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); - - /* delete keyframe immediately before/after newly added */ - switch (insert_mode) { - case KEYNEEDED_DELPREV: - delete_icu_key(icu, icu->totvert-2, 1); - break; - case KEYNEEDED_DELNEXT: - delete_icu_key(icu, 1, 1); - break; - } - - /* only return success if keyframe added */ - if (insert_mode) - return 1; - } - else { - /* just insert keyframe */ - insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); - - /* return success */ - return 1; - } - } - - /* return failure */ - return 0; -} - - -/* ************************************************** */ -/* KEYFRAME DELETION */ - -/* Main Keyframing API call: - * Use this when validation of necessary animation data isn't necessary as it - * already exists. It will delete a keyframe at the current frame. - * - * The flag argument is used for special settings that alter the behaviour of - * the keyframe deletion. These include the quick refresh options. - */ -short deletekey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) -{ - Ipo *ipo; - IpoCurve *icu; - - /* get ipo-curve - * Note: here is one of the places where we don't want new ipo + ipo-curve added! - * so 'add' var must be 0 - */ - // XXX funcs here need to be recoded in blenkernel... - //ipo= verify_ipo(id, blocktype, actname, constname, NULL, 0); - //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 0); - - /* only continue if we have an ipo-curve to remove keyframes from */ - if (icu) { - float cfra = frame_to_float(CFRA); - short found = -1; - int i; - - /* apply special time tweaking */ - if (GS(id->name) == ID_OB) { - Object *ob= (Object *)id; - - /* apply NLA-scaling (if applicable) */ - if (actname && actname[0]) - 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)*G.scene->r.framelen; - } - } - - /* try to find index of beztriple to get rid of */ - i = binarysearch_bezt_index(icu->bezt, cfra, icu->totvert, &found); - if (found) { - /* delete the key at the index (will sanity check + do recalc afterwards ) */ - delete_icu_key(icu, i, 1); - - /* Only delete curve too if there isn't an ipo-driver still hanging around on an empty curve */ - if (icu->totvert==0 && icu->driver==NULL) { - BLI_remlink(&ipo->curve, icu); - free_ipo_curve(icu); - } - - /* return success */ - return 1; - } - } - - /* return failure */ - return 0; -} - -/* ************************************************** */ -/* COMMON KEYFRAME MANAGEMENT (common_insertkey/deletekey) */ - -/* mode for common_modifykey */ -enum { - COMMONKEY_MODE_INSERT = 0, - COMMONKEY_MODE_DELETE, -} eCommonModifyKey_Modes; - -/* --------- KeyingSet Adrcode Getters ------------ */ - -/* initialise a channel-getter storage */ -static void ks_adrcodegetter_init (bKS_AdrcodeGetter *kag, bKeyingSet *ks, bCommonKeySrc *cks) -{ - /* error checking */ - if (kag == NULL) - return; - - if (ELEM(NULL, ks, cks)) { - /* set invalid settings that won't cause harm */ - kag->ks= NULL; - kag->cks= NULL; - kag->index= -2; - kag->tot= 0; - } - else { - /* store settings */ - kag->ks= ks; - kag->cks= cks; - - /* - index is -1, as that allows iterators to return first element - * - tot is chan_num by default, but may get overriden if -1 is encountered (for extension-type getters) - */ - kag->index= -1; - kag->tot= ks->chan_num; - } -} - -/* 'default' channel-getter that will be used when iterating through keyingset's channels - * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape - */ -static short ks_getnextadrcode_default (bKS_AdrcodeGetter *kag) -{ - bKeyingSet *ks= (kag)? kag->ks : NULL; - - /* error checking */ - if (ELEM(NULL, kag, ks)) return 0; - if (kag->tot <= 0) return 0; - - kag->index++; - if ((kag->index < 0) || (kag->index >= kag->tot)) return 0; - - /* return the adrcode stored at index then */ - return ks->adrcodes[kag->index]; -} - -/* add map flag (for MTex channels, as certain ones need special offset) */ -static short ks_getnextadrcode_addmap (bKS_AdrcodeGetter *kag) -{ - short adrcode= ks_getnextadrcode_default(kag); - - /* if there was an adrcode returned, assume that kag stuff is set ok */ - if (adrcode) { - bCommonKeySrc *cks= kag->cks; - bKeyingSet *ks= kag->ks; - - if (ELEM3(ks->blocktype, ID_MA, ID_LA, ID_WO)) { - switch (adrcode) { - case MAP_OFS_X: case MAP_OFS_Y: case MAP_OFS_Z: - case MAP_SIZE_X: case MAP_SIZE_Y: case MAP_SIZE_Z: - case MAP_R: case MAP_G: case MAP_B: case MAP_DVAR: - case MAP_COLF: case MAP_NORF: case MAP_VARF: case MAP_DISP: - adrcode += cks->map; - break; - } - } - } - - /* adrcode must be returned! */ - return adrcode; -} - -/* extend posechannel keyingsets with rotation info (when KAG_CHAN_EXTEND is encountered) - * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape - * - when we encounter KAG_CHAN_EXTEND as adrcode, start returning our own - */ -static short ks_getnextadrcode_pchanrot (bKS_AdrcodeGetter *kag) -{ - /* hardcoded adrcode channels used here only - * - length is keyed-channels + 1 (last item must be 0 to escape) - */ - static short quat_adrcodes[5] = {AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z, 0}; - static short eul_adrcodes[4] = {AC_EUL_X, AC_EUL_Y, AC_EUL_Z, 0}; - - /* useful variables */ - bKeyingSet *ks= (kag)? kag->ks : NULL; - bCommonKeySrc *cks= (kag) ? kag->cks : NULL; - short index, adrcode; - - /* error checking */ - if (ELEM3(NULL, kag, ks, cks)) return 0; - if (ks->chan_num <= 0) return 0; - - /* get index - * - if past the last item (kag->tot), return stuff from our static arrays - * - otherwise, just keep returning stuff from the keyingset (but check out for -1!) - */ - kag->index++; - if (kag->index < 0) - return 0; - - /* normal (static stuff) */ - if (kag->index < kag->tot) { - /* get adrcode, and return if not KAG_CHAN_EXTEND (i.e. point for start of iteration) */ - adrcode= ks->adrcodes[kag->index]; - - if (adrcode != KAG_CHAN_EXTEND) - return adrcode; - else - kag->tot= kag->index; - } - - /* based on current rotation-mode - * - index can be at most 5, if we are to prevent segfaults - */ - index= kag->index - kag->tot; - if ((index < 0) || (index > 5)) - return 0; - - if (cks->pchan && cks->pchan->rotmode) - return eul_adrcodes[index]; - else - return quat_adrcodes[index]; -} - -/* ------------- KeyingSet Defines ------------ */ -/* Note: these must all be named with the defks_* prefix, otherwise the template macro will not work! */ - -/* macro for defining keyingset contexts */ -#define KSC_TEMPLATE(ctx_name) {&defks_##ctx_name[0], NULL, sizeof(defks_##ctx_name)/sizeof(bKeyingSet)} - -/* --- */ - -/* check if option not available for deleting keys */ -static short incl_non_del_keys (bKeyingSet *ks, const char mode[]) -{ - /* as optimisation, assume that it is sufficient to check only first letter - * of mode (int comparison should be faster than string!) - */ - //if (strcmp(mode, "Delete")==0) - if (mode && mode[0]=='D') - return 0; - - return 1; -} - -/* Object KeyingSets ------ */ - -/* check if include shapekey entry */ -static short incl_v3d_ob_shapekey (bKeyingSet *ks, const char mode[]) -{ - Object *ob= (G.obedit)? (G.obedit) : (OBACT); - char *newname= NULL; - - if(ob==NULL) - return 0; - - /* not available for delete mode */ - if (strcmp(mode, "Delete")==0) - return 0; - - /* check if is geom object that can get shapekeys */ - switch (ob->type) { - /* geometry? */ - case OB_MESH: newname= "Mesh"; break; - case OB_CURVE: newname= "Curve"; break; - case OB_SURF: newname= "Surface"; break; - case OB_LATTICE: newname= "Lattice"; break; - - /* not geometry! */ - default: - return 0; - } - - /* if ks is shapekey entry (this could be callled for separator before too!) */ - if (ks->flag == -3) - sprintf(ks->name, newname); - - /* if it gets here, it's ok */ - return 1; -} - -/* array for object keyingset defines */ -bKeyingSet defks_v3d_object[] = -{ - /* include_cb, adrcode-getter, name, blocktype, flag, chan_num, adrcodes */ - {NULL, "Loc", ID_OB, 0, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, - {NULL, "Rot", ID_OB, 0, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, - {NULL, "Scale", ID_OB, 0, 3, {OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "LocRot", ID_OB, 0, 6, - {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, - OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, - - {NULL, "LocScale", ID_OB, 0, 6, - {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, - OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, - - {NULL, "LocRotScale", ID_OB, 0, 9, - {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, - OB_ROT_X,OB_ROT_Y,OB_ROT_Z, - OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, - - {NULL, "RotScale", ID_OB, 0, 6, - {OB_ROT_X,OB_ROT_Y,OB_ROT_Z, - OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, - - {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator - - {incl_non_del_keys, "VisualLoc", ID_OB, INSERTKEY_MATRIX, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, - {incl_non_del_keys, "VisualRot", ID_OB, INSERTKEY_MATRIX, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, - - {incl_non_del_keys, "VisualLocRot", ID_OB, INSERTKEY_MATRIX, 6, - {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, - OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Layer", ID_OB, 0, 1, {OB_LAY}}, // icky option... - {NULL, "Available", ID_OB, -2, 0, {0}}, - - {incl_v3d_ob_shapekey, "%l%l", 0, -1, 0, {0}}, // separator (linked to shapekey entry) - {incl_v3d_ob_shapekey, "", ID_OB, -3, 0, {0}} -}; - -/* PoseChannel KeyingSets ------ */ - -/* array for posechannel keyingset defines */ -bKeyingSet defks_v3d_pchan[] = -{ - /* include_cb, name, blocktype, flag, chan_num, adrcodes */ - {NULL, "Loc", ID_PO, 0, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, - {NULL, "Rot", ID_PO, COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, - {NULL, "Scale", ID_PO, 0, 3, {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "LocRot", ID_PO, COMMONKEY_PCHANROT, 4, - {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, - KAG_CHAN_EXTEND}}, - - {NULL, "LocScale", ID_PO, 0, 6, - {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, - AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, - - {NULL, "LocRotScale", ID_PO, COMMONKEY_PCHANROT, 7, - {AC_LOC_X,AC_LOC_Y,AC_LOC_Z,AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, - KAG_CHAN_EXTEND}}, - - {NULL, "RotScale", ID_PO, 0, 4, - {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, - KAG_CHAN_EXTEND}}, - - {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator - - {incl_non_del_keys, "VisualLoc", ID_PO, INSERTKEY_MATRIX, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, - {incl_non_del_keys, "VisualRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, - - {incl_non_del_keys, "VisualLocRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 4, - {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, KAG_CHAN_EXTEND}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Available", ID_PO, -2, 0, {0}} -}; - -/* Material KeyingSets ------ */ - -/* array for material keyingset defines */ -bKeyingSet defks_buts_shading_mat[] = -{ - /* include_cb, name, blocktype, flag, chan_num, adrcodes */ - {NULL, "RGB", ID_MA, 0, 3, {MA_COL_R,MA_COL_G,MA_COL_B}}, - {NULL, "Alpha", ID_MA, 0, 1, {MA_ALPHA}}, - {NULL, "Halo Size", ID_MA, 0, 1, {MA_HASIZE}}, - {NULL, "Mode", ID_MA, 0, 1, {MA_MODE}}, // evil bitflags - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "All Color", ID_MA, 0, 18, - {MA_COL_R,MA_COL_G,MA_COL_B, - MA_ALPHA,MA_HASIZE, MA_MODE, - MA_SPEC_R,MA_SPEC_G,MA_SPEC_B, - MA_REF,MA_EMIT,MA_AMB,MA_SPEC,MA_HARD, - MA_MODE,MA_TRANSLU,MA_ADD}}, - - {NULL, "All Mirror", ID_MA, 0, 5, - {MA_RAYM,MA_FRESMIR,MA_FRESMIRI, - MA_FRESTRA,MA_FRESTRAI}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Ofs", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, - {NULL, "Size", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, - - {NULL, "All Mapping", ID_MA, COMMONKEY_ADDMAP, 14, - {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, - MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, - MAP_R,MAP_G,MAP_B,MAP_DVAR, - MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Available", ID_MA, -2, 0, {0}} -}; - -/* World KeyingSets ------ */ - -/* array for world keyingset defines */ -bKeyingSet defks_buts_shading_wo[] = -{ - /* include_cb, name, blocktype, flag, chan_num, adrcodes */ - {NULL, "Zenith RGB", ID_WO, 0, 3, {WO_ZEN_R,WO_ZEN_G,WO_ZEN_B}}, - {NULL, "Horizon RGB", ID_WO, 0, 3, {WO_HOR_R,WO_HOR_G,WO_HOR_B}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Mist", ID_WO, 0, 4, {WO_MISI,WO_MISTDI,WO_MISTSTA,WO_MISTHI}}, - {NULL, "Stars", ID_WO, 0, 5, {WO_STAR_R,WO_STAR_G,WO_STAR_B,WO_STARDIST,WO_STARSIZE}}, - - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Ofs", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, - {NULL, "Size", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, - - {NULL, "All Mapping", ID_WO, COMMONKEY_ADDMAP, 14, - {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, - MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, - MAP_R,MAP_G,MAP_B,MAP_DVAR, - MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Available", ID_WO, -2, 0, {0}} -}; - -/* Lamp KeyingSets ------ */ - -/* array for lamp keyingset defines */ -bKeyingSet defks_buts_shading_la[] = -{ - /* include_cb, name, blocktype, flag, chan_num, adrcodes */ - {NULL, "RGB", ID_LA, 0, 3, {LA_COL_R,LA_COL_G,LA_COL_B}}, - {NULL, "Energy", ID_LA, 0, 1, {LA_ENERGY}}, - {NULL, "Spot Size", ID_LA, 0, 1, {LA_SPOTSI}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Ofs", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, - {NULL, "Size", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, - - {NULL, "All Mapping", ID_LA, COMMONKEY_ADDMAP, 14, - {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, - MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, - MAP_R,MAP_G,MAP_B,MAP_DVAR, - MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Available", ID_LA, -2, 0, {0}} -}; - -/* Texture KeyingSets ------ */ - -/* array for texture keyingset defines */ -bKeyingSet defks_buts_shading_tex[] = -{ - /* include_cb, name, blocktype, flag, chan_num, adrcodes */ - {NULL, "Clouds", ID_TE, 0, 5, - {TE_NSIZE,TE_NDEPTH,TE_NTYPE, - TE_MG_TYP,TE_N_BAS1}}, - - {NULL, "Marble", ID_TE, 0, 7, - {TE_NSIZE,TE_NDEPTH,TE_NTYPE, - TE_TURB,TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, - - {NULL, "Stucci", ID_TE, 0, 5, - {TE_NSIZE,TE_NTYPE,TE_TURB, - TE_MG_TYP,TE_N_BAS1}}, - - {NULL, "Wood", ID_TE, 0, 6, - {TE_NSIZE,TE_NTYPE,TE_TURB, - TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, - - {NULL, "Magic", ID_TE, 0, 2, {TE_NDEPTH,TE_TURB}}, - - {NULL, "Blend", ID_TE, 0, 1, {TE_MG_TYP}}, - - {NULL, "Musgrave", ID_TE, 0, 6, - {TE_MG_TYP,TE_MGH,TE_MG_LAC, - TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN}}, - - {NULL, "Voronoi", ID_TE, 0, 9, - {TE_VNW1,TE_VNW2,TE_VNW3,TE_VNW4, - TE_VNMEXP,TE_VN_DISTM,TE_VN_COLT, - TE_ISCA,TE_NSIZE}}, - - {NULL, "Distorted Noise", ID_TE, 0, 4, - {TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN,TE_DISTA}}, - - {NULL, "Color Filter", ID_TE, 0, 5, - {TE_COL_R,TE_COL_G,TE_COL_B,TE_BRIGHT,TE_CONTRA}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Available", ID_TE, -2, 0, {0}} -}; - -/* Object Buttons KeyingSets ------ */ - -/* check if include particles entry */ -static short incl_buts_ob (bKeyingSet *ks, const char mode[]) -{ - Object *ob= OBACT; - /* only if object is mesh type */ - - if(ob==NULL) return 0; - return (ob->type == OB_MESH); -} - -/* array for texture keyingset defines */ -bKeyingSet defks_buts_object[] = -{ - /* include_cb, name, blocktype, flag, chan_num, adrcodes */ - {incl_buts_ob, "Surface Damping", ID_OB, 0, 1, {OB_PD_SDAMP}}, - {incl_buts_ob, "Random Damping", ID_OB, 0, 1, {OB_PD_RDAMP}}, - {incl_buts_ob, "Permeability", ID_OB, 0, 1, {OB_PD_PERM}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Force Strength", ID_OB, 0, 1, {OB_PD_FSTR}}, - {NULL, "Force Falloff", ID_OB, 0, 1, {OB_PD_FFALL}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Available", ID_OB, -2, 0, {0}} // this will include ob-transforms too! -}; - -/* Camera Buttons KeyingSets ------ */ - -/* check if include internal-renderer entry */ -static short incl_buts_cam1 (bKeyingSet *ks, const char mode[]) -{ - /* only if renderer is internal renderer */ - return (G.scene->r.renderer==R_INTERN); -} - -/* check if include external-renderer entry */ -static short incl_buts_cam2 (bKeyingSet *ks, const char mode[]) -{ - /* only if renderer is internal renderer */ - return (G.scene->r.renderer!=R_INTERN); -} - -/* array for camera keyingset defines */ -bKeyingSet defks_buts_cam[] = -{ - /* include_cb, name, blocktype, flag, chan_num, adrcodes */ - {NULL, "Lens", ID_CA, 0, 1, {CAM_LENS}}, - {NULL, "Clipping", ID_CA, 0, 2, {CAM_STA,CAM_END}}, - {NULL, "Focal Distance", ID_CA, 0, 1, {CAM_YF_FDIST}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - - {incl_buts_cam2, "Aperture", ID_CA, 0, 1, {CAM_YF_APERT}}, - {incl_buts_cam1, "Viewplane Shift", ID_CA, 0, 2, {CAM_SHIFT_X,CAM_SHIFT_Y}}, - - {NULL, "%l", 0, -1, 0, {0}}, // separator - - {NULL, "Available", ID_CA, -2, 0, {0}} -}; - -/* --- */ - -/* Keying Context Defines - Must keep in sync with enumeration (eKS_Contexts) */ -bKeyingContext ks_contexts[] = -{ - KSC_TEMPLATE(v3d_object), - KSC_TEMPLATE(v3d_pchan), - - KSC_TEMPLATE(buts_shading_mat), - KSC_TEMPLATE(buts_shading_wo), - KSC_TEMPLATE(buts_shading_la), - KSC_TEMPLATE(buts_shading_tex), - - KSC_TEMPLATE(buts_object), - KSC_TEMPLATE(buts_cam) -}; - -/* Keying Context Enumeration - Must keep in sync with definitions*/ -typedef enum eKS_Contexts { - KSC_V3D_OBJECT = 0, - KSC_V3D_PCHAN, - - KSC_BUTS_MAT, - KSC_BUTS_WO, - KSC_BUTS_LA, - KSC_BUTS_TEX, - - KSC_BUTS_OB, - KSC_BUTS_CAM, - - /* make sure this last one remains untouched! */ - KSC_TOT_TYPES -} eKS_Contexts; - - -/* ---------------- KeyingSet Tools ------------------- */ - -/* helper for commonkey_context_get() - get keyingsets for 3d-view */ -static void commonkey_context_getv3d (ListBase *sources, bKeyingContext **ksc) -{ - Object *ob; - IpoCurve *icu; - - if ((OBACT) && (OBACT->flag & OB_POSEMODE)) { - bPoseChannel *pchan; - - /* pose-level */ - ob= OBACT; - *ksc= &ks_contexts[KSC_V3D_PCHAN]; - set_pose_keys(ob); /* sets pchan->flag to POSE_KEY if bone selected, and clears if not */ - - /* loop through posechannels */ - for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) { - if (pchan->flag & POSE_KEY) { - bCommonKeySrc *cks; - - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set id-block to key to, and action */ - cks->id= (ID *)ob; - cks->act= ob->action; - - /* set pchan */ - cks->pchan= pchan; - cks->actname= pchan->name; - } - } - } - else { - Base *base; - - /* object-level */ - *ksc= &ks_contexts[KSC_V3D_OBJECT]; - - /* loop through bases */ - for (base= FIRSTBASE; base; base= base->next) { - if (TESTBASELIB(base)) { - bCommonKeySrc *cks; - - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set id-block to key to */ - ob= base->object; - cks->id= (ID *)ob; - - /* when ob's keyframes are in an action, default to using 'Object' as achan name */ - if (ob->ipoflag & OB_ACTION_OB) - cks->actname= "Object"; - - /* set ipo-flags */ - // TODO: add checks for lib-linked data - if ((ob->ipo) || (ob->action)) { - if (ob->ipo) { - cks->ipo= ob->ipo; - } - else { - bActionChannel *achan; - - cks->act= ob->action; - achan= get_action_channel(ob->action, cks->actname); - - if (achan && achan->ipo) - cks->ipo= achan->ipo; - } - /* cks->ipo can be NULL while editing */ - if(cks->ipo) { - /* deselect all ipo-curves */ - for (icu= cks->ipo->curve.first; icu; icu= icu->next) { - icu->flag &= ~IPO_SELECT; - } - } - } - } - } - } -} - -/* helper for commonkey_context_get() - get keyingsets for buttons window */ -static void commonkey_context_getsbuts (ListBase *sources, bKeyingContext **ksc) -{ - bCommonKeySrc *cks; - -#if 0 // XXX dunno what's the future of this stuff... - /* check on tab-type */ - switch (G.buts->mainb) { - case CONTEXT_SHADING: /* ------------- Shading buttons ---------------- */ - /* subtabs include "Material", "Texture", "Lamp", "World"*/ - switch (G.buts->tab[CONTEXT_SHADING]) { - case TAB_SHADING_MAT: /* >------------- Material Tab -------------< */ - { - Material *ma= editnode_get_active_material(G.buts->lockpoin); - - if (ma) { - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set data */ - cks->id= (ID *)ma; - cks->ipo= ma->ipo; - cks->map= texchannel_to_adrcode(ma->texact); - - /* set keyingsets */ - *ksc= &ks_contexts[KSC_BUTS_MAT]; - return; - } - } - break; - case TAB_SHADING_WORLD: /* >------------- World Tab -------------< */ - { - World *wo= G.buts->lockpoin; - - if (wo) { - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set data */ - cks->id= (ID *)wo; - cks->ipo= wo->ipo; - cks->map= texchannel_to_adrcode(wo->texact); - - /* set keyingsets */ - *ksc= &ks_contexts[KSC_BUTS_WO]; - return; - } - } - break; - case TAB_SHADING_LAMP: /* >------------- Lamp Tab -------------< */ - { - Lamp *la= G.buts->lockpoin; - - if (la) { - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set data */ - cks->id= (ID *)la; - cks->ipo= la->ipo; - cks->map= texchannel_to_adrcode(la->texact); - - /* set keyingsets */ - *ksc= &ks_contexts[KSC_BUTS_LA]; - return; - } - } - break; - case TAB_SHADING_TEX: /* >------------- Texture Tab -------------< */ - { - Tex *tex= G.buts->lockpoin; - - if (tex) { - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set data */ - cks->id= (ID *)tex; - cks->ipo= tex->ipo; - - /* set keyingsets */ - *ksc= &ks_contexts[KSC_BUTS_TEX]; - return; - } - } - break; - } - break; - - case CONTEXT_OBJECT: /* ------------- Object buttons ---------------- */ - { - Object *ob= OBACT; - - if (ob) { - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set id-block to key to */ - cks->id= (ID *)ob; - cks->ipo= ob->ipo; - - /* set keyingsets */ - *ksc= &ks_contexts[KSC_BUTS_OB]; - return; - } - } - break; - - case CONTEXT_EDITING: /* ------------- Editing buttons ---------------- */ - { - Object *ob= OBACT; - - if ((ob) && (ob->type==OB_CAMERA) && (G.buts->lockpoin)) { /* >---------------- camera buttons ---------------< */ - Camera *ca= G.buts->lockpoin; - - /* add new keyframing destination */ - cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); - BLI_addtail(sources, cks); - - /* set id-block to key to */ - cks->id= (ID *)ca; - cks->ipo= ca->ipo; - - /* set keyingsets */ - *ksc= &ks_contexts[KSC_BUTS_CAM]; - return; - } - } - break; - } -#endif // XXX end of buttons stuff to port... - - /* if nothing happened... */ - *ksc= NULL; -} - - -/* get keyingsets for appropriate context */ -static void commonkey_context_get (ScrArea *sa, short mode, ListBase *sources, bKeyingContext **ksc) -{ - /* check view type */ - switch (sa->spacetype) { - /* 3d view - first one tested as most often used */ - case SPACE_VIEW3D: - { - commonkey_context_getv3d(sources, ksc); - } - break; - - /* buttons view */ - case SPACE_BUTS: - { - commonkey_context_getsbuts(sources, ksc); - } - break; - - /* spaces with their own methods */ - case SPACE_IPO: - //if (mode == COMMONKEY_MODE_INSERT) - // insertkey_editipo(); // XXX old calls... - return; - case SPACE_ACTION: - //if (mode == COMMONKEY_MODE_INSERT) - // insertkey_action(); // XXX old calls... - return; - - /* timeline view - keyframe buttons */ - case SPACE_TIME: - { - ScrArea *sab; - int bigarea= 0; - - /* try to find largest 3d-view available - * (mostly of the time, this is what when user will want this, - * as it's a standard feature in all other apps) - */ - //sab= find_biggest_area_of_type(SPACE_VIEW3D); - sab= NULL; // XXX for now... - if (sab) { - commonkey_context_getv3d(sources, ksc); - return; - } - - /* if not found, sab is now NULL, so perform own biggest area test */ - for (sa= G.curscreen->areabase.first; sa; sa= sa->next) { // XXX this has changed! - int area= sa->winx * sa->winy; - - if (sa->spacetype != SPACE_TIME) { - if ( (!sab) || (area > bigarea) ) { - sab= sa; - bigarea= area; - } - } - } - - /* use whichever largest area was found (it shouldn't be a time window) */ - if (sab) - commonkey_context_get(sab, mode, sources, ksc); - } - break; - } -} - -/* flush updates after all operations */ -static void commonkey_context_finish (ListBase *sources) -{ - /* check view type */ - switch (curarea->spacetype) { - /* 3d view - first one tested as most often used */ - case SPACE_VIEW3D: - { - /* either pose or object level */ - if (OBACT && (OBACT->pose)) { - Object *ob= OBACT; - - /* recalculate ipo handles, etc. */ - if (ob->action) - remake_action_ipos(ob->action); - - /* recalculate bone-paths on adding new keyframe? */ - // TODO: currently, there is no setting to turn this on/off globally - if (ob->pose->flag & POSE_RECALCPATHS) - pose_recalculate_paths(ob); - } - else { - bCommonKeySrc *cks; - - /* loop over bases (as seen in sources) */ - for (cks= sources->first; cks; cks= cks->next) { - Object *ob= (Object *)cks->id; - - /* simply set recalc flag */ - ob->recalc |= OB_RECALC_OB; - } - } - } - break; - } -} - -/* flush refreshes after undo */ -static void commonkey_context_refresh (void) -{ - /* check view type */ - switch (curarea->spacetype) { - /* 3d view - first one tested as most often used */ - case SPACE_VIEW3D: - { - /* do refreshes */ - DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); - - //allspace(REMAKEIPO, 0); - //allqueue(REDRAWVIEW3D, 0); - //allqueue(REDRAWMARKER, 0); - } - break; - - /* buttons window */ - case SPACE_BUTS: - { - //allspace(REMAKEIPO, 0); - //allqueue(REDRAWVIEW3D, 0); - //allqueue(REDRAWMARKER, 0); - } - break; - } -} - -/* --- */ - -/* Build menu-string of available keying-sets (allocates memory for string) - * NOTE: mode must not be longer than 64 chars - */ -static char *build_keyingsets_menu (bKeyingContext *ksc, const char mode[48]) -{ - DynStr *pupds= BLI_dynstr_new(); - bKeyingSet *ks; - char buf[64]; - char *str; - int i, n; - - /* add title first */ - BLI_snprintf(buf, 64, "%s Key %%t|", mode); - BLI_dynstr_append(pupds, buf); - - /* loop through keyingsets, adding them */ - for (ks=ksc->keyingsets, i=0, n=1; i < ksc->tot; ks++, i++, n++) { - /* check if keyingset can be used */ - if (ks->flag == -1) { - /* optional separator? */ - if (ks->include_cb) { - if (ks->include_cb(ks, mode)) { - BLI_snprintf( buf, 64, "%s%s", ks->name, ((n < ksc->tot)?"|":"") ); - BLI_dynstr_append(pupds, buf); - } - } - else { - BLI_snprintf( buf, 64, "%%l%s", ((n < ksc->tot)?"|":"") ); - BLI_dynstr_append(pupds, buf); - } - } - else if ( (ks->include_cb==NULL) || (ks->include_cb(ks, mode)) ) { - /* entry can be included */ - BLI_dynstr_append(pupds, ks->name); - - /* check if special "shapekey" entry */ - if (ks->flag == -3) - BLI_snprintf( buf, 64, "%%x0%s", ((n < ksc->tot)?"|":"") ); - else - BLI_snprintf( buf, 64, "%%x%d%s", n, ((n < ksc->tot)?"|":"") ); - BLI_dynstr_append(pupds, buf); - } - } - - /* convert to normal MEM_malloc'd string */ - str= BLI_dynstr_get_cstring(pupds); - BLI_dynstr_free(pupds); - - return str; -} - -/* Get the keying set that was chosen by the user from the menu */ -static bKeyingSet *get_keyingset_fromcontext (bKeyingContext *ksc, short index) -{ - /* check if index is valid */ - if (ELEM(NULL, ksc, ksc->keyingsets)) - return NULL; - if ((index < 1) || (index > ksc->tot)) - return NULL; - - /* index starts from 1, and should directly correspond to keyingset in array */ - return (bKeyingSet *)(ksc->keyingsets + (index - 1)); -} - -/* ---------------- Keyframe Management API -------------------- */ - -/* Display a menu for handling the insertion of keyframes based on the active view */ -// TODO: add back an option for repeating last keytype -void common_modifykey (short mode) -{ - ListBase dsources = {NULL, NULL}; - bKeyingContext *ksc= NULL; - bCommonKeySrc *cks; - bKeyingSet *ks = NULL; - char *menustr, buf[64]; - short menu_nr; - - /* check if mode is valid */ - if (ELEM(mode, COMMONKEY_MODE_INSERT, COMMONKEY_MODE_DELETE)==0) - return; - - /* delegate to other functions or get keyingsets to use - * - if the current area doesn't have its own handling, there will be data returned... - */ - commonkey_context_get(curarea, mode, &dsources, &ksc); - - /* check that there is data to operate on */ - if (ELEM(NULL, dsources.first, ksc)) { - BLI_freelistN(&dsources); - return; - } - - /* get menu and process it */ - if (mode == COMMONKEY_MODE_DELETE) - menustr= build_keyingsets_menu(ksc, "Delete"); - else - menustr= build_keyingsets_menu(ksc, "Insert"); - menu_nr= pupmenu(menustr); - if (menustr) MEM_freeN(menustr); - - /* no item selected or shapekey entry? */ - if (menu_nr < 1) { - /* free temp sources */ - BLI_freelistN(&dsources); - - /* check if insert new shapekey */ - if ((menu_nr == 0) && (mode == COMMONKEY_MODE_INSERT)) - insert_shapekey(OBACT); - else - ksc->lastused= NULL; - - return; - } - else { - /* try to get keyingset */ - ks= get_keyingset_fromcontext(ksc, menu_nr); - - if (ks == NULL) { - BLI_freelistN(&dsources); - return; - } - } - - /* loop over each destination, applying the keying set */ - for (cks= dsources.first; cks; cks= cks->next) { - short success= 0; - - /* special hacks for 'available' option */ - if (ks->flag == -2) { - IpoCurve *icu= NULL, *icn= NULL; - - /* get first IPO-curve */ - if (cks->act && cks->actname) { - bActionChannel *achan= get_action_channel(cks->act, cks->actname); - - // FIXME: what about constraint channels? - if (achan && achan->ipo) - icu= achan->ipo->curve.first; - } - else if(cks->ipo) - icu= cks->ipo->curve.first; - - /* we get adrcodes directly from IPO curves (see method below...) */ - for (; icu; icu= icn) { - short flag; - - /* get next ipo-curve in case current is deleted */ - icn= icu->next; - - /* insert mode or delete mode */ - if (mode == COMMONKEY_MODE_DELETE) { - /* local flags only add on to global flags */ - flag = 0; - - /* delete keyframe */ - success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); - } - else { - /* local flags only add on to global flags */ - flag = ks->flag; - if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; - if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; - // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; - - /* insert keyframe */ - success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); - } - } - } - else { - bKS_AdrcodeGetter kag; - short (*get_next_adrcode)(bKS_AdrcodeGetter *); - int adrcode; - - /* initialise keyingset channel iterator */ - ks_adrcodegetter_init(&kag, ks, cks); - - /* get iterator - only one can be in use at a time... the flags should be mutually exclusive in this regard */ - if (ks->flag & COMMONKEY_PCHANROT) - get_next_adrcode= ks_getnextadrcode_pchanrot; - else if (ks->flag & COMMONKEY_ADDMAP) - get_next_adrcode= ks_getnextadrcode_addmap; - else - get_next_adrcode= ks_getnextadrcode_default; - - /* loop over channels available in keyingset */ - for (adrcode= get_next_adrcode(&kag); adrcode > 0; adrcode= get_next_adrcode(&kag)) { - short flag; - - /* insert mode or delete mode */ - if (mode == COMMONKEY_MODE_DELETE) { - /* local flags only add on to global flags */ - flag = 0; - //flag &= ~COMMONKEY_MODES; - - /* delete keyframe */ - success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); - } - else { - /* local flags only add on to global flags */ - flag = ks->flag; - if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; - if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; - // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; - flag &= ~COMMONKEY_MODES; - - /* insert keyframe */ - success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); - } - } - } - - /* special handling for some key-sources */ - if (success) { - /* set pose recalc-paths flag */ - if (cks->pchan) { - Object *ob= (Object *)cks->id; - bPoseChannel *pchan= cks->pchan; - - /* set flag to trigger path recalc */ - if (pchan->path) - ob->pose->flag |= POSE_RECALCPATHS; - - /* clear unkeyed flag (it doesn't matter if it's set or not) */ - if (pchan->bone) - pchan->bone->flag &= ~BONE_UNKEYED; - } - } - } - - /* apply post-keying flushes for this data sources */ - commonkey_context_finish(&dsources); - ksc->lastused= ks; - - /* free temp data */ - BLI_freelistN(&dsources); - - /* undo pushes */ - if (mode == COMMONKEY_MODE_DELETE) - BLI_snprintf(buf, 64, "Delete %s Key", ks->name); - else - BLI_snprintf(buf, 64, "Insert %s Key", ks->name); - BIF_undo_push(buf); - - /* queue updates for contexts */ - commonkey_context_refresh(); -} - -/* ---- */ - -/* used to insert keyframes from any view */ -void common_insertkey (void) -{ - common_modifykey(COMMONKEY_MODE_INSERT); -} - -/* used to insert keyframes from any view */ -void common_deletekey (void) -{ - common_modifykey(COMMONKEY_MODE_DELETE); -} - -#endif // XXX reenable this file again later... - -/* ************************************************** */ -/* KEYFRAME DETECTION */ - -/* --------------- API/Per-Datablock Handling ------------------- */ - -/* Checks whether an IPO-block has a keyframe for a given frame - * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... - */ -short ipo_frame_has_keyframe (Ipo *ipo, float frame, short filter) -{ - IpoCurve *icu; - - /* can only find if there is data */ - if (ipo == NULL) - return 0; - - /* if only check non-muted, check if muted */ - if ((filter & ANIMFILTER_KEYS_MUTED) || (ipo->muteipo)) - return 0; - - /* loop over IPO-curves, using binary-search to try to find matches - * - this assumes that keyframes are only beztriples - */ - for (icu= ipo->curve.first; icu; icu= icu->next) { - /* only check if there are keyframes (currently only of type BezTriple) */ - if (icu->bezt) { - /* we either include all regardless of muting, or only non-muted */ - if ((filter & ANIMFILTER_KEYS_MUTED) || (icu->flag & IPO_MUTE)==0) { - short replace = -1; - int i = binarysearch_bezt_index(icu->bezt, frame, icu->totvert, &replace); - - /* binarysearch_bezt_index will set replace to be 0 or 1 - * - obviously, 1 represents a match - */ - if (replace) { - /* sanity check: 'i' may in rare cases exceed arraylen */ - if ((i >= 0) && (i < icu->totvert)) - return 1; - } - } - } - } - - /* nothing found */ - return 0; -} - -/* Checks whether an action-block has a keyframe for a given frame - * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... - */ -short action_frame_has_keyframe (bAction *act, float frame, short filter) -{ - bActionChannel *achan; - - /* error checking */ - if (act == NULL) - return 0; - - /* check thorugh action-channels for match */ - for (achan= act->chanbase.first; achan; achan= achan->next) { - /* we either include all regardless of muting, or only non-muted - * - here we include 'hidden' channels in the muted definition - */ - if ((filter & ANIMFILTER_KEYS_MUTED) || (achan->flag & ACHAN_HIDDEN)==0) { - if (ipo_frame_has_keyframe(achan->ipo, frame, filter)) - return 1; - } - } - - /* nothing found */ - return 0; -} - -/* Checks whether an Object has a keyframe for a given frame */ -short object_frame_has_keyframe (Object *ob, float frame, short filter) -{ - /* error checking */ - if (ob == NULL) - return 0; - - /* check for an action - actions take priority over normal IPO's */ - if (ob->action) { - float aframe; - - /* apply nla-action scaling if needed */ - if ((ob->nlaflag & OB_NLA_OVERRIDE) && (ob->nlastrips.first)) - aframe= get_action_frame(ob, frame); - else - aframe= frame; - - /* priority check here goes to pose-channel checks (for armatures) */ - if ((ob->pose) && (ob->flag & OB_POSEMODE)) { - /* only relevant check here is to only show active... */ - if (filter & ANIMFILTER_KEYS_ACTIVE) { - bPoseChannel *pchan= get_active_posechannel(ob); - bActionChannel *achan= (pchan) ? get_action_channel(ob->action, pchan->name) : NULL; - - /* since we're only interested in whether the selected one has any keyframes... */ - return (achan && ipo_frame_has_keyframe(achan->ipo, aframe, filter)); - } - } - - /* for everything else, just use the standard test (only return if success) */ - if (action_frame_has_keyframe(ob->action, aframe, filter)) - return 1; - } - else if (ob->ipo) { - /* only return if success */ - if (ipo_frame_has_keyframe(ob->ipo, frame, filter)) - return 1; - } - - /* try shapekey keyframes (if available, and allowed by filter) */ - if ( !(filter & ANIMFILTER_KEYS_LOCAL) && !(filter & ANIMFILTER_KEYS_NOSKEY) ) { - Key *key= ob_get_key(ob); - - /* shapekeys can have keyframes ('Relative Shape Keys') - * or depend on time (old 'Absolute Shape Keys') - */ - - /* 1. test for relative (with keyframes) */ - if (id_frame_has_keyframe((ID *)key, frame, filter)) - return 1; - - /* 2. test for time */ - // TODO... yet to be implemented (this feature may evolve before then anyway) - } - - /* try materials */ - if ( !(filter & ANIMFILTER_KEYS_LOCAL) && !(filter & ANIMFILTER_KEYS_NOMAT) ) { - /* if only active, then we can skip a lot of looping */ - if (filter & ANIMFILTER_KEYS_ACTIVE) { - Material *ma= give_current_material(ob, (ob->actcol + 1)); - - /* we only retrieve the active material... */ - if (id_frame_has_keyframe((ID *)ma, frame, filter)) - return 1; - } - else { - int a; - - /* loop over materials */ - for (a=0; atotcol; a++) { - Material *ma= give_current_material(ob, a+1); - - if (id_frame_has_keyframe((ID *)ma, frame, filter)) - return 1; - } - } - } - - /* nothing found */ - return 0; -} - -/* --------------- API ------------------- */ - -/* Checks whether a keyframe exists for the given ID-block one the given frame */ -short id_frame_has_keyframe (ID *id, float frame, short filter) -{ - /* error checking */ - if (id == NULL) - return 0; - - /* check for a valid id-type */ - switch (GS(id->name)) { - /* animation data-types */ - case ID_IP: /* ipo */ - return ipo_frame_has_keyframe((Ipo *)id, frame, filter); - case ID_AC: /* action */ - return action_frame_has_keyframe((bAction *)id, frame, filter); - - case ID_OB: /* object */ - return object_frame_has_keyframe((Object *)id, frame, filter); - - case ID_MA: /* material */ - { - Material *ma= (Material *)id; - - /* currently, material's only have an ipo-block */ - return ipo_frame_has_keyframe(ma->ipo, frame, filter); - } - break; - - case ID_KE: /* shapekey */ - { - Key *key= (Key *)id; - - /* currently, shapekey's only have an ipo-block */ - return ipo_frame_has_keyframe(key->ipo, frame, filter); - } - break; - } - - /* no keyframe found */ - return 0; -} - -/* ************************************************** */ diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c new file mode 100644 index 00000000000..b72b2bd27fa --- /dev/null +++ b/source/blender/editors/animation/keyframes_draw.c @@ -0,0 +1,704 @@ +/** + * $Id: drawaction.c 17746 2008-12-08 11:19:44Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* System includes ----------------------------------------------------- */ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +/* Types --------------------------------------------------------------- */ + +#include "DNA_listBase.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_constraint_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_material_types.h" +#include "DNA_userdef_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_action.h" +#include "BKE_depsgraph.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_global.h" // XXX remove me! +#include "BKE_context.h" +#include "BKE_utildefines.h" + +/* Everything from source (BIF, BDR, BSE) ------------------------------ */ + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_interface.h" +#include "UI_interface_icons.h" +#include "UI_resources.h" +#include "UI_text.h" +#include "UI_view2d.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_keyframes_draw.h" +#include "ED_screen.h" +#include "ED_space_api.h" + + +#if 0 // XXX old includes for reference only + #include "BIF_editaction.h" + #include "BIF_editkey.h" + #include "BIF_editnla.h" + #include "BIF_drawgpencil.h" + #include "BIF_keyframing.h" + #include "BIF_language.h" + #include "BIF_space.h" + + #include "BDR_editcurve.h" + #include "BDR_gpencil.h" + + #include "BSE_drawnla.h" + #include "BSE_drawipo.h" + #include "BSE_drawview.h" + #include "BSE_editaction_types.h" + #include "BSE_editipo.h" + #include "BSE_headerbuttons.h" + #include "BSE_time.h" + #include "BSE_view.h" +#endif // XXX old defines for reference only + +/* *************************** Keyframe Drawing *************************** */ + +static void add_bezt_to_keycolumnslist(ListBase *keys, BezTriple *bezt) +{ + /* The equivilant of add_to_cfra_elem except this version + * makes ActKeyColumns - one of the two datatypes required + * for action editor drawing. + */ + ActKeyColumn *ak, *akn; + + if (ELEM(NULL, keys, bezt)) return; + + /* try to any existing key to replace, or where to insert after */ + for (ak= keys->last; ak; ak= ak->prev) { + /* do because of double keys */ + if (ak->cfra == bezt->vec[1][0]) { + /* set selection status and 'touched' status */ + if (BEZSELECTED(bezt)) ak->sel = SELECT; + ak->modified += 1; + + return; + } + else if (ak->cfra < bezt->vec[1][0]) break; + } + + /* add new block */ + akn= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); + if (ak) BLI_insertlinkafter(keys, ak, akn); + else BLI_addtail(keys, akn); + + akn->cfra= bezt->vec[1][0]; + akn->modified += 1; + + // TODO: handle type = bezt->h1 or bezt->h2 + akn->handle_type= 0; + + if (BEZSELECTED(bezt)) + akn->sel = SELECT; + else + akn->sel = 0; +} + +static void add_bezt_to_keyblockslist(ListBase *blocks, IpoCurve *icu, int index) +{ + /* The equivilant of add_to_cfra_elem except this version + * makes ActKeyBlocks - one of the two datatypes required + * for action editor drawing. + */ + ActKeyBlock *ab, *abn; + BezTriple *beztn=NULL, *prev=NULL; + BezTriple *bezt; + int v; + + /* get beztriples */ + beztn= (icu->bezt + index); + + /* we need to go through all beztriples, as they may not be in order (i.e. during transform) */ + for (v=0, bezt=icu->bezt; vtotvert; v++, bezt++) { + /* skip if beztriple is current */ + if (v != index) { + /* check if beztriple is immediately before */ + if (beztn->vec[1][0] > bezt->vec[1][0]) { + /* check if closer than previous was */ + if (prev) { + if (prev->vec[1][0] < bezt->vec[1][0]) + prev= bezt; + } + else { + prev= bezt; + } + } + } + } + + /* check if block needed - same value(s)? + * -> firstly, handles must have same central value as each other + * -> secondly, handles which control that section of the curve must be constant + */ + if ((!prev) || (!beztn)) return; + if (IS_EQ(beztn->vec[1][1], prev->vec[1][1])==0) return; + if (IS_EQ(beztn->vec[1][1], beztn->vec[0][1])==0) return; + if (IS_EQ(prev->vec[1][1], prev->vec[2][1])==0) return; + + /* try to find a keyblock that starts on the previous beztriple + * Note: we can't search from end to try to optimise this as it causes errors there's + * an A ___ B |---| B situation + */ + // FIXME: here there is a bug where we are trying to get the summary for the following channels + // A|--------------|A ______________ B|--------------|B + // A|------------------------------------------------|A + // A|----|A|---|A|-----------------------------------|A + for (ab= blocks->first; ab; ab= ab->next) { + /* check if alter existing block or add new block */ + if (ab->start == prev->vec[1][0]) { + /* set selection status and 'touched' status */ + if (BEZSELECTED(beztn)) ab->sel = SELECT; + ab->modified += 1; + + return; + } + else if (ab->start < prev->vec[1][0]) break; + } + + /* add new block */ + abn= MEM_callocN(sizeof(ActKeyBlock), "ActKeyBlock"); + if (ab) BLI_insertlinkbefore(blocks, ab, abn); + else BLI_addtail(blocks, abn); + + abn->start= prev->vec[1][0]; + abn->end= beztn->vec[1][0]; + abn->val= beztn->vec[1][1]; + + if (BEZSELECTED(prev) || BEZSELECTED(beztn)) + abn->sel = SELECT; + else + abn->sel = 0; + abn->modified = 1; +} + +/* helper function - find actkeycolumn that occurs on cframe */ +static ActKeyColumn *cfra_find_actkeycolumn (ListBase *keys, float cframe) +{ + ActKeyColumn *ak, *ak2; + + if (keys==NULL) + return NULL; + + /* search from both ends at the same time, and stop if we find match or if both ends meet */ + for (ak=keys->first, ak2=keys->last; ak && ak2; ak=ak->next, ak2=ak2->prev) { + /* return whichever end encounters the frame */ + if (ak->cfra == cframe) + return ak; + if (ak2->cfra == cframe) + return ak2; + + /* no matches on either end, so return NULL */ + if (ak == ak2) + return NULL; + } + + return NULL; +} + +#if 0 // disabled, as some intel cards have problems with this +/* Draw a simple diamond shape with a filled in center (in screen space) */ +static void draw_key_but(int x, int y, short w, short h, int sel) +{ + int xmin= x, ymin= y; + int xmax= x+w-1, ymax= y+h-1; + int xc= (xmin+xmax)/2, yc= (ymin+ymax)/2; + + /* interior - hardcoded colors (for selected and unselected only) */ + if (sel) glColor3ub(0xF1, 0xCA, 0x13); + else glColor3ub(0xE9, 0xE9, 0xE9); + + glBegin(GL_QUADS); + glVertex2i(xc, ymin); + glVertex2i(xmax, yc); + glVertex2i(xc, ymax); + glVertex2i(xmin, yc); + glEnd(); + + + /* outline */ + glColor3ub(0, 0, 0); + + glBegin(GL_LINE_LOOP); + glVertex2i(xc, ymin); + glVertex2i(xmax, yc); + glVertex2i(xc, ymax); + glVertex2i(xmin, yc); + glEnd(); +} +#endif + +static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, float ypos) +{ + ActKeyColumn *ak; + ActKeyBlock *ab; + + glEnable(GL_BLEND); + + /* draw keyblocks */ + if (blocks) { + for (ab= blocks->first; ab; ab= ab->next) { + short startCurves, endCurves, totCurves; + + /* find out how many curves occur at each keyframe */ + ak= cfra_find_actkeycolumn(keys, ab->start); + startCurves = (ak)? ak->totcurve: 0; + + ak= cfra_find_actkeycolumn(keys, ab->end); + endCurves = (ak)? ak->totcurve: 0; + + /* only draw keyblock if it appears in at all of the keyframes at lowest end */ + if (!startCurves && !endCurves) + continue; + else + totCurves = (startCurves>endCurves)? endCurves: startCurves; + + if (ab->totcurve >= totCurves) { + int sc_xa, sc_xb, sc_ya, sc_yb; + + /* get co-ordinates of block */ + gla2DDrawTranslatePt(di, ab->start, ypos, &sc_xa, &sc_ya); + gla2DDrawTranslatePt(di, ab->end, ypos, &sc_xb, &sc_yb); + + /* draw block */ + if (ab->sel) + UI_ThemeColor4(TH_STRIP_SELECT); + else + UI_ThemeColor4(TH_STRIP); + glRectf((float)sc_xa, (float)sc_ya-3, (float)sc_xb, (float)sc_yb+5); + } + } + } + + /* draw keys */ + if (keys) { + for (ak= keys->first; ak; ak= ak->next) { + int sc_x, sc_y; + + /* get co-ordinate to draw at */ + gla2DDrawTranslatePt(di, ak->cfra, ypos, &sc_x, &sc_y); + + /* draw using icons - old way which is slower but more proven */ + if (ak->sel & SELECT) UI_icon_draw_aspect((float)sc_x-7, (float)sc_y-6, ICON_SPACE2, 1.0f); + else UI_icon_draw_aspect((float)sc_x-7, (float)sc_y-6, ICON_SPACE3, 1.0f); + + /* draw using OpenGL - slightly uglier but faster */ + // NOTE: disabled for now, as some intel cards seem to have problems with this + //draw_key_but(sc_x-5, sc_y-4, 11, 11, (ak->sel & SELECT)); + } + } + + glDisable(GL_BLEND); +} + +/* *************************** Channel Drawing Funcs *************************** */ + +void draw_object_channel(gla2DDrawInfo *di, ActKeysInc *aki, Object *ob, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + ob_to_keylist(ob, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_ipo_channel(gla2DDrawInfo *di, ActKeysInc *aki, Ipo *ipo, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + ipo_to_keylist(ipo, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_icu_channel(gla2DDrawInfo *di, ActKeysInc *aki, IpoCurve *icu, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + icu_to_keylist(icu, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_agroup_channel(gla2DDrawInfo *di, ActKeysInc *aki, bActionGroup *agrp, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + agroup_to_keylist(agrp, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_action_channel(gla2DDrawInfo *di, ActKeysInc *aki, bAction *act, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + action_to_keylist(act, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_gpl_channel(gla2DDrawInfo *di, ActKeysInc *aki, bGPDlayer *gpl, float ypos) +{ + ListBase keys = {0, 0}; + + gpl_to_keylist(gpl, &keys, NULL, aki); + draw_keylist(di, &keys, NULL, ypos); + BLI_freelistN(&keys); +} + +/* *************************** Keyframe List Conversions *************************** */ + +void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bConstraintChannel *conchan; + Key *key= ob_get_key(ob); + + if (ob) { + bDopeSheet *ads= (aki)? (aki->ads) : NULL; + int filterflag; + + /* get filterflag */ + if (ads) + filterflag= ads->filterflag; + else if ((aki) && (aki->actmode == -1)) /* only set like this by NLA */ + filterflag= ADS_FILTER_NLADUMMY; + else + filterflag= 0; + + /* Add object keyframes */ + if ((ob->ipo) && !(filterflag & ADS_FILTER_NOIPOS)) + ipo_to_keylist(ob->ipo, keys, blocks, aki); + + /* Add action keyframes */ + if ((ob->action) && !(filterflag & ADS_FILTER_NOACTS)) + action_nlascaled_to_keylist(ob, ob->action, keys, blocks, aki); + + /* Add shapekey keyframes (only if dopesheet allows, if it is available) */ + if ((key && key->ipo) && !(filterflag & ADS_FILTER_NOSHAPEKEYS)) + ipo_to_keylist(key->ipo, keys, blocks, aki); + + /* Add material keyframes (only if dopesheet allows, if it is available) */ + if ((ob->totcol) && !(filterflag & ADS_FILTER_NOMAT)) { + short a; + + for (a=0; atotcol; a++) { + Material *ma= give_current_material(ob, a); + + if (ELEM(NULL, ma, ma->ipo) == 0) + ipo_to_keylist(ma->ipo, keys, blocks, aki); + } + } + + /* Add object data keyframes */ + switch (ob->type) { + case OB_CAMERA: /* ------- Camera ------------ */ + { + Camera *ca= (Camera *)ob->data; + if ((ca->ipo) && !(ads->filterflag & ADS_FILTER_NOCAM)) + ipo_to_keylist(ca->ipo, keys, blocks, aki); + } + break; + case OB_LAMP: /* ---------- Lamp ----------- */ + { + Lamp *la= (Lamp *)ob->data; + if ((la->ipo) && !(ads->filterflag & ADS_FILTER_NOLAM)) + ipo_to_keylist(la->ipo, keys, blocks, aki); + } + break; + case OB_CURVE: /* ------- Curve ---------- */ + { + Curve *cu= (Curve *)ob->data; + if ((cu->ipo) && !(ads->filterflag & ADS_FILTER_NOCUR)) + ipo_to_keylist(cu->ipo, keys, blocks, aki); + } + break; + } + + /* Add constraint keyframes */ + if (!(filterflag & ADS_FILTER_NOCONSTRAINTS)) { + for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next) { + if (conchan->ipo) + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + } + } + } +} + +static short bezt_in_aki_range (ActKeysInc *aki, BezTriple *bezt) +{ + /* when aki == NULL, we don't care about range */ + if (aki == NULL) + return 1; + + /* if start and end are both 0, then don't care about range */ + if (IS_EQ(aki->start, 0) && IS_EQ(aki->end, 0)) + return 1; + + /* if nla-scaling is in effect, apply appropriate scaling adjustments */ +#if 0 // XXX this was from some buggy code... do not port for now + if (aki->ob) { + float frame= get_action_frame_inv(aki->ob, bezt->vec[1][0]); + return IN_RANGE(frame, aki->start, aki->end); + } + else { + /* check if in range */ + return IN_RANGE(bezt->vec[1][0], aki->start, aki->end); + } +#endif // XXX this was from some buggy code... do not port for now + return 1; +} + +void icu_to_keylist(IpoCurve *icu, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + BezTriple *bezt; + ActKeyColumn *ak, *ak2; + ActKeyBlock *ab, *ab2; + int v; + + if (icu && icu->totvert) { + /* loop through beztriples, making ActKeys and ActKeyBlocks */ + bezt= icu->bezt; + + for (v=0; vtotvert; v++, bezt++) { + /* only if keyframe is in range (optimisation) */ + if (bezt_in_aki_range(aki, bezt)) { + add_bezt_to_keycolumnslist(keys, bezt); + if (blocks) add_bezt_to_keyblockslist(blocks, icu, v); + } + } + + /* update the number of curves that elements have appeared in */ + if (keys) { + for (ak=keys->first, ak2=keys->last; ak && ak2; ak=ak->next, ak2=ak2->prev) { + if (ak->modified) { + ak->modified = 0; + ak->totcurve += 1; + } + + if (ak == ak2) + break; + + if (ak2->modified) { + ak2->modified = 0; + ak2->totcurve += 1; + } + } + } + if (blocks) { + for (ab=blocks->first, ab2=blocks->last; ab && ab2; ab=ab->next, ab2=ab2->prev) { + if (ab->modified) { + ab->modified = 0; + ab->totcurve += 1; + } + + if (ab == ab2) + break; + + if (ab2->modified) { + ab2->modified = 0; + ab2->totcurve += 1; + } + } + } + } +} + +void ipo_to_keylist(Ipo *ipo, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + IpoCurve *icu; + + if (ipo) { + for (icu= ipo->curve.first; icu; icu= icu->next) + icu_to_keylist(icu, keys, blocks, aki); + } +} + +void agroup_to_keylist(bActionGroup *agrp, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bActionChannel *achan; + bConstraintChannel *conchan; + + if (agrp) { + /* loop through action channels */ + for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) { + if (VISIBLE_ACHAN(achan)) { + /* firstly, add keys from action channel's ipo block */ + if (achan->ipo) + ipo_to_keylist(achan->ipo, keys, blocks, aki); + + /* then, add keys from constraint channels */ + for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { + if (conchan->ipo) + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + } + } + } + } +} + +void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bActionChannel *achan; + bConstraintChannel *conchan; + + if (act) { + /* loop through action channels */ + for (achan= act->chanbase.first; achan; achan= achan->next) { + /* firstly, add keys from action channel's ipo block */ + if (achan->ipo) + ipo_to_keylist(achan->ipo, keys, blocks, aki); + + /* then, add keys from constraint channels */ + for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { + if (conchan->ipo) + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + } + } + } +} + +void action_nlascaled_to_keylist(Object *ob, bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bActionChannel *achan; + bConstraintChannel *conchan; + Object *oldob= NULL; + + /* although apply and clearing NLA-scaling pre-post creating keylist does impact on performance, + * the effects should be fairly minimal, as we're already going through the keyframes multiple times + * already for blocks too... + */ + if (act) { + /* if 'aki' is provided, store it's current ob to restore later as it might not be the same */ + if (aki) { + oldob= aki->ob; + aki->ob= ob; + } + + /* loop through action channels */ + for (achan= act->chanbase.first; achan; achan= achan->next) { + /* firstly, add keys from action channel's ipo block + * - scaling correction only does times for center-points, so should be faster + */ + if (achan->ipo) { + //actstrip_map_ipo_keys(ob, achan->ipo, 0, 1); // XXX + ipo_to_keylist(achan->ipo, keys, blocks, aki); + //actstrip_map_ipo_keys(ob, achan->ipo, 1, 1); // XXX + } + + /* then, add keys from constraint channels + * - scaling correction only does times for center-points, so should be faster + */ + for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { + if (conchan->ipo) { + //actstrip_map_ipo_keys(ob, conchan->ipo, 0, 1); // XXX + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + //actstrip_map_ipo_keys(ob, conchan->ipo, 1, 1); // XXX + } + } + } + + /* if 'aki' is provided, restore ob */ + if (aki) + aki->ob= oldob; + } +} + +void gpl_to_keylist(bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bGPDframe *gpf; + ActKeyColumn *ak; + + if (gpl && keys) { + /* loop over frames, converting directly to 'keyframes' (should be in order too) */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + ak= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); + BLI_addtail(keys, ak); + + ak->cfra= (float)gpf->framenum; + ak->modified = 1; + ak->handle_type= 0; + + if (gpf->flag & GP_FRAME_SELECT) + ak->sel = SELECT; + else + ak->sel = 0; + } + } +} + diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c new file mode 100644 index 00000000000..152d07b83f6 --- /dev/null +++ b/source/blender/editors/animation/keyframes_edit.c @@ -0,0 +1,785 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" + +#include "BKE_action.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_utildefines.h" + +#include "ED_keyframes_edit.h" +#include "ED_markers.h" + +/* This file defines an API and set of callback-operators for editing keyframe data. + * + * Two API functions are defined for actually performing the operations on the data: + * ipo_keys_bezier_loop() and icu_keys_bezier_loop() + * which take the data they operate on, a few callbacks defining what operations to perform. + * + * As operators which work on keyframes usually apply the same operation on all BezTriples in + * every channel, the code has been optimised providing a set of functions which will get the + * appropriate bezier-modify function to set. These functions (ANIM_editkeyframes_*) will need + * to be called before getting any channels. + * + * - Joshua Leung, Dec 2008 + */ + +/* ************************************************************************** */ +/* IPO Editing Loops - Exposed API */ + +// FIXME: it would be useful to be able to supply custom properties to the bezt function... +// workaround for those callbacks that need this now, is to set globals... + +/* This function is used to loop over BezTriples in the given IpoCurve, applying a given + * operation on them, and optionally applies an IPO-curve validate function afterwards. + */ +short icu_keys_bezier_loop(Scene *scene, IpoCurve *icu, BeztEditFunc bezt_cb, IcuEditFunc icu_cb) +{ + BezTriple *bezt; + int b; + + /* if function to apply to bezier curves is set, then loop through executing it on beztriples */ + if (bezt_cb) { + for (b=0, bezt=icu->bezt; b < icu->totvert; b++, bezt++) { + /* Exit with return-code '1' if function returns positive + * This is useful if finding if some BezTriple satisfies a condition. + */ + if (bezt_cb(scene, bezt)) return 1; + } + } + + /* if ipocurve_function has been specified then execute it */ + if (icu_cb) + icu_cb(icu); + + /* done */ + return 0; +} + +/* This function is used to loop over the IPO curves (and subsequently the keyframes in them) */ +short ipo_keys_bezier_loop(Scene *scene, Ipo *ipo, BeztEditFunc bezt_cb, IcuEditFunc icu_cb) +{ + IpoCurve *icu; + + /* Sanity check */ + if (ipo == NULL) + return 0; + + /* Loop through each curve in the Ipo */ + for (icu= ipo->curve.first; icu; icu=icu->next) { + if (icu_keys_bezier_loop(scene, icu, bezt_cb, icu_cb)) + return 1; + } + + return 0; +} + +/* ******************************************* */ +/* Transform */ + +static short snap_bezier_nearest(Scene *scene, BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->vec[1][0]= (float)(floor(bezt->vec[1][0]+0.5)); + return 0; +} + +static short snap_bezier_nearestsec(Scene *scene, BezTriple *bezt) +{ + float secf = FPS; + if (bezt->f2 & SELECT) + bezt->vec[1][0]= (float)(floor(bezt->vec[1][0]/secf + 0.5f) * secf); + return 0; +} + +static short snap_bezier_cframe(Scene *scene, BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->vec[1][0]= (float)CFRA; + return 0; +} + +static short snap_bezier_nearmarker(Scene *scene, BezTriple *bezt) +{ + //if (bezt->f2 & SELECT) + // bezt->vec[1][0]= (float)find_nearest_marker_time(bezt->vec[1][0]); // XXX missing function! + return 0; +} + +// calchandles_ipocurve +BeztEditFunc ANIM_editkeys_snap(short type) +{ + switch (type) { + case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */ + return snap_bezier_nearest; + case SNAP_KEYS_CURFRAME: /* snap to current frame */ + return snap_bezier_cframe; + case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */ + return snap_bezier_nearmarker; + case SNAP_KEYS_NEARSEC: /* snap to nearest second */ + return snap_bezier_nearestsec; + default: /* just in case */ + return snap_bezier_nearest; + } +} + +/* --------- */ + +static short mirror_bezier_cframe(Scene *scene, BezTriple *bezt) +{ + float diff; + + if (bezt->f2 & SELECT) { + diff= ((float)CFRA - bezt->vec[1][0]); + bezt->vec[1][0]= ((float)CFRA + diff); + } + + return 0; +} + +static short mirror_bezier_yaxis(Scene *scene, BezTriple *bezt) +{ + float diff; + + if (bezt->f2 & SELECT) { + diff= (0.0f - bezt->vec[1][0]); + bezt->vec[1][0]= (0.0f + diff); + } + + return 0; +} + +static short mirror_bezier_xaxis(Scene *scene, BezTriple *bezt) +{ + float diff; + + if (bezt->f2 & SELECT) { + diff= (0.0f - bezt->vec[1][1]); + bezt->vec[1][1]= (0.0f + diff); + } + + return 0; +} + +static short mirror_bezier_marker(Scene *scene, BezTriple *bezt) +{ + static TimeMarker *marker; + static short initialised = 0; + + /* In order for this mirror function to work without + * any extra arguments being added, we use the case + * of bezt==NULL to denote that we should find the + * marker to mirror over. The static pointer is safe + * to use this way, as it will be set to null after + * each cycle in which this is called. + */ + + if (bezt) { + /* mirroring time */ + if ((bezt->f2 & SELECT) && (marker)) { + const float diff= (marker->frame - bezt->vec[1][0]); + bezt->vec[1][0]= (marker->frame + diff); + } + } + else { + /* initialisation time */ + if (initialised) { + /* reset everything for safety */ + marker = NULL; + initialised = 0; + } + else { + /* try to find a marker */ + for (marker= scene->markers.first; marker; marker=marker->next) { + if (marker->flag & SELECT) { + initialised = 1; + break; + } + } + + if (initialised == 0) + marker = NULL; + } + } + + return 0; +} + +/* Note: for markers case, need to set global vars (eww...) */ +// calchandles_ipocurve +BeztEditFunc ANIM_editkeyframes_mirror(short type) +{ + switch (type) { + case 1: /* mirror over current frame */ + return mirror_bezier_cframe; + case 2: /* mirror over frame 0 */ + return mirror_bezier_yaxis; + case 3: /* mirror over value 0 */ + return mirror_bezier_xaxis; + case 4: /* mirror over marker */ + return mirror_bezier_marker; // XXX in past, this func was called before/after with NULL, probably will need globals instead + default: /* just in case */ + return mirror_bezier_yaxis; + break; + } +} + +/* This function is called to calculate the average location of the + * selected keyframes, and place the current frame at that location. + * + * It must be called like so: + * snap_cfra_ipo_keys(scene, NULL, -1); // initialise the static vars first + * for (ipo...) snap_cfra_ipo_keys(scene, ipo, 0); // sum up keyframe times + * snap_cfra_ipo_keys(scene, NULL, 1); // set current frame after taking average + */ +void snap_cfra_ipo_keys(Scene *scene, Ipo *ipo, short mode) +{ + static int cfra; + static int tot; + + IpoCurve *icu; + BezTriple *bezt; + int a; + + + if (mode == -1) { + /* initialise a new snap-operation */ + cfra= 0; + tot= 0; + } + else if (mode == 1) { + /* set current frame - using average frame */ + if (tot != 0) + CFRA = cfra / tot; + } + else { + /* loop through keys in ipo, summing the frame + * numbers of those that are selected + */ + if (ipo == NULL) + return; + + for (icu= ipo->curve.first; icu; icu= icu->next) { + for (a=0, bezt=icu->bezt; a < icu->totvert; a++, bezt++) { + if (BEZSELECTED(bezt)) { + cfra += bezt->vec[1][0]; + tot++; + } + } + } + } +} + +/* ******************************************* */ +/* Settings */ + +/* Sets the selected bezier handles to type 'auto' */ +static short set_bezier_auto(Scene *scene, BezTriple *bezt) +{ + /* is a handle selected? If so set it to type auto */ + if((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { + if (bezt->f1 & SELECT) bezt->h1= 1; /* the secret code for auto */ + if (bezt->f3 & SELECT) bezt->h2= 1; + + /* if the handles are not of the same type, set them + * to type free + */ + if (bezt->h1 != bezt->h2) { + if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE; + if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE; + } + } + return 0; +} + +/* Sets the selected bezier handles to type 'vector' */ +static short set_bezier_vector(Scene *scene, BezTriple *bezt) +{ + /* is a handle selected? If so set it to type vector */ + if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { + if (bezt->f1 & SELECT) bezt->h1= HD_VECT; + if (bezt->f3 & SELECT) bezt->h2= HD_VECT; + + /* if the handles are not of the same type, set them + * to type free + */ + if (bezt->h1 != bezt->h2) { + if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE; + if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE; + } + } + return 0; +} + +#if 0 // xxx currently not used (only used by old code as a check) +static short bezier_isfree(Scene *scene, BezTriple *bezt) +{ + /* queries whether the handle should be set + * to type 'free' or 'align' + */ + if ((bezt->f1 & SELECT) && (bezt->h1)) return 1; + if ((bezt->f3 & SELECT) && (bezt->h2)) return 1; + return 0; +} + +static short set_bezier_align(Scene *scene, BezTriple *bezt) +{ + /* Sets selected bezier handles to type 'align' */ + if (bezt->f1 & SELECT) bezt->h1= HD_ALIGN; + if (bezt->f3 & SELECT) bezt->h2= HD_ALIGN; + return 0; +} +#endif // xxx currently not used (only used by old code as a check, but can't replicate that now) + +static short set_bezier_free(Scene *scene, BezTriple *bezt) +{ + /* Sets selected bezier handles to type 'free' */ + if (bezt->f1 & SELECT) bezt->h1= HD_FREE; + if (bezt->f3 & SELECT) bezt->h2= HD_FREE; + return 0; +} + +/* Set all Bezier Handles to a single type */ +// calchandles_ipocurve +BeztEditFunc ANIM_editkeyframes_sethandles(short code) +{ + switch (code) { + case 1: /* auto */ + return set_bezier_auto; + case 2: /* vector */ + return set_bezier_vector; + + default: /* free or align? */ + return set_bezier_free; // err.. to set align, we need 'align' to be set + } +} + +#if 0 +void sethandles_ipo_keys(Ipo *ipo, int code) +{ + /* this function lets you set bezier handles all to + * one type for some Ipo's (e.g. with hotkeys through + * the action window). + */ + + /* code==1: set autohandle */ + /* code==2: set vectorhandle */ + /* als code==3 (HD_ALIGN) toggelt het, vectorhandles worden HD_FREE */ + + switch (code) { + case 1: /* auto */ + ipo_keys_bezier_loop(ipo, set_bezier_auto, calchandles_ipocurve); + break; + case 2: /* vector */ + ipo_keys_bezier_loop(ipo, set_bezier_vector, calchandles_ipocurve); + break; + default: /* free or align? */ + if (ipo_keys_bezier_loop(ipo, bezier_isfree, NULL)) /* free */ + ipo_keys_bezier_loop(ipo, set_bezier_free, calchandles_ipocurve); + else /* align */ + ipo_keys_bezier_loop(ipo, set_bezier_align, calchandles_ipocurve); + break; + } +} +#endif + +/* ------- */ + +void set_ipocurve_mixed(IpoCurve *icu) +{ + /* Sets the type of the IPO curve to mixed, as some (selected) + * keyframes were set to other interpolation modes + */ + icu->ipo= IPO_MIXED; + + /* recalculate handles, as some changes may have occurred */ + calchandles_ipocurve(icu); +} + +static short set_bezt_constant(Scene *scene, BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo= IPO_CONST; + return 0; +} + +static short set_bezt_linear(Scene *scene, BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo= IPO_LIN; + return 0; +} + +static short set_bezt_bezier(Scene *scene, BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo= IPO_BEZ; + return 0; +} + +/* Set the interpolation type of the selected BezTriples in each IPO curve to the specified one */ +// set_ipocurve_mixed() ! +BeztEditFunc ANIM_editkeyframes_ipo(short code) +{ + switch (code) { + case 1: /* constant */ + return set_bezt_constant; + case 2: /* linear */ + return set_bezt_linear; + default: /* bezier */ + return set_bezt_bezier; + } +} + +#if 0 +void setipotype_ipo(Ipo *ipo, int code) +{ + /* Sets the type of the selected bezts in each ipo curve in the + * Ipo to a value based on the code + */ + switch (code) { + case 1: + ipo_keys_bezier_loop(ipo, set_bezt_constant, set_ipocurve_mixed); + break; + case 2: + ipo_keys_bezier_loop(ipo, set_bezt_linear, set_ipocurve_mixed); + break; + case 3: + ipo_keys_bezier_loop(ipo, set_bezt_bezier, set_ipocurve_mixed); + break; + } +} +#endif + +// XXX will we keep this? +void setexprap_ipoloop(Ipo *ipo, int code) +{ + IpoCurve *icu; + + /* Loop through each curve in the Ipo */ + for (icu=ipo->curve.first; icu; icu=icu->next) + icu->extrap= code; +} + +/* ******************************************* */ +/* Selection */ + +static short select_bezier_add(Scene *scene, BezTriple *bezt) +{ + /* Select the bezier triple */ + BEZ_SEL(bezt); + return 0; +} + +static short select_bezier_subtract(Scene *scene, BezTriple *bezt) +{ + /* Deselect the bezier triple */ + BEZ_DESEL(bezt); + return 0; +} + +static short select_bezier_invert(Scene *scene, BezTriple *bezt) +{ + /* Invert the selection for the bezier triple */ + bezt->f2 ^= SELECT; + if (bezt->f2 & SELECT) { + bezt->f1 |= SELECT; + bezt->f3 |= SELECT; + } + else { + bezt->f1 &= ~SELECT; + bezt->f3 &= ~SELECT; + } + return 0; +} + +// NULL +BeztEditFunc ANIM_editkeyframes_select(short selectmode) +{ + switch (selectmode) { + case SELECT_ADD: /* add */ + return select_bezier_add; + case SELECT_SUBTRACT: /* subtract */ + return select_bezier_subtract; + case SELECT_INVERT: /* invert */ + return select_bezier_invert; + default: /* replace (need to clear all, then add) */ + return select_bezier_add; + } +} + + +short is_ipo_key_selected(Ipo *ipo) +{ + IpoCurve *icu; + BezTriple *bezt; + int i; + + if (ipo == NULL) + return 0; + + for (icu=ipo->curve.first; icu; icu=icu->next) { + for (i=0, bezt=icu->bezt; itotvert; i++, bezt++) { + if (BEZSELECTED(bezt)) + return 1; + } + } + + return 0; +} + +void set_ipo_key_selection(Ipo *ipo, short sel) +{ + IpoCurve *icu; + BezTriple *bezt; + int i; + + if (ipo == NULL) + return; + + for (icu=ipo->curve.first; icu; icu=icu->next) { + for (i=0, bezt=icu->bezt; itotvert; i++, bezt++) { + if (sel == 2) { + BEZ_INVSEL(bezt); + } + else if (sel == 1) { + BEZ_SEL(bezt); + } + else { + BEZ_DESEL(bezt); + } + } + } +} + +// err... this is this still used? +int fullselect_ipo_keys(Ipo *ipo) +{ + IpoCurve *icu; + int tvtot = 0; + int i; + + if (!ipo) + return tvtot; + + for (icu=ipo->curve.first; icu; icu=icu->next) { + for (i=0; itotvert; i++) { + if (icu->bezt[i].f2 & SELECT) { + tvtot+=3; + icu->bezt[i].f1 |= SELECT; + icu->bezt[i].f3 |= SELECT; + } + } + } + + return tvtot; +} + + +void borderselect_icu_key(Scene *scene, IpoCurve *icu, float xmin, float xmax, BeztEditFunc select_cb) +{ + /* Selects all bezier triples in the Ipocurve + * between times xmin and xmax, using the selection + * function. + */ + BezTriple *bezt; + int i; + + /* loop through all of the bezier triples in + * the Ipocurve -- if the triple occurs between + * times xmin and xmax then select it using the selection + * function + */ + for (i=0, bezt=icu->bezt; itotvert; i++, bezt++) { + if ((bezt->vec[1][0] > xmin) && (bezt->vec[1][0] < xmax)) { + select_cb(scene, bezt); + } + } +} + +void borderselect_ipo_key(Scene *scene, Ipo *ipo, float xmin, float xmax, short selectmode) +{ + /* Selects all bezier triples in each Ipocurve of the + * Ipo between times xmin and xmax, using the selection mode. + */ + + IpoCurve *icu; + BeztEditFunc select_cb; + + /* If the ipo is no good then return */ + if (ipo == NULL) + return; + + /* Set the selection function based on the + * selection mode. + */ + select_cb= ANIM_editkeyframes_select(selectmode); + if (select_cb == NULL) + return; + + /* loop through all of the bezier triples in all + * of the Ipocurves -- if the triple occurs between + * times xmin and xmax then select it using the selection + * function + */ + for (icu=ipo->curve.first; icu; icu=icu->next) { + borderselect_icu_key(scene, icu, xmin, xmax, select_cb); + } +} + + +#if 0 +void select_ipo_bezier_keys(Ipo *ipo, int selectmode) +{ + /* Select all of the beziers in all + * of the Ipo curves belonging to the + * Ipo, using the selection mode. + */ + switch (selectmode) { + case SELECT_ADD: + ipo_keys_bezier_loop(ipo, select_bezier_add, NULL); + break; + case SELECT_SUBTRACT: + ipo_keys_bezier_loop(ipo, select_bezier_subtract, NULL); + break; + case SELECT_INVERT: + ipo_keys_bezier_loop(ipo, select_bezier_invert, NULL); + break; + } +} + +void select_icu_bezier_keys(IpoCurve *icu, int selectmode) +{ + /* Select all of the beziers in all + * of the Ipo curves belonging to the + * Ipo, using the selection mode. + */ + switch (selectmode) { + case SELECT_ADD: + icu_keys_bezier_loop(icu, select_bezier_add, NULL); + break; + case SELECT_SUBTRACT: + icu_keys_bezier_loop(icu, select_bezier_subtract, NULL); + break; + case SELECT_INVERT: + icu_keys_bezier_loop(icu, select_bezier_invert, NULL); + break; + } +} +#endif + +void select_icu_key(Scene *scene, IpoCurve *icu, float selx, short selectmode) +{ + /* Selects all bezier triples in the Ipocurve + * at time selx, using the selection mode. + * This is kind of sloppy the obvious similarities + * with the above function, forgive me ... + */ + BeztEditFunc select_cb; + BezTriple *bezt; + int i; + + /* If the icu is no good then return */ + if (icu == NULL) + return; + + /* Set the selection function based on the selection mode. */ + switch (selectmode) { + case SELECT_ADD: + select_cb = select_bezier_add; + break; + case SELECT_SUBTRACT: + select_cb = select_bezier_subtract; + break; + case SELECT_INVERT: + select_cb = select_bezier_invert; + break; + default: + return; + } + + /* loop through all of the bezier triples in + * the Ipocurve -- if the triple occurs at + * time selx then select it using the selection + * function + */ + for (i=0, bezt=icu->bezt; itotvert; i++, bezt++) { + if (bezt->vec[1][0] == selx) { + select_cb(scene, bezt); + } + } +} + +void select_ipo_key(Scene *scene, Ipo *ipo, float selx, short selectmode) +{ + /* Selects all bezier triples in each Ipocurve of the + * Ipo at time selx, using the selection mode. + */ + IpoCurve *icu; + BezTriple *bezt; + BeztEditFunc select_cb; + int i; + + /* If the ipo is no good then return */ + if (ipo == NULL) + return; + + /* Set the selection function based on the + * selection mode. + */ + select_cb= ANIM_editkeyframes_select(selectmode); + if (select_cb == NULL) + return; + + /* loop through all of the bezier triples in all + * of the Ipocurves -- if the triple occurs at + * time selx then select it using the selection + * function + */ + for (icu=ipo->curve.first; icu; icu=icu->next) { + for (i=0, bezt=icu->bezt; itotvert; i++, bezt++) { + if (bezt->vec[1][0] == selx) { + select_cb(scene, bezt); + } + } + } +} + + diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c new file mode 100644 index 00000000000..a86cf3719d3 --- /dev/null +++ b/source/blender/editors/animation/keyframing.c @@ -0,0 +1,2287 @@ +/** + * $Id: keyframing.c 17745 2008-12-08 09:16:09Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender (with some old code) + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + + + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_dynstr.h" + +#include "DNA_listBase.h" +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_constraint_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_object_types.h" +#include "DNA_material_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_texture_types.h" +#include "DNA_userdef_types.h" +#include "DNA_vec_types.h" +#include "DNA_view3d_types.h" +#include "DNA_world_types.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_blender.h" +#include "BKE_main.h" // XXX not needed old cruft? +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_object.h" +#include "BKE_material.h" + +#include "ED_keyframing.h" + +#if 0 // XXX resolve these old dependencies! +#include "BIF_butspace.h" +#include "BIF_editaction.h" +#include "BIF_editkey.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_poseobject.h" +#include "BIF_screen.h" +#include "BIF_space.h" +#include "BIF_toolbox.h" +#include "BIF_toets.h" + +#include "BSE_editipo.h" +#include "BSE_node.h" +#include "BSE_time.h" +#include "BSE_view.h" + +#include "blendef.h" + +#include "PIL_time.h" /* sleep */ +#include "mydevice.h" +#endif // XXX resolve these old dependencies! + + + +/* ************************************************** */ +/* LOCAL TYPES AND DEFINES */ + +/* ----------- Common KeyData Sources ------------ */ + +/* temporary struct to gather data combos to keyframe */ +typedef struct bCommonKeySrc { + struct bCommonKeySrc *next, *prev; + + /* general data/destination-source settings */ + ID *id; /* id-block this comes from */ + char *actname; /* name of action channel */ + char *constname; /* name of constraint channel */ + + /* general destination source settings */ + Ipo *ipo; /* ipo-block that id-block has (optional) */ + bAction *act; /* action-block that id-block has (optional) */ + + /* pose-level settings */ + bPoseChannel *pchan; /* pose channel */ + + /* buttons-window settings */ + int map; /* offset to apply to certain adrcodes */ +} bCommonKeySrc; + +/* -------------- Keying Sets ------------------- */ + +/* storage for iterator for looping over keyingset channels */ +typedef struct bKS_AdrcodeGetter { + struct bKeyingSet *ks; /* keyingset this applies to */ + struct bCommonKeySrc *cks; /* data to insert/delete keyframes... */ + + short index; /* index of current channel to resume from */ + short tot; /* index after which we start returning from some special collection */ +} bKS_AdrcodeGetter; + +/* flags to look out for in keyingset channels... */ +#define KAG_CHAN_EXTEND (-1) + + +/* keying set - a set of channels that will be keyframed together */ +// TODO: move this to a header to allow custom sets someday? +typedef struct bKeyingSet { + /* callback func to consider if keyingset should be included + * (by default, if this is undefined, item will be shown) + */ + short (*include_cb)(struct bKeyingSet *, const char *); + + char name[48]; /* name of keyingset */ + int blocktype; /* blocktype that all channels belong to */ // in future, this may be eliminated + short flag; /* flags to use when setting keyframes */ + + short chan_num; /* number of channels to insert keyframe in */ + short adrcodes[32]; /* adrcodes for channels to insert keys for (ideally would be variable-len, but limit of 32 will suffice) */ +} bKeyingSet; + +/* keying set context - an array of keying sets and the number of them */ +typedef struct bKeyingContext { + bKeyingSet *keyingsets; /* array containing the keyingsets of interest */ + bKeyingSet *lastused; /* item that was chosen last time*/ + int tot; /* number of keyingsets in */ +} bKeyingContext; + +/* ************************************************** */ +/* KEYFRAME INSERTION */ + +/* -------------- BezTriple Insertion -------------------- */ + +/* threshold for inserting keyframes - threshold here should be good enough for now, but should become userpref */ +#define BEZT_INSERT_THRESH 0.00001 + +/* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_icu) + * Returns the index to insert at (data already at that index will be offset if replace is 0) + */ +static int binarysearch_bezt_index (BezTriple array[], float frame, int arraylen, short *replace) +{ + int start=0, end=arraylen; + int loopbreaker= 0, maxloop= arraylen * 2; + + /* initialise replace-flag first */ + *replace= 0; + + /* sneaky optimisations (don't go through searching process if...): + * - keyframe to be added is to be added out of current bounds + * - keyframe to be added would replace one of the existing ones on bounds + */ + if ((arraylen <= 0) || (array == NULL)) { + printf("Warning: binarysearch_bezt_index() encountered invalid array \n"); + return 0; + } + else { + /* check whether to add before/after/on */ + float framenum; + + /* 'First' Keyframe (when only one keyframe, this case is used) */ + framenum= array[0].vec[1][0]; + if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { + *replace = 1; + return 0; + } + else if (frame < framenum) + return 0; + + /* 'Last' Keyframe */ + framenum= array[(arraylen-1)].vec[1][0]; + if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { + *replace= 1; + return (arraylen - 1); + } + else if (frame > framenum) + return arraylen; + } + + + /* most of the time, this loop is just to find where to put it + * 'loopbreaker' is just here to prevent infinite loops + */ + for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { + /* compute and get midpoint */ + int mid = (start + end) / 2; + float midfra= array[mid].vec[1][0]; + + /* check if exactly equal to midpoint */ + if (IS_EQT(frame, midfra, BEZT_INSERT_THRESH)) { + *replace = 1; + return mid; + } + + /* repeat in upper/lower half */ + if (frame > midfra) + start= mid + 1; + else if (frame < midfra) + end= mid - 1; + } + + /* print error if loop-limit exceeded */ + if (loopbreaker == (maxloop-1)) { + printf("Error: binarysearch_bezt_index() was taking too long \n"); + + // include debug info + printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen); + } + + /* not found, so return where to place it */ + return start; +} + +/* This function adds a given BezTriple to an IPO-Curve. It will allocate + * memory for the array if needed, and will insert the BezTriple into a + * suitable place in chronological order. + * + * NOTE: any recalculate of the IPO-Curve that needs to be done will need to + * be done by the caller. + */ +int insert_bezt_icu (IpoCurve *icu, BezTriple *bezt) +{ + BezTriple *newb; + int i= 0; + + if (icu->bezt) { + short replace = -1; + i = binarysearch_bezt_index(icu->bezt, bezt->vec[1][0], icu->totvert, &replace); + + if (replace) { + /* sanity check: 'i' may in rare cases exceed arraylen */ + // FIXME: do not overwrite handletype if just replacing...? + if ((i >= 0) && (i < icu->totvert)) + *(icu->bezt + i) = *bezt; + } + else { + /* add new */ + newb= MEM_callocN((icu->totvert+1)*sizeof(BezTriple), "beztriple"); + + /* add the beztriples that should occur before the beztriple to be pasted (originally in ei->icu) */ + if (i > 0) + memcpy(newb, icu->bezt, i*sizeof(BezTriple)); + + /* add beztriple to paste at index i */ + *(newb + i)= *bezt; + + /* add the beztriples that occur after the beztriple to be pasted (originally in icu) */ + if (i < icu->totvert) + memcpy(newb+i+1, icu->bezt+i, (icu->totvert-i)*sizeof(BezTriple)); + + /* replace (+ free) old with new */ + MEM_freeN(icu->bezt); + icu->bezt= newb; + + icu->totvert++; + } + } + else { + icu->bezt= MEM_callocN(sizeof(BezTriple), "beztriple"); + *(icu->bezt)= *bezt; + icu->totvert= 1; + } + + + /* we need to return the index, so that some tools which do post-processing can + * detect where we added the BezTriple in the array + */ + return i; +} + +/* This function is a wrapper for insert_bezt_icu, and should be used when + * adding a new keyframe to a curve, when the keyframe doesn't exist anywhere + * else yet. + * + * 'fast' - is only for the python API where importing BVH's would take an extreamly long time. + */ +void insert_vert_icu (IpoCurve *icu, float x, float y, short fast) +{ + BezTriple beztr; + int a, h1, h2; + + /* set all three points, for nicer start position */ + memset(&beztr, 0, sizeof(BezTriple)); + beztr.vec[0][0]= x; + beztr.vec[0][1]= y; + beztr.vec[1][0]= x; + beztr.vec[1][1]= y; + beztr.vec[2][0]= x; + beztr.vec[2][1]= y; + beztr.hide= IPO_BEZ; + beztr.f1= beztr.f2= beztr.f3= SELECT; + beztr.h1= beztr.h2= HD_AUTO; + + /* add temp beztriple to keyframes */ + a= insert_bezt_icu(icu, &beztr); + + /* what if 'a' is a negative index? + * for now, just exit to prevent any segfaults + */ + if (a < 0) return; + + /* don't recalculate handles if fast is set + * - this is a hack to make importers faster + * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) + */ + if (!fast) calchandles_ipocurve(icu); + + /* set handletype and interpolation */ + if (icu->totvert > 2) { + BezTriple *bezt= (icu->bezt + a); + + /* set handles (autohandles by default) */ + h1= h2= HD_AUTO; + + if (a > 0) h1= (bezt-1)->h2; + if (a < icu->totvert-1) h2= (bezt+1)->h1; + + bezt->h1= h1; + bezt->h2= h2; + + /* set interpolation (if curve is using IPO_MIXED, then take from previous) */ + if (icu->ipo == IPO_MIXED) { + if (a > 0) bezt->ipo= (bezt-1)->ipo; + else if (a < icu->totvert-1) bezt->ipo= (bezt+1)->ipo; + } + else + bezt->ipo= icu->ipo; + + /* don't recalculate handles if fast is set + * - this is a hack to make importers faster + * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) + */ + if (!fast) calchandles_ipocurve(icu); + } + else { + BezTriple *bezt= (icu->bezt + a); + + /* set interpolation directly from ipo-curve */ + bezt->ipo= icu->ipo; + } +} + +#if 0 // XXX code to clean up + +/* ------------------- Get Data ------------------------ */ + +/* Get pointer to use to get values from */ +// FIXME: this should not be possible with Data-API +static void *get_context_ipo_poin (ID *id, int blocktype, char *actname, char *constname, IpoCurve *icu, int *vartype) +{ + switch (blocktype) { + case ID_PO: /* posechannel */ + if (GS(id->name)==ID_OB) { + Object *ob= (Object *)id; + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + + if (pchan) { + if (ELEM3(icu->adrcode, AC_EUL_X, AC_EUL_Y, AC_EUL_Z)) + *vartype= IPO_FLOAT_DEGR; + else + *vartype= IPO_FLOAT; + return get_pchan_ipo_poin(pchan, icu->adrcode); + } + } + break; + + case ID_CO: /* constraint */ + if ((GS(id->name)==ID_OB) && (constname && constname[0])) { + Object *ob= (Object *)id; + bConstraint *con; + + /* assume that we only want the influence (as only used for Constraint Channels) */ + if ((ob->ipoflag & OB_ACTION_OB) && !strcmp(actname, "Object")) { + for (con= ob->constraints.first; con; con= con->next) { + if (strcmp(constname, con->name)==0) { + *vartype= IPO_FLOAT; + return &con->enforce; + } + } + } + else if (ob->pose) { + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + + if (pchan) { + for (con= pchan->constraints.first; con; con= con->next) { + if (strcmp(constname, con->name)==0) { + *vartype= IPO_FLOAT; + return &con->enforce; + } + } + } + } + } + break; + + case ID_OB: /* object */ + /* hack: layer channels for object need to be keyed WITHOUT localview flag... + * tsk... tsk... why must we just dump bitflags upon users :/ + */ + if ((GS(id->name)==ID_OB) && (icu->adrcode==OB_LAY)) { + Object *ob= (Object *)id; + static int layer = 0; + + /* init layer to be the object's layer var, then remove local view from it */ + layer = ob->lay; + layer &= 0xFFFFFF; + *vartype= IPO_INT_BIT; + + /* return pointer to this static var + * - assumes that this pointer won't be stored for use later, so may not be threadsafe + * if multiple keyframe calls are made, but that is unlikely to happen in the near future + */ + return (void *)(&layer); + } + /* no break here for other ob channel-types - as they can be done normally */ + + default: /* normal data-source */ + return get_ipo_poin(id, icu, vartype); + } + + /* not valid... */ + return NULL; +} + + +/* -------------- 'Smarter' Keyframing Functions -------------------- */ +/* return codes for new_key_needed */ +enum { + KEYNEEDED_DONTADD = 0, + KEYNEEDED_JUSTADD, + KEYNEEDED_DELPREV, + KEYNEEDED_DELNEXT +} eKeyNeededStatus; + +/* This helper function determines whether a new keyframe is needed */ +/* Cases where keyframes should not be added: + * 1. Keyframe to be added bewteen two keyframes with similar values + * 2. Keyframe to be added on frame where two keyframes are already situated + * 3. Keyframe lies at point that intersects the linear line between two keyframes + */ +static short new_key_needed (IpoCurve *icu, float cFrame, float nValue) +{ + BezTriple *bezt=NULL, *prev=NULL; + int totCount, i; + float valA = 0.0f, valB = 0.0f; + + /* safety checking */ + if (icu == NULL) return KEYNEEDED_JUSTADD; + totCount= icu->totvert; + if (totCount == 0) return KEYNEEDED_JUSTADD; + + /* loop through checking if any are the same */ + bezt= icu->bezt; + for (i=0; ivec[1][0]; + beztVal= bezt->vec[1][1]; + + if (prev) { + /* there is a keyframe before the one currently being examined */ + + /* get previous time+value */ + prevPosi= prev->vec[1][0]; + prevVal= prev->vec[1][1]; + + /* keyframe to be added at point where there are already two similar points? */ + if (IS_EQ(prevPosi, cFrame) && IS_EQ(beztPosi, cFrame) && IS_EQ(beztPosi, prevPosi)) { + return KEYNEEDED_DONTADD; + } + + /* keyframe between prev+current points ? */ + if ((prevPosi <= cFrame) && (cFrame <= beztPosi)) { + /* is the value of keyframe to be added the same as keyframes on either side ? */ + if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) { + return KEYNEEDED_DONTADD; + } + else { + float realVal; + + /* get real value of curve at that point */ + realVal= eval_icu(icu, cFrame); + + /* compare whether it's the same as proposed */ + if (IS_EQ(realVal, nValue)) + return KEYNEEDED_DONTADD; + else + return KEYNEEDED_JUSTADD; + } + } + + /* new keyframe before prev beztriple? */ + if (cFrame < prevPosi) { + /* A new keyframe will be added. However, whether the previous beztriple + * stays around or not depends on whether the values of previous/current + * beztriples and new keyframe are the same. + */ + if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) + return KEYNEEDED_DELNEXT; + else + return KEYNEEDED_JUSTADD; + } + } + else { + /* just add a keyframe if there's only one keyframe + * and the new one occurs before the exisiting one does. + */ + if ((cFrame < beztPosi) && (totCount==1)) + return KEYNEEDED_JUSTADD; + } + + /* continue. frame to do not yet passed (or other conditions not met) */ + if (i < (totCount-1)) { + prev= bezt; + bezt++; + } + else + break; + } + + /* Frame in which to add a new-keyframe occurs after all other keys + * -> If there are at least two existing keyframes, then if the values of the + * last two keyframes and the new-keyframe match, the last existing keyframe + * gets deleted as it is no longer required. + * -> Otherwise, a keyframe is just added. 1.0 is added so that fake-2nd-to-last + * keyframe is not equal to last keyframe. + */ + bezt= (icu->bezt + (icu->totvert - 1)); + valA= bezt->vec[1][1]; + + if (prev) + valB= prev->vec[1][1]; + else + valB= bezt->vec[1][1] + 1.0f; + + if (IS_EQ(valA, nValue) && IS_EQ(valA, valB)) + return KEYNEEDED_DELPREV; + else + return KEYNEEDED_JUSTADD; +} + +/* ------------------ 'Visual' Keyframing Functions ------------------ */ + +/* internal status codes for visualkey_can_use */ +enum { + VISUALKEY_NONE = 0, + VISUALKEY_LOC, + VISUALKEY_ROT +}; + +/* This helper function determines if visual-keyframing should be used when + * inserting keyframes for the given channel. As visual-keyframing only works + * on Object and Pose-Channel blocks, this should only get called for those + * blocktypes, when using "standard" keying but 'Visual Keying' option in Auto-Keying + * settings is on. + */ +static short visualkey_can_use (ID *id, int blocktype, char *actname, char *constname, int adrcode) +{ + Object *ob= NULL; + bConstraint *con= NULL; + short searchtype= VISUALKEY_NONE; + + /* validate data */ + if ((id == NULL) || (GS(id->name)!=ID_OB) || !(ELEM(blocktype, ID_OB, ID_PO))) + return 0; + + /* get first constraint and determine type of keyframe constraints to check for*/ + ob= (Object *)id; + + if (blocktype == ID_OB) { + con= ob->constraints.first; + + if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) + searchtype= VISUALKEY_LOC; + else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) + searchtype= VISUALKEY_ROT; + } + else if (blocktype == ID_PO) { + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + con= pchan->constraints.first; + + if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) + searchtype= VISUALKEY_LOC; + else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) + searchtype= VISUALKEY_ROT; + } + + /* only search if a searchtype and initial constraint are available */ + if (searchtype && con) { + for (; con; con= con->next) { + /* only consider constraint if it is not disabled, and has influence */ + if (con->flag & CONSTRAINT_DISABLE) continue; + if (con->enforce == 0.0f) continue; + + /* some constraints may alter these transforms */ + switch (con->type) { + /* multi-transform constraints */ + case CONSTRAINT_TYPE_CHILDOF: + return 1; + case CONSTRAINT_TYPE_TRANSFORM: + return 1; + case CONSTRAINT_TYPE_FOLLOWPATH: + return 1; + case CONSTRAINT_TYPE_KINEMATIC: + return 1; + + /* single-transform constraits */ + case CONSTRAINT_TYPE_TRACKTO: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_ROTLIMIT: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_LOCLIMIT: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_ROTLIKE: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_DISTLIMIT: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_LOCLIKE: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_LOCKTRACK: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_MINMAX: + if (searchtype==VISUALKEY_LOC) return 1; + break; + + default: + break; + } + } + } + + /* when some condition is met, this function returns, so here it can be 0 */ + return 0; +} + +/* This helper function extracts the value to use for visual-keyframing + * In the event that it is not possible to perform visual keying, try to fall-back + * to using the poin method. Assumes that all data it has been passed is valid. + */ +static float visualkey_get_value (ID *id, int blocktype, char *actname, char *constname, int adrcode, IpoCurve *icu) +{ + Object *ob; + void *poin = NULL; + int index, vartype; + + /* validate situtation */ + if ((id==NULL) || (GS(id->name)!=ID_OB) || (ELEM(blocktype, ID_OB, ID_PO)==0)) + return 0.0f; + + /* get object */ + ob= (Object *)id; + + /* only valid for objects or posechannels */ + if (blocktype == ID_OB) { + /* parented objects are not supported, as the effects of the parent + * are included in the matrix, which kindof beats the point + */ + if (ob->parent == NULL) { + /* only Location or Rotation keyframes are supported now */ + if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) { + /* assumes that OB_LOC_Z > OB_LOC_Y > OB_LOC_X */ + index= adrcode - OB_LOC_X; + + return ob->obmat[3][index]; + } + else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) { + float eul[3]; + + /* assumes that OB_ROT_Z > OB_ROT_Y > OB_ROT_X */ + index= adrcode - OB_ROT_X; + + Mat4ToEul(ob->obmat, eul); + return eul[index]*(5.72958f); + } + } + } + else if (blocktype == ID_PO) { + bPoseChannel *pchan; + float tmat[4][4]; + + /* get data to use */ + pchan= get_pose_channel(ob->pose, actname); + + /* Although it is not strictly required for this particular space conversion, + * arg1 must not be null, as there is a null check for the other conversions to + * be safe. Therefore, the active object is passed here, and in many cases, this + * will be what owns the pose-channel that is getting this anyway. + */ + Mat4CpyMat4(tmat, pchan->pose_mat); + constraint_mat_convertspace(ob, pchan, tmat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL); + + /* Loc, Rot/Quat keyframes are supported... */ + if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) { + /* assumes that AC_LOC_Z > AC_LOC_Y > AC_LOC_X */ + index= adrcode - AC_LOC_X; + + /* only use for non-connected bones */ + if ((pchan->bone->parent) && !(pchan->bone->flag & BONE_CONNECTED)) + return tmat[3][index]; + else if (pchan->bone->parent == NULL) + return tmat[3][index]; + } + else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) { + float trimat[3][3], quat[4]; + + /* assumes that AC_QUAT_Z > AC_QUAT_Y > AC_QUAT_X > AC_QUAT_W */ + index= adrcode - AC_QUAT_W; + + Mat3CpyMat4(trimat, tmat); + Mat3ToQuat_is_ok(trimat, quat); + + return quat[index]; + } + } + + /* as the function hasn't returned yet, try reading from poin */ + poin= get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); + if (poin) + return read_ipo_poin(poin, vartype); + else + return 0.0; +} + +/* ------------------------- Insert Key API ------------------------- */ + +/* 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. + * + * 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 insertkey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) +{ + IpoCurve *icu; + + /* get ipo-curve */ + //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 1); // XXX this call needs to be in blenkernel + + /* only continue if we have an ipo-curve to add keyframe to */ + if (icu) { + float cfra = frame_to_float(CFRA); + float curval= 0.0f; + + /* apply special time tweaking */ + if (GS(id->name) == ID_OB) { + Object *ob= (Object *)id; + + /* apply NLA-scaling (if applicable) */ + if (actname && actname[0]) + 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)*G.scene->r.framelen; + } + } + + /* obtain value to give keyframe */ + if ( (flag & INSERTKEY_MATRIX) && + (visualkey_can_use(id, blocktype, actname, constname, adrcode)) ) + { + /* 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(id, blocktype, actname, constname, adrcode, icu); + } + else { + void *poin; + int vartype; + + /* get pointer to data to read from */ + poin = get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); + if (poin == NULL) { + printf("Insert Key: No pointer to variable obtained \n"); + return 0; + } + + /* use kt's read_poin function to extract value (kt->read_poin should + * exist in all cases, but it never hurts to check) + */ + curval= read_ipo_poin(poin, vartype); + } + + /* 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(icu, cfra, curval); + + /* insert new keyframe at current frame */ + if (insert_mode) + insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* delete keyframe immediately before/after newly added */ + switch (insert_mode) { + case KEYNEEDED_DELPREV: + delete_icu_key(icu, icu->totvert-2, 1); + break; + case KEYNEEDED_DELNEXT: + delete_icu_key(icu, 1, 1); + break; + } + + /* only return success if keyframe added */ + if (insert_mode) + return 1; + } + else { + /* just insert keyframe */ + insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* return success */ + return 1; + } + } + + /* return failure */ + return 0; +} + + +/* ************************************************** */ +/* KEYFRAME DELETION */ + +/* Main Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it + * already exists. It will delete a keyframe at the current frame. + * + * The flag argument is used for special settings that alter the behaviour of + * the keyframe deletion. These include the quick refresh options. + */ +short deletekey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) +{ + Ipo *ipo; + IpoCurve *icu; + + /* get ipo-curve + * Note: here is one of the places where we don't want new ipo + ipo-curve added! + * so 'add' var must be 0 + */ + // XXX funcs here need to be recoded in blenkernel... + //ipo= verify_ipo(id, blocktype, actname, constname, NULL, 0); + //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 0); + + /* only continue if we have an ipo-curve to remove keyframes from */ + if (icu) { + float cfra = frame_to_float(CFRA); + short found = -1; + int i; + + /* apply special time tweaking */ + if (GS(id->name) == ID_OB) { + Object *ob= (Object *)id; + + /* apply NLA-scaling (if applicable) */ + if (actname && actname[0]) + 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)*G.scene->r.framelen; + } + } + + /* try to find index of beztriple to get rid of */ + i = binarysearch_bezt_index(icu->bezt, cfra, icu->totvert, &found); + if (found) { + /* delete the key at the index (will sanity check + do recalc afterwards ) */ + delete_icu_key(icu, i, 1); + + /* Only delete curve too if there isn't an ipo-driver still hanging around on an empty curve */ + if (icu->totvert==0 && icu->driver==NULL) { + BLI_remlink(&ipo->curve, icu); + free_ipo_curve(icu); + } + + /* return success */ + return 1; + } + } + + /* return failure */ + return 0; +} + +/* ************************************************** */ +/* COMMON KEYFRAME MANAGEMENT (common_insertkey/deletekey) */ + +/* mode for common_modifykey */ +enum { + COMMONKEY_MODE_INSERT = 0, + COMMONKEY_MODE_DELETE, +} eCommonModifyKey_Modes; + +/* --------- KeyingSet Adrcode Getters ------------ */ + +/* initialise a channel-getter storage */ +static void ks_adrcodegetter_init (bKS_AdrcodeGetter *kag, bKeyingSet *ks, bCommonKeySrc *cks) +{ + /* error checking */ + if (kag == NULL) + return; + + if (ELEM(NULL, ks, cks)) { + /* set invalid settings that won't cause harm */ + kag->ks= NULL; + kag->cks= NULL; + kag->index= -2; + kag->tot= 0; + } + else { + /* store settings */ + kag->ks= ks; + kag->cks= cks; + + /* - index is -1, as that allows iterators to return first element + * - tot is chan_num by default, but may get overriden if -1 is encountered (for extension-type getters) + */ + kag->index= -1; + kag->tot= ks->chan_num; + } +} + +/* 'default' channel-getter that will be used when iterating through keyingset's channels + * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape + */ +static short ks_getnextadrcode_default (bKS_AdrcodeGetter *kag) +{ + bKeyingSet *ks= (kag)? kag->ks : NULL; + + /* error checking */ + if (ELEM(NULL, kag, ks)) return 0; + if (kag->tot <= 0) return 0; + + kag->index++; + if ((kag->index < 0) || (kag->index >= kag->tot)) return 0; + + /* return the adrcode stored at index then */ + return ks->adrcodes[kag->index]; +} + +/* add map flag (for MTex channels, as certain ones need special offset) */ +static short ks_getnextadrcode_addmap (bKS_AdrcodeGetter *kag) +{ + short adrcode= ks_getnextadrcode_default(kag); + + /* if there was an adrcode returned, assume that kag stuff is set ok */ + if (adrcode) { + bCommonKeySrc *cks= kag->cks; + bKeyingSet *ks= kag->ks; + + if (ELEM3(ks->blocktype, ID_MA, ID_LA, ID_WO)) { + switch (adrcode) { + case MAP_OFS_X: case MAP_OFS_Y: case MAP_OFS_Z: + case MAP_SIZE_X: case MAP_SIZE_Y: case MAP_SIZE_Z: + case MAP_R: case MAP_G: case MAP_B: case MAP_DVAR: + case MAP_COLF: case MAP_NORF: case MAP_VARF: case MAP_DISP: + adrcode += cks->map; + break; + } + } + } + + /* adrcode must be returned! */ + return adrcode; +} + +/* extend posechannel keyingsets with rotation info (when KAG_CHAN_EXTEND is encountered) + * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape + * - when we encounter KAG_CHAN_EXTEND as adrcode, start returning our own + */ +static short ks_getnextadrcode_pchanrot (bKS_AdrcodeGetter *kag) +{ + /* hardcoded adrcode channels used here only + * - length is keyed-channels + 1 (last item must be 0 to escape) + */ + static short quat_adrcodes[5] = {AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z, 0}; + static short eul_adrcodes[4] = {AC_EUL_X, AC_EUL_Y, AC_EUL_Z, 0}; + + /* useful variables */ + bKeyingSet *ks= (kag)? kag->ks : NULL; + bCommonKeySrc *cks= (kag) ? kag->cks : NULL; + short index, adrcode; + + /* error checking */ + if (ELEM3(NULL, kag, ks, cks)) return 0; + if (ks->chan_num <= 0) return 0; + + /* get index + * - if past the last item (kag->tot), return stuff from our static arrays + * - otherwise, just keep returning stuff from the keyingset (but check out for -1!) + */ + kag->index++; + if (kag->index < 0) + return 0; + + /* normal (static stuff) */ + if (kag->index < kag->tot) { + /* get adrcode, and return if not KAG_CHAN_EXTEND (i.e. point for start of iteration) */ + adrcode= ks->adrcodes[kag->index]; + + if (adrcode != KAG_CHAN_EXTEND) + return adrcode; + else + kag->tot= kag->index; + } + + /* based on current rotation-mode + * - index can be at most 5, if we are to prevent segfaults + */ + index= kag->index - kag->tot; + if ((index < 0) || (index > 5)) + return 0; + + if (cks->pchan && cks->pchan->rotmode) + return eul_adrcodes[index]; + else + return quat_adrcodes[index]; +} + +/* ------------- KeyingSet Defines ------------ */ +/* Note: these must all be named with the defks_* prefix, otherwise the template macro will not work! */ + +/* macro for defining keyingset contexts */ +#define KSC_TEMPLATE(ctx_name) {&defks_##ctx_name[0], NULL, sizeof(defks_##ctx_name)/sizeof(bKeyingSet)} + +/* --- */ + +/* check if option not available for deleting keys */ +static short incl_non_del_keys (bKeyingSet *ks, const char mode[]) +{ + /* as optimisation, assume that it is sufficient to check only first letter + * of mode (int comparison should be faster than string!) + */ + //if (strcmp(mode, "Delete")==0) + if (mode && mode[0]=='D') + return 0; + + return 1; +} + +/* Object KeyingSets ------ */ + +/* check if include shapekey entry */ +static short incl_v3d_ob_shapekey (bKeyingSet *ks, const char mode[]) +{ + Object *ob= (G.obedit)? (G.obedit) : (OBACT); + char *newname= NULL; + + if(ob==NULL) + return 0; + + /* not available for delete mode */ + if (strcmp(mode, "Delete")==0) + return 0; + + /* check if is geom object that can get shapekeys */ + switch (ob->type) { + /* geometry? */ + case OB_MESH: newname= "Mesh"; break; + case OB_CURVE: newname= "Curve"; break; + case OB_SURF: newname= "Surface"; break; + case OB_LATTICE: newname= "Lattice"; break; + + /* not geometry! */ + default: + return 0; + } + + /* if ks is shapekey entry (this could be callled for separator before too!) */ + if (ks->flag == -3) + sprintf(ks->name, newname); + + /* if it gets here, it's ok */ + return 1; +} + +/* array for object keyingset defines */ +bKeyingSet defks_v3d_object[] = +{ + /* include_cb, adrcode-getter, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Loc", ID_OB, 0, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, + {NULL, "Rot", ID_OB, 0, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + {NULL, "Scale", ID_OB, 0, 3, {OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "LocRot", ID_OB, 0, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {NULL, "LocScale", ID_OB, 0, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "LocRotScale", ID_OB, 0, 9, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "RotScale", ID_OB, 0, 6, + {OB_ROT_X,OB_ROT_Y,OB_ROT_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator + + {incl_non_del_keys, "VisualLoc", ID_OB, INSERTKEY_MATRIX, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, + {incl_non_del_keys, "VisualRot", ID_OB, INSERTKEY_MATRIX, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {incl_non_del_keys, "VisualLocRot", ID_OB, INSERTKEY_MATRIX, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Layer", ID_OB, 0, 1, {OB_LAY}}, // icky option... + {NULL, "Available", ID_OB, -2, 0, {0}}, + + {incl_v3d_ob_shapekey, "%l%l", 0, -1, 0, {0}}, // separator (linked to shapekey entry) + {incl_v3d_ob_shapekey, "", ID_OB, -3, 0, {0}} +}; + +/* PoseChannel KeyingSets ------ */ + +/* array for posechannel keyingset defines */ +bKeyingSet defks_v3d_pchan[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Loc", ID_PO, 0, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, + {NULL, "Rot", ID_PO, COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, + {NULL, "Scale", ID_PO, 0, 3, {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "LocRot", ID_PO, COMMONKEY_PCHANROT, 4, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, + KAG_CHAN_EXTEND}}, + + {NULL, "LocScale", ID_PO, 0, 6, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, + AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, + + {NULL, "LocRotScale", ID_PO, COMMONKEY_PCHANROT, 7, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z,AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, + KAG_CHAN_EXTEND}}, + + {NULL, "RotScale", ID_PO, 0, 4, + {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, + KAG_CHAN_EXTEND}}, + + {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator + + {incl_non_del_keys, "VisualLoc", ID_PO, INSERTKEY_MATRIX, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, + {incl_non_del_keys, "VisualRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, + + {incl_non_del_keys, "VisualLocRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 4, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, KAG_CHAN_EXTEND}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_PO, -2, 0, {0}} +}; + +/* Material KeyingSets ------ */ + +/* array for material keyingset defines */ +bKeyingSet defks_buts_shading_mat[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "RGB", ID_MA, 0, 3, {MA_COL_R,MA_COL_G,MA_COL_B}}, + {NULL, "Alpha", ID_MA, 0, 1, {MA_ALPHA}}, + {NULL, "Halo Size", ID_MA, 0, 1, {MA_HASIZE}}, + {NULL, "Mode", ID_MA, 0, 1, {MA_MODE}}, // evil bitflags + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "All Color", ID_MA, 0, 18, + {MA_COL_R,MA_COL_G,MA_COL_B, + MA_ALPHA,MA_HASIZE, MA_MODE, + MA_SPEC_R,MA_SPEC_G,MA_SPEC_B, + MA_REF,MA_EMIT,MA_AMB,MA_SPEC,MA_HARD, + MA_MODE,MA_TRANSLU,MA_ADD}}, + + {NULL, "All Mirror", ID_MA, 0, 5, + {MA_RAYM,MA_FRESMIR,MA_FRESMIRI, + MA_FRESTRA,MA_FRESTRAI}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_MA, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_MA, -2, 0, {0}} +}; + +/* World KeyingSets ------ */ + +/* array for world keyingset defines */ +bKeyingSet defks_buts_shading_wo[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Zenith RGB", ID_WO, 0, 3, {WO_ZEN_R,WO_ZEN_G,WO_ZEN_B}}, + {NULL, "Horizon RGB", ID_WO, 0, 3, {WO_HOR_R,WO_HOR_G,WO_HOR_B}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Mist", ID_WO, 0, 4, {WO_MISI,WO_MISTDI,WO_MISTSTA,WO_MISTHI}}, + {NULL, "Stars", ID_WO, 0, 5, {WO_STAR_R,WO_STAR_G,WO_STAR_B,WO_STARDIST,WO_STARSIZE}}, + + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_WO, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_WO, -2, 0, {0}} +}; + +/* Lamp KeyingSets ------ */ + +/* array for lamp keyingset defines */ +bKeyingSet defks_buts_shading_la[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "RGB", ID_LA, 0, 3, {LA_COL_R,LA_COL_G,LA_COL_B}}, + {NULL, "Energy", ID_LA, 0, 1, {LA_ENERGY}}, + {NULL, "Spot Size", ID_LA, 0, 1, {LA_SPOTSI}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_LA, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_LA, -2, 0, {0}} +}; + +/* Texture KeyingSets ------ */ + +/* array for texture keyingset defines */ +bKeyingSet defks_buts_shading_tex[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Clouds", ID_TE, 0, 5, + {TE_NSIZE,TE_NDEPTH,TE_NTYPE, + TE_MG_TYP,TE_N_BAS1}}, + + {NULL, "Marble", ID_TE, 0, 7, + {TE_NSIZE,TE_NDEPTH,TE_NTYPE, + TE_TURB,TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, + + {NULL, "Stucci", ID_TE, 0, 5, + {TE_NSIZE,TE_NTYPE,TE_TURB, + TE_MG_TYP,TE_N_BAS1}}, + + {NULL, "Wood", ID_TE, 0, 6, + {TE_NSIZE,TE_NTYPE,TE_TURB, + TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, + + {NULL, "Magic", ID_TE, 0, 2, {TE_NDEPTH,TE_TURB}}, + + {NULL, "Blend", ID_TE, 0, 1, {TE_MG_TYP}}, + + {NULL, "Musgrave", ID_TE, 0, 6, + {TE_MG_TYP,TE_MGH,TE_MG_LAC, + TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN}}, + + {NULL, "Voronoi", ID_TE, 0, 9, + {TE_VNW1,TE_VNW2,TE_VNW3,TE_VNW4, + TE_VNMEXP,TE_VN_DISTM,TE_VN_COLT, + TE_ISCA,TE_NSIZE}}, + + {NULL, "Distorted Noise", ID_TE, 0, 4, + {TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN,TE_DISTA}}, + + {NULL, "Color Filter", ID_TE, 0, 5, + {TE_COL_R,TE_COL_G,TE_COL_B,TE_BRIGHT,TE_CONTRA}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_TE, -2, 0, {0}} +}; + +/* Object Buttons KeyingSets ------ */ + +/* check if include particles entry */ +static short incl_buts_ob (bKeyingSet *ks, const char mode[]) +{ + Object *ob= OBACT; + /* only if object is mesh type */ + + if(ob==NULL) return 0; + return (ob->type == OB_MESH); +} + +/* array for texture keyingset defines */ +bKeyingSet defks_buts_object[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {incl_buts_ob, "Surface Damping", ID_OB, 0, 1, {OB_PD_SDAMP}}, + {incl_buts_ob, "Random Damping", ID_OB, 0, 1, {OB_PD_RDAMP}}, + {incl_buts_ob, "Permeability", ID_OB, 0, 1, {OB_PD_PERM}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Force Strength", ID_OB, 0, 1, {OB_PD_FSTR}}, + {NULL, "Force Falloff", ID_OB, 0, 1, {OB_PD_FFALL}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_OB, -2, 0, {0}} // this will include ob-transforms too! +}; + +/* Camera Buttons KeyingSets ------ */ + +/* check if include internal-renderer entry */ +static short incl_buts_cam1 (bKeyingSet *ks, const char mode[]) +{ + /* only if renderer is internal renderer */ + return (G.scene->r.renderer==R_INTERN); +} + +/* check if include external-renderer entry */ +static short incl_buts_cam2 (bKeyingSet *ks, const char mode[]) +{ + /* only if renderer is internal renderer */ + return (G.scene->r.renderer!=R_INTERN); +} + +/* array for camera keyingset defines */ +bKeyingSet defks_buts_cam[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Lens", ID_CA, 0, 1, {CAM_LENS}}, + {NULL, "Clipping", ID_CA, 0, 2, {CAM_STA,CAM_END}}, + {NULL, "Focal Distance", ID_CA, 0, 1, {CAM_YF_FDIST}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + + {incl_buts_cam2, "Aperture", ID_CA, 0, 1, {CAM_YF_APERT}}, + {incl_buts_cam1, "Viewplane Shift", ID_CA, 0, 2, {CAM_SHIFT_X,CAM_SHIFT_Y}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_CA, -2, 0, {0}} +}; + +/* --- */ + +/* Keying Context Defines - Must keep in sync with enumeration (eKS_Contexts) */ +bKeyingContext ks_contexts[] = +{ + KSC_TEMPLATE(v3d_object), + KSC_TEMPLATE(v3d_pchan), + + KSC_TEMPLATE(buts_shading_mat), + KSC_TEMPLATE(buts_shading_wo), + KSC_TEMPLATE(buts_shading_la), + KSC_TEMPLATE(buts_shading_tex), + + KSC_TEMPLATE(buts_object), + KSC_TEMPLATE(buts_cam) +}; + +/* Keying Context Enumeration - Must keep in sync with definitions*/ +typedef enum eKS_Contexts { + KSC_V3D_OBJECT = 0, + KSC_V3D_PCHAN, + + KSC_BUTS_MAT, + KSC_BUTS_WO, + KSC_BUTS_LA, + KSC_BUTS_TEX, + + KSC_BUTS_OB, + KSC_BUTS_CAM, + + /* make sure this last one remains untouched! */ + KSC_TOT_TYPES +} eKS_Contexts; + + +/* ---------------- KeyingSet Tools ------------------- */ + +/* helper for commonkey_context_get() - get keyingsets for 3d-view */ +static void commonkey_context_getv3d (ListBase *sources, bKeyingContext **ksc) +{ + Object *ob; + IpoCurve *icu; + + if ((OBACT) && (OBACT->flag & OB_POSEMODE)) { + bPoseChannel *pchan; + + /* pose-level */ + ob= OBACT; + *ksc= &ks_contexts[KSC_V3D_PCHAN]; + set_pose_keys(ob); /* sets pchan->flag to POSE_KEY if bone selected, and clears if not */ + + /* loop through posechannels */ + for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if (pchan->flag & POSE_KEY) { + bCommonKeySrc *cks; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to, and action */ + cks->id= (ID *)ob; + cks->act= ob->action; + + /* set pchan */ + cks->pchan= pchan; + cks->actname= pchan->name; + } + } + } + else { + Base *base; + + /* object-level */ + *ksc= &ks_contexts[KSC_V3D_OBJECT]; + + /* loop through bases */ + for (base= FIRSTBASE; base; base= base->next) { + if (TESTBASELIB(base)) { + bCommonKeySrc *cks; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + ob= base->object; + cks->id= (ID *)ob; + + /* when ob's keyframes are in an action, default to using 'Object' as achan name */ + if (ob->ipoflag & OB_ACTION_OB) + cks->actname= "Object"; + + /* set ipo-flags */ + // TODO: add checks for lib-linked data + if ((ob->ipo) || (ob->action)) { + if (ob->ipo) { + cks->ipo= ob->ipo; + } + else { + bActionChannel *achan; + + cks->act= ob->action; + achan= get_action_channel(ob->action, cks->actname); + + if (achan && achan->ipo) + cks->ipo= achan->ipo; + } + /* cks->ipo can be NULL while editing */ + if(cks->ipo) { + /* deselect all ipo-curves */ + for (icu= cks->ipo->curve.first; icu; icu= icu->next) { + icu->flag &= ~IPO_SELECT; + } + } + } + } + } + } +} + +/* helper for commonkey_context_get() - get keyingsets for buttons window */ +static void commonkey_context_getsbuts (ListBase *sources, bKeyingContext **ksc) +{ + bCommonKeySrc *cks; + +#if 0 // XXX dunno what's the future of this stuff... + /* check on tab-type */ + switch (G.buts->mainb) { + case CONTEXT_SHADING: /* ------------- Shading buttons ---------------- */ + /* subtabs include "Material", "Texture", "Lamp", "World"*/ + switch (G.buts->tab[CONTEXT_SHADING]) { + case TAB_SHADING_MAT: /* >------------- Material Tab -------------< */ + { + Material *ma= editnode_get_active_material(G.buts->lockpoin); + + if (ma) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)ma; + cks->ipo= ma->ipo; + cks->map= texchannel_to_adrcode(ma->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_MAT]; + return; + } + } + break; + case TAB_SHADING_WORLD: /* >------------- World Tab -------------< */ + { + World *wo= G.buts->lockpoin; + + if (wo) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)wo; + cks->ipo= wo->ipo; + cks->map= texchannel_to_adrcode(wo->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_WO]; + return; + } + } + break; + case TAB_SHADING_LAMP: /* >------------- Lamp Tab -------------< */ + { + Lamp *la= G.buts->lockpoin; + + if (la) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)la; + cks->ipo= la->ipo; + cks->map= texchannel_to_adrcode(la->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_LA]; + return; + } + } + break; + case TAB_SHADING_TEX: /* >------------- Texture Tab -------------< */ + { + Tex *tex= G.buts->lockpoin; + + if (tex) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)tex; + cks->ipo= tex->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_TEX]; + return; + } + } + break; + } + break; + + case CONTEXT_OBJECT: /* ------------- Object buttons ---------------- */ + { + Object *ob= OBACT; + + if (ob) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + cks->id= (ID *)ob; + cks->ipo= ob->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_OB]; + return; + } + } + break; + + case CONTEXT_EDITING: /* ------------- Editing buttons ---------------- */ + { + Object *ob= OBACT; + + if ((ob) && (ob->type==OB_CAMERA) && (G.buts->lockpoin)) { /* >---------------- camera buttons ---------------< */ + Camera *ca= G.buts->lockpoin; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + cks->id= (ID *)ca; + cks->ipo= ca->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_CAM]; + return; + } + } + break; + } +#endif // XXX end of buttons stuff to port... + + /* if nothing happened... */ + *ksc= NULL; +} + + +/* get keyingsets for appropriate context */ +static void commonkey_context_get (ScrArea *sa, short mode, ListBase *sources, bKeyingContext **ksc) +{ + /* check view type */ + switch (sa->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + commonkey_context_getv3d(sources, ksc); + } + break; + + /* buttons view */ + case SPACE_BUTS: + { + commonkey_context_getsbuts(sources, ksc); + } + break; + + /* spaces with their own methods */ + case SPACE_IPO: + //if (mode == COMMONKEY_MODE_INSERT) + // insertkey_editipo(); // XXX old calls... + return; + case SPACE_ACTION: + //if (mode == COMMONKEY_MODE_INSERT) + // insertkey_action(); // XXX old calls... + return; + + /* timeline view - keyframe buttons */ + case SPACE_TIME: + { + ScrArea *sab; + int bigarea= 0; + + /* try to find largest 3d-view available + * (mostly of the time, this is what when user will want this, + * as it's a standard feature in all other apps) + */ + //sab= find_biggest_area_of_type(SPACE_VIEW3D); + sab= NULL; // XXX for now... + if (sab) { + commonkey_context_getv3d(sources, ksc); + return; + } + + /* if not found, sab is now NULL, so perform own biggest area test */ + for (sa= G.curscreen->areabase.first; sa; sa= sa->next) { // XXX this has changed! + int area= sa->winx * sa->winy; + + if (sa->spacetype != SPACE_TIME) { + if ( (!sab) || (area > bigarea) ) { + sab= sa; + bigarea= area; + } + } + } + + /* use whichever largest area was found (it shouldn't be a time window) */ + if (sab) + commonkey_context_get(sab, mode, sources, ksc); + } + break; + } +} + +/* flush updates after all operations */ +static void commonkey_context_finish (ListBase *sources) +{ + /* check view type */ + switch (curarea->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + /* either pose or object level */ + if (OBACT && (OBACT->pose)) { + Object *ob= OBACT; + + /* recalculate ipo handles, etc. */ + if (ob->action) + remake_action_ipos(ob->action); + + /* recalculate bone-paths on adding new keyframe? */ + // TODO: currently, there is no setting to turn this on/off globally + if (ob->pose->flag & POSE_RECALCPATHS) + pose_recalculate_paths(ob); + } + else { + bCommonKeySrc *cks; + + /* loop over bases (as seen in sources) */ + for (cks= sources->first; cks; cks= cks->next) { + Object *ob= (Object *)cks->id; + + /* simply set recalc flag */ + ob->recalc |= OB_RECALC_OB; + } + } + } + break; + } +} + +/* flush refreshes after undo */ +static void commonkey_context_refresh (void) +{ + /* check view type */ + switch (curarea->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + /* do refreshes */ + DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); + + //allspace(REMAKEIPO, 0); + //allqueue(REDRAWVIEW3D, 0); + //allqueue(REDRAWMARKER, 0); + } + break; + + /* buttons window */ + case SPACE_BUTS: + { + //allspace(REMAKEIPO, 0); + //allqueue(REDRAWVIEW3D, 0); + //allqueue(REDRAWMARKER, 0); + } + break; + } +} + +/* --- */ + +/* Build menu-string of available keying-sets (allocates memory for string) + * NOTE: mode must not be longer than 64 chars + */ +static char *build_keyingsets_menu (bKeyingContext *ksc, const char mode[48]) +{ + DynStr *pupds= BLI_dynstr_new(); + bKeyingSet *ks; + char buf[64]; + char *str; + int i, n; + + /* add title first */ + BLI_snprintf(buf, 64, "%s Key %%t|", mode); + BLI_dynstr_append(pupds, buf); + + /* loop through keyingsets, adding them */ + for (ks=ksc->keyingsets, i=0, n=1; i < ksc->tot; ks++, i++, n++) { + /* check if keyingset can be used */ + if (ks->flag == -1) { + /* optional separator? */ + if (ks->include_cb) { + if (ks->include_cb(ks, mode)) { + BLI_snprintf( buf, 64, "%s%s", ks->name, ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + else { + BLI_snprintf( buf, 64, "%%l%s", ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + else if ( (ks->include_cb==NULL) || (ks->include_cb(ks, mode)) ) { + /* entry can be included */ + BLI_dynstr_append(pupds, ks->name); + + /* check if special "shapekey" entry */ + if (ks->flag == -3) + BLI_snprintf( buf, 64, "%%x0%s", ((n < ksc->tot)?"|":"") ); + else + BLI_snprintf( buf, 64, "%%x%d%s", n, ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + + /* convert to normal MEM_malloc'd string */ + str= BLI_dynstr_get_cstring(pupds); + BLI_dynstr_free(pupds); + + return str; +} + +/* Get the keying set that was chosen by the user from the menu */ +static bKeyingSet *get_keyingset_fromcontext (bKeyingContext *ksc, short index) +{ + /* check if index is valid */ + if (ELEM(NULL, ksc, ksc->keyingsets)) + return NULL; + if ((index < 1) || (index > ksc->tot)) + return NULL; + + /* index starts from 1, and should directly correspond to keyingset in array */ + return (bKeyingSet *)(ksc->keyingsets + (index - 1)); +} + +/* ---------------- Keyframe Management API -------------------- */ + +/* Display a menu for handling the insertion of keyframes based on the active view */ +// TODO: add back an option for repeating last keytype +void common_modifykey (short mode) +{ + ListBase dsources = {NULL, NULL}; + bKeyingContext *ksc= NULL; + bCommonKeySrc *cks; + bKeyingSet *ks = NULL; + char *menustr, buf[64]; + short menu_nr; + + /* check if mode is valid */ + if (ELEM(mode, COMMONKEY_MODE_INSERT, COMMONKEY_MODE_DELETE)==0) + return; + + /* delegate to other functions or get keyingsets to use + * - if the current area doesn't have its own handling, there will be data returned... + */ + commonkey_context_get(curarea, mode, &dsources, &ksc); + + /* check that there is data to operate on */ + if (ELEM(NULL, dsources.first, ksc)) { + BLI_freelistN(&dsources); + return; + } + + /* get menu and process it */ + if (mode == COMMONKEY_MODE_DELETE) + menustr= build_keyingsets_menu(ksc, "Delete"); + else + menustr= build_keyingsets_menu(ksc, "Insert"); + menu_nr= pupmenu(menustr); + if (menustr) MEM_freeN(menustr); + + /* no item selected or shapekey entry? */ + if (menu_nr < 1) { + /* free temp sources */ + BLI_freelistN(&dsources); + + /* check if insert new shapekey */ + if ((menu_nr == 0) && (mode == COMMONKEY_MODE_INSERT)) + insert_shapekey(OBACT); + else + ksc->lastused= NULL; + + return; + } + else { + /* try to get keyingset */ + ks= get_keyingset_fromcontext(ksc, menu_nr); + + if (ks == NULL) { + BLI_freelistN(&dsources); + return; + } + } + + /* loop over each destination, applying the keying set */ + for (cks= dsources.first; cks; cks= cks->next) { + short success= 0; + + /* special hacks for 'available' option */ + if (ks->flag == -2) { + IpoCurve *icu= NULL, *icn= NULL; + + /* get first IPO-curve */ + if (cks->act && cks->actname) { + bActionChannel *achan= get_action_channel(cks->act, cks->actname); + + // FIXME: what about constraint channels? + if (achan && achan->ipo) + icu= achan->ipo->curve.first; + } + else if(cks->ipo) + icu= cks->ipo->curve.first; + + /* we get adrcodes directly from IPO curves (see method below...) */ + for (; icu; icu= icn) { + short flag; + + /* get next ipo-curve in case current is deleted */ + icn= icu->next; + + /* insert mode or delete mode */ + if (mode == COMMONKEY_MODE_DELETE) { + /* local flags only add on to global flags */ + flag = 0; + + /* delete keyframe */ + success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); + } + else { + /* local flags only add on to global flags */ + flag = ks->flag; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; + // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; + + /* insert keyframe */ + success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); + } + } + } + else { + bKS_AdrcodeGetter kag; + short (*get_next_adrcode)(bKS_AdrcodeGetter *); + int adrcode; + + /* initialise keyingset channel iterator */ + ks_adrcodegetter_init(&kag, ks, cks); + + /* get iterator - only one can be in use at a time... the flags should be mutually exclusive in this regard */ + if (ks->flag & COMMONKEY_PCHANROT) + get_next_adrcode= ks_getnextadrcode_pchanrot; + else if (ks->flag & COMMONKEY_ADDMAP) + get_next_adrcode= ks_getnextadrcode_addmap; + else + get_next_adrcode= ks_getnextadrcode_default; + + /* loop over channels available in keyingset */ + for (adrcode= get_next_adrcode(&kag); adrcode > 0; adrcode= get_next_adrcode(&kag)) { + short flag; + + /* insert mode or delete mode */ + if (mode == COMMONKEY_MODE_DELETE) { + /* local flags only add on to global flags */ + flag = 0; + //flag &= ~COMMONKEY_MODES; + + /* delete keyframe */ + success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); + } + else { + /* local flags only add on to global flags */ + flag = ks->flag; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; + // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; + flag &= ~COMMONKEY_MODES; + + /* insert keyframe */ + success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); + } + } + } + + /* special handling for some key-sources */ + if (success) { + /* set pose recalc-paths flag */ + if (cks->pchan) { + Object *ob= (Object *)cks->id; + bPoseChannel *pchan= cks->pchan; + + /* set flag to trigger path recalc */ + if (pchan->path) + ob->pose->flag |= POSE_RECALCPATHS; + + /* clear unkeyed flag (it doesn't matter if it's set or not) */ + if (pchan->bone) + pchan->bone->flag &= ~BONE_UNKEYED; + } + } + } + + /* apply post-keying flushes for this data sources */ + commonkey_context_finish(&dsources); + ksc->lastused= ks; + + /* free temp data */ + BLI_freelistN(&dsources); + + /* undo pushes */ + if (mode == COMMONKEY_MODE_DELETE) + BLI_snprintf(buf, 64, "Delete %s Key", ks->name); + else + BLI_snprintf(buf, 64, "Insert %s Key", ks->name); + BIF_undo_push(buf); + + /* queue updates for contexts */ + commonkey_context_refresh(); +} + +/* ---- */ + +/* used to insert keyframes from any view */ +void common_insertkey (void) +{ + common_modifykey(COMMONKEY_MODE_INSERT); +} + +/* used to insert keyframes from any view */ +void common_deletekey (void) +{ + common_modifykey(COMMONKEY_MODE_DELETE); +} + +#endif // XXX reenable this file again later... + +/* ************************************************** */ +/* KEYFRAME DETECTION */ + +/* --------------- API/Per-Datablock Handling ------------------- */ + +/* Checks whether an IPO-block has a keyframe for a given frame + * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... + */ +short ipo_frame_has_keyframe (Ipo *ipo, float frame, short filter) +{ + IpoCurve *icu; + + /* can only find if there is data */ + if (ipo == NULL) + return 0; + + /* if only check non-muted, check if muted */ + if ((filter & ANIMFILTER_KEYS_MUTED) || (ipo->muteipo)) + return 0; + + /* loop over IPO-curves, using binary-search to try to find matches + * - this assumes that keyframes are only beztriples + */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + /* only check if there are keyframes (currently only of type BezTriple) */ + if (icu->bezt) { + /* we either include all regardless of muting, or only non-muted */ + if ((filter & ANIMFILTER_KEYS_MUTED) || (icu->flag & IPO_MUTE)==0) { + short replace = -1; + int i = binarysearch_bezt_index(icu->bezt, frame, icu->totvert, &replace); + + /* binarysearch_bezt_index will set replace to be 0 or 1 + * - obviously, 1 represents a match + */ + if (replace) { + /* sanity check: 'i' may in rare cases exceed arraylen */ + if ((i >= 0) && (i < icu->totvert)) + return 1; + } + } + } + } + + /* nothing found */ + return 0; +} + +/* Checks whether an action-block has a keyframe for a given frame + * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... + */ +short action_frame_has_keyframe (bAction *act, float frame, short filter) +{ + bActionChannel *achan; + + /* error checking */ + if (act == NULL) + return 0; + + /* check thorugh action-channels for match */ + for (achan= act->chanbase.first; achan; achan= achan->next) { + /* we either include all regardless of muting, or only non-muted + * - here we include 'hidden' channels in the muted definition + */ + if ((filter & ANIMFILTER_KEYS_MUTED) || (achan->flag & ACHAN_HIDDEN)==0) { + if (ipo_frame_has_keyframe(achan->ipo, frame, filter)) + return 1; + } + } + + /* nothing found */ + return 0; +} + +/* Checks whether an Object has a keyframe for a given frame */ +short object_frame_has_keyframe (Object *ob, float frame, short filter) +{ + /* error checking */ + if (ob == NULL) + return 0; + + /* check for an action - actions take priority over normal IPO's */ + if (ob->action) { + float aframe; + + /* apply nla-action scaling if needed */ + if ((ob->nlaflag & OB_NLA_OVERRIDE) && (ob->nlastrips.first)) + aframe= get_action_frame(ob, frame); + else + aframe= frame; + + /* priority check here goes to pose-channel checks (for armatures) */ + if ((ob->pose) && (ob->flag & OB_POSEMODE)) { + /* only relevant check here is to only show active... */ + if (filter & ANIMFILTER_KEYS_ACTIVE) { + bPoseChannel *pchan= get_active_posechannel(ob); + bActionChannel *achan= (pchan) ? get_action_channel(ob->action, pchan->name) : NULL; + + /* since we're only interested in whether the selected one has any keyframes... */ + return (achan && ipo_frame_has_keyframe(achan->ipo, aframe, filter)); + } + } + + /* for everything else, just use the standard test (only return if success) */ + if (action_frame_has_keyframe(ob->action, aframe, filter)) + return 1; + } + else if (ob->ipo) { + /* only return if success */ + if (ipo_frame_has_keyframe(ob->ipo, frame, filter)) + return 1; + } + + /* try shapekey keyframes (if available, and allowed by filter) */ + if ( !(filter & ANIMFILTER_KEYS_LOCAL) && !(filter & ANIMFILTER_KEYS_NOSKEY) ) { + Key *key= ob_get_key(ob); + + /* shapekeys can have keyframes ('Relative Shape Keys') + * or depend on time (old 'Absolute Shape Keys') + */ + + /* 1. test for relative (with keyframes) */ + if (id_frame_has_keyframe((ID *)key, frame, filter)) + return 1; + + /* 2. test for time */ + // TODO... yet to be implemented (this feature may evolve before then anyway) + } + + /* try materials */ + if ( !(filter & ANIMFILTER_KEYS_LOCAL) && !(filter & ANIMFILTER_KEYS_NOMAT) ) { + /* if only active, then we can skip a lot of looping */ + if (filter & ANIMFILTER_KEYS_ACTIVE) { + Material *ma= give_current_material(ob, (ob->actcol + 1)); + + /* we only retrieve the active material... */ + if (id_frame_has_keyframe((ID *)ma, frame, filter)) + return 1; + } + else { + int a; + + /* loop over materials */ + for (a=0; atotcol; a++) { + Material *ma= give_current_material(ob, a+1); + + if (id_frame_has_keyframe((ID *)ma, frame, filter)) + return 1; + } + } + } + + /* nothing found */ + return 0; +} + +/* --------------- API ------------------- */ + +/* Checks whether a keyframe exists for the given ID-block one the given frame */ +short id_frame_has_keyframe (ID *id, float frame, short filter) +{ + /* error checking */ + if (id == NULL) + return 0; + + /* check for a valid id-type */ + switch (GS(id->name)) { + /* animation data-types */ + case ID_IP: /* ipo */ + return ipo_frame_has_keyframe((Ipo *)id, frame, filter); + case ID_AC: /* action */ + return action_frame_has_keyframe((bAction *)id, frame, filter); + + case ID_OB: /* object */ + return object_frame_has_keyframe((Object *)id, frame, filter); + + case ID_MA: /* material */ + { + Material *ma= (Material *)id; + + /* currently, material's only have an ipo-block */ + return ipo_frame_has_keyframe(ma->ipo, frame, filter); + } + break; + + case ID_KE: /* shapekey */ + { + Key *key= (Key *)id; + + /* currently, shapekey's only have an ipo-block */ + return ipo_frame_has_keyframe(key->ipo, frame, filter); + } + break; + } + + /* no keyframe found */ + return 0; +} + +/* ************************************************** */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index aa1333f3ce7..efd104081c1 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -32,6 +32,9 @@ struct ID; struct ListBase; struct bContext; +struct wmWindowManager; +struct ScrArea; +struct ARegion; struct View2D; struct gla2DDrawInfo; struct Object; @@ -54,6 +57,7 @@ typedef struct bAnimContext { short spacetype; /* sa->spacetype */ short regiontype; /* active region -> type (channels or main) */ struct ScrArea *sa; /* editor */ + struct ARegion *ar; /* region within editor */ struct Scene *scene; /* active scene */ struct Object *obact; /* active object */ @@ -215,7 +219,7 @@ typedef enum eAnimFilter_Flags { /* ---------------- API -------------------- */ /* Obtain list of filtered Animation channels to operate on */ -void ANIM_animdata_filter(struct ListBase *anim_data, int filter_mode, void *data, short datatype); +int ANIM_animdata_filter(struct ListBase *anim_data, int filter_mode, void *data, short datatype); /* Obtain current anim-data context from Blender Context info */ /** Example usage (example to be removed...): @@ -268,9 +272,12 @@ unsigned int ipo_rainbow(int cur, int tot); /* Obtain the Object providing NLA-scaling for the given channel if applicable */ struct Object *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale); -/* set/clear temporary mapping of coordinates from 'local-action' time to 'global-nla-scaled' time */ +/* Set/clear temporary mapping of coordinates from 'local-action' time to 'global-nla-scaled' time */ void ANIM_nla_mapping_draw(struct gla2DDrawInfo *di, struct Object *ob, short restore); +/* Apply/Unapply NLA mapping to all keyframes in the nominated IPO block */ +void ANIM_nla_mapping_apply(struct Object *ob, struct Ipo *ipo, short restore, short only_keys); + /* ------------- xxx macros ----------------------- */ #define BEZSELECTED(bezt) ((bezt->f2 & SELECT) || (bezt->f1 & SELECT) || (bezt->f3 & SELECT)) diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h new file mode 100644 index 00000000000..ed3c09c8a98 --- /dev/null +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -0,0 +1,104 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ED_KEYFRAMES_EDIT_H +#define ED_KEYFRAMES_EDIT_H + +struct Ipo; +struct IpoCurve; +struct BezTriple; +struct Scene; + +/* ************************************************ */ +/* Common Macros and Defines */ + +/* --------- BezTriple Selection ------------- */ + +#define BEZSELECTED(bezt) ((bezt->f2 & SELECT) || (bezt->f1 & SELECT) || (bezt->f3 & SELECT)) + +#define BEZ_SEL(bezt) { (bezt)->f1 |= SELECT; (bezt)->f2 |= SELECT; (bezt)->f3 |= SELECT; } +#define BEZ_DESEL(bezt) { (bezt)->f1 &= ~SELECT; (bezt)->f2 &= ~SELECT; (bezt)->f3 &= ~SELECT; } +#define BEZ_INVSEL(bezt) { (bezt)->f1 ^= SELECT; (bezt)->f2 ^= SELECT; (bezt)->f3 ^= SELECT; } + +/* --------- Tool Flags ------------ */ + +/* select tools */ +typedef enum eEditKeyframes_Select { + SELECT_REPLACE = (1<<0), + SELECT_ADD = (1<<1), + SELECT_SUBTRACT = (1<<2), + SELECT_INVERT = (1<<4), +} eEditKeyframes_Select; + +/* snapping tools */ +typedef enum eEditKeyframes_Snap { + SNAP_KEYS_NEARFRAME = 1, + SNAP_KEYS_CURFRAME, + SNAP_KEYS_NEARMARKER, + SNAP_KEYS_NEARSEC, +} eEditKeyframes_Snap; + +/* mirroring tools */ +//typedef enum eEditKeyframes_Mirror { + +//} eEditKeyframes_Mirror; + +/* ************************************************ */ +/* Editing API */ + +/* ------- Function Pointer Typedefs --------------- */ + + /* callback function that refreshes the IPO curve after use */ +typedef void (*IcuEditFunc)(struct IpoCurve *icu); +typedef short (*BeztEditFunc)(struct Scene *scene, struct BezTriple *bezt); + +/* ------------- Looping API ------------------- */ + +short icu_keys_bezier_loop(struct Scene *scene, struct IpoCurve *icu, BeztEditFunc bezt_cb, IcuEditFunc icu_cb); +short ipo_keys_bezier_loop(struct Scene *scene, struct Ipo *ipo, BeztEditFunc bezt_cb, IcuEditFunc icu_cb); + +/* ------------ BezTriple Callback Getters --------------- */ + +BeztEditFunc ANIM_editkeyframes_snap(short mode); +BeztEditFunc ANIM_editkeyframes_mirror(short mode); +BeztEditFunc ANIM_editkeyframes_select(short mode); +BeztEditFunc ANIM_editkeyframes_handles(short mode); +BeztEditFunc ANIM_editkeyframes_ipo(short mode); + +/* ------------ Helper Funcs -------------- */ +// XXX will these be needed to set globals for some funcs? + +/* ************************************************ */ + +void select_ipo_key(struct Scene *scene, struct Ipo *ipo, float selx, short selectmode); +void select_icu_key(struct Scene *scene, struct IpoCurve *icu, float selx, short selectmode); + + +/* ************************************************ */ + +#endif /* ED_KEYFRAMES_EDIT_H */ diff --git a/source/blender/editors/include/ED_markers.h b/source/blender/editors/include/ED_markers.h index 49f805e4ddb..001c61c1589 100644 --- a/source/blender/editors/include/ED_markers.h +++ b/source/blender/editors/include/ED_markers.h @@ -28,6 +28,7 @@ #ifndef ED_MARKERS_H #define ED_MARKERS_H + /* flags for drawing markers */ enum { DRAW_MARKERS_LINES = (1<<0), @@ -35,8 +36,9 @@ enum { }; struct wmWindowManager; +struct bContext; -void draw_markers_time(const bContext *C, int flag); +void draw_markers_time(const struct bContext *C, int flag); /* called in screen_ops.c:ED_operatortypes_screen() */ void ED_marker_operatortypes(void); diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 19054f48dba..8992e71759c 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -167,8 +167,12 @@ void UI_view2d_to_region_no_clip(struct View2D *v2d, float x, float y, short *re /* utilities */ struct View2D *UI_view2d_fromcontext(const struct bContext *C); struct View2D *UI_view2d_fromcontext_rwin(const struct bContext *C); + void UI_view2d_getscale(struct View2D *v2d, float *x, float *y); +short UI_view2d_mouse_in_scrollers(const struct bContext *C, struct View2D *v2d, int x, int y); + + /* operators */ void ui_view2d_operatortypes(void); void UI_view2d_keymap(struct wmWindowManager *wm); diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index f827d68f697..2398f8e2e35 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1689,3 +1689,32 @@ void UI_view2d_getscale(View2D *v2d, float *x, float *y) if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin); } +/* Check if mouse is within scrollers + * - Returns appropriate code for match + * 'h' = in horizontal scroller + * 'v' = in vertical scroller + * 0 = not in scroller + * + * - x,y = mouse coordinates in screen (not region) space + */ +short UI_view2d_mouse_in_scrollers (const bContext *C, View2D *v2d, int x, int y) +{ + ARegion *ar= CTX_wm_region(C); + int co[2]; + + /* clamp x,y to region-coordinates first */ + co[0]= x - ar->winrct.xmin; + co[1]= y - ar->winrct.ymin; + + /* check if within scrollbars */ + if (v2d->scroll & V2D_SCROLL_HORIZONTAL) { + if (IN_2D_HORIZ_SCROLL(v2d, co)) return 'h'; + } + if (v2d->scroll & V2D_SCROLL_VERTICAL) { + if (IN_2D_VERT_SCROLL(v2d, co)) return 'v'; + } + + /* not found */ + return 0; +} + diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 132413477f1..40017669345 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -53,38 +53,6 @@ #include "UI_resources.h" #include "UI_view2d.h" -/* ********************************************************* */ -/* General Polling Funcs */ - -/* Check if mouse is within scrollbars - * - Returns appropriate code for match - * 'h' = in horizontal scrollbar - * 'v' = in vertical scrollbar - * 0 = not in scrollbar - * - * - x,y = mouse coordinates in screen (not region) space - */ -static short mouse_in_v2d_scrollers (const bContext *C, View2D *v2d, int x, int y) -{ - ARegion *ar= CTX_wm_region(C); - int co[2]; - - /* clamp x,y to region-coordinates first */ - co[0]= x - ar->winrct.xmin; - co[1]= y - ar->winrct.ymin; - - /* check if within scrollbars */ - if (v2d->scroll & V2D_SCROLL_HORIZONTAL) { - if (IN_2D_HORIZ_SCROLL(v2d, co)) return 'h'; - } - if (v2d->scroll & V2D_SCROLL_VERTICAL) { - if (IN_2D_VERT_SCROLL(v2d, co)) return 'v'; - } - - /* not found */ - return 0; -} - /* ********************************************************* */ /* VIEW PANNING OPERATOR */ @@ -1106,7 +1074,7 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event) v2d= &ar->v2d; /* check if mouse in scrollbars, if they're enabled */ - in_scroller= mouse_in_v2d_scrollers(C, v2d, event->x, event->y); + in_scroller= UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y); /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */ if (in_scroller) { diff --git a/source/blender/editors/space_action/SConscript b/source/blender/editors/space_action/SConscript index 3d22e8ed5ab..01684e1c2ee 100644 --- a/source/blender/editors/space_action/SConscript +++ b/source/blender/editors/space_action/SConscript @@ -3,7 +3,7 @@ Import ('env') sources = env.Glob('*.c') -incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../makesrna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' env.BlenderLib ( 'bf_editors_space_action', sources, Split(incs), [], libtype=['core','intern'], priority=[33, 37] ) diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index 67fdff6190e..ef9ab5a9d66 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -32,6 +32,8 @@ struct bContext; struct bAnimContext; struct SpaceAction; struct ARegion; +struct wmWindowManager; +struct wmOperatorType; /* internal exports only */ @@ -42,6 +44,12 @@ void draw_channel_strips(struct bAnimContext *ac, struct SpaceAction *saction, s /* action_header.c */ void action_header_buttons(const struct bContext *C, struct ARegion *ar); +/* action_select.c */ +void ED_ACT_OT_keyframes_clickselect(struct wmOperatorType *ot); + +/* action_ops.c */ +void action_operatortypes(void); +void action_keymap(struct wmWindowManager *wm); #endif /* ED_ACTION_INTERN_H */ diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c new file mode 100644 index 00000000000..9a64a026f81 --- /dev/null +++ b/source/blender/editors/space_action/action_ops.c @@ -0,0 +1,91 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_action_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" + +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_utildefines.h" + +#include "UI_interface.h" +#include "UI_view2d.h" + +#include "action_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + + +/* ************************** registration - operator types **********************************/ + +void action_operatortypes(void) +{ + /* channels */ + + /* keyframes */ + WM_operatortype_append(ED_ACT_OT_keyframes_clickselect); +} + +/* ************************** registration - keymaps **********************************/ + +static void action_keymap_keyframes (ListBase *keymap) +{ + /* click-select */ + WM_keymap_add_item(keymap, "ED_ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "ED_ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend_select", 1); + RNA_boolean_set(WM_keymap_add_item(keymap, "ED_ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT, 0)->ptr, "left_right", 1); +} + +/* --------------- */ + +void action_keymap(wmWindowManager *wm) +{ + ListBase *keymap; + + /* channels */ + keymap= WM_keymap_listbase(wm, "Action_Channels", SPACE_ACTION, 0); + + /* keyframes */ + keymap= WM_keymap_listbase(wm, "Action_Keys", SPACE_ACTION, 0); + action_keymap_keyframes(keymap); +} + diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c new file mode 100644 index 00000000000..050674ee32d --- /dev/null +++ b/source/blender/editors/space_action/action_select.c @@ -0,0 +1,551 @@ +/** + * $Id: editaction.c 17746 2008-12-08 11:19:44Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "DNA_listBase.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_constraint_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_material_types.h" +#include "DNA_userdef_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_windowmanager_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "BKE_action.h" +#include "BKE_depsgraph.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_context.h" +#include "BKE_utildefines.h" + +#include "UI_view2d.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_keyframes_draw.h" +#include "ED_keyframes_edit.h" +#include "ED_screen.h" +#include "ED_space_api.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* ************************************************************************** */ +/* GENERAL STUFF */ + +#if 0 +/* this function finds the channel that mouse is floating over */ +void *get_nearest_act_channel (short mval[], short *ret_type, void **owner) +{ + ListBase act_data = {NULL, NULL}; + bActListElem *ale; + void *data; + short datatype; + int filter; + + int clickmin, clickmax; + float x,y; + + /* init 'owner' return val */ + *owner= NULL; + + /* determine what type of data we are operating on */ + data = get_action_context(&datatype); + if (data == NULL) { + *ret_type= ACTTYPE_NONE; + return NULL; + } + + areamouseco_to_ipoco(G.v2d, mval, &x, &y); + clickmin = (int) (((CHANNELHEIGHT/2) - y) / (CHANNELHEIGHT+CHANNELSKIP)); + clickmax = clickmin; + + if (clickmax < 0) { + *ret_type= ACTTYPE_NONE; + return NULL; + } + + /* filter data */ + filter= (ACTFILTER_FORDRAWING | ACTFILTER_VISIBLE | ACTFILTER_CHANNELS); + actdata_filter(&act_data, filter, data, datatype); + + for (ale= act_data.first; ale; ale= ale->next) { + if (clickmax < 0) + break; + if (clickmin <= 0) { + /* found match */ + *ret_type= ale->type; + data= ale->data; + + /* if an 'ID' has been set, this takes presidence as owner (for dopesheet) */ + if (datatype == ACTCONT_DOPESHEET) { + /* return pointer to ID as owner instead */ + if (ale->id) + *owner= ale->id; + else + *owner= ale->owner; + } + else { + /* just use own owner */ + *owner= ale->owner; + } + + BLI_freelistN(&act_data); + + return data; + } + --clickmin; + --clickmax; + } + + /* cleanup */ + BLI_freelistN(&act_data); + + *ret_type= ACTTYPE_NONE; + return NULL; +} +#endif + +/* used only by mouse_action. It is used to find the location of the nearest + * keyframe to where the mouse clicked, + */ +static void *get_nearest_action_key (bAnimContext *ac, int mval[2], float *selx, short *sel, short *ret_type, bActionChannel **par) +{ + ListBase anim_data = {NULL, NULL}; + ListBase anim_keys = {NULL, NULL}; + bAnimListElem *ale; + ActKeyColumn *ak; + View2D *v2d= &ac->ar->v2d; + int filter; + + rctf rectf; + void *data = NULL; + float xmin, xmax, x, y; + int clickmin, clickmax; + short found = 0; + + /* action-channel */ + *par= NULL; + + UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); + clickmin = (int) (((ACHANNEL_HEIGHT_HALF) - y) / (ACHANNEL_STEP)); // xxx max y-co (first) is -ACHANNEL_HEIGHT + clickmax = clickmin; + + /* x-range to check is +/- 7 on either side of mouse click (size of keyframe icon) */ + UI_view2d_region_to_view(v2d, mval[0]-7, mval[1], &rectf.xmin, &rectf.ymin); + UI_view2d_region_to_view(v2d, mval[0]+7, mval[1], &rectf.xmax, &rectf.ymax); + + if (clickmax < 0) { + *ret_type= ANIMTYPE_NONE; + return NULL; + } + + /* filter data */ + filter= (ANIMFILTER_FORDRAWING | ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS); + ANIM_animdata_filter(&anim_data, filter, ac->data, ac->datatype); + + for (ale= anim_data.first; ale; ale= ale->next) { + if (clickmax < 0) + break; + if (clickmin <= 0) { + /* found match - must return here... */ + Object *nob= ANIM_nla_mapping_get(ac, ale); + + /* apply NLA-scaling correction? */ + if (nob) { + xmin= get_action_frame(nob, rectf.xmin); + xmax= get_action_frame(nob, rectf.xmax); + } + else { + xmin= rectf.xmin; + xmax= rectf.xmax; + } + + /* make list of keyframes */ + if (ale->key_data) { + switch (ale->datatype) { + case ALE_OB: + { + Object *ob= (Object *)ale->key_data; + ob_to_keylist(ob, &anim_keys, NULL, NULL); + } + break; + case ALE_ACT: + { + bAction *act= (bAction *)ale->key_data; + action_to_keylist(act, &anim_keys, NULL, NULL); + } + break; + case ALE_IPO: + { + Ipo *ipo= (Ipo *)ale->key_data; + ipo_to_keylist(ipo, &anim_keys, NULL, NULL); + } + break; + case ALE_ICU: + { + IpoCurve *icu= (IpoCurve *)ale->key_data; + icu_to_keylist(icu, &anim_keys, NULL, NULL); + } + break; + } + } + else if (ale->type == ANIMTYPE_GROUP) { + bActionGroup *agrp= (bActionGroup *)ale->data; + agroup_to_keylist(agrp, &anim_keys, NULL, NULL); + } + else if (ale->type == ANIMTYPE_GPDATABLOCK) { + /* cleanup */ + BLI_freelistN(&anim_data); + + /* this channel currently doens't have any keyframes... must ignore! */ + *ret_type= ANIMTYPE_NONE; + return NULL; + } + else if (ale->type == ANIMTYPE_GPLAYER) { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + gpl_to_keylist(gpl, &anim_keys, NULL, NULL); + } + + /* loop through keyframes, finding one that was clicked on */ + for (ak= anim_keys.first; ak; ak= ak->next) { + if (IN_RANGE(ak->cfra, xmin, xmax)) { + *selx= ak->cfra; + found= 1; + break; + } + } + /* no matching keyframe found - set to mean frame value so it doesn't actually select anything */ + if (found == 0) + *selx= ((xmax+xmin) / 2); + + /* figure out what to return */ + if (ac->datatype == ANIMCONT_ACTION) { + *par= ale->owner; /* assume that this is an action channel */ + *ret_type= ale->type; + data = ale->data; + } + else if (ac->datatype == ANIMCONT_SHAPEKEY) { + data = ale->key_data; + *ret_type= ANIMTYPE_ICU; + } + else if (ac->datatype == ANIMCONT_DOPESHEET) { + data = ale->data; + *ret_type= ale->type; + } + else if (ac->datatype == ANIMCONT_GPENCIL) { + data = ale->data; + *ret_type= ANIMTYPE_GPLAYER; + } + + /* cleanup tempolary lists */ + BLI_freelistN(&anim_keys); + anim_keys.first = anim_keys.last = NULL; + + BLI_freelistN(&anim_data); + + return data; + } + --clickmin; + --clickmax; + } + + /* cleanup */ + BLI_freelistN(&anim_data); + + *ret_type= ANIMTYPE_NONE; + return NULL; +} + +/* ************************************************************************** */ +/* CHANNEL STUFF */ + +/* ************************************************************************** */ +/* KEYFRAMES STUFF */ + +/* ******************** Column Select Operator **************************** */ + +/* ******************** Mouse-Click Select Operator *********************** */ +/* This operator works in one of four ways: + * - main area + * -> 1) without alt-key - selects keyframe that was under mouse position + * -> 2) with alt-key - only those keyframes on same side of current frame + * - 3) horizontal scroller (*) - select all keyframes in frame (err... maybe integrate this with column select only)? + * - 4) vertical scroller (*) - select all keyframes in channel + * + * (*) - these are not obviously presented in UI. We need to find a new way to showcase them. + */ + +/* option 1) select keyframe directly under mouse */ +static void mouse_action_keys (bAnimContext *ac, int mval[2], short selectmode) +{ + Object *ob= NULL; + bDopeSheet *ads= NULL; + bAction *act= NULL; + bActionGroup *agrp= NULL; + bActionChannel *achan= NULL; + bConstraintChannel *conchan= NULL; + Ipo *ipo= NULL; + IpoCurve *icu= NULL; + bGPdata *gpd = NULL; + bGPDlayer *gpl = NULL; + + void *act_channel; + short sel, act_type = 0; + float selx = 0.0f, selxa; + + /* determine what type of data we are operating on */ + if (ac->datatype == ANIMCONT_ACTION) + act= (bAction *)ac->data; + else if (ac->datatype == ANIMCONT_DOPESHEET) + ads= (bDopeSheet *)ac->data; + else if (ac->datatype == ANIMCONT_GPENCIL) + gpd= (bGPdata *)ac->data; + + act_channel= get_nearest_action_key(ac, mval, &selx, &sel, &act_type, &achan); + if (act_channel) { + /* must have been a channel */ + switch (act_type) { + case ANIMTYPE_ICU: + icu= (IpoCurve *)act_channel; + break; + case ANIMTYPE_CONCHAN: + conchan= (bConstraintChannel *)act_channel; + break; + case ANIMTYPE_ACHAN: + achan= (bActionChannel *)act_channel; + break; + case ANIMTYPE_GROUP: + agrp= (bActionGroup *)act_channel; + break; + case ANIMTYPE_DSMAT: + ipo= ((Material *)act_channel)->ipo; + break; + case ANIMTYPE_DSLAM: + ipo= ((Lamp *)act_channel)->ipo; + break; + case ANIMTYPE_DSCAM: + ipo= ((Camera *)act_channel)->ipo; + break; + case ANIMTYPE_DSCUR: + ipo= ((Curve *)act_channel)->ipo; + break; + case ANIMTYPE_DSSKEY: + ipo= ((Key *)act_channel)->ipo; + break; + case ANIMTYPE_FILLACTD: + act= (bAction *)act_channel; + break; + case ANIMTYPE_FILLIPOD: + ipo= ((Object *)act_channel)->ipo; + break; + case ANIMTYPE_OBJECT: + ob= ((Base *)act_channel)->object; + break; + case ANIMTYPE_GPLAYER: + gpl= (bGPDlayer *)act_channel; + break; + default: + return; + } + + if (selectmode == SELECT_REPLACE) { + selectmode = SELECT_ADD; + + //deselect_action_keys(0, 0); // XXX fixme + + if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) { + //deselect_action_channels(0); + + /* Highlight either an Action-Channel or Action-Group */ + if (achan) { + achan->flag |= ACHAN_SELECTED; + //hilight_channel(act, achan, 1); + //select_poseelement_by_name(achan->name, 2); /* 2 is activate */ + } + else if (agrp) { + agrp->flag |= AGRP_SELECTED; + //set_active_actiongroup(act, agrp, 1); + } + } + else if (ac->datatype == ANIMCONT_GPENCIL) { + //deselect_action_channels(0); + + /* Highlight gpencil layer */ + gpl->flag |= GP_LAYER_SELECT; + //gpencil_layer_setactive(gpd, gpl); + } + } + + if (icu) + select_icu_key(ac->scene, icu, selx, selectmode); + else if (ipo) + select_ipo_key(ac->scene, ipo, selx, selectmode); + else if (conchan) + select_ipo_key(ac->scene, conchan->ipo, selx, selectmode); + else if (achan) + select_ipo_key(ac->scene, achan->ipo, selx, selectmode); + else if (agrp) { + for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) { + select_ipo_key(ac->scene, achan->ipo, selx, selectmode); + + for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) + select_ipo_key(ac->scene, conchan->ipo, selx, selectmode); + } + } + else if (act) { + for (achan= act->chanbase.first; achan; achan= achan->next) { + select_ipo_key(ac->scene, achan->ipo, selx, selectmode); + + for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) + select_ipo_key(ac->scene, conchan->ipo, selx, selectmode); + } + } + else if (ob) { + if (ob->ipo) + select_ipo_key(ac->scene, ob->ipo, selx, selectmode); + + if (ob->action) { + selxa= get_action_frame(ob, selx); + + for (achan= ob->action->chanbase.first; achan; achan= achan->next) { + select_ipo_key(ac->scene, achan->ipo, selxa, selectmode); + + for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) + select_ipo_key(ac->scene, conchan->ipo, selxa, selectmode); + } + } + + for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next) + select_ipo_key(ac->scene, conchan->ipo, selx, selectmode); + } + //else if (gpl) + // select_gpencil_frame(gpl, (int)selx, selectmode); + } +} + +/* ------------------- */ + +static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + bAnimContext ac; + ARegion *ar; + short in_scroller, selectmode; + int mval[2]; + + puts("Action click select invoke"); + + /* get editor data */ + if ((ANIM_animdata_get_context(C, &ac) == 0) || (ac.data == NULL)) + return OPERATOR_CANCELLED; + + /* get useful pointers from animation context data */ + ar= ac.ar; + + /* get mouse coordinates (in region coordinates) */ + mval[0]= (event->x - ar->winrct.xmin); + mval[1]= (event->y - ar->winrct.ymin); + + /* check where in view mouse is */ + in_scroller = UI_view2d_mouse_in_scrollers(C, &ar->v2d, event->x, event->y); + + /* select mode is either replace (deselect all, then add) or add/extend */ + if (RNA_boolean_get(op->ptr, "extend_select")) + selectmode= SELECT_ADD; + else + selectmode= SELECT_REPLACE; + + /* check which scroller mouse is in, and figure out how to handle this */ + if (in_scroller == 'h') { + /* horizontal - column select in current frame */ + // FIXME.... todo + } + else if (in_scroller == 'v') { + /* vertical - row select in current channel */ + // FIXME... + } + else if (RNA_boolean_get(op->ptr, "left_right")) { + /* select all keys on same side of current frame as mouse */ + + } + else { + /* select keyframe under mouse */ + mouse_action_keys(&ac, mval, selectmode); + // XXX activate transform... + } + + /* set notifier tha things have changed */ + ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead! + + return OPERATOR_FINISHED; +} + +void ED_ACT_OT_keyframes_clickselect (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Mouse Select Keys"; + ot->idname= "ED_ACT_OT_keyframes_clickselect"; + + /* api callbacks */ + ot->invoke= actkeys_clickselect_invoke; + //ot->poll= ED_operator_areaactive; + + /* id-props */ + RNA_def_property(ot->srna, "left_right", PROP_BOOLEAN, PROP_NONE); // ALTKEY + RNA_def_property(ot->srna, "extend_select", PROP_BOOLEAN, PROP_NONE); // SHIFTKEY +} + +/* ************************************************************************** */ diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 7d6a4804dec..2d600bfe747 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -159,7 +159,7 @@ static void action_main_area_init(wmWindowManager *wm, ARegion *ar) UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy); /* own keymap */ - keymap= WM_keymap_listbase(wm, "Action", SPACE_ACTION, 0); /* XXX weak? */ + keymap= WM_keymap_listbase(wm, "Action_Keys", SPACE_ACTION, 0); /* XXX weak? */ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); } @@ -214,16 +214,6 @@ static void action_main_area_draw(const bContext *C, ARegion *ar) UI_view2d_scrollers_free(scrollers); } -void action_operatortypes(void) -{ - -} - -void action_keymap(struct wmWindowManager *wm) -{ - -} - /* add handlers, stuff you only do once or on area/region changes */ static void action_channel_area_init(wmWindowManager *wm, ARegion *ar) { @@ -232,7 +222,7 @@ static void action_channel_area_init(wmWindowManager *wm, ARegion *ar) UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy); /* own keymap */ - keymap= WM_keymap_listbase(wm, "Action", SPACE_ACTION, 0); /* XXX weak? */ + keymap= WM_keymap_listbase(wm, "Action_Channels", SPACE_ACTION, 0); /* XXX weak? */ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); } @@ -298,6 +288,11 @@ static void action_header_area_draw(const bContext *C, ARegion *ar) static void action_main_area_listener(ARegion *ar, wmNotifier *wmn) { /* context changes */ + switch(wmn->type) { + case WM_NOTE_MARKERS_CHANGED: + ED_region_tag_redraw(ar); + break; + } } /* only called once, from space/spacetypes.c */ -- cgit v1.2.3