diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index f0796697670..0c9bba5e413 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -68,6 +68,9 @@ void make_local_action(struct bAction *act); /* Some kind of bounding box operation on the action */ void calc_action_range(const struct bAction *act, float *start, float *end, int incl_hidden); +/* Does action have any motion data at all? */ +short action_has_motion(const struct bAction *act); + /* Action Groups API ----------------- */ /* Make the given Action Group the active one */ @@ -145,12 +148,6 @@ void copy_pose_result(struct bPose *to, struct bPose *from); /* clear all transforms */ void rest_pose(struct bPose *pose); -/* map global time (frame nr) to strip converted time, doesn't clip */ -float get_action_frame(struct Object *ob, float cframe); -/* map strip time to global time (frame nr) */ -float get_action_frame_inv(struct Object *ob, float cframe); - - /* functions used by the game engine */ void game_copy_pose(struct bPose **dst, struct bPose *src); void game_free_pose(struct bPose *pose); diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 9b8a2990fe5..cda64c6b241 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -1,12 +1,33 @@ -/* Testing code for new animation system in 2.5 - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #ifndef BKE_FCURVE_H #define BKE_FCURVE_H -//struct ListBase; - struct FCurve; struct FModifier; struct ChannelDriver; @@ -54,8 +75,8 @@ typedef struct FModifierTypeInfo { short size; /* size in bytes of the struct */ short acttype; /* eFMI_Action_Types */ short requires; /* eFMI_Requirement_Flags */ - char name[32]; /* name of modifier in interface */ - char structName[32]; /* name of struct for SDNA */ + char name[64]; /* name of modifier in interface */ + char structName[64]; /* name of struct for SDNA */ /* data management function pointers - special handling */ /* free any data that is allocated separately (optional) */ @@ -104,14 +125,20 @@ FModifierTypeInfo *get_fmodifier_typeinfo(int type); /* ---------------------- */ -struct FModifier *fcurve_add_modifier(struct FCurve *fcu, int type); -void fcurve_copy_modifiers(ListBase *dst, ListBase *src); -void fcurve_remove_modifier(struct FCurve *fcu, struct FModifier *fcm); -void fcurve_free_modifiers(struct FCurve *fcu); -void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end); +struct FModifier *add_fmodifier(ListBase *modifiers, int type); +void copy_fmodifiers(ListBase *dst, ListBase *src); +void remove_fmodifier(ListBase *modifiers, struct FModifier *fcm); +void free_fmodifiers(ListBase *modifiers); -struct FModifier *fcurve_find_active_modifier(struct FCurve *fcu); -void fcurve_set_active_modifier(struct FCurve *fcu, struct FModifier *fcm); +struct FModifier *find_active_fmodifier(ListBase *modifiers); +void set_active_fmodifier(ListBase *modifiers, struct FModifier *fcm); + +short list_has_suitable_fmodifier(ListBase *modifiers, int mtype, short acttype); + +float evaluate_time_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float cvalue, float evaltime); +void evaluate_value_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float *cvalue, float evaltime); + +void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end); /* ************** F-Curves API ******************** */ @@ -126,9 +153,6 @@ void copy_fcurves(ListBase *dst, ListBase *src); /* find matching F-Curve in the given list of F-Curves */ struct FCurve *list_find_fcurve(ListBase *list, const char rna_path[], const int array_index); -/* test if there is a keyframe at cfra */ -short on_keyframe_fcurve(struct FCurve *fcu, float cfra); - /* get the time extents for F-Curve */ void calc_fcurve_range(struct FCurve *fcu, float *min, float *max); diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 230096d7ea7..bb5a2782663 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -17,12 +17,12 @@ * 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. + * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung * All rights reserved. * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Joshua Leung (full recode) * * ***** END GPL LICENSE BLOCK ***** */ @@ -30,15 +30,90 @@ #ifndef BKE_NLA_H #define BKE_NLA_H -struct bActionStrip; -struct ListBase; -struct Object; +struct AnimData; +struct NlaStrip; +struct NlaTrack; +struct bAction; + +/* ----------------------------- */ +/* Data Management */ + +void free_nlastrip(ListBase *strips, struct NlaStrip *strip); +void free_nlatrack(ListBase *tracks, struct NlaTrack *nlt); +void free_nladata(ListBase *tracks); + +struct NlaStrip *copy_nlastrip(struct NlaStrip *strip); +struct NlaTrack *copy_nlatrack(struct NlaTrack *nlt); +void copy_nladata(ListBase *dst, ListBase *src); + +struct NlaTrack *add_nlatrack(struct AnimData *adt, struct NlaTrack *prev); +struct NlaStrip *add_nlastrip(struct bAction *act); +struct NlaStrip *add_nlastrip_to_stack(struct AnimData *adt, struct bAction *act); + +/* ----------------------------- */ +/* API */ + +short BKE_nlastrips_has_space(ListBase *strips, float start, float end); +void BKE_nlastrips_sort_strips(ListBase *strips); + +short BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip); + + +void BKE_nlastrips_make_metas(ListBase *strips, short temp); +void BKE_nlastrips_clear_metas(ListBase *strips, short onlySel, short onlyTemp); +void BKE_nlastrips_clear_metastrip(ListBase *strips, struct NlaStrip *strip); +short BKE_nlameta_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip); +void BKE_nlameta_flush_transforms(struct NlaStrip *mstrip); + +/* ............ */ + +struct NlaTrack *BKE_nlatrack_find_active(ListBase *tracks); +void BKE_nlatrack_set_active(ListBase *tracks, struct NlaTrack *nlt); + +void BKE_nlatrack_solo_toggle(struct AnimData *adt, struct NlaTrack *nlt); + +short BKE_nlatrack_has_space(struct NlaTrack *nlt, float start, float end); +void BKE_nlatrack_sort_strips(struct NlaTrack *nlt); + +short BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip); + +/* ............ */ + +struct NlaStrip *BKE_nlastrip_find_active(struct NlaTrack *nlt); + +short BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max); + +void BKE_nlastrip_validate_name(struct AnimData *adt, struct NlaStrip *strip); + +/* ............ */ + +short BKE_nlatrack_has_animated_strips(struct NlaTrack *nlt); +short BKE_nlatracks_have_animated_strips(ListBase *tracks); +void BKE_nlastrip_validate_fcurves(struct NlaStrip *strip); + +/* ............ */ + +void BKE_nla_action_pushdown(struct AnimData *adt); + +short BKE_nla_tweakmode_enter(struct AnimData *adt); +void BKE_nla_tweakmode_exit(struct AnimData *adt); + +/* ----------------------------- */ +/* Time Mapping */ + +/* time mapping conversion modes */ +enum { + /* convert from global time to strip time - for evaluation */ + NLATIME_CONVERT_EVAL = 0, + /* convert from global time to strip time - for editing corrections */ + // XXX old 0 invert + NLATIME_CONVERT_UNMAP, + /* convert from strip time to global time */ + // xxx old 1 invert + NLATIME_CONVERT_MAP, +} eNlaTime_ConvertModes; + +float BKE_nla_tweakedit_remap(struct AnimData *adt, float cframe, short mode); -void free_actionstrip (struct bActionStrip* strip); -void free_nlastrips (struct ListBase *nlalist); -void copy_nlastrips (struct ListBase *dst, struct ListBase *src); -void copy_actionstrip (struct bActionStrip **dst, struct bActionStrip **src); -void find_stridechannel(struct Object *ob, struct bActionStrip *strip); -struct bActionStrip *convert_action_to_strip (struct Object *ob); #endif diff --git a/source/blender/blenkernel/BKE_utildefines.h b/source/blender/blenkernel/BKE_utildefines.h index 419f0f5beeb..f6c305b202d 100644 --- a/source/blender/blenkernel/BKE_utildefines.h +++ b/source/blender/blenkernel/BKE_utildefines.h @@ -128,6 +128,7 @@ #define IS_EQT(a, b, c) ((a > b)? (((a-b) <= c)? 1:0) : ((((b-a) <= c)? 1:0))) #define IN_RANGE(a, b, c) ((b < c)? ((baction==NULL) - return NULL; - - for (strip=ob->nlastrips.first; strip; strip=strip->next) - if(strip->flag & ACTSTRIP_ACTIVE) - break; + /* return on the first F-Curve that has some keyframes/samples defined */ + if (act) { + for (fcu= act->curves.first; fcu; fcu= fcu->next) { + if (fcu->totvert) + return 1; + } + } - if(strip && strip->act==ob->action) - return strip; -#endif // XXX old animation system - - return NULL; + /* nothing found */ + return 0; } -/* non clipped mapping of strip */ -static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert) -{ - float length, actlength, repeat, scale; - - if (strip->repeat == 0.0f) strip->repeat = 1.0f; - repeat = (strip->flag & ACTSTRIP_USESTRIDE) ? (1.0f) : (strip->repeat); - - if (strip->scale == 0.0f) strip->scale= 1.0f; - scale = (float)fabs(strip->scale); /* scale must be positive (for now) */ - - actlength = strip->actend-strip->actstart; - if (actlength == 0.0f) actlength = 1.0f; - length = repeat * scale * actlength; - - /* invert = convert action-strip time to global time */ - if (invert) - return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start; - else - return repeat*actlength*(cframe - strip->start)/length + strip->actstart; -} - -/* if the conditions match, it converts current time to strip time */ -float get_action_frame(Object *ob, float cframe) -{ - bActionStrip *strip= get_active_strip(ob); - - if(strip) - return get_actionstrip_frame(strip, cframe, 0); - return cframe; -} - -/* inverted, strip time to current time */ -float get_action_frame_inv(Object *ob, float cframe) -{ - bActionStrip *strip= get_active_strip(ob); - - if(strip) - return get_actionstrip_frame(strip, cframe, 1); - return cframe; -} - - - - /* Calculate the extents of given action */ void calc_action_range(const bAction *act, float *start, float *end, int incl_hidden) { diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 441e17f3318..19337f9de5d 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1,10 +1,37 @@ -/* Testing code for new animation system in 2.5 - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #include #include #include +#include +#include #include "MEM_guardedalloc.h" @@ -12,9 +39,12 @@ #include "BLI_arithb.h" #include "BLI_dynstr.h" +#include "DNA_anim_types.h" + #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_fcurve.h" +#include "BKE_nla.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_utildefines.h" @@ -22,7 +52,7 @@ #include "RNA_access.h" #include "RNA_types.h" -#include "DNA_anim_types.h" +#include "nla_private.h" /* ***************************************** */ /* AnimData API */ @@ -116,7 +146,13 @@ void BKE_free_animdata (ID *id) /* unlink action (don't free, as it's in its own list) */ if (adt->action) adt->action->id.us--; + /* same goes for the temporarily displaced action */ + if (adt->tmpact) + adt->tmpact->id.us--; + /* free nla data */ + free_nladata(&adt->nla_tracks); + /* free drivers - stored as a list of F-Curves */ free_fcurves(&adt->drivers); @@ -146,9 +182,10 @@ AnimData *BKE_copy_animdata (AnimData *adt) // XXX review this... it might not be optimal behaviour yet... //id_us_plus((ID *)dadt->action); dadt->action= copy_action(adt->action); + dadt->tmpact= copy_action(adt->tmpact); /* duplicate NLA data */ - // XXX todo... + copy_nladata(&dadt->nla_tracks, &adt->nla_tracks); /* duplicate drivers (F-Curves) */ copy_fcurves(&dadt->drivers, &adt->drivers); @@ -355,10 +392,10 @@ void BKE_keyingsets_free (ListBase *list) short animsys_remap_path (AnimMapper *remap, char *path, char **dst) { /* is there a valid remapping table to use? */ - if (remap) { + //if (remap) { /* find a matching entry... to use to remap */ // ...TODO... - } + //} /* nothing suitable found, so just set dst to look at path (i.e. no alloc/free needed) */ *dst= path; @@ -455,11 +492,14 @@ static void animsys_evaluate_fcurves (PointerRNA *ptr, ListBase *list, AnimMappe /* calculate then execute each curve */ for (fcu= list->first; fcu; fcu= fcu->next) { - /* check if this curve should be skipped */ - if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) - { - calculate_fcurve(fcu, ctime); - animsys_execute_fcurve(ptr, remap, fcu); + /* check if this F-Curve doesn't belong to a muted group */ + if ((fcu->grp == NULL) || (fcu->grp->flag & AGRP_MUTED)==0) { + /* check if this curve should be skipped */ + if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) + { + calculate_fcurve(fcu, ctime); + animsys_execute_fcurve(ptr, remap, fcu); + } } } } @@ -481,7 +521,6 @@ static void animsys_evaluate_drivers (PointerRNA *ptr, AnimData *adt, float ctim short ok= 0; /* check if this driver's curve should be skipped */ - // FIXME: maybe we shouldn't check for muted, though that would make things more confusing, as there's already too many ways to disable? if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) { /* check if driver itself is tagged for recalculation */ @@ -514,6 +553,10 @@ void animsys_evaluate_action_group (PointerRNA *ptr, bAction *act, bActionGroup if ELEM(NULL, act, agrp) return; if ((remap) && (remap->target != act)) remap= NULL; + /* if group is muted, don't evaluated any of the F-Curve */ + if (agrp->flag & AGRP_MUTED) + return; + /* calculate then execute each curve */ for (fcu= agrp->channels.first; (fcu) && (fcu->grp == agrp); fcu= fcu->next) { @@ -540,152 +583,590 @@ void animsys_evaluate_action (PointerRNA *ptr, bAction *act, AnimMapper *remap, /* ***************************************** */ /* NLA System - Evaluation */ -/* used for list of strips to accumulate at current time */ -typedef struct NlaEvalStrip { - struct NlaEvalStrip *next, *prev; - - NlaTrack *track; /* track that this strip belongs to */ - NlaStrip *strip; /* strip that's being used */ - NlaStrip *sblend; /* strip that's being blended towards (if applicable) */ - - short track_index; /* the index of the track within the list */ - short strip_mode; /* which end of the strip are we looking at */ -} NlaEvalStrip; - -/* bNlaEvalStrip->strip_mode */ -enum { - NES_TIME_BEFORE = -1, - NES_TIME_WITHIN, - NES_TIME_AFTER, - NES_TIME_AFTER_BLEND -} eNlaEvalStrip_StripMode; - - -/* temp channel for accumulating data from NLA (avoids needing to clear all values first) */ -// TODO: maybe this will be used as the 'cache' stuff needed for editable values too? -typedef struct NlaEvalChannel { - struct NlaEvalChannel *next, *prev; - - char *path; /* ready-to-use path (i.e. remapped already) */ - int array_index; /* if applicable... */ - - float value; /* value of this channel */ -} NlaEvalChannel; - - -/* ---------------------- */ - -/* evaluate the F-Curves controlling settings for the NLA-strips (currently, not relinkable) */ -static void nlastrip_evaluate_fcurves (NlaStrip *strip, float ctime) +/* calculate influence of strip based for given frame based on blendin/out values */ +static float nlastrip_get_influence (NlaStrip *strip, float cframe) { - //PointerRNA actstrip_ptr; - //FCurve *fcu; + /* sanity checks - normalise the blendin/out values? */ + strip->blendin= (float)fabs(strip->blendin); + strip->blendout= (float)fabs(strip->blendout); - /* create RNA-pointer needed to set values */ - //RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &actstrip_ptr); - - /* execute these settings as per normal */ - //animsys_evaluate_fcurves(&actstrip_ptr, &strip->fcurves, NULL, ctime); + /* result depends on where frame is in respect to blendin/out values */ + if (IS_EQ(strip->blendin, 0)==0 && (cframe <= (strip->start + strip->blendin))) { + /* there is some blend-in */ + return (float)fabs(cframe - strip->start) / (strip->blendin); + } + else if (IS_EQ(strip->blendout, 0)==0 && (cframe >= (strip->end - strip->blendout))) { + /* there is some blend-out */ + return (float)fabs(strip->end - cframe) / (strip->blendout); + } + else { + /* in the middle of the strip, we should be full strength */ + return 1.0f; + } } - -/* gets the strip active at the current time for a track */ -static void nlatrack_ctime_get_strip (ListBase *list, NlaTrack *nlt, short index, float ctime) +/* evaluate the evaluation time and influence for the strip, storing the results in the strip */ +static void nlastrip_evaluate_controls (NlaStrip *strip, float ctime) { - NlaStrip *strip, *astrip=NULL, *bstrip=NULL; + /* firstly, analytically generate values for influence and time (if applicable) */ + if ((strip->flag & NLASTRIP_FLAG_USR_TIME) == 0) + strip->strip_time= nlastrip_get_frame(strip, ctime, NLATIME_CONVERT_EVAL); + if ((strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) == 0) + strip->influence= nlastrip_get_influence(strip, ctime); + + /* now strip's evaluate F-Curves for these settings (if applicable) */ + if (strip->fcurves.first) { + PointerRNA strip_ptr; + + /* create RNA-pointer needed to set values */ + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); + + /* execute these settings as per normal */ + animsys_evaluate_fcurves(&strip_ptr, &strip->fcurves, NULL, ctime); + } +} + +/* gets the strip active at the current time for a list of strips for evaluation purposes */ +NlaEvalStrip *nlastrips_ctime_get_strip (ListBase *list, ListBase *strips, short index, float ctime) +{ + NlaStrip *strip, *estrip=NULL; NlaEvalStrip *nes; short side= 0; - /* skip if track is muted */ - if (nlt->flag & NLATRACK_MUTED) - return; - /* loop over strips, checking if they fall within the range */ - for (strip= nlt->strips.first; strip; strip= strip->next) { - /* only consider if: - * - current time occurs within strip's extents - * - current time occurs before strip (if it is the first) - * - current time occurs after strip (if hold is on) - * - current time occurs between strips (1st of those isn't holding) - blend! - */ - if (IN_RANGE(ctime, strip->start, strip->end)) { - astrip= strip; + for (strip= strips->first; strip; strip= strip->next) { + /* check if current time occurs within this strip */ + if (IN_RANGE_INCL(ctime, strip->start, strip->end)) { + /* this strip is active, so try to use it */ + estrip= strip; side= NES_TIME_WITHIN; break; } - else if (ctime < strip->start) { - if (strip == nlt->strips.first) { - astrip= strip; + + /* if time occurred before current strip... */ + if (ctime < strip->start) { + if (strip == strips->first) { + /* before first strip - only try to use it if it extends backwards in time too */ + if (strip->extendmode == NLASTRIP_EXTEND_HOLD) + estrip= strip; + + /* side is 'before' regardless of whether there's a useful strip */ side= NES_TIME_BEFORE; - break; } else { - astrip= strip->prev; + /* before next strip - previous strip has ended, but next hasn't begun, + * so blending mode depends on whether strip is being held or not... + * - only occurs when no transition strip added, otherwise the transition would have + * been picked up above... + */ + strip= strip->prev; - if (astrip->flag & NLASTRIP_HOLDLASTFRAME) { - side= NES_TIME_AFTER; - break; - } - else { - bstrip= strip; - side= NES_TIME_AFTER_BLEND; - break; - } + if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) + estrip= strip; + side= NES_TIME_AFTER; } + break; + } + + /* if time occurred after current strip... */ + if (ctime > strip->end) { + /* only if this is the last strip should we do anything, and only if that is being held */ + if (strip == strips->last) { + if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) + estrip= strip; + + side= NES_TIME_AFTER; + break; + } + + /* otherwise, skip... as the 'before' case will catch it more elegantly! */ } } - /* check if strip has been found (and whether it has data worth considering) */ - if (ELEM(NULL, astrip, astrip->act)) - return; - if (astrip->flag & NLASTRIP_MUTE) - return; - - /* check if blending between strips */ - if (side == NES_TIME_AFTER_BLEND) { - /* blending between strips... so calculate influence+act_time of both */ - nlastrip_evaluate_fcurves(astrip, ctime); - nlastrip_evaluate_fcurves(bstrip, ctime); + /* check if a valid strip was found + * - must not be muted (i.e. will have contribution + */ + if ((estrip == NULL) || (estrip->flag & NLASTRIP_FLAG_MUTED)) + return NULL; - if ((astrip->influence <= 0.0f) && (bstrip->influence <= 0.0f)) - return; - } - else { - /* calculate/set the influence+act_time of this strip - don't consider if 0 influence */ - nlastrip_evaluate_fcurves(astrip, ctime); - - if (astrip->influence <= 0.0f) - return; + /* if ctime was not within the boundaries of the strip, clamp! */ + switch (side) { + case NES_TIME_BEFORE: /* extend first frame only */ + ctime= estrip->start; + break; + case NES_TIME_AFTER: /* extend last frame only */ + ctime= estrip->end; + break; } + /* evaluate strip's evaluation controls + * - skip if no influence (i.e. same effect as muting the strip) + * - negative influence is not supported yet... how would that be defined? + */ + // TODO: this sounds a bit hacky having a few isolated F-Curves stuck on some data it operates on... + nlastrip_evaluate_controls(estrip, ctime); + if (estrip->influence <= 0.0f) + return NULL; + + /* check if strip has valid data to evaluate, + * and/or perform any additional type-specific actions + */ + switch (estrip->type) { + case NLASTRIP_TYPE_CLIP: + /* clip must have some action to evaluate */ + if (estrip->act == NULL) + return NULL; + break; + case NLASTRIP_TYPE_TRANSITION: + /* there must be strips to transition from and to (i.e. prev and next required) */ + if (ELEM(NULL, estrip->prev, estrip->next)) + return NULL; + + /* evaluate controls for the relevant extents of the bordering strips... */ + nlastrip_evaluate_controls(estrip->prev, estrip->start); + nlastrip_evaluate_controls(estrip->next, estrip->end); + break; + } - /* allocate new eval-strip for this strip + add to stack */ + /* add to list of strips we need to evaluate */ nes= MEM_callocN(sizeof(NlaEvalStrip), "NlaEvalStrip"); - nes->track= nlt; - nes->strip= astrip; - nes->sblend= bstrip; - nes->track_index= index; + nes->strip= estrip; nes->strip_mode= side; + nes->track_index= index; + nes->strip_time= estrip->strip_time; - BLI_addtail(list, nes); + if (list) + BLI_addtail(list, nes); + + return nes; } /* ---------------------- */ -/* evaluates the given evaluation strip */ -// FIXME: will we need the evaluation cache table set up to blend stuff in? -// TODO: only evaluate here, but flush in one go using the accumulated channels at end... -static void nlastrip_ctime_evaluate (ListBase *channels, NlaEvalStrip *nes, float ctime) +/* find an NlaEvalChannel that matches the given criteria + * - ptr and prop are the RNA data to find a match for + */ +static NlaEvalChannel *nlaevalchan_find_match (ListBase *channels, PointerRNA *ptr, PropertyRNA *prop, int array_index) { - // 1. (in old code) was to extract 'IPO-channels' from actions - // 2. blend between the 'accumulated' data, and the new data + NlaEvalChannel *nec; + + /* sanity check */ + if (channels == NULL) + return NULL; + + /* loop through existing channels, checking for a channel which affects the same property */ + for (nec= channels->first; nec; nec= nec->next) { + /* - comparing the PointerRNA's is done by comparing the pointers + * to the actual struct the property resides in, since that all the + * other data stored in PointerRNA cannot allow us to definitively + * identify the data + */ + if ((nec->ptr.data == ptr->data) && (nec->prop == prop) && (nec->index == array_index)) + return nec; + } + + /* not found */ + return NULL; +} + +/* verify that an appropriate NlaEvalChannel for this F-Curve exists */ +static NlaEvalChannel *nlaevalchan_verify (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes, FCurve *fcu, short *newChan) +{ + NlaEvalChannel *nec; + NlaStrip *strip= nes->strip; + PropertyRNA *prop; + PointerRNA new_ptr; + char *path = NULL; + short free_path=0; + + /* sanity checks */ + if (channels == NULL) + return NULL; + + /* get RNA pointer+property info from F-Curve for more convenient handling */ + /* get path, remapped as appropriate to work in its new environment */ + free_path= animsys_remap_path(strip->remap, fcu->rna_path, &path); + + /* a valid property must be available, and it must be animateable */ + if (RNA_path_resolve(ptr, path, &new_ptr, &prop) == 0) { + if (G.f & G_DEBUG) printf("NLA Strip Eval: Cannot resolve path \n"); + return NULL; + } + /* only ok if animateable */ + else if (RNA_property_animateable(&new_ptr, prop) == 0) { + if (G.f & G_DEBUG) printf("NLA Strip Eval: Property not animateable \n"); + return NULL; + } + + /* try to find a match */ + nec= nlaevalchan_find_match(channels, &new_ptr, prop, fcu->array_index); + + /* allocate a new struct for this if none found */ + if (nec == NULL) { + nec= MEM_callocN(sizeof(NlaEvalChannel), "NlaEvalChannel"); + *newChan= 1; + BLI_addtail(channels, nec); + + nec->ptr= new_ptr; + nec->prop= prop; + nec->index= fcu->array_index; + } + else + *newChan= 0; + + /* we can now return */ + return nec; +} + +/* accumulate (i.e. blend) the given value on to the channel it affects */ +static void nlaevalchan_accumulate (NlaEvalChannel *nec, NlaEvalStrip *nes, short newChan, float value) +{ + NlaStrip *strip= nes->strip; + short blendmode= strip->blendmode; + float inf= strip->influence; + + /* if channel is new, just store value regardless of blending factors, etc. */ + if (newChan) { + nec->value= value; + return; + } + + /* if this is being performed as part of transition evaluation, incorporate + * an additional weighting factor for the influence + */ + if (nes->strip_mode == NES_TIME_TRANSITION_END) + inf *= nes->strip_time; + + /* premultiply the value by the weighting factor */ + if (IS_EQ(inf, 0)) return; + value *= inf; + + /* perform blending */ + switch (blendmode) { + case NLASTRIP_MODE_ADD: + /* simply add the scaled value on to the stack */ + nec->value += value; + break; + + case NLASTRIP_MODE_SUBTRACT: + /* simply subtract the scaled value from the stack */ + nec->value -= value; + break; + + case NLASTRIP_MODE_MULTIPLY: + /* multiply the scaled value with the stack */ + nec->value *= value; + break; + + case NLASTRIP_MODE_REPLACE: + default: // TODO: do we really want to blend by default? it seems more uses might prefer add... + /* do linear interpolation + * - the influence of the accumulated data (elsewhere, that is called dstweight) + * is 1 - influence, since the strip's influence is srcweight + */ + nec->value= nec->value * (1.0f - inf) + value; + break; + } +} + +/* accumulate the results of a temporary buffer with the results of the full-buffer */ +static void nlaevalchan_buffers_accumulate (ListBase *channels, ListBase *tmp_buffer, NlaEvalStrip *nes) +{ + NlaEvalChannel *nec, *necn, *necd; + + /* optimise - abort if no channels */ + if (tmp_buffer->first == NULL) + return; + + /* accumulate results in tmp_channels buffer to the accumulation buffer */ + for (nec= tmp_buffer->first; nec; nec= necn) { + /* get pointer to next channel in case we remove the current channel from the temp-buffer */ + necn= nec->next; + + /* try to find an existing matching channel for this setting in the accumulation buffer */ + necd= nlaevalchan_find_match(channels, &nec->ptr, nec->prop, nec->index); + + /* if there was a matching channel already in the buffer, accumulate to it, + * otherwise, add the current channel to the buffer for efficiency + */ + if (necd) + nlaevalchan_accumulate(necd, nes, 0, nec->value); + else { + BLI_remlink(tmp_buffer, nec); + BLI_addtail(channels, nec); + } + } + + /* free temp-channels that haven't been assimilated into the buffer */ + BLI_freelistN(tmp_buffer); +} + +/* ---------------------- */ +/* F-Modifier stack joining/separation utilities - should we generalise these for BLI_listbase.h interface? */ + +/* Temporarily join two lists of modifiers together, storing the result in a third list */ +static void nlaeval_fmodifiers_join_stacks (ListBase *result, ListBase *list1, ListBase *list2) +{ + FModifier *fcm1, *fcm2; + + /* if list1 is invalid... */ + if ELEM(NULL, list1, list1->first) { + if (list2 && list2->first) { + result->first= list2->first; + result->last= list2->last; + } + } + /* if list 2 is invalid... */ + else if ELEM(NULL, list2, list2->first) { + result->first= list1->first; + result->last= list1->last; + } + else { + /* list1 should be added first, and list2 second, with the endpoints of these being the endpoints for result + * - the original lists must be left unchanged though, as we need that fact for restoring + */ + result->first= list1->first; + result->last= list2->last; + + fcm1= list1->last; + fcm2= list2->first; + + fcm1->next= fcm2; + fcm2->prev= fcm1; + } +} + +/* Split two temporary lists of modifiers */ +static void nlaeval_fmodifiers_split_stacks (ListBase *list1, ListBase *list2) +{ + FModifier *fcm1, *fcm2; + + /* if list1/2 is invalid... just skip */ + if ELEM(NULL, list1, list2) + return; + if ELEM(NULL, list1->first, list2->first) + return; + + /* get endpoints */ + fcm1= list1->last; + fcm2= list2->first; + + /* clear their links */ + fcm1->next= NULL; + fcm2->prev= NULL; +} + +/* ---------------------- */ + +/* evaluate action-clip strip */ +static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) +{ + ListBase tmp_modifiers = {NULL, NULL}; + NlaStrip *strip= nes->strip; + FCurve *fcu; + float evaltime; + + /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ + nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); + + /* evaluate strip's modifiers which modify time to evaluate the base curves at */ + evaltime= evaluate_time_fmodifiers(&tmp_modifiers, NULL, 0.0f, strip->strip_time); + + /* evaluate all the F-Curves in the action, saving the relevant pointers to data that will need to be used */ + for (fcu= strip->act->curves.first; fcu; fcu= fcu->next) { + NlaEvalChannel *nec; + float value = 0.0f; + short newChan = -1; + + /* check if this curve should be skipped */ + if (fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) + continue; + if ((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) + continue; + + /* evaluate the F-Curve's value for the time given in the strip + * NOTE: we use the modified time here, since strip's F-Curve Modifiers are applied on top of this + */ + value= evaluate_fcurve(fcu, evaltime); + + /* apply strip's F-Curve Modifiers on this value + * NOTE: we apply the strip's original evaluation time not the modified one (as per standard F-Curve eval) + */ + evaluate_value_fmodifiers(&tmp_modifiers, fcu, &value, strip->strip_time); + + + /* get an NLA evaluation channel to work with, and accumulate the evaluated value with the value(s) + * stored in this channel if it has been used already + */ + nec= nlaevalchan_verify(ptr, channels, nes, fcu, &newChan); + if (nec) + nlaevalchan_accumulate(nec, nes, newChan, value); + } + + /* unlink this strip's modifiers from the parent's modifiers again */ + nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); +} + +/* evaluate transition strip */ +static void nlastrip_evaluate_transition (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) +{ + ListBase tmp_channels = {NULL, NULL}; + ListBase tmp_modifiers = {NULL, NULL}; + NlaEvalStrip tmp_nes; + NlaStrip *s1, *s2; + + /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ + nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &nes->strip->modifiers, modifiers); + + /* get the two strips to operate on + * - we use the endpoints of the strips directly flanking our strip + * using these as the endpoints of the transition (destination and source) + * - these should have already been determined to be valid... + * - if this strip is being played in reverse, we need to swap these endpoints + * otherwise they will be interpolated wrong + */ + if (nes->strip->flag & NLASTRIP_FLAG_REVERSE) { + s1= nes->strip->next; + s2= nes->strip->prev; + } + else { + s1= nes->strip->prev; + s2= nes->strip->next; + } + + /* prepare template for 'evaluation strip' + * - based on the transition strip's evaluation strip data + * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint + * - strip_time is the 'normalised' (i.e. in-strip) time for evaluation, + * which doubles up as an additional weighting factor for the strip influences + * which allows us to appear to be 'interpolating' between the two extremes + */ + tmp_nes= *nes; + + /* evaluate these strips into a temp-buffer (tmp_channels) */ + // FIXME: modifier evalation here needs some work... + /* first strip */ + tmp_nes.strip_mode= NES_TIME_TRANSITION_START; + tmp_nes.strip= s1; + nlastrip_evaluate_actionclip(ptr, &tmp_channels, &tmp_modifiers, &tmp_nes); + + /* second strip */ + tmp_nes.strip_mode= NES_TIME_TRANSITION_END; + tmp_nes.strip= s2; + nlastrip_evaluate_actionclip(ptr, &tmp_channels, &tmp_modifiers, &tmp_nes); + + + /* assumulate temp-buffer and full-buffer, using the 'real' strip */ + nlaevalchan_buffers_accumulate(channels, &tmp_channels, nes); + + /* unlink this strip's modifiers from the parent's modifiers again */ + nlaeval_fmodifiers_split_stacks(&nes->strip->modifiers, modifiers); +} + +/* evaluate meta-strip */ +static void nlastrip_evaluate_meta (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) +{ + ListBase tmp_channels = {NULL, NULL}; + ListBase tmp_modifiers = {NULL, NULL}; + NlaStrip *strip= nes->strip; + NlaEvalStrip *tmp_nes; + float evaltime; + + /* meta-strip was calculated normally to have some time to be evaluated at + * and here we 'look inside' the meta strip, treating it as a decorated window to + * it's child strips, which get evaluated as if they were some tracks on a strip + * (but with some extra modifiers to apply). + * + * NOTE: keep this in sync with animsys_evaluate_nla() + */ + + /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ + nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); + + /* find the child-strip to evaluate */ + evaltime= (nes->strip_time * (strip->end - strip->start)) + strip->start; + tmp_nes= nlastrips_ctime_get_strip(NULL, &strip->strips, -1, evaltime); + if (tmp_nes == NULL) + return; + + /* evaluate child-strip into tmp_channels buffer before accumulating + * in the accumulation buffer + */ + nlastrip_evaluate(ptr, &tmp_channels, &tmp_modifiers, tmp_nes); + + /* assumulate temp-buffer and full-buffer, using the 'real' strip */ + nlaevalchan_buffers_accumulate(channels, &tmp_channels, nes); + + /* free temp eval-strip */ + MEM_freeN(tmp_nes); + + /* unlink this strip's modifiers from the parent's modifiers again */ + nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); +} + +/* evaluates the given evaluation strip */ +void nlastrip_evaluate (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) +{ + /* actions to take depend on the type of strip */ + switch (nes->strip->type) { + case NLASTRIP_TYPE_CLIP: /* action-clip */ + nlastrip_evaluate_actionclip(ptr, channels, modifiers, nes); + break; + case NLASTRIP_TYPE_TRANSITION: /* transition */ + nlastrip_evaluate_transition(ptr, channels, modifiers, nes); + break; + case NLASTRIP_TYPE_META: /* meta */ + nlastrip_evaluate_meta(ptr, channels, modifiers, nes); + break; + } } /* write the accumulated settings to */ -static void nladata_flush_channels (PointerRNA *ptr, ListBase *channels) +void nladata_flush_channels (ListBase *channels) { + NlaEvalChannel *nec; + /* sanity checks */ + if (channels == NULL) + return; + + /* for each channel with accumulated values, write its value on the property it affects */ + for (nec= channels->first; nec; nec= nec->next) { + PointerRNA *ptr= &nec->ptr; + PropertyRNA *prop= nec->prop; + int array_index= nec->index; + float value= nec->value; + + /* write values - see animsys_write_rna_setting() to sync the code */ + switch (RNA_property_type(prop)) + { + case PROP_BOOLEAN: + if (RNA_property_array_length(prop)) + RNA_property_boolean_set_index(ptr, prop, array_index, (int)value); + else + RNA_property_boolean_set(ptr, prop, (int)value); + break; + case PROP_INT: + if (RNA_property_array_length(prop)) + RNA_property_int_set_index(ptr, prop, array_index, (int)value); + else + RNA_property_int_set(ptr, prop, (int)value); + break; + case PROP_FLOAT: + if (RNA_property_array_length(prop)) + RNA_property_float_set_index(ptr, prop, array_index, value); + else + RNA_property_float_set(ptr, prop, value); + break; + case PROP_ENUM: + RNA_property_enum_set(ptr, prop, (int)value); + break; + default: + // can't do anything with other types of property.... + break; + } + } } /* ---------------------- */ @@ -703,9 +1184,26 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime) ListBase echannels= {NULL, NULL}; NlaEvalStrip *nes; + // TODO: need to zero out all channels used, otherwise we have problems with threadsafety + // and also when the user jumps between different times instead of moving sequentially... + /* 1. get the stack of strips to evaluate at current time (influence calculated here) */ - for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) - nlatrack_ctime_get_strip(&estrips, nlt, track_index, ctime); + for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) { + /* if tweaking is on and this strip is the tweaking track, stop on this one */ + if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED)) + break; + + /* skip if we're only considering a track tagged 'solo' */ + if ((adt->flag & ADT_NLA_SOLO_TRACK) && (nlt->flag & NLATRACK_SOLO)==0) + continue; + /* skip if track is muted */ + if (nlt->flag & NLATRACK_MUTED) + continue; + + /* otherwise, get strip to evaluate for this channel */ + nes= nlastrips_ctime_get_strip(&estrips, &nlt->strips, track_index, ctime); + if (nes) nes->track= nlt; + } /* only continue if there are strips to evaluate */ if (estrips.first == NULL) @@ -714,10 +1212,10 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime) /* 2. for each strip, evaluate then accumulate on top of existing channels, but don't set values yet */ for (nes= estrips.first; nes; nes= nes->next) - nlastrip_ctime_evaluate(&echannels, nes, ctime); + nlastrip_evaluate(ptr, &echannels, NULL, nes); /* 3. flush effects of accumulating channels in NLA to the actual data they affect */ - nladata_flush_channels(ptr, &echannels); + nladata_flush_channels(&echannels); /* 4. free temporary evaluation data */ BLI_freelistN(&estrips); @@ -799,17 +1297,23 @@ void BKE_animsys_evaluate_animdata (ID *id, AnimData *adt, float ctime, short re * - NLA before Active Action, as Active Action behaves as 'tweaking track' * that overrides 'rough' work in NLA */ + // TODO: need to double check that this all works correctly if ((recalc & ADT_RECALC_ANIM) || (adt->recalc & ADT_RECALC_ANIM)) { /* evaluate NLA data */ if ((adt->nla_tracks.first) && !(adt->flag & ADT_NLA_EVAL_OFF)) { + /* evaluate NLA-stack */ animsys_evaluate_nla(&id_ptr, adt, ctime); + + /* evaluate 'active' Action (may be tweaking track) on top of results of NLA-evaluation + * - only do this if we're not exclusively evaluating the 'solo' NLA-track + */ + if ((adt->action) && !(adt->flag & ADT_NLA_SOLO_TRACK)) + animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime); } - - /* evaluate Action data */ - // FIXME: what if the solo track was not tweaking one, then nla-solo should be checked too? - if (adt->action) + /* evaluate Active Action only */ + else if (adt->action) animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime); /* reset tag */ @@ -876,10 +1380,22 @@ void BKE_animsys_evaluate_all_animation (Main *main, float ctime) EVAL_ANIM_IDS(main->camera.first, ADT_RECALC_ANIM); /* shapekeys */ + // TODO: we probably need the same hack as for curves (ctime-hack) EVAL_ANIM_IDS(main->key.first, ADT_RECALC_ANIM); /* curves */ - // TODO... + /* we need to perform a special hack here to ensure that the ctime + * value of the curve gets set in case there's no animation for that + * - it needs to be set before animation is evaluated just so that + * animation can successfully override... + */ + for (id= main->curve.first; id; id= id->next) { + AnimData *adt= BKE_animdata_from_id(id); + Curve *cu= (Curve *)id; + + cu->ctime= ctime; + BKE_animsys_evaluate_animdata(id, adt, ctime, ADT_RECALC_ANIM); + } /* meshes */ // TODO... diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index ad8115ba9aa..ebd94b94f8c 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1,5 +1,30 @@ -/* Testing code for new animation system in 2.5 - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ @@ -31,7 +56,7 @@ #include "RNA_types.h" #ifndef DISABLE_PYTHON -#include "BPY_extern.h" /* for BPY_pydriver_eval() */ +#include "BPY_extern.h" #endif #define SMALL -1.0e-10 @@ -59,7 +84,7 @@ void free_fcurve (FCurve *fcu) /* free extra data - i.e. modifiers, and driver */ fcurve_free_driver(fcu); - fcurve_free_modifiers(fcu); + free_fmodifiers(&fcu->modifiers); /* free f-curve itself */ MEM_freeN(fcu); @@ -115,7 +140,7 @@ FCurve *copy_fcurve (FCurve *fcu) fcu_d->driver= fcurve_copy_driver(fcu_d->driver); /* copy modifiers */ - fcurve_copy_modifiers(&fcu_d->modifiers, &fcu->modifiers); + copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers); /* return new data */ return fcu_d; @@ -175,20 +200,6 @@ FCurve *list_find_fcurve (ListBase *list, const char rna_path[], const int array return NULL; } -short on_keyframe_fcurve(FCurve *fcu, float cfra) -{ - BezTriple *bezt; - unsigned i; - - bezt= fcu->bezt; - for (i=0; itotvert; i++, bezt++) { - if (IS_EQ(bezt->vec[1][0], cfra)) - return 1; - } - - return 0; -} - /* Calculate the extents of F-Curve's data */ void calc_fcurve_bounds (FCurve *fcu, float *xmin, float *xmax, float *ymin, float *ymax) { @@ -1245,1016 +1256,6 @@ static float fcurve_eval_samples (FCurve *fcu, FPoint *fpts, float evaltime) return cvalue; } -/* ******************************** F-Curve Modifiers ********************************* */ - -/* Template --------------------------- */ - -/* Each modifier defines a set of functions, which will be called at the appropriate - * times. In addition to this, each modifier should have a type-info struct, where - * its functions are attached for use. - */ - -/* Template for type-info data: - * - make a copy of this when creating new modifiers, and just change the functions - * pointed to as necessary - * - although the naming of functions doesn't matter, it would help for code - * readability, to follow the same naming convention as is presented here - * - any functions that a constraint doesn't need to define, don't define - * for such cases, just use NULL - * - these should be defined after all the functions have been defined, so that - * forward-definitions/prototypes don't need to be used! - * - keep this copy #if-def'd so that future constraints can get based off this - */ -#if 0 -static FModifierTypeInfo FMI_MODNAME = { - FMODIFIER_TYPE_MODNAME, /* type */ - sizeof(FMod_ModName), /* size */ - FMI_TYPE_SOME_ACTION, /* action type */ - FMI_REQUIRES_SOME_REQUIREMENT, /* requirements */ - "Modifier Name", /* name */ - "FMod_ModName", /* struct name */ - fcm_modname_free, /* free data */ - fcm_modname_relink, /* relink data */ - fcm_modname_copy, /* copy data */ - fcm_modname_new_data, /* new data */ - fcm_modname_verify, /* verify */ - fcm_modname_time, /* evaluate time */ - fcm_modname_evaluate /* evaluate */ -}; -#endif - -/* Generator F-Curve Modifier --------------------------- */ - -/* Generators available: - * 1) simple polynomial generator: - * - Exanded form - (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n]) - * - Factorised form - (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1])) - * 2) simple builin 'functions': - * of the form (y = C[0] * fn( C[1]*x + C[2] ) + C[3]) - * where fn() can be any one of: - * sin, cos, tan, ln, sqrt - * 3) expression... - */ - -static void fcm_generator_free (FModifier *fcm) -{ - FMod_Generator *data= (FMod_Generator *)fcm->data; - - /* free polynomial coefficients array */ - if (data->coefficients) - MEM_freeN(data->coefficients); -} - -static void fcm_generator_copy (FModifier *fcm, FModifier *src) -{ - FMod_Generator *gen= (FMod_Generator *)fcm->data; - FMod_Generator *ogen= (FMod_Generator *)src->data; - - /* copy coefficients array? */ - if (ogen->coefficients) - gen->coefficients= MEM_dupallocN(ogen->coefficients); -} - -static void fcm_generator_new_data (void *mdata) -{ - FMod_Generator *data= (FMod_Generator *)mdata; - float *cp; - - /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */ - data->poly_order= 1; - data->arraysize= 2; - cp= data->coefficients= MEM_callocN(sizeof(float)*2, "FMod_Generator_Coefs"); - cp[0] = 0; // y-offset - cp[1] = 1; // gradient -} - -static void fcm_generator_verify (FModifier *fcm) -{ - FMod_Generator *data= (FMod_Generator *)fcm->data; - - /* requirements depend on mode */ - switch (data->mode) { - case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */ - { - /* arraysize needs to be order+1, so resize if not */ - if (data->arraysize != (data->poly_order+1)) { - float *nc; - - /* make new coefficients array, and copy over as much data as can fit */ - nc= MEM_callocN(sizeof(float)*(data->poly_order+1), "FMod_Generator_Coefs"); - - if (data->coefficients) { - if (data->arraysize > (data->poly_order+1)) - memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order+1)); - else - memcpy(nc, data->coefficients, sizeof(float)*data->arraysize); - - /* free the old data */ - MEM_freeN(data->coefficients); - } - - /* set the new data */ - data->coefficients= nc; - data->arraysize= data->poly_order+1; - } - } - break; - - case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */ - { - /* arraysize needs to be 2*order, so resize if not */ - if (data->arraysize != (data->poly_order * 2)) { - float *nc; - - /* make new coefficients array, and copy over as much data as can fit */ - nc= MEM_callocN(sizeof(float)*(data->poly_order*2), "FMod_Generator_Coefs"); - - if (data->coefficients) { - if (data->arraysize > (data->poly_order * 2)) - memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order * 2)); - else - memcpy(nc, data->coefficients, sizeof(float)*data->arraysize); - - /* free the old data */ - MEM_freeN(data->coefficients); - } - - /* set the new data */ - data->coefficients= nc; - data->arraysize= data->poly_order * 2; - } - } - break; - - case FCM_GENERATOR_FUNCTION: /* builtin function */ - { - /* arraysize needs to be 4*/ - if (data->arraysize != 4) { - float *nc; - - /* free the old data */ - if (data->coefficients) - MEM_freeN(data->coefficients); - - /* make new coefficients array, and init using default values */ - nc= data->coefficients= MEM_callocN(sizeof(float)*4, "FMod_Generator_Coefs"); - data->arraysize= 4; - - nc[0]= 1.0f; - nc[1]= 1.0f; - nc[2]= 0.0f; - nc[3]= 0.0f; - } - } - break; - } -} - -static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Generator *data= (FMod_Generator *)fcm->data; - - /* behaviour depends on mode - * NOTE: the data in its default state is fine too - */ - switch (data->mode) { - case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */ - { - /* we overwrite cvalue with the sum of the polynomial */ - float *powers = MEM_callocN(sizeof(float)*data->arraysize, "Poly Powers"); - float value= 0.0f; - unsigned int i; - - /* for each x^n, precalculate value based on previous one first... this should be - * faster that calling pow() for each entry - */ - for (i=0; i < data->arraysize; i++) { - /* first entry is x^0 = 1, otherwise, calculate based on previous */ - if (i) - powers[i]= powers[i-1] * evaltime; - else - powers[0]= 1; - } - - /* for each coefficient, add to value, which we'll write to *cvalue in one go */ - for (i=0; i < data->arraysize; i++) - value += data->coefficients[i] * powers[i]; - - /* only if something changed, write *cvalue in one go */ - if (data->poly_order) { - if (data->flag & FCM_GENERATOR_ADDITIVE) - *cvalue += value; - else - *cvalue= value; - } - - /* cleanup */ - if (powers) - MEM_freeN(powers); - } - break; - - case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */ - { - float value= 1.0f, *cp=NULL; - unsigned int i; - - /* for each coefficient pair, solve for that bracket before accumulating in value by multiplying */ - for (cp=data->coefficients, i=0; (cp) && (i < data->poly_order); cp+=2, i++) - value *= (cp[0]*evaltime + cp[1]); - - /* only if something changed, write *cvalue in one go */ - if (data->poly_order) { - if (data->flag & FCM_GENERATOR_ADDITIVE) - *cvalue += value; - else - *cvalue= value; - } - } - break; - - case FCM_GENERATOR_FUNCTION: /* builtin function */ - { - double arg= data->coefficients[1]*evaltime + data->coefficients[2]; - double (*fn)(double v) = NULL; - - /* get function pointer to the func to use: - * WARNING: must perform special argument validation hereto guard against crashes - */ - switch (data->func_type) - { - /* simple ones */ - case FCM_GENERATOR_FN_SIN: /* sine wave */ - fn= sin; - break; - case FCM_GENERATOR_FN_COS: /* cosine wave */ - fn= cos; - break; - - /* validation required */ - case FCM_GENERATOR_FN_TAN: /* tangent wave */ - { - /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */ - if IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0) { - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - *cvalue = 0.0f; /* no value possible here */ - } - else - fn= tan; - } - break; - case FCM_GENERATOR_FN_LN: /* natural log */ - { - /* check that value is greater than 1? */ - if (arg > 1.0f) { - fn= log; - } - else { - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - *cvalue = 0.0f; /* no value possible here */ - } - } - break; - case FCM_GENERATOR_FN_SQRT: /* square root */ - { - /* no negative numbers */ - if (arg > 0.0f) { - fn= sqrt; - } - else { - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - *cvalue = 0.0f; /* no value possible here */ - } - } - break; - - default: - printf("Invalid Function-Generator for F-Modifier - %d \n", data->func_type); - } - - /* execute function callback to set value if appropriate */ - if (fn) { - float value= (float)(data->coefficients[0]*fn(arg) + data->coefficients[3]); - - if (data->flag & FCM_GENERATOR_ADDITIVE) - *cvalue += value; - else - *cvalue= value; - } - } - break; - -#ifndef DISABLE_PYTHON - case FCM_GENERATOR_EXPRESSION: /* py-expression */ - // TODO... - break; -#endif /* DISABLE_PYTHON */ - } -} - -static FModifierTypeInfo FMI_GENERATOR = { - FMODIFIER_TYPE_GENERATOR, /* type */ - sizeof(FMod_Generator), /* size */ - FMI_TYPE_GENERATE_CURVE, /* action type */ - FMI_REQUIRES_NOTHING, /* requirements */ - "Generator", /* name */ - "FMod_Generator", /* struct name */ - fcm_generator_free, /* free data */ - fcm_generator_copy, /* copy data */ - fcm_generator_new_data, /* new data */ - fcm_generator_verify, /* verify */ - NULL, /* evaluate time */ - fcm_generator_evaluate /* evaluate */ -}; - -/* Envelope F-Curve Modifier --------------------------- */ - -static void fcm_envelope_free (FModifier *fcm) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - - /* free envelope data array */ - if (env->data) - MEM_freeN(env->data); -} - -static void fcm_envelope_copy (FModifier *fcm, FModifier *src) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - FMod_Envelope *oenv= (FMod_Envelope *)src->data; - - /* copy envelope data array */ - if (oenv->data) - env->data= MEM_dupallocN(oenv->data); -} - -static void fcm_envelope_new_data (void *mdata) -{ - FMod_Envelope *env= (FMod_Envelope *)mdata; - - /* set default min/max ranges */ - env->min= -1.0f; - env->max= 1.0f; -} - -static void fcm_envelope_verify (FModifier *fcm) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - - /* if the are points, perform bubble-sort on them, as user may have changed the order */ - if (env->data) { - // XXX todo... - } -} - -static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - FCM_EnvelopeData *fed, *prevfed, *lastfed; - float min=0.0f, max=0.0f, fac=0.0f; - int a; - - /* get pointers */ - if (env->data == NULL) return; - prevfed= env->data; - fed= prevfed + 1; - lastfed= prevfed + (env->totvert-1); - - /* get min/max values for envelope at evaluation time (relative to mid-value) */ - if (prevfed->time >= evaltime) { - /* before or on first sample, so just extend value */ - min= prevfed->min; - max= prevfed->max; - } - else if (lastfed->time <= evaltime) { - /* after or on last sample, so just extend value */ - min= lastfed->min; - max= lastfed->max; - } - else { - /* evaltime occurs somewhere between segments */ - // TODO: implement binary search for this to make it faster? - for (a=0; prevfed && fed && (a < env->totvert-1); a++, prevfed=fed, fed++) { - /* evaltime occurs within the interval defined by these two envelope points */ - if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) { - float afac, bfac, diff; - - diff= fed->time - prevfed->time; - afac= (evaltime - prevfed->time) / diff; - bfac= (fed->time - evaltime) / diff; - - min= bfac*prevfed->min + afac*fed->min; - max= bfac*prevfed->max + afac*fed->max; - - break; - } - } - } - - /* adjust *cvalue - * - fac is the ratio of how the current y-value corresponds to the reference range - * - thus, the new value is found by mapping the old range to the new! - */ - fac= (*cvalue - (env->midval + env->min)) / (env->max - env->min); - *cvalue= min + fac*(max - min); -} - -static FModifierTypeInfo FMI_ENVELOPE = { - FMODIFIER_TYPE_ENVELOPE, /* type */ - sizeof(FMod_Envelope), /* size */ - FMI_TYPE_REPLACE_VALUES, /* action type */ - 0, /* requirements */ - "Envelope", /* name */ - "FMod_Envelope", /* struct name */ - fcm_envelope_free, /* free data */ - fcm_envelope_copy, /* copy data */ - fcm_envelope_new_data, /* new data */ - fcm_envelope_verify, /* verify */ - NULL, /* evaluate time */ - fcm_envelope_evaluate /* evaluate */ -}; - -/* Cycles F-Curve Modifier --------------------------- */ - -/* This modifier changes evaltime to something that exists within the curve's frame-range, - * then re-evaluates modifier stack up to this point using the new time. This re-entrant behaviour - * is very likely to be more time-consuming than the original approach... (which was tighly integrated into - * the calculation code...). - * - * NOTE: this needs to be at the start of the stack to be of use, as it needs to know the extents of the keyframes/sample-data - * Possible TODO - store length of cycle information that can be initialised from the extents of the keyframes/sample-data, and adjusted - * as appropriate - */ - -/* temp data used during evaluation */ -typedef struct tFCMED_Cycles { - float cycyofs; /* y-offset to apply */ -} tFCMED_Cycles; - -static void fcm_cycles_new_data (void *mdata) -{ - FMod_Cycles *data= (FMod_Cycles *)mdata; - - /* turn on cycles by default */ - data->before_mode= data->after_mode= FCM_EXTRAPOLATE_CYCLIC; -} - -static float fcm_cycles_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime) -{ - FMod_Cycles *data= (FMod_Cycles *)fcm->data; - float prevkey[2], lastkey[2], cycyofs=0.0f; - short side=0, mode=0; - int cycles=0; - - /* check if modifier is first in stack, otherwise disable ourself... */ - // FIXME... - if (fcm->prev) { - fcm->flag |= FMODIFIER_FLAG_DISABLED; - return evaltime; - } - - /* calculate new evaltime due to cyclic interpolation */ - if (fcu && fcu->bezt) { - BezTriple *prevbezt= fcu->bezt; - BezTriple *lastbezt= prevbezt + fcu->totvert-1; - - prevkey[0]= prevbezt->vec[1][0]; - prevkey[1]= prevbezt->vec[1][1]; - - lastkey[0]= lastbezt->vec[1][0]; - lastkey[1]= lastbezt->vec[1][1]; - } - else if (fcu && fcu->fpt) { - FPoint *prevfpt= fcu->fpt; - FPoint *lastfpt= prevfpt + fcu->totvert-1; - - prevkey[0]= prevfpt->vec[0]; - prevkey[1]= prevfpt->vec[1]; - - lastkey[0]= lastfpt->vec[0]; - lastkey[1]= lastfpt->vec[1]; - } - else - return evaltime; - - /* check if modifier will do anything - * 1) if in data range, definitely don't do anything - * 2) if before first frame or after last frame, make sure some cycling is in use - */ - if (evaltime < prevkey[0]) { - if (data->before_mode) { - side= -1; - mode= data->before_mode; - cycles= data->before_cycles; - } - } - else if (evaltime > lastkey[0]) { - if (data->after_mode) { - side= 1; - mode= data->after_mode; - cycles= data->after_cycles; - } - } - if ELEM(0, side, mode) - return evaltime; - - /* find relative place within a cycle */ - { - float cycdx=0, cycdy=0, ofs=0; - float cycle= 0; - - /* ofs is start frame of cycle */ - ofs= prevkey[0]; - - /* calculate period and amplitude (total height) of a cycle */ - cycdx= lastkey[0] - prevkey[0]; - cycdy= lastkey[1] - prevkey[1]; - - /* check if cycle is infinitely small, to be point of being impossible to use */ - if (cycdx == 0) - return evaltime; - - /* calculate the 'number' of the cycle */ - cycle= ((float)side * (evaltime - ofs) / cycdx); - - /* check that cyclic is still enabled for the specified time */ - if (cycles == 0) { - /* catch this case so that we don't exit when we have cycles=0 - * as this indicates infinite cycles... - */ - } - else if (cycle > (cycles+1)) { - /* we are too far away from range to evaluate - * TODO: but we should still hold last value... - */ - return evaltime; - } - - /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */ - if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) { - cycyofs = (float)floor((evaltime - ofs) / cycdx); - cycyofs *= cycdy; - } - - /* calculate where in the cycle we are (overwrite evaltime to reflect this) */ - if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)(cycle) % 2)) { - /* when 'mirror' option is used and cycle number is odd, this cycle is played in reverse - * - for 'before' extrapolation, we need to flip in a different way, otherwise values past - * then end of the curve get referenced (result of fmod will be negative, and with different phase) - */ - if (side < 0) - evaltime= (float)(prevkey[0] - fmod(evaltime-ofs, cycdx)); - else - evaltime= (float)(lastkey[0] - fmod(evaltime-ofs, cycdx)); - } - else { - /* the cycle is played normally... */ - evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs); - } - if (evaltime < ofs) evaltime += cycdx; - } - - /* store temp data if needed */ - if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) { - tFCMED_Cycles *edata; - - /* for now, this is just a float, but we could get more stuff... */ - fcm->edata= edata= MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles"); - edata->cycyofs= cycyofs; - } - - /* return the new frame to evaluate */ - return evaltime; -} - -static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - tFCMED_Cycles *edata= (tFCMED_Cycles *)fcm->edata; - - /* use temp data */ - if (edata) { - /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */ - *cvalue += edata->cycyofs; - - /* free temp data */ - MEM_freeN(edata); - fcm->edata= NULL; - } -} - -static FModifierTypeInfo FMI_CYCLES = { - FMODIFIER_TYPE_CYCLES, /* type */ - sizeof(FMod_Cycles), /* size */ - FMI_TYPE_EXTRAPOLATION, /* action type */ - FMI_REQUIRES_ORIGINAL_DATA, /* requirements */ - "Cycles", /* name */ - "FMod_Cycles", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - fcm_cycles_new_data, /* new data */ - NULL /*fcm_cycles_verify*/, /* verify */ - fcm_cycles_time, /* evaluate time */ - fcm_cycles_evaluate /* evaluate */ -}; - -/* Noise F-Curve Modifier --------------------------- */ - -static void fcm_noise_new_data (void *mdata) -{ - FMod_Noise *data= (FMod_Noise *)mdata; - - /* defaults */ - data->size= 1.0f; - data->strength= 1.0f; - data->phase= 1.0f; - data->depth = 0; - data->modification = FCM_NOISE_MODIF_REPLACE; -} - -static void fcm_noise_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Noise *data= (FMod_Noise *)fcm->data; - float noise; - - noise = BLI_turbulence(data->size, evaltime, data->phase, 0.f, data->depth); - - switch (data->modification) { - case FCM_NOISE_MODIF_ADD: - *cvalue= *cvalue + noise * data->strength; - break; - case FCM_NOISE_MODIF_SUBTRACT: - *cvalue= *cvalue - noise * data->strength; - break; - case FCM_NOISE_MODIF_MULTIPLY: - *cvalue= *cvalue * noise * data->strength; - break; - case FCM_NOISE_MODIF_REPLACE: - default: - *cvalue= *cvalue + (noise - 0.5f) * data->strength; - break; - } -} - -static FModifierTypeInfo FMI_NOISE = { - FMODIFIER_TYPE_NOISE, /* type */ - sizeof(FMod_Noise), /* size */ - FMI_TYPE_REPLACE_VALUES, /* action type */ - 0, /* requirements */ - "Noise", /* name */ - "FMod_Noise", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - fcm_noise_new_data, /* new data */ - NULL /*fcm_noise_verify*/, /* verify */ - NULL, /* evaluate time */ - fcm_noise_evaluate /* evaluate */ -}; - -/* Filter F-Curve Modifier --------------------------- */ - -#if 0 // XXX not yet implemented -static FModifierTypeInfo FMI_FILTER = { - FMODIFIER_TYPE_FILTER, /* type */ - sizeof(FMod_Filter), /* size */ - FMI_TYPE_REPLACE_VALUES, /* action type */ - 0, /* requirements */ - "Filter", /* name */ - "FMod_Filter", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - NULL, /* new data */ - NULL /*fcm_filter_verify*/, /* verify */ - NULL, /* evlauate time */ - fcm_filter_evaluate /* evaluate */ -}; -#endif // XXX not yet implemented - - -/* Python F-Curve Modifier --------------------------- */ - -static void fcm_python_free (FModifier *fcm) -{ - FMod_Python *data= (FMod_Python *)fcm->data; - - /* id-properties */ - IDP_FreeProperty(data->prop); - MEM_freeN(data->prop); -} - -static void fcm_python_new_data (void *mdata) -{ - FMod_Python *data= (FMod_Python *)mdata; - - /* everything should be set correctly by calloc, except for the prop->type constant.*/ - data->prop = MEM_callocN(sizeof(IDProperty), "PyFModifierProps"); - data->prop->type = IDP_GROUP; -} - -static void fcm_python_copy (FModifier *fcm, FModifier *src) -{ - FMod_Python *pymod = (FMod_Python *)fcm->data; - FMod_Python *opymod = (FMod_Python *)src->data; - - pymod->prop = IDP_CopyProperty(opymod->prop); -} - -static void fcm_python_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ -#ifndef DISABLE_PYTHON - //FMod_Python *data= (FMod_Python *)fcm->data; - - /* FIXME... need to implement this modifier... - * It will need it execute a script using the custom properties - */ -#endif /* DISABLE_PYTHON */ -} - -static FModifierTypeInfo FMI_PYTHON = { - FMODIFIER_TYPE_PYTHON, /* type */ - sizeof(FMod_Python), /* size */ - FMI_TYPE_GENERATE_CURVE, /* action type */ - FMI_REQUIRES_RUNTIME_CHECK, /* requirements */ - "Python", /* name */ - "FMod_Python", /* struct name */ - fcm_python_free, /* free data */ - fcm_python_copy, /* copy data */ - fcm_python_new_data, /* new data */ - NULL /*fcm_python_verify*/, /* verify */ - NULL /*fcm_python_time*/, /* evaluate time */ - fcm_python_evaluate /* evaluate */ -}; - - -/* Limits F-Curve Modifier --------------------------- */ - -static float fcm_limits_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime) -{ - FMod_Limits *data= (FMod_Limits *)fcm->data; - - /* check for the time limits */ - if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin)) - return data->rect.xmin; - if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax)) - return data->rect.xmax; - - /* modifier doesn't change time */ - return evaltime; -} - -static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Limits *data= (FMod_Limits *)fcm->data; - - /* value limits now */ - if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin)) - *cvalue= data->rect.ymin; - if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax)) - *cvalue= data->rect.ymax; -} - -static FModifierTypeInfo FMI_LIMITS = { - FMODIFIER_TYPE_LIMITS, /* type */ - sizeof(FMod_Limits), /* size */ - FMI_TYPE_GENERATE_CURVE, /* action type */ /* XXX... err... */ - FMI_REQUIRES_RUNTIME_CHECK, /* requirements */ - "Limits", /* name */ - "FMod_Limits", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - NULL, /* new data */ - NULL, /* verify */ - fcm_limits_time, /* evaluate time */ - fcm_limits_evaluate /* evaluate */ -}; - -/* F-Curve Modifier API --------------------------- */ -/* All of the F-Curve Modifier api functions use FModifierTypeInfo structs to carry out - * and operations that involve F-Curve modifier specific code. - */ - -/* These globals only ever get directly accessed in this file */ -static FModifierTypeInfo *fmodifiersTypeInfo[FMODIFIER_NUM_TYPES]; -static short FMI_INIT= 1; /* when non-zero, the list needs to be updated */ - -/* This function only gets called when FMI_INIT is non-zero */ -static void fmods_init_typeinfo () -{ - fmodifiersTypeInfo[0]= NULL; /* 'Null' F-Curve Modifier */ - fmodifiersTypeInfo[1]= &FMI_GENERATOR; /* Generator F-Curve Modifier */ - fmodifiersTypeInfo[2]= &FMI_ENVELOPE; /* Envelope F-Curve Modifier */ - fmodifiersTypeInfo[3]= &FMI_CYCLES; /* Cycles F-Curve Modifier */ - fmodifiersTypeInfo[4]= &FMI_NOISE; /* Apply-Noise F-Curve Modifier */ - fmodifiersTypeInfo[5]= NULL/*&FMI_FILTER*/; /* Filter F-Curve Modifier */ // XXX unimplemented - fmodifiersTypeInfo[6]= &FMI_PYTHON; /* Custom Python F-Curve Modifier */ - fmodifiersTypeInfo[7]= &FMI_LIMITS; /* Limits F-Curve Modifier */ -} - -/* This function should be used for getting the appropriate type-info when only - * a F-Curve modifier type is known - */ -FModifierTypeInfo *get_fmodifier_typeinfo (int type) -{ - /* initialise the type-info list? */ - if (FMI_INIT) { - fmods_init_typeinfo(); - FMI_INIT = 0; - } - - /* only return for valid types */ - if ( (type >= FMODIFIER_TYPE_NULL) && - (type <= FMODIFIER_NUM_TYPES ) ) - { - /* there shouldn't be any segfaults here... */ - return fmodifiersTypeInfo[type]; - } - else { - printf("No valid F-Curve Modifier type-info data available. Type = %i \n", type); - } - - return NULL; -} - -/* This function should always be used to get the appropriate type-info, as it - * has checks which prevent segfaults in some weird cases. - */ -FModifierTypeInfo *fmodifier_get_typeinfo (FModifier *fcm) -{ - /* only return typeinfo for valid modifiers */ - if (fcm) - return get_fmodifier_typeinfo(fcm->type); - else - return NULL; -} - -/* API --------------------------- */ - -/* Add a new F-Curve Modifier to the given F-Curve of a certain type */ -FModifier *fcurve_add_modifier (FCurve *fcu, int type) -{ - FModifierTypeInfo *fmi= get_fmodifier_typeinfo(type); - FModifier *fcm; - - /* sanity checks */ - if ELEM(NULL, fcu, fmi) - return NULL; - - /* special checks for whether modifier can be added */ - if ((fcu->modifiers.first) && (type == FMODIFIER_TYPE_CYCLES)) { - /* cycles modifier must be first in stack, so for now, don't add if it can't be */ - // TODO: perhaps there is some better way, but for now, - printf("Error: Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be first in stack. \n"); - return NULL; - } - - /* add modifier itself */ - fcm= MEM_callocN(sizeof(FModifier), "F-Curve Modifier"); - fcm->type = type; - fcm->flag = FMODIFIER_FLAG_EXPANDED; - BLI_addtail(&fcu->modifiers, fcm); - - /* add modifier's data */ - fcm->data= MEM_callocN(fmi->size, fmi->structName); - - /* init custom settings if necessary */ - if (fmi->new_data) - fmi->new_data(fcm->data); - - /* return modifier for further editing */ - return fcm; -} - -/* Duplicate all of the F-Curve Modifiers in the Modifier stacks */ -void fcurve_copy_modifiers (ListBase *dst, ListBase *src) -{ - FModifier *fcm, *srcfcm; - - if ELEM(NULL, dst, src) - return; - - dst->first= dst->last= NULL; - BLI_duplicatelist(dst, src); - - for (fcm=dst->first, srcfcm=src->first; fcm && srcfcm; srcfcm=srcfcm->next, fcm=fcm->next) { - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* make a new copy of the F-Modifier's data */ - fcm->data = MEM_dupallocN(fcm->data); - - /* only do specific constraints if required */ - if (fmi && fmi->copy_data) - fmi->copy_data(fcm, srcfcm); - } -} - -/* Remove and free the given F-Curve Modifier from the given F-Curve's stack */ -void fcurve_remove_modifier (FCurve *fcu, FModifier *fcm) -{ - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* sanity check */ - if (fcm == NULL) - return; - - /* free modifier's special data (stored inside fcm->data) */ - if (fcm->data) { - if (fmi && fmi->free_data) - fmi->free_data(fcm); - - /* free modifier's data (fcm->data) */ - MEM_freeN(fcm->data); - } - - /* remove modifier from stack */ - if (fcu) - BLI_freelinkN(&fcu->modifiers, fcm); - else { - // XXX this case can probably be removed some day, as it shouldn't happen... - printf("fcurve_remove_modifier() - no fcurve \n"); - MEM_freeN(fcm); - } -} - -/* Remove all of a given F-Curve's modifiers */ -void fcurve_free_modifiers (FCurve *fcu) -{ - FModifier *fcm, *fmn; - - /* sanity check */ - if (fcu == NULL) - return; - - /* free each modifier in order - modifier is unlinked from list and freed */ - for (fcm= fcu->modifiers.first; fcm; fcm= fmn) { - fmn= fcm->next; - fcurve_remove_modifier(fcu, fcm); - } -} - -/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined - * by start and end (inclusive). - */ -void fcurve_bake_modifiers (FCurve *fcu, int start, int end) -{ - ChannelDriver *driver; - - /* sanity checks */ - // TODO: make these tests report errors using reports not printf's - if ELEM(NULL, fcu, fcu->modifiers.first) { - printf("Error: No F-Curve with F-Curve Modifiers to Bake\n"); - return; - } - - /* temporarily, disable driver while we sample, so that they don't influence the outcome */ - driver= fcu->driver; - fcu->driver= NULL; - - /* bake the modifiers, by sampling the curve at each frame */ - fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve); - - /* free the modifiers now */ - fcurve_free_modifiers(fcu); - - /* restore driver */ - fcu->driver= driver; -} - -/* Find the active F-Curve Modifier */ -FModifier *fcurve_find_active_modifier (FCurve *fcu) -{ - FModifier *fcm; - - /* sanity checks */ - if ELEM(NULL, fcu, fcu->modifiers.first) - return NULL; - - /* loop over modifiers until 'active' one is found */ - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { - if (fcm->flag & FMODIFIER_FLAG_ACTIVE) - return fcm; - } - - /* no modifier is active */ - return NULL; -} - -/* Set the active F-Curve Modifier */ -void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm) -{ - FModifier *fm; - - /* sanity checks */ - if ELEM(NULL, fcu, fcu->modifiers.first) - return; - - /* deactivate all, and set current one active */ - for (fm= fcu->modifiers.first; fm; fm= fm->next) - fm->flag &= ~FMODIFIER_FLAG_ACTIVE; - - /* make given modifier active */ - if (fcm) - fcm->flag |= FMODIFIER_FLAG_ACTIVE; -} - /* ***************************** F-Curve - Evaluation ********************************* */ /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") @@ -2262,7 +1263,6 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm) */ float evaluate_fcurve (FCurve *fcu, float evaltime) { - FModifier *fcm; float cvalue= 0.0f; float devaltime; @@ -2275,28 +1275,8 @@ float evaluate_fcurve (FCurve *fcu, float evaltime) evaltime= cvalue= evaluate_driver(fcu->driver, evaltime); } - /* evaluate time modifications imposed by some F-Curve Modifiers - * - this step acts as an optimisation to prevent the F-Curve stack being evaluated - * several times by modifiers requesting the time be modified, as the final result - * would have required using the modified time - * - modifiers only ever recieve the unmodified time, as subsequent modifiers should be - * working on the 'global' result of the modified curve, not some localised segment, - * so nevaltime gets set to whatever the last time-modifying modifier likes... - * - we start from the end of the stack, as only the last one matters for now - */ - devaltime= evaltime; - - for (fcm= fcu->modifiers.last; fcm; fcm= fcm->prev) { - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* only evaluate if there's a callback for this */ - // TODO: implement the 'influence' control feature... - if (fmi && fmi->evaluate_modifier_time) { - if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0) - devaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime); - break; - } - } + /* evaluate modifiers which modify time to evaluate the base curve at */ + devaltime= evaluate_time_fmodifiers(&fcu->modifiers, fcu, cvalue, evaltime); /* evaluate curve-data * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying @@ -2308,16 +1288,7 @@ float evaluate_fcurve (FCurve *fcu, float evaltime) cvalue= fcurve_eval_samples(fcu, fcu->fpt, devaltime); /* evaluate modifiers */ - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* only evaluate if there's a callback for this */ - // TODO: implement the 'influence' control feature... - if (fmi && fmi->evaluate_modifier) { - if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0) - fmi->evaluate_modifier(fcu, fcm, &cvalue, evaltime); - } - } + evaluate_value_fmodifiers(&fcu->modifiers, fcu, &cvalue, evaltime); /* if curve can only have integral values, perform truncation (i.e. drop the decimal part) * here so that the curve can be sampled correctly @@ -2330,10 +1301,16 @@ float evaluate_fcurve (FCurve *fcu, float evaltime) } /* Calculate the value of the given F-Curve at the given frame, and set its curval */ -// TODO: will this be necessary? void calculate_fcurve (FCurve *fcu, float ctime) { - /* calculate and set curval (evaluates driver too) */ - fcu->curval= evaluate_fcurve(fcu, ctime); + /* only calculate + set curval (overriding the existing value) if curve has + * any data which warrants this... + */ + if ( (fcu->totvert) || (fcu->driver && !(fcu->driver->flag & DRIVER_FLAG_INVALID)) || + list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) ) + { + /* calculate and set curval (evaluates driver too if necessary) */ + fcu->curval= evaluate_fcurve(fcu, ctime); + } } diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c new file mode 100644 index 00000000000..aa5214979d9 --- /dev/null +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -0,0 +1,1197 @@ +/** + * $Id: fcurve.c 21299 2009-07-02 02:12:37Z 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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_noise.h" + +#include "BKE_fcurve.h" +#include "BKE_curve.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_utildefines.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#ifndef DISABLE_PYTHON +#include "BPY_extern.h" /* for BPY_pydriver_eval() */ +#endif + +#define SMALL -1.0e-10 +#define SELECT 1 + +/* ******************************** F-Modifiers ********************************* */ + +/* Info ------------------------------- */ + +/* F-Modifiers are modifiers which operate on F-Curves. However, they can also be defined + * on NLA-Strips to affect all of the F-Curves referenced by the NLA-Strip. + */ + +/* Template --------------------------- */ + +/* Each modifier defines a set of functions, which will be called at the appropriate + * times. In addition to this, each modifier should have a type-info struct, where + * its functions are attached for use. + */ + +/* Template for type-info data: + * - make a copy of this when creating new modifiers, and just change the functions + * pointed to as necessary + * - although the naming of functions doesn't matter, it would help for code + * readability, to follow the same naming convention as is presented here + * - any functions that a constraint doesn't need to define, don't define + * for such cases, just use NULL + * - these should be defined after all the functions have been defined, so that + * forward-definitions/prototypes don't need to be used! + * - keep this copy #if-def'd so that future constraints can get based off this + */ +#if 0 +static FModifierTypeInfo FMI_MODNAME = { + FMODIFIER_TYPE_MODNAME, /* type */ + sizeof(FMod_ModName), /* size */ + FMI_TYPE_SOME_ACTION, /* action type */ + FMI_REQUIRES_SOME_REQUIREMENT, /* requirements */ + "Modifier Name", /* name */ + "FMod_ModName", /* struct name */ + fcm_modname_free, /* free data */ + fcm_modname_relink, /* relink data */ + fcm_modname_copy, /* copy data */ + fcm_modname_new_data, /* new data */ + fcm_modname_verify, /* verify */ + fcm_modname_time, /* evaluate time */ + fcm_modname_evaluate /* evaluate */ +}; +#endif + +/* Generator F-Curve Modifier --------------------------- */ + +/* Generators available: + * 1) simple polynomial generator: + * - Exanded form - (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n]) + * - Factorised form - (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1])) + */ + +static void fcm_generator_free (FModifier *fcm) +{ + FMod_Generator *data= (FMod_Generator *)fcm->data; + + /* free polynomial coefficients array */ + if (data->coefficients) + MEM_freeN(data->coefficients); +} + +static void fcm_generator_copy (FModifier *fcm, FModifier *src) +{ + FMod_Generator *gen= (FMod_Generator *)fcm->data; + FMod_Generator *ogen= (FMod_Generator *)src->data; + + /* copy coefficients array? */ + if (ogen->coefficients) + gen->coefficients= MEM_dupallocN(ogen->coefficients); +} + +static void fcm_generator_new_data (void *mdata) +{ + FMod_Generator *data= (FMod_Generator *)mdata; + float *cp; + + /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */ + data->poly_order= 1; + data->arraysize= 2; + cp= data->coefficients= MEM_callocN(sizeof(float)*2, "FMod_Generator_Coefs"); + cp[0] = 0; // y-offset + cp[1] = 1; // gradient +} + +static void fcm_generator_verify (FModifier *fcm) +{ + FMod_Generator *data= (FMod_Generator *)fcm->data; + + /* requirements depend on mode */ + switch (data->mode) { + case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */ + { + /* arraysize needs to be order+1, so resize if not */ + if (data->arraysize != (data->poly_order+1)) { + float *nc; + + /* make new coefficients array, and copy over as much data as can fit */ + nc= MEM_callocN(sizeof(float)*(data->poly_order+1), "FMod_Generator_Coefs"); + + if (data->coefficients) { + if (data->arraysize > (data->poly_order+1)) + memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order+1)); + else + memcpy(nc, data->coefficients, sizeof(float)*data->arraysize); + + /* free the old data */ + MEM_freeN(data->coefficients); + } + + /* set the new data */ + data->coefficients= nc; + data->arraysize= data->poly_order+1; + } + } + break; + + case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */ + { + /* arraysize needs to be 2*order, so resize if not */ + if (data->arraysize != (data->poly_order * 2)) { + float *nc; + + /* make new coefficients array, and copy over as much data as can fit */ + nc= MEM_callocN(sizeof(float)*(data->poly_order*2), "FMod_Generator_Coefs"); + + if (data->coefficients) { + if (data->arraysize > (data->poly_order * 2)) + memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order * 2)); + else + memcpy(nc, data->coefficients, sizeof(float)*data->arraysize); + + /* free the old data */ + MEM_freeN(data->coefficients); + } + + /* set the new data */ + data->coefficients= nc; + data->arraysize= data->poly_order * 2; + } + } + break; + } +} + +static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ + FMod_Generator *data= (FMod_Generator *)fcm->data; + + /* behaviour depends on mode + * NOTE: the data in its default state is fine too + */ + switch (data->mode) { + case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */ + { + /* we overwrite cvalue with the sum of the polynomial */ + float *powers = MEM_callocN(sizeof(float)*data->arraysize, "Poly Powers"); + float value= 0.0f; + unsigned int i; + + /* for each x^n, precalculate value based on previous one first... this should be + * faster that calling pow() for each entry + */ + for (i=0; i < data->arraysize; i++) { + /* first entry is x^0 = 1, otherwise, calculate based on previous */ + if (i) + powers[i]= powers[i-1] * evaltime; + else + powers[0]= 1; + } + + /* for each coefficient, add to value, which we'll write to *cvalue in one go */ + for (i=0; i < data->arraysize; i++) + value += data->coefficients[i] * powers[i]; + + /* only if something changed, write *cvalue in one go */ + if (data->poly_order) { + if (data->flag & FCM_GENERATOR_ADDITIVE) + *cvalue += value; + else + *cvalue= value; + } + + /* cleanup */ + if (powers) + MEM_freeN(powers); + } + break; + + case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */ + { + float value= 1.0f, *cp=NULL; + unsigned int i; + + /* for each coefficient pair, solve for that bracket before accumulating in value by multiplying */ + for (cp=data->coefficients, i=0; (cp) && (i < data->poly_order); cp+=2, i++) + value *= (cp[0]*evaltime + cp[1]); + + /* only if something changed, write *cvalue in one go */ + if (data->poly_order) { + if (data->flag & FCM_GENERATOR_ADDITIVE) + *cvalue += value; + else + *cvalue= value; + } + } + break; + } +} + +static FModifierTypeInfo FMI_GENERATOR = { + FMODIFIER_TYPE_GENERATOR, /* type */ + sizeof(FMod_Generator), /* size */ + FMI_TYPE_GENERATE_CURVE, /* action type */ + FMI_REQUIRES_NOTHING, /* requirements */ + "Generator", /* name */ + "FMod_Generator", /* struct name */ + fcm_generator_free, /* free data */ + fcm_generator_copy, /* copy data */ + fcm_generator_new_data, /* new data */ + fcm_generator_verify, /* verify */ + NULL, /* evaluate time */ + fcm_generator_evaluate /* evaluate */ +}; + +/* Built-In Function Generator F-Curve Modifier --------------------------- */ + +/* This uses the general equation for equations: + * y = amplitude * fn(phase_multiplier*x + phase_offset) + y_offset + * + * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients, + * x is the evaluation 'time', and 'y' is the resultant value + * + * Functions available are + * sin, cos, tan, sinc (normalised sin), natural log, square root + */ + +static void fcm_fn_generator_new_data (void *mdata) +{ + FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)mdata; + + /* set amplitude and phase multiplier to 1.0f so that something is generated */ + data->amplitude= 1.0f; + data->phase_multiplier= 1.0f; +} + +/* Unary 'normalised sine' function + * y = sin(PI + x) / (PI * x), + * except for x = 0 when y = 1. + */ +static double sinc (double x) +{ + if (fabs(x) < 0.0001) + return 1.0; + else + return sin(M_PI * x) / (M_PI * x); +} + +static void fcm_fn_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ + FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)fcm->data; + double arg= data->phase_multiplier*evaltime + data->phase_offset; + double (*fn)(double v) = NULL; + + /* get function pointer to the func to use: + * WARNING: must perform special argument validation hereto guard against crashes + */ + switch (data->type) + { + /* simple ones */ + case FCM_GENERATOR_FN_SIN: /* sine wave */ + fn= sin; + break; + case FCM_GENERATOR_FN_COS: /* cosine wave */ + fn= cos; + break; + case FCM_GENERATOR_FN_SINC: /* normalised sine wave */ + fn= sinc; + break; + + /* validation required */ + case FCM_GENERATOR_FN_TAN: /* tangent wave */ + { + /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */ + if IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0) { + if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) + *cvalue = 0.0f; /* no value possible here */ + } + else + fn= tan; + } + break; + case FCM_GENERATOR_FN_LN: /* natural log */ + { + /* check that value is greater than 1? */ + if (arg > 1.0f) { + fn= log; + } + else { + if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) + *cvalue = 0.0f; /* no value possible here */ + } + } + break; + case FCM_GENERATOR_FN_SQRT: /* square root */ + { + /* no negative numbers */ + if (arg > 0.0f) { + fn= sqrt; + } + else { + if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) + *cvalue = 0.0f; /* no value possible here */ + } + } + break; + + default: + printf("Invalid Function-Generator for F-Modifier - %d \n", data->type); + } + + /* execute function callback to set value if appropriate */ + if (fn) { + float value= (float)(data->amplitude*fn(arg) + data->value_offset); + + if (data->flag & FCM_GENERATOR_ADDITIVE) + *cvalue += value; + else + *cvalue= value; + } +} + +static FModifierTypeInfo FMI_FN_GENERATOR = { + FMODIFIER_TYPE_FN_GENERATOR, /* type */ + sizeof(FMod_FunctionGenerator), /* size */ + FMI_TYPE_GENERATE_CURVE, /* action type */ + FMI_REQUIRES_NOTHING, /* requirements */ + "Built-In Function", /* name */ + "FMod_FunctionGenerator", /* struct name */ + NULL, /* free data */ + NULL, /* copy data */ + fcm_fn_generator_new_data, /* new data */ + NULL, /* verify */ + NULL, /* evaluate time */ + fcm_fn_generator_evaluate /* evaluate */ +}; + +/* Envelope F-Curve Modifier --------------------------- */ + +static void fcm_envelope_free (FModifier *fcm) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + + /* free envelope data array */ + if (env->data) + MEM_freeN(env->data); +} + +static void fcm_envelope_copy (FModifier *fcm, FModifier *src) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + FMod_Envelope *oenv= (FMod_Envelope *)src->data; + + /* copy envelope data array */ + if (oenv->data) + env->data= MEM_dupallocN(oenv->data); +} + +static void fcm_envelope_new_data (void *mdata) +{ + FMod_Envelope *env= (FMod_Envelope *)mdata; + + /* set default min/max ranges */ + env->min= -1.0f; + env->max= 1.0f; +} + +static void fcm_envelope_verify (FModifier *fcm) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + + /* if the are points, perform bubble-sort on them, as user may have changed the order */ + if (env->data) { + // XXX todo... + } +} + +static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + FCM_EnvelopeData *fed, *prevfed, *lastfed; + float min=0.0f, max=0.0f, fac=0.0f; + int a; + + /* get pointers */ + if (env->data == NULL) return; + prevfed= env->data; + fed= prevfed + 1; + lastfed= prevfed + (env->totvert-1); + + /* get min/max values for envelope at evaluation time (relative to mid-value) */ + if (prevfed->time >= evaltime) { + /* before or on first sample, so just extend value */ + min= prevfed->min; + max= prevfed->max; + } + else if (lastfed->time <= evaltime) { + /* after or on last sample, so just extend value */ + min= lastfed->min; + max= lastfed->max; + } + else { + /* evaltime occurs somewhere between segments */ + // TODO: implement binary search for this to make it faster? + for (a=0; prevfed && fed && (a < env->totvert-1); a++, prevfed=fed, fed++) { + /* evaltime occurs within the interval defined by these two envelope points */ + if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) { + float afac, bfac, diff; + + diff= fed->time - prevfed->time; + afac= (evaltime - prevfed->time) / diff; + bfac= (fed->time - evaltime) / diff; + + min= bfac*prevfed->min + afac*fed->min; + max= bfac*prevfed->max + afac*fed->max; + + break; + } + } + } + + /* adjust *cvalue + * - fac is the ratio of how the current y-value corresponds to the reference range + * - thus, the new value is found by mapping the old range to the new! + */ + fac= (*cvalue - (env->midval + env->min)) / (env->max - env->min); + *cvalue= min + fac*(max - min); +} + +static FModifierTypeInfo FMI_ENVELOPE = { + FMODIFIER_TYPE_ENVELOPE, /* type */ + sizeof(FMod_Envelope), /* size */ + FMI_TYPE_REPLACE_VALUES, /* action type */ + 0, /* requirements */ + "Envelope", /* name */ + "FMod_Envelope", /* struct name */ + fcm_envelope_free, /* free data */ + fcm_envelope_copy, /* copy data */ + fcm_envelope_new_data, /* new data */ + fcm_envelope_verify, /* verify */ + NULL, /* evaluate time */ + fcm_envelope_evaluate /* evaluate */ +}; + +/* Cycles F-Curve Modifier --------------------------- */ + +/* This modifier changes evaltime to something that exists within the curve's frame-range, + * then re-evaluates modifier stack up to this point using the new time. This re-entrant behaviour + * is very likely to be more time-consuming than the original approach... (which was tighly integrated into + * the calculation code...). + * + * NOTE: this needs to be at the start of the stack to be of use, as it needs to know the extents of the keyframes/sample-data + * Possible TODO - store length of cycle information that can be initialised from the extents of the keyframes/sample-data, and adjusted + * as appropriate + */ + +/* temp data used during evaluation */ +typedef struct tFCMED_Cycles { + float cycyofs; /* y-offset to apply */ +} tFCMED_Cycles; + +static void fcm_cycles_new_data (void *mdata) +{ + FMod_Cycles *data= (FMod_Cycles *)mdata; + + /* turn on cycles by default */ + data->before_mode= data->after_mode= FCM_EXTRAPOLATE_CYCLIC; +} + +static float fcm_cycles_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime) +{ + FMod_Cycles *data= (FMod_Cycles *)fcm->data; + float prevkey[2], lastkey[2], cycyofs=0.0f; + short side=0, mode=0; + int cycles=0; + + /* check if modifier is first in stack, otherwise disable ourself... */ + // FIXME... + if (fcm->prev) { + fcm->flag |= FMODIFIER_FLAG_DISABLED; + return evaltime; + } + + /* calculate new evaltime due to cyclic interpolation */ + if (fcu && fcu->bezt) { + BezTriple *prevbezt= fcu->bezt; + BezTriple *lastbezt= prevbezt + fcu->totvert-1; + + prevkey[0]= prevbezt->vec[1][0]; + prevkey[1]= prevbezt->vec[1][1]; + + lastkey[0]= lastbezt->vec[1][0]; + lastkey[1]= lastbezt->vec[1][1]; + } + else if (fcu && fcu->fpt) { + FPoint *prevfpt= fcu->fpt; + FPoint *lastfpt= prevfpt + fcu->totvert-1; + + prevkey[0]= prevfpt->vec[0]; + prevkey[1]= prevfpt->vec[1]; + + lastkey[0]= lastfpt->vec[0]; + lastkey[1]= lastfpt->vec[1]; + } + else + return evaltime; + + /* check if modifier will do anything + * 1) if in data range, definitely don't do anything + * 2) if before first frame or after last frame, make sure some cycling is in use + */ + if (evaltime < prevkey[0]) { + if (data->before_mode) { + side= -1; + mode= data->before_mode; + cycles= data->before_cycles; + } + } + else if (evaltime > lastkey[0]) { + if (data->after_mode) { + side= 1; + mode= data->after_mode; + cycles= data->after_cycles; + } + } + if ELEM(0, side, mode) + return evaltime; + + /* find relative place within a cycle */ + { + float cycdx=0, cycdy=0, ofs=0; + float cycle= 0; + + /* ofs is start frame of cycle */ + ofs= prevkey[0]; + + /* calculate period and amplitude (total height) of a cycle */ + cycdx= lastkey[0] - prevkey[0]; + cycdy= lastkey[1] - prevkey[1]; + + /* check if cycle is infinitely small, to be point of being impossible to use */ + if (cycdx == 0) + return evaltime; + + /* calculate the 'number' of the cycle */ + cycle= ((float)side * (evaltime - ofs) / cycdx); + + /* check that cyclic is still enabled for the specified time */ + if (cycles == 0) { + /* catch this case so that we don't exit when we have cycles=0 + * as this indicates infinite cycles... + */ + } + else if (cycle > (cycles+1)) { + /* we are too far away from range to evaluate + * TODO: but we should still hold last value... + */ + return evaltime; + } + + /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */ + if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) { + cycyofs = (float)floor((evaltime - ofs) / cycdx); + cycyofs *= cycdy; + } + + /* calculate where in the cycle we are (overwrite evaltime to reflect this) */ + if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)(cycle) % 2)) { + /* when 'mirror' option is used and cycle number is odd, this cycle is played in reverse + * - for 'before' extrapolation, we need to flip in a different way, otherwise values past + * then end of the curve get referenced (result of fmod will be negative, and with different phase) + */ + if (side < 0) + evaltime= (float)(prevkey[0] - fmod(evaltime-ofs, cycdx)); + else + evaltime= (float)(lastkey[0] - fmod(evaltime-ofs, cycdx)); + } + else { + /* the cycle is played normally... */ + evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs); + } + if (evaltime < ofs) evaltime += cycdx; + } + + /* store temp data if needed */ + if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) { + tFCMED_Cycles *edata; + + /* for now, this is just a float, but we could get more stuff... */ + fcm->edata= edata= MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles"); + edata->cycyofs= cycyofs; + } + + /* return the new frame to evaluate */ + return evaltime; +} + +static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ + tFCMED_Cycles *edata= (tFCMED_Cycles *)fcm->edata; + + /* use temp data */ + if (edata) { + /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */ + *cvalue += edata->cycyofs; + + /* free temp data */ + MEM_freeN(edata); + fcm->edata= NULL; + } +} + +static FModifierTypeInfo FMI_CYCLES = { + FMODIFIER_TYPE_CYCLES, /* type */ + sizeof(FMod_Cycles), /* size */ + FMI_TYPE_EXTRAPOLATION, /* action type */ + FMI_REQUIRES_ORIGINAL_DATA, /* requirements */ + "Cycles", /* name */ + "FMod_Cycles", /* struct name */ + NULL, /* free data */ + NULL, /* copy data */ + fcm_cycles_new_data, /* new data */ + NULL /*fcm_cycles_verify*/, /* verify */ + fcm_cycles_time, /* evaluate time */ + fcm_cycles_evaluate /* evaluate */ +}; + +/* Noise F-Curve Modifier --------------------------- */ + +static void fcm_noise_new_data (void *mdata) +{ + FMod_Noise *data= (FMod_Noise *)mdata; + + /* defaults */ + data->size= 1.0f; + data->strength= 1.0f; + data->phase= 1.0f; + data->depth = 0; + data->modification = FCM_NOISE_MODIF_REPLACE; +} + +static void fcm_noise_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ + FMod_Noise *data= (FMod_Noise *)fcm->data; + float noise; + + noise = BLI_turbulence(data->size, evaltime, data->phase, 0.f, data->depth); + + switch (data->modification) { + case FCM_NOISE_MODIF_ADD: + *cvalue= *cvalue + noise * data->strength; + break; + case FCM_NOISE_MODIF_SUBTRACT: + *cvalue= *cvalue - noise * data->strength; + break; + case FCM_NOISE_MODIF_MULTIPLY: + *cvalue= *cvalue * noise * data->strength; + break; + case FCM_NOISE_MODIF_REPLACE: + default: + *cvalue= *cvalue + (noise - 0.5f) * data->strength; + break; + } +} + +static FModifierTypeInfo FMI_NOISE = { + FMODIFIER_TYPE_NOISE, /* type */ + sizeof(FMod_Noise), /* size */ + FMI_TYPE_REPLACE_VALUES, /* action type */ + 0, /* requirements */ + "Noise", /* name */ + "FMod_Noise", /* struct name */ + NULL, /* free data */ + NULL, /* copy data */ + fcm_noise_new_data, /* new data */ + NULL /*fcm_noise_verify*/, /* verify */ + NULL, /* evaluate time */ + fcm_noise_evaluate /* evaluate */ +}; + +/* Filter F-Curve Modifier --------------------------- */ + +#if 0 // XXX not yet implemented +static FModifierTypeInfo FMI_FILTER = { + FMODIFIER_TYPE_FILTER, /* type */ + sizeof(FMod_Filter), /* size */ + FMI_TYPE_REPLACE_VALUES, /* action type */ + 0, /* requirements */ + "Filter", /* name */ + "FMod_Filter", /* struct name */ + NULL, /* free data */ + NULL, /* copy data */ + NULL, /* new data */ + NULL /*fcm_filter_verify*/, /* verify */ + NULL, /* evlauate time */ + fcm_filter_evaluate /* evaluate */ +}; +#endif // XXX not yet implemented + + +/* Python F-Curve Modifier --------------------------- */ + +static void fcm_python_free (FModifier *fcm) +{ + FMod_Python *data= (FMod_Python *)fcm->data; + + /* id-properties */ + IDP_FreeProperty(data->prop); + MEM_freeN(data->prop); +} + +static void fcm_python_new_data (void *mdata) +{ + FMod_Python *data= (FMod_Python *)mdata; + + /* everything should be set correctly by calloc, except for the prop->type constant.*/ + data->prop = MEM_callocN(sizeof(IDProperty), "PyFModifierProps"); + data->prop->type = IDP_GROUP; +} + +static void fcm_python_copy (FModifier *fcm, FModifier *src) +{ + FMod_Python *pymod = (FMod_Python *)fcm->data; + FMod_Python *opymod = (FMod_Python *)src->data; + + pymod->prop = IDP_CopyProperty(opymod->prop); +} + +static void fcm_python_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ +#ifndef DISABLE_PYTHON + //FMod_Python *data= (FMod_Python *)fcm->data; + + /* FIXME... need to implement this modifier... + * It will need it execute a script using the custom properties + */ +#endif /* DISABLE_PYTHON */ +} + +static FModifierTypeInfo FMI_PYTHON = { + FMODIFIER_TYPE_PYTHON, /* type */ + sizeof(FMod_Python), /* size */ + FMI_TYPE_GENERATE_CURVE, /* action type */ + FMI_REQUIRES_RUNTIME_CHECK, /* requirements */ + "Python", /* name */ + "FMod_Python", /* struct name */ + fcm_python_free, /* free data */ + fcm_python_copy, /* copy data */ + fcm_python_new_data, /* new data */ + NULL /*fcm_python_verify*/, /* verify */ + NULL /*fcm_python_time*/, /* evaluate time */ + fcm_python_evaluate /* evaluate */ +}; + + +/* Limits F-Curve Modifier --------------------------- */ + +static float fcm_limits_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime) +{ + FMod_Limits *data= (FMod_Limits *)fcm->data; + + /* check for the time limits */ + if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin)) + return data->rect.xmin; + if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax)) + return data->rect.xmax; + + /* modifier doesn't change time */ + return evaltime; +} + +static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ + FMod_Limits *data= (FMod_Limits *)fcm->data; + + /* value limits now */ + if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin)) + *cvalue= data->rect.ymin; + if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax)) + *cvalue= data->rect.ymax; +} + +static FModifierTypeInfo FMI_LIMITS = { + FMODIFIER_TYPE_LIMITS, /* type */ + sizeof(FMod_Limits), /* size */ + FMI_TYPE_GENERATE_CURVE, /* action type */ /* XXX... err... */ + FMI_REQUIRES_RUNTIME_CHECK, /* requirements */ + "Limits", /* name */ + "FMod_Limits", /* struct name */ + NULL, /* free data */ + NULL, /* copy data */ + NULL, /* new data */ + NULL, /* verify */ + fcm_limits_time, /* evaluate time */ + fcm_limits_evaluate /* evaluate */ +}; + +/* F-Curve Modifier API --------------------------- */ +/* All of the F-Curve Modifier api functions use FModifierTypeInfo structs to carry out + * and operations that involve F-Curve modifier specific code. + */ + +/* These globals only ever get directly accessed in this file */ +static FModifierTypeInfo *fmodifiersTypeInfo[FMODIFIER_NUM_TYPES]; +static short FMI_INIT= 1; /* when non-zero, the list needs to be updated */ + +/* This function only gets called when FMI_INIT is non-zero */ +static void fmods_init_typeinfo () +{ + fmodifiersTypeInfo[0]= NULL; /* 'Null' F-Curve Modifier */ + fmodifiersTypeInfo[1]= &FMI_GENERATOR; /* Generator F-Curve Modifier */ + fmodifiersTypeInfo[2]= &FMI_FN_GENERATOR; /* Built-In Function Generator F-Curve Modifier */ + fmodifiersTypeInfo[3]= &FMI_ENVELOPE; /* Envelope F-Curve Modifier */ + fmodifiersTypeInfo[4]= &FMI_CYCLES; /* Cycles F-Curve Modifier */ + fmodifiersTypeInfo[5]= &FMI_NOISE; /* Apply-Noise F-Curve Modifier */ + fmodifiersTypeInfo[6]= NULL/*&FMI_FILTER*/; /* Filter F-Curve Modifier */ // XXX unimplemented + fmodifiersTypeInfo[7]= &FMI_PYTHON; /* Custom Python F-Curve Modifier */ + fmodifiersTypeInfo[8]= &FMI_LIMITS; /* Limits F-Curve Modifier */ +} + +/* This function should be used for getting the appropriate type-info when only + * a F-Curve modifier type is known + */ +FModifierTypeInfo *get_fmodifier_typeinfo (int type) +{ + /* initialise the type-info list? */ + if (FMI_INIT) { + fmods_init_typeinfo(); + FMI_INIT = 0; + } + + /* only return for valid types */ + if ( (type >= FMODIFIER_TYPE_NULL) && + (type <= FMODIFIER_NUM_TYPES ) ) + { + /* there shouldn't be any segfaults here... */ + return fmodifiersTypeInfo[type]; + } + else { + printf("No valid F-Curve Modifier type-info data available. Type = %i \n", type); + } + + return NULL; +} + +/* This function should always be used to get the appropriate type-info, as it + * has checks which prevent segfaults in some weird cases. + */ +FModifierTypeInfo *fmodifier_get_typeinfo (FModifier *fcm) +{ + /* only return typeinfo for valid modifiers */ + if (fcm) + return get_fmodifier_typeinfo(fcm->type); + else + return NULL; +} + +/* API --------------------------- */ + +/* Add a new F-Curve Modifier to the given F-Curve of a certain type */ +FModifier *add_fmodifier (ListBase *modifiers, int type) +{ + FModifierTypeInfo *fmi= get_fmodifier_typeinfo(type); + FModifier *fcm; + + /* sanity checks */ + if ELEM(NULL, modifiers, fmi) + return NULL; + + /* special checks for whether modifier can be added */ + if ((modifiers->first) && (type == FMODIFIER_TYPE_CYCLES)) { + /* cycles modifier must be first in stack, so for now, don't add if it can't be */ + // TODO: perhaps there is some better way, but for now, + printf("Error: Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be first in stack. \n"); + return NULL; + } + + /* add modifier itself */ + fcm= MEM_callocN(sizeof(FModifier), "F-Curve Modifier"); + fcm->type = type; + fcm->flag = FMODIFIER_FLAG_EXPANDED; + BLI_addtail(modifiers, fcm); + + /* add modifier's data */ + fcm->data= MEM_callocN(fmi->size, fmi->structName); + + /* init custom settings if necessary */ + if (fmi->new_data) + fmi->new_data(fcm->data); + + /* return modifier for further editing */ + return fcm; +} + +/* Duplicate all of the F-Modifiers in the Modifier stacks */ +void copy_fmodifiers (ListBase *dst, ListBase *src) +{ + FModifier *fcm, *srcfcm; + + if ELEM(NULL, dst, src) + return; + + dst->first= dst->last= NULL; + BLI_duplicatelist(dst, src); + + for (fcm=dst->first, srcfcm=src->first; fcm && srcfcm; srcfcm=srcfcm->next, fcm=fcm->next) { + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + + /* make a new copy of the F-Modifier's data */ + fcm->data = MEM_dupallocN(fcm->data); + + /* only do specific constraints if required */ + if (fmi && fmi->copy_data) + fmi->copy_data(fcm, srcfcm); + } +} + +/* Remove and free the given F-Modifier from the given stack */ +void remove_fmodifier (ListBase *modifiers, FModifier *fcm) +{ + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + + /* sanity check */ + if (fcm == NULL) + return; + + /* free modifier's special data (stored inside fcm->data) */ + if (fcm->data) { + if (fmi && fmi->free_data) + fmi->free_data(fcm); + + /* free modifier's data (fcm->data) */ + MEM_freeN(fcm->data); + } + + /* remove modifier from stack */ + if (modifiers) + BLI_freelinkN(modifiers, fcm); + else { + // XXX this case can probably be removed some day, as it shouldn't happen... + printf("remove_fmodifier() - no modifier stack given \n"); + MEM_freeN(fcm); + } +} + +/* Remove all of a given F-Curve's modifiers */ +void free_fmodifiers (ListBase *modifiers) +{ + FModifier *fcm, *fmn; + + /* sanity check */ + if (modifiers == NULL) + return; + + /* free each modifier in order - modifier is unlinked from list and freed */ + for (fcm= modifiers->first; fcm; fcm= fmn) { + fmn= fcm->next; + remove_fmodifier(modifiers, fcm); + } +} + +/* Find the active F-Modifier */ +FModifier *find_active_fmodifier (ListBase *modifiers) +{ + FModifier *fcm; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return NULL; + + /* loop over modifiers until 'active' one is found */ + for (fcm= modifiers->first; fcm; fcm= fcm->next) { + if (fcm->flag & FMODIFIER_FLAG_ACTIVE) + return fcm; + } + + /* no modifier is active */ + return NULL; +} + +/* Set the active F-Modifier */ +void set_active_fmodifier (ListBase *modifiers, FModifier *fcm) +{ + FModifier *fm; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return; + + /* deactivate all, and set current one active */ + for (fm= modifiers->first; fm; fm= fm->next) + fm->flag &= ~FMODIFIER_FLAG_ACTIVE; + + /* make given modifier active */ + if (fcm) + fcm->flag |= FMODIFIER_FLAG_ACTIVE; +} + +/* Do we have any modifiers which match certain criteria + * - mtype - type of modifier (if 0, doesn't matter) + * - acttype - type of action to perform (if -1, doesn't matter) + */ +short list_has_suitable_fmodifier (ListBase *modifiers, int mtype, short acttype) +{ + FModifier *fcm; + + /* if there are no specific filtering criteria, just skip */ + if ((mtype == 0) && (acttype == 0)) + return (modifiers && modifiers->first); + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return 0; + + /* find the first mdifier fitting these criteria */ + for (fcm= modifiers->first; fcm; fcm= fcm->next) { + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + short mOk=1, aOk=1; /* by default 1, so that when only one test, won't fail */ + + /* check if applicable ones are fullfilled */ + if (mtype) + mOk= (fcm->type == mtype); + if (acttype > -1) + aOk= (fmi->acttype == acttype); + + /* if both are ok, we've found a hit */ + if (mOk && aOk) + return 1; + } + + /* no matches */ + return 0; +} + +/* Evaluation API --------------------------- */ + +/* evaluate time modifications imposed by some F-Curve Modifiers + * - this step acts as an optimisation to prevent the F-Curve stack being evaluated + * several times by modifiers requesting the time be modified, as the final result + * would have required using the modified time + * - modifiers only ever recieve the unmodified time, as subsequent modifiers should be + * working on the 'global' result of the modified curve, not some localised segment, + * so nevaltime gets set to whatever the last time-modifying modifier likes... + * - we start from the end of the stack, as only the last one matters for now + */ +float evaluate_time_fmodifiers (ListBase *modifiers, FCurve *fcu, float cvalue, float evaltime) +{ + FModifier *fcm; + float m_evaltime= evaltime; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->last) + return evaltime; + + /* find the first modifier from end of stack that modifies time, and calculate the time the modifier + * would calculate time at + */ + for (fcm= modifiers->last; fcm; fcm= fcm->prev) { + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + + /* only evaluate if there's a callback for this */ + // TODO: implement the 'influence' control feature... + if (fmi && fmi->evaluate_modifier_time) { + if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0) + m_evaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime); + break; + } + } + + /* return the modified evaltime */ + return m_evaltime; +} + +/* Evalautes the given set of F-Curve Modifiers using the given data + * Should only be called after evaluate_time_fmodifiers() has been called... + */ +void evaluate_value_fmodifiers (ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime) +{ + FModifier *fcm; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return; + + /* evaluate modifiers */ + for (fcm= modifiers->first; fcm; fcm= fcm->next) { + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + + /* only evaluate if there's a callback for this */ + // TODO: implement the 'influence' control feature... + if (fmi && fmi->evaluate_modifier) { + if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0) + fmi->evaluate_modifier(fcu, fcm, cvalue, evaltime); + } + } +} + +/* ---------- */ + +/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined + * by start and end (inclusive). + */ +void fcurve_bake_modifiers (FCurve *fcu, int start, int end) +{ + ChannelDriver *driver; + + /* sanity checks */ + // TODO: make these tests report errors using reports not printf's + if ELEM(NULL, fcu, fcu->modifiers.first) { + printf("Error: No F-Curve with F-Curve Modifiers to Bake\n"); + return; + } + + /* temporarily, disable driver while we sample, so that they don't influence the outcome */ + driver= fcu->driver; + fcu->driver= NULL; + + /* bake the modifiers, by sampling the curve at each frame */ + fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve); + + /* free the modifiers now */ + free_fmodifiers(&fcu->modifiers); + + /* restore driver */ + fcu->driver= driver; +} diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 54618813a0b..cf7e486613b 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -58,6 +58,7 @@ #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_nla_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" #include "DNA_particle_types.h" @@ -85,6 +86,7 @@ #include "BKE_library.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_nla.h" #include "BKE_object.h" @@ -825,6 +827,10 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname char buf[512]; int dummy_index= 0; + /* hack: if constname is set, we can only be dealing with an Constraint curve */ + if (constname) + blocktype= ID_CO; + /* get property name based on blocktype */ switch (blocktype) { case ID_OB: /* object */ @@ -840,7 +846,7 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname break; case ID_CO: /* constraint */ - propname= constraint_adrcodes_to_paths(adrcode, &dummy_index); + propname= constraint_adrcodes_to_paths(adrcode, &dummy_index); break; case ID_TE: /* texture */ @@ -870,7 +876,10 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname /* XXX problematic blocktypes */ case ID_CU: /* curve */ - propname= "speed"; // XXX this was a 'dummy curve' that didn't really correspond to any real var... + /* this used to be a 'dummy' curve which got evaluated on the fly... + * now we've got real var for this! + */ + propname= "eval_time"; break; case ID_SEQ: /* sequencer strip */ @@ -1144,7 +1153,7 @@ static void icu_to_fcurves (ListBase *groups, ListBase *list, IpoCurve *icu, cha /* Add a new FModifier (Cyclic) instead of setting extend value * as that's the new equivilant of that option. */ - FModifier *fcm= fcurve_add_modifier(fcu, FMODIFIER_TYPE_CYCLES); + FModifier *fcm= add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES); FMod_Cycles *data= (FMod_Cycles *)fcm->data; /* if 'offset' one is in use, set appropriate settings */ @@ -1463,6 +1472,87 @@ static void action_to_animdata (ID *id, bAction *act) action_to_animato(act, &adt->action->groups, &adt->action->curves, &adt->drivers); } +/* ------------------------- */ + +// TODO: +// - NLA group duplicators info +// - NLA curve/stride modifiers... + +/* Convert NLA-Strip to new system */ +static void nlastrips_to_animdata (ID *id, ListBase *strips) +{ + AnimData *adt= BKE_animdata_from_id(id); + NlaTrack *nlt = NULL; + NlaStrip *strip; + bActionStrip *as, *asn; + + /* for each one of the original strips, convert to a new strip and free the old... */ + for (as= strips->first; as; as= asn) { + asn= as->next; + + /* this old strip is only worth something if it had an action... */ + if (as->act) { + /* convert Action data (if not yet converted), storing the results in the same Action */ + action_to_animato(as->act, &as->act->groups, &as->act->curves, &adt->drivers); + + /* create a new-style NLA-strip which references this Action, then copy over relevant settings */ + { + /* init a new strip, and assign the action to it + * - no need to muck around with the user-counts, since this is just + * passing over the ref to the new owner, not creating an additional ref + */ + strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip"); + strip->act= as->act; + + /* endpoints */ + strip->start= as->start; + strip->end= as->end; + strip->actstart= as->actstart; + strip->actend= as->actend; + + /* action reuse */ + strip->repeat= as->repeat; + strip->scale= as->scale; + if (as->flag & ACTSTRIP_LOCK_ACTION) strip->flag |= NLASTRIP_FLAG_SYNC_LENGTH; + + /* blending */ + strip->blendin= as->blendin; + strip->blendout= as->blendout; + strip->blendmode= (as->mode==ACTSTRIPMODE_ADD) ? NLASTRIP_MODE_ADD : NLASTRIP_MODE_REPLACE; + if (as->flag & ACTSTRIP_AUTO_BLENDS) strip->flag |= NLASTRIP_FLAG_AUTO_BLENDS; + + /* assorted setting flags */ + if (as->flag & ACTSTRIP_SELECT) strip->flag |= NLASTRIP_FLAG_SELECT; + if (as->flag & ACTSTRIP_ACTIVE) strip->flag |= NLASTRIP_FLAG_ACTIVE; + + if (as->flag & ACTSTRIP_MUTE) strip->flag |= NLASTRIP_FLAG_MUTED; + if (as->flag & ACTSTRIP_REVERSE) strip->flag |= NLASTRIP_FLAG_REVERSE; + + /* by default, we now always extrapolate, while in the past this was optional */ + if ((as->flag & ACTSTRIP_HOLDLASTFRAME)==0) + strip->extendmode= NLASTRIP_EXTEND_NOTHING; + } + + /* try to add this strip to the current NLA-Track (i.e. the 'last' one on the stack atm) */ + if (BKE_nlatrack_add_strip(nlt, strip) == 0) { + /* trying to add to the current failed (no space), + * so add a new track to the stack, and add to that... + */ + nlt= add_nlatrack(adt, NULL); + BKE_nlatrack_add_strip(nlt, strip); + } + } + + /* modifiers */ + // FIXME: for now, we just free them... + if (as->modifiers.first) + BLI_freelistN(&as->modifiers); + + /* free the old strip */ + BLI_freelinkN(strips, as); + } +} + /* *************************************************** */ /* External API - Only Called from do_versions() */ @@ -1509,7 +1599,30 @@ void do_versions_ipos_to_animato(Main *main) if (G.f & G_DEBUG) printf("\tconverting ob %s \n", id->name+2); /* check if object has any animation data */ - if ((ob->ipo) || (ob->action) || (ob->nlastrips.first)) { + if (ob->nlastrips.first) { + /* Add AnimData block */ + adt= BKE_id_add_animdata(id); + + /* IPO first to take into any non-NLA'd Object Animation */ + if (ob->ipo) { + ipo_to_animdata(id, ob->ipo, NULL, NULL); + + ob->ipo->id.us--; + ob->ipo= NULL; + } + + /* Action is skipped since it'll be used by some strip in the NLA anyway, + * causing errors with evaluation in the new evaluation pipeline + */ + if (ob->action) { + ob->action->id.us--; + ob->action= NULL; + } + + /* finally NLA */ + nlastrips_to_animdata(id, &ob->nlastrips); + } + else if ((ob->ipo) || (ob->action)) { /* Add AnimData block */ adt= BKE_id_add_animdata(id); @@ -1530,9 +1643,6 @@ void do_versions_ipos_to_animato(Main *main) ob->ipo->id.us--; ob->ipo= NULL; } - - /* finally NLA */ - // XXX todo... for now, new NLA code not hooked up yet, so keep old stuff (but not for too long!) } /* check PoseChannels for constraints with local data */ diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index dc2bf26759f..457df9be7a9 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -17,171 +17,1433 @@ * 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. + * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung * All rights reserved. * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Joshua Leung (full recode) * * ***** END GPL LICENSE BLOCK ***** */ #include +#include +#include +#include +#include +#include #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" -#include "DNA_space_types.h" -#include "DNA_nla_types.h" +#include "DNA_anim_types.h" #include "DNA_action_types.h" -#include "DNA_ID.h" -#include "DNA_ipo_types.h" -#include "DNA_object_types.h" -#include "BKE_nla.h" +#include "BKE_animsys.h" #include "BKE_action.h" +#include "BKE_fcurve.h" +#include "BKE_nla.h" #include "BKE_blender.h" #include "BKE_library.h" -#include "BKE_object.h" /* for convert_action_to_strip(ob) */ +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "RNA_access.h" +#include "nla_private.h" #ifdef HAVE_CONFIG_H #include #endif -/* NOTE: in group.c the strips get copied for group-nla override, this assumes - that strips are one single block, without additional data to be copied */ -void copy_actionstrip (bActionStrip **dst, bActionStrip **src){ - bActionStrip *dstrip; - bActionStrip *sstrip = *src; +/* *************************************************** */ +/* Data Management */ - if (!*src){ - *dst=NULL; +/* Freeing ------------------------------------------- */ + +/* Remove the given NLA strip from the NLA track it occupies, free the strip's data, + * and the strip itself. + */ +void free_nlastrip (ListBase *strips, NlaStrip *strip) +{ + NlaStrip *cs, *csn; + + /* sanity checks */ + if (strip == NULL) return; + + /* free child-strips */ + for (cs= strip->strips.first; cs; cs= csn) { + csn= cs->next; + free_nlastrip(&strip->strips, cs); } - - *dst = MEM_dupallocN(sstrip); - - dstrip = *dst; - if (dstrip->act) - dstrip->act->id.us++; - - if (dstrip->ipo) - dstrip->ipo->id.us++; + + /* remove reference to action */ + if (strip->act) + strip->act->id.us--; + + /* free remapping info */ + //if (strip->remap) + // BKE_animremap_free(); - if (dstrip->modifiers.first) { - BLI_duplicatelist (&dstrip->modifiers, &sstrip->modifiers); - } + /* free own F-Curves */ + free_fcurves(&strip->fcurves); + /* free own F-Modifiers */ + free_fmodifiers(&strip->modifiers); + + /* free the strip itself */ + if (strips) + BLI_freelinkN(strips, strip); + else + MEM_freeN(strip); } -void copy_nlastrips (ListBase *dst, ListBase *src) +/* Remove the given NLA track from the set of NLA tracks, free the track's data, + * and the track itself. + */ +void free_nlatrack (ListBase *tracks, NlaTrack *nlt) { - bActionStrip *strip; - - dst->first=dst->last=NULL; - - BLI_duplicatelist (dst, src); - - /* Update specific data */ - if (!dst->first) + NlaStrip *strip, *stripn; + + /* sanity checks */ + if (nlt == NULL) return; + + /* free strips */ + for (strip= nlt->strips.first; strip; strip= stripn) { + stripn= strip->next; + free_nlastrip(&nlt->strips, strip); + } + + /* free NLA track itself now */ + if (tracks) + BLI_freelinkN(tracks, nlt); + else + MEM_freeN(nlt); +} - for (strip = dst->first; strip; strip=strip->next){ - if (strip->act) - strip->act->id.us++; - if (strip->ipo) - strip->ipo->id.us++; - if (strip->modifiers.first) { - ListBase listb; - BLI_duplicatelist (&listb, &strip->modifiers); - strip->modifiers= listb; +/* Free the elements of type NLA Tracks provided in the given list, but do not free + * the list itself since that is not free-standing + */ +void free_nladata (ListBase *tracks) +{ + NlaTrack *nlt, *nltn; + + /* sanity checks */ + if ELEM(NULL, tracks, tracks->first) + return; + + /* free tracks one by one */ + for (nlt= tracks->first; nlt; nlt= nltn) { + nltn= nlt->next; + free_nlatrack(tracks, nlt); + } + + /* clear the list's pointers to be safe */ + tracks->first= tracks->last= NULL; +} + +/* Copying ------------------------------------------- */ + +/* Copy NLA strip */ +NlaStrip *copy_nlastrip (NlaStrip *strip) +{ + NlaStrip *strip_d; + NlaStrip *cs, *cs_d; + + /* sanity check */ + if (strip == NULL) + return NULL; + + /* make a copy */ + strip_d= MEM_dupallocN(strip); + strip_d->next= strip_d->prev= NULL; + + /* increase user-count of action */ + if (strip_d->act) + strip_d->act->id.us++; + + /* copy F-Curves and modifiers */ + copy_fcurves(&strip_d->fcurves, &strip->fcurves); + copy_fmodifiers(&strip_d->modifiers, &strip->modifiers); + + /* make a copy of all the child-strips, one at a time */ + strip_d->strips.first= strip_d->strips.last= NULL; + + for (cs= strip->strips.first; cs; cs= cs->next) { + cs_d= copy_nlastrip(cs); + BLI_addtail(&strip_d->strips, cs_d); + } + + /* return the strip */ + return strip_d; +} + +/* Copy NLA Track */ +NlaTrack *copy_nlatrack (NlaTrack *nlt) +{ + NlaStrip *strip, *strip_d; + NlaTrack *nlt_d; + + /* sanity check */ + if (nlt == NULL) + return NULL; + + /* make a copy */ + nlt_d= MEM_dupallocN(nlt); + nlt_d->next= nlt_d->prev= NULL; + + /* make a copy of all the strips, one at a time */ + nlt_d->strips.first= nlt_d->strips.last= NULL; + + for (strip= nlt->strips.first; strip; strip= strip->next) { + strip_d= copy_nlastrip(strip); + BLI_addtail(&nlt_d->strips, strip_d); + } + + /* return the copy */ + return nlt_d; +} + +/* Copy all NLA data */ +void copy_nladata (ListBase *dst, ListBase *src) +{ + NlaTrack *nlt, *nlt_d; + + /* sanity checks */ + if ELEM(NULL, dst, src) + return; + + /* copy each NLA-track, one at a time */ + for (nlt= src->first; nlt; nlt= nlt->next) { + /* make a copy, and add the copy to the destination list */ + nlt_d= copy_nlatrack(nlt); + BLI_addtail(dst, nlt_d); + } +} + +/* Adding ------------------------------------------- */ + +/* Add a NLA Track to the given AnimData + * - prev: NLA-Track to add the new one after + */ +NlaTrack *add_nlatrack (AnimData *adt, NlaTrack *prev) +{ + NlaTrack *nlt; + + /* sanity checks */ + if (adt == NULL) + return NULL; + + /* allocate new track */ + nlt= MEM_callocN(sizeof(NlaTrack), "NlaTrack"); + + /* set settings requiring the track to not be part of the stack yet */ + nlt->flag = NLATRACK_SELECTED; + nlt->index= BLI_countlist(&adt->nla_tracks); + + /* add track to stack, and make it the active one */ + if (prev) + BLI_insertlinkafter(&adt->nla_tracks, prev, nlt); + else + BLI_addtail(&adt->nla_tracks, nlt); + BKE_nlatrack_set_active(&adt->nla_tracks, nlt); + + /* must have unique name, but we need to seed this */ + sprintf(nlt->name, "NlaTrack"); + BLI_uniquename(&adt->nla_tracks, nlt, "NlaTrack", '.', offsetof(NlaTrack, name), 64); + + /* return the new track */ + return nlt; +} + +/* Add a NLA Strip referencing the given Action */ +NlaStrip *add_nlastrip (bAction *act) +{ + NlaStrip *strip; + + /* sanity checks */ + if (act == NULL) + return NULL; + + /* allocate new strip */ + strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip"); + + /* generic settings + * - selected flag to highlight this to the user + * - auto-blends to ensure that blend in/out values are automatically + * determined by overlaps of strips + * - (XXX) synchronisation of strip-length in accordance with changes to action-length + * is not done though, since this should only really happens in editmode for strips now + * though this decision is still subject to further review... + */ + strip->flag = NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_AUTO_BLENDS; + + /* assign the action reference */ + strip->act= act; + id_us_plus(&act->id); + + /* determine initial range + * - strip length cannot be 0... ever... + */ + calc_action_range(strip->act, &strip->actstart, &strip->actend, 1); + + strip->start = strip->actstart; + strip->end = (IS_EQ(strip->actstart, strip->actend)) ? (strip->actstart + 1.0f): (strip->actend); + + /* strip should be referenced as-is */ + strip->scale= 1.0f; + strip->repeat = 1.0f; + + /* return the new strip */ + return strip; +} + +/* Add new NLA-strip to the top of the NLA stack - i.e. into the last track if space, or a new one otherwise */ +NlaStrip *add_nlastrip_to_stack (AnimData *adt, bAction *act) +{ + NlaStrip *strip; + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, adt, act) + return NULL; + + /* create a new NLA strip */ + strip= add_nlastrip(act); + if (strip == NULL) + return NULL; + + /* firstly try adding strip to last track, but if that fails, add to a new track */ + if (BKE_nlatrack_add_strip(adt->nla_tracks.last, strip) == 0) { + /* trying to add to the last track failed (no track or no space), + * so add a new track to the stack, and add to that... + */ + nlt= add_nlatrack(adt, NULL); + BKE_nlatrack_add_strip(nlt, strip); + } + + /* automatically name it too */ + BKE_nlastrip_validate_name(adt, strip); + + /* returns the strip added */ + return strip; +} + +/* *************************************************** */ +/* NLA Evaluation <-> Editing Stuff */ + +/* Strip Mapping ------------------------------------- */ + +/* non clipped mapping for strip-time <-> global time (for Action-Clips) + * invert = convert action-strip time to global time + */ +static float nlastrip_get_frame_actionclip (NlaStrip *strip, float cframe, short mode) +{ + float actlength, repeat, scale; + + /* get number of repeats */ + if (IS_EQ(strip->repeat, 0.0f)) strip->repeat = 1.0f; + repeat = strip->repeat; + + /* scaling */ + if (IS_EQ(strip->scale, 0.0f)) strip->scale= 1.0f; + scale = (float)fabs(strip->scale); /* scale must be positive - we've got a special flag for reversing */ + + /* length of referenced action */ + actlength = strip->actend - strip->actstart; + if (IS_EQ(actlength, 0.0f)) actlength = 1.0f; + + /* reversed = play strip backwards */ + if (strip->flag & NLASTRIP_FLAG_REVERSE) { + // FIXME: this won't work right with Graph Editor? + if (mode == NLATIME_CONVERT_MAP) { + return strip->end - scale*(cframe - strip->actstart); + } + else if (mode == NLATIME_CONVERT_UNMAP) { + int repeatsNum = (int)((cframe - strip->start) / (actlength * scale)); + + /* this method doesn't clip the values to lie within the action range only + * - the '(repeatsNum * actlength * scale)' compensates for the fmod(...) + * - the fmod(...) works in the same way as for eval + */ + return strip->actend - (repeatsNum * actlength * scale) + - (fmod(cframe - strip->start, actlength*scale) / scale); + } + else { + if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) { + /* this case prevents the motion snapping back to the first frame at the end of the strip + * by catching the case where repeats is a whole number, which means that the end of the strip + * could also be interpreted as the end of the start of a repeat + */ + return strip->actstart; + } + else { + /* - the 'fmod(..., actlength*scale)' is needed to get the repeats working + * - the '/ scale' is needed to ensure that scaling influences the timing within the repeat + */ + return strip->actend - fmod(cframe - strip->start, actlength*scale) / scale; + } + } + } + else { + if (mode == NLATIME_CONVERT_MAP) { + return strip->start + scale*(cframe - strip->actstart); + } + else if (mode == NLATIME_CONVERT_UNMAP) { + int repeatsNum = (int)((cframe - strip->start) / (actlength * scale)); + + /* this method doesn't clip the values to lie within the action range only + * - the '(repeatsNum * actlength * scale)' compensates for the fmod(...) + * - the fmod(...) works in the same way as for eval + */ + return strip->actstart + (repeatsNum * actlength * scale) + + (fmod(cframe - strip->start, actlength*scale) / scale); + } + else /* if (mode == NLATIME_CONVERT_EVAL) */{ + if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) { + /* this case prevents the motion snapping back to the first frame at the end of the strip + * by catching the case where repeats is a whole number, which means that the end of the strip + * could also be interpreted as the end of the start of a repeat + */ + return strip->actend; + } + else { + /* - the 'fmod(..., actlength*scale)' is needed to get the repeats working + * - the '/ scale' is needed to ensure that scaling influences the timing within the repeat + */ + return strip->actstart + fmod(cframe - strip->start, actlength*scale) / scale; + } } } } -/* from editnla, for convert_action_to_strip -- no UI code so should be ok here.. */ -void find_stridechannel(Object *ob, bActionStrip *strip) +/* non clipped mapping for strip-time <-> global time (for Transitions) + * invert = convert action-strip time to global time + */ +static float nlastrip_get_frame_transition (NlaStrip *strip, float cframe, short mode) { - if(ob && ob->pose) { - bPoseChannel *pchan; - for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) - if(pchan->flag & POSE_STRIDE) - break; - if(pchan) - BLI_strncpy(strip->stridechannel, pchan->name, 32); + float length; + + /* length of strip */ + length= strip->end - strip->start; + + /* reversed = play strip backwards */ + if (strip->flag & NLASTRIP_FLAG_REVERSE) { + if (mode == NLATIME_CONVERT_MAP) + return strip->end - (length * cframe); else - strip->stridechannel[0]= 0; + return (strip->end - cframe) / length; + } + else { + if (mode == NLATIME_CONVERT_MAP) + return (length * cframe) + strip->start; + else + return (cframe - strip->start) / length; } } -//called by convert_nla / bpy api with an object with the action to be converted to a new strip -bActionStrip *convert_action_to_strip (Object *ob) +/* non clipped mapping for strip-time <-> global time + * mode = eNlaTime_ConvertModes[] -> NLATIME_CONVERT_* + * + * only secure for 'internal' (i.e. within AnimSys evaluation) operations, + * but should not be directly relied on for stuff which interacts with editors + */ +float nlastrip_get_frame (NlaStrip *strip, float cframe, short mode) { - bActionStrip *nstrip; - - /* Make new actionstrip */ - nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip"); - - /* Link the action to the nstrip */ - nstrip->act = ob->action; - id_us_plus(&nstrip->act->id); - calc_action_range(nstrip->act, &nstrip->actstart, &nstrip->actend, 1); - nstrip->start = nstrip->actstart; - nstrip->end = nstrip->actend; - nstrip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION; - - find_stridechannel(ob, nstrip); - //set_active_strip(ob, nstrip); /* is in editnla as does UI calls */ - - nstrip->repeat = 1.0; - - if(ob->nlastrips.first == NULL) - ob->nlaflag |= OB_NLA_OVERRIDE; - - BLI_addtail(&ob->nlastrips, nstrip); - return nstrip; /* is created, malloced etc. here so is safe to just return the pointer? - this is needed for setting this active in UI, and probably useful for API too */ - + switch (strip->type) { + case NLASTRIP_TYPE_META: /* meta - for now, does the same as transition (is really just an empty container) */ + case NLASTRIP_TYPE_TRANSITION: /* transition */ + return nlastrip_get_frame_transition(strip, cframe, mode); + + case NLASTRIP_TYPE_CLIP: /* action-clip (default) */ + default: + return nlastrip_get_frame_actionclip(strip, cframe, mode); + } } -/* not strip itself! */ -void free_actionstrip(bActionStrip* strip) +/* Non clipped mapping for strip-time <-> global time + * mode = eNlaTime_ConvertModesp[] -> NLATIME_CONVERT_* + * + * Public API method - perform this mapping using the given AnimData block + * and perform any necessary sanity checks on the value + */ +float BKE_nla_tweakedit_remap (AnimData *adt, float cframe, short mode) { - if (!strip) + NlaStrip *strip; + + /* sanity checks + * - obviously we've got to have some starting data + * - when not in tweakmode, the active Action does not have any scaling applied :) + * - when in tweakmode, if the no-mapping flag is set, do not map + */ + if ((adt == NULL) || (adt->flag & ADT_NLA_EDIT_ON)==0 || (adt->flag & ADT_NLA_EDIT_NOMAP)) + return cframe; + + /* if the active-strip info has been stored already, access this, otherwise look this up + * and store for (very probable) future usage + */ + if (adt->actstrip == NULL) { + NlaTrack *nlt= BKE_nlatrack_find_active(&adt->nla_tracks); + adt->actstrip= BKE_nlastrip_find_active(nlt); + } + strip= adt->actstrip; + + /* sanity checks + * - in rare cases, we may not be able to find this strip for some reason (internal error) + * - for now, if the user has defined a curve to control the time, this correction cannot be performed + * reliably... + */ + if ((strip == NULL) || (strip->flag & NLASTRIP_FLAG_USR_TIME)) + return cframe; + + /* perform the correction now... */ + return nlastrip_get_frame(strip, cframe, mode); +} + +/* *************************************************** */ +/* NLA API */ + +/* List of Strips ------------------------------------ */ +/* (these functions are used for NLA-Tracks and also for nested/meta-strips) */ + +/* Check if there is any space in the given list to add the given strip */ +short BKE_nlastrips_has_space (ListBase *strips, float start, float end) +{ + NlaStrip *strip; + + /* sanity checks */ + if ((strips == NULL) || IS_EQ(start, end)) + return 0; + if (start > end) { + puts("BKE_nlastrips_has_space() error... start and end arguments swapped"); + SWAP(float, start, end); + } + + /* loop over NLA strips checking for any overlaps with this area... */ + for (strip= strips->first; strip; strip= strip->next) { + /* if start frame of strip is past the target end-frame, that means that + * we've gone past the window we need to check for, so things are fine + */ + if (strip->start > end) + return 1; + + /* if the end of the strip is greater than either of the boundaries, the range + * must fall within the extents of the strip + */ + if ((strip->end > start) || (strip->end > end)) + return 0; + } + + /* if we are still here, we haven't encountered any overlapping strips */ + return 1; +} + +/* Rearrange the strips in the track so that they are always in order + * (usually only needed after a strip has been moved) + */ +void BKE_nlastrips_sort_strips (ListBase *strips) +{ + ListBase tmp = {NULL, NULL}; + NlaStrip *strip, *sstrip, *stripn; + + /* sanity checks */ + if ELEM(NULL, strips, strips->first) return; - - if (strip->act){ - strip->act->id.us--; - strip->act = NULL; - } - if (strip->ipo){ - strip->ipo->id.us--; - strip->ipo = NULL; - } - if (strip->modifiers.first) { - BLI_freelistN(&strip->modifiers); + + /* we simply perform insertion sort on this list, since it is assumed that per track, + * there are only likely to be at most 5-10 strips + */ + for (strip= strips->first; strip; strip= stripn) { + short not_added = 1; + + stripn= strip->next; + + /* remove this strip from the list, and add it to the new list, searching from the end of + * the list, assuming that the lists are in order + */ + BLI_remlink(strips, strip); + + for (sstrip= tmp.last; sstrip; sstrip= sstrip->prev) { + /* check if add after */ + if (sstrip->end <= strip->start) { + BLI_insertlinkafter(&tmp, sstrip, strip); + not_added= 0; + break; + } + } + + /* add before first? */ + if (not_added) + BLI_addhead(&tmp, strip); } + /* reassign the start and end points of the strips */ + strips->first= tmp.first; + strips->last= tmp.last; } -void free_nlastrips (ListBase *nlalist) +/* Add the given NLA-Strip to the given list of strips, assuming that it + * isn't currently a member of another list + */ +short BKE_nlastrips_add_strip (ListBase *strips, NlaStrip *strip) { - bActionStrip *strip; + NlaStrip *ns; + short not_added = 1; + + /* sanity checks */ + if ELEM(NULL, strips, strip) + return 0; + + /* check if any space to add */ + if (BKE_nlastrips_has_space(strips, strip->start, strip->end)==0) + return 0; + + /* find the right place to add the strip to the nominated track */ + for (ns= strips->first; ns; ns= ns->next) { + /* if current strip occurs after the new strip, add it before */ + if (ns->start > strip->end) { + BLI_insertlinkbefore(strips, ns, strip); + not_added= 0; + break; + } + } + if (not_added) { + /* just add to the end of the list of the strips then... */ + BLI_addtail(strips, strip); + } + + /* added... */ + return 1; +} - if (!nlalist->first) + +/* Meta-Strips ------------------------------------ */ + +/* Convert 'islands' (i.e. continuous string of) selected strips to be + * contained within 'Meta-Strips' which act as strips which contain strips. + * temp: are the meta-strips to be created 'temporary' ones used for transforms? + */ +void BKE_nlastrips_make_metas (ListBase *strips, short temp) +{ + NlaStrip *mstrip = NULL; + NlaStrip *strip, *stripn; + + /* sanity checks */ + if ELEM(NULL, strips, strips->first) return; + + /* group all continuous chains of selected strips into meta-strips */ + for (strip= strips->first; strip; strip= stripn) { + stripn= strip->next; + + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* if there is an existing meta-strip, add this strip to it, otherwise, create a new one */ + if (mstrip == NULL) { + /* add a new meta-strip, and add it before the current strip that it will replace... */ + mstrip= MEM_callocN(sizeof(NlaStrip), "Meta-NlaStrip"); + mstrip->type = NLASTRIP_TYPE_META; + BLI_insertlinkbefore(strips, strip, mstrip); + + /* set flags */ + mstrip->flag = NLASTRIP_FLAG_SELECT; + + /* set temp flag if appropriate (i.e. for transform-type editing) */ + if (temp) + mstrip->flag |= NLASTRIP_FLAG_TEMP_META; + + /* set default repeat/scale values to prevent warnings */ + mstrip->repeat= mstrip->scale= 1.0f; + + /* make its start frame be set to the start frame of the current strip */ + mstrip->start= strip->start; + } + + /* remove the selected strips from the track, and add to the meta */ + BLI_remlink(strips, strip); + BLI_addtail(&mstrip->strips, strip); + + /* expand the meta's dimensions to include the newly added strip- i.e. its last frame */ + mstrip->end= strip->end; + } + else { + /* current strip wasn't selected, so the end of 'island' of selected strips has been reached, + * so stop adding strips to the current meta + */ + mstrip= NULL; + } + } +} - /* Do any specific freeing */ - for (strip=nlalist->first; strip; strip=strip->next) +/* Split a meta-strip into a set of normal strips */ +void BKE_nlastrips_clear_metastrip (ListBase *strips, NlaStrip *strip) +{ + NlaStrip *cs, *csn; + + /* sanity check */ + if ELEM(NULL, strips, strip) + return; + + /* move each one of the meta-strip's children before the meta-strip + * in the list of strips after unlinking them from the meta-strip + */ + for (cs= strip->strips.first; cs; cs= csn) { + csn= cs->next; + BLI_remlink(&strip->strips, cs); + BLI_insertlinkbefore(strips, strip, cs); + } + + /* free the meta-strip now */ + BLI_freelinkN(strips, strip); +} + +/* Remove meta-strips (i.e. flatten the list of strips) from the top-level of the list of strips + * sel: only consider selected meta-strips, otherwise all meta-strips are removed + * onlyTemp: only remove the 'temporary' meta-strips used for transforms + */ +void BKE_nlastrips_clear_metas (ListBase *strips, short onlySel, short onlyTemp) +{ + NlaStrip *strip, *stripn; + + /* sanity checks */ + if ELEM(NULL, strips, strips->first) + return; + + /* remove meta-strips fitting the criteria of the arguments */ + for (strip= strips->first; strip; strip= stripn) { + stripn= strip->next; + + /* check if strip is a meta-strip */ + if (strip->type == NLASTRIP_TYPE_META) { + /* if check if selection and 'temporary-only' considerations are met */ + if ((onlySel==0) || (strip->flag & NLASTRIP_FLAG_SELECT)) { + if ((!onlyTemp) || (strip->flag & NLASTRIP_FLAG_TEMP_META)) { + BKE_nlastrips_clear_metastrip(strips, strip); + } + } + } + } +} + +/* Add the given NLA-Strip to the given Meta-Strip, assuming that the + * strip isn't attached to anyy list of strips + */ +short BKE_nlameta_add_strip (NlaStrip *mstrip, NlaStrip *strip) +{ + /* sanity checks */ + if ELEM(NULL, mstrip, strip) + return 0; + + /* firstly, check if the meta-strip has space for this */ + if (BKE_nlastrips_has_space(&mstrip->strips, strip->start, strip->end) == 0) + return 0; + + /* check if this would need to be added to the ends of the meta, + * and subsequently, if the neighbouring strips allow us enough room + */ + if (strip->start < mstrip->start) { + /* check if strip to the left (if it exists) ends before the + * start of the strip we're trying to add + */ + if ((mstrip->prev == NULL) || (mstrip->prev->end <= strip->start)) { + /* add strip to start of meta's list, and expand dimensions */ + BLI_addhead(&mstrip->strips, strip); + mstrip->start= strip->start; + + return 1; + } + else /* failed... no room before */ + return 0; + } + else if (strip->end > mstrip->end) { + /* check if strip to the right (if it exists) starts before the + * end of the strip we're trying to add + */ + if ((mstrip->next == NULL) || (mstrip->next->start >= strip->end)) { + /* add strip to end of meta's list, and expand dimensions */ + BLI_addtail(&mstrip->strips, strip); + mstrip->end= strip->end; + + return 1; + } + else /* failed... no room after */ + return 0; + } + else { + /* just try to add to the meta-strip (no dimension changes needed) */ + return BKE_nlastrips_add_strip(&mstrip->strips, strip); + } +} + +/* Adjust the settings of NLA-Strips contained within a Meta-Strip (recursively), + * until the Meta-Strips children all fit within the Meta-Strip's new dimensions + */ +void BKE_nlameta_flush_transforms (NlaStrip *mstrip) +{ + NlaStrip *strip; + float oStart, oEnd, offset; + float oLen, nLen; + short scaleChanged= 0; + + /* sanity checks + * - strip must exist + * - strip must be a meta-strip with some contents + */ + if ELEM(NULL, mstrip, mstrip->strips.first) + return; + if (mstrip->type != NLASTRIP_TYPE_META) + return; + + /* get the original start/end points, and calculate the start-frame offset + * - these are simply the start/end frames of the child strips, + * since we assume they weren't transformed yet + */ + oStart= ((NlaStrip *)mstrip->strips.first)->start; + oEnd= ((NlaStrip *)mstrip->strips.last)->end; + offset= mstrip->start - oStart; + + /* optimisation: + * don't flush if nothing changed yet + * TODO: maybe we need a flag to say always flush? + */ + if (IS_EQ(oStart, mstrip->start) && IS_EQ(oEnd, mstrip->end)) + return; + + /* check if scale changed */ + oLen = oEnd - oStart; + nLen = mstrip->end - mstrip->start; + if (IS_EQ(nLen, oLen) == 0) + scaleChanged= 1; + + /* for each child-strip, calculate new start/end points based on this new info */ + for (strip= mstrip->strips.first; strip; strip= strip->next) { + if (scaleChanged) { + PointerRNA ptr; + float p1, p2, nStart, nEnd; + + /* compute positions of endpoints relative to old extents of strip */ + p1= (strip->start - oStart) / oLen; + p2= (strip->end - oStart) / oLen; + + /* compute the new strip endpoints using the proportions */ + nStart= (p1 * nLen) + mstrip->start; + nEnd= (p2 * nLen) + mstrip->start; + + /* firstly, apply the new positions manually, then apply using RNA + * - first time is to make sure no truncation errors from one endpoint not being + * set yet occur + * - second time is to make sure scale is computed properly... + */ + strip->start= nStart; + strip->end= nEnd; + + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &ptr); + RNA_float_set(&ptr, "start_frame", nStart); + RNA_float_set(&ptr, "end_frame", nEnd); + } + else { + /* just apply the changes in offset to both ends of the strip */ + strip->start += offset; + strip->end += offset; + } + + /* finally, make sure the strip's children (if it is a meta-itself), get updated */ + BKE_nlameta_flush_transforms(strip); + } +} + +/* NLA-Tracks ---------------------------------------- */ + +/* Find the active NLA-track for the given stack */ +NlaTrack *BKE_nlatrack_find_active (ListBase *tracks) +{ + NlaTrack *nlt; + + /* sanity check */ + if ELEM(NULL, tracks, tracks->first) + return NULL; + + /* try to find the first active track */ + for (nlt= tracks->first; nlt; nlt= nlt->next) { + if (nlt->flag & NLATRACK_ACTIVE) + return nlt; + } + + /* none found */ + return NULL; +} + +/* Toggle the 'solo' setting for the given NLA-track, making sure that it is the only one + * that has this status in its AnimData block. + */ +void BKE_nlatrack_solo_toggle (AnimData *adt, NlaTrack *nlt) +{ + NlaTrack *nt; + + /* sanity check */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return; + + /* firstly, make sure 'solo' flag for all tracks is disabled */ + for (nt= adt->nla_tracks.first; nt; nt= nt->next) { + if (nt != nlt) + nt->flag &= ~NLATRACK_SOLO; + } + + /* now, enable 'solo' for the given track if appropriate */ + if (nlt) { + /* toggle solo status */ + nlt->flag ^= NLATRACK_SOLO; + + /* set or clear solo-status on AnimData */ + if (nlt->flag & NLATRACK_SOLO) + adt->flag |= ADT_NLA_SOLO_TRACK; + else + adt->flag &= ~ADT_NLA_SOLO_TRACK; + } + else + adt->flag &= ~ADT_NLA_SOLO_TRACK; +} + +/* Make the given NLA-track the active one for the given stack. If no track is provided, + * this function can be used to simply deactivate all the NLA tracks in the given stack too. + */ +void BKE_nlatrack_set_active (ListBase *tracks, NlaTrack *nlt_a) +{ + NlaTrack *nlt; + + /* sanity check */ + if ELEM(NULL, tracks, tracks->first) + return; + + /* deactive all the rest */ + for (nlt= tracks->first; nlt; nlt= nlt->next) + nlt->flag &= ~NLATRACK_ACTIVE; + + /* set the given one as the active one */ + if (nlt_a) + nlt_a->flag |= NLATRACK_ACTIVE; +} + +/* Check if there is any space in the given track to add a strip of the given length */ +short BKE_nlatrack_has_space (NlaTrack *nlt, float start, float end) +{ + /* sanity checks */ + if ((nlt == NULL) || IS_EQ(start, end)) + return 0; + if (start > end) { + puts("BKE_nlatrack_has_space() error... start and end arguments swapped"); + SWAP(float, start, end); + } + + /* check if there's any space left in the track for a strip of the given length */ + return BKE_nlastrips_has_space(&nlt->strips, start, end); +} + +/* Rearrange the strips in the track so that they are always in order + * (usually only needed after a strip has been moved) + */ +void BKE_nlatrack_sort_strips (NlaTrack *nlt) +{ + /* sanity checks */ + if ELEM(NULL, nlt, nlt->strips.first) + return; + + /* sort the strips with a more generic function */ + BKE_nlastrips_sort_strips(&nlt->strips); +} + +/* Add the given NLA-Strip to the given NLA-Track, assuming that it + * isn't currently attached to another one + */ +short BKE_nlatrack_add_strip (NlaTrack *nlt, NlaStrip *strip) +{ + /* sanity checks */ + if ELEM(NULL, nlt, strip) + return 0; + + /* try to add the strip to the track using a more generic function */ + return BKE_nlastrips_add_strip(&nlt->strips, strip); +} + +/* NLA Strips -------------------------------------- */ + +/* Find the active NLA-strip within the given track */ +NlaStrip *BKE_nlastrip_find_active (NlaTrack *nlt) +{ + NlaStrip *strip; + + /* sanity check */ + if ELEM(NULL, nlt, nlt->strips.first) + return NULL; + + /* try to find the first active strip */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->flag & NLASTRIP_FLAG_ACTIVE) + return strip; + } + + /* none found */ + return NULL; +} + +/* Does the given NLA-strip fall within the given bounds (times)? */ +short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max) +{ + const float stripLen= (strip) ? strip->end - strip->start : 0.0f; + const float boundsLen= (float)fabs(max - min); + + /* sanity checks */ + if ((strip == NULL) || IS_EQ(stripLen, 0.0f) || IS_EQ(boundsLen, 0.0f)) + return 0; + + /* only ok if at least part of the strip is within the bounding window + * - first 2 cases cover when the strip length is less than the bounding area + * - second 2 cases cover when the strip length is greater than the bounding area + */ + if ( (stripLen < boundsLen) && + !(IN_RANGE(strip->start, min, max) || + IN_RANGE(strip->end, min, max)) ) { - free_actionstrip (strip); - }; - - /* Free the whole list */ - BLI_freelistN(nlalist); + return 0; + } + if ( (stripLen > boundsLen) && + !(IN_RANGE(min, strip->start, strip->end) || + IN_RANGE(max, strip->start, strip->end)) ) + { + return 0; + } + + /* should be ok! */ + return 1; } + +/* Is the given NLA-strip the first one to occur for the given AnimData block */ +// TODO: make this an api method if necesary, but need to add prefix first +short nlastrip_is_first (AnimData *adt, NlaStrip *strip) +{ + NlaTrack *nlt; + NlaStrip *ns; + + /* sanity checks */ + if ELEM(NULL, adt, strip) + return 0; + + /* check if strip has any strips before it */ + if (strip->prev) + return 0; + + /* check other tracks to see if they have a strip that's earlier */ + // TODO: or should we check that the strip's track is also the first? + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + /* only check the first strip, assuming that they're all in order */ + ns= nlt->strips.first; + if (ns) { + if (ns->start < strip->start) + return 0; + } + } + + /* should be first now */ + return 1; +} + +/* Animated Strips ------------------------------------------- */ + +/* Check if the given NLA-Track has any strips with own F-Curves */ +short BKE_nlatrack_has_animated_strips (NlaTrack *nlt) +{ + NlaStrip *strip; + + /* sanity checks */ + if ELEM(NULL, nlt, nlt->strips.first) + return 0; + + /* check each strip for F-Curves only (don't care about whether the flags are set) */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->fcurves.first) + return 1; + } + + /* none found */ + return 0; +} + +/* Check if given NLA-Tracks have any strips with own F-Curves */ +short BKE_nlatracks_have_animated_strips (ListBase *tracks) +{ + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, tracks, tracks->first) + return 0; + + /* check each track, stopping on the first hit */ + for (nlt= tracks->first; nlt; nlt= nlt->next) { + if (BKE_nlatrack_has_animated_strips(nlt)) + return 1; + } + + /* none found */ + return 0; +} + +/* Validate the NLA-Strips 'control' F-Curves based on the flags set*/ +void BKE_nlastrip_validate_fcurves (NlaStrip *strip) +{ + FCurve *fcu; + + /* sanity checks */ + if (strip == NULL) + return; + + /* if controlling influence... */ + if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { + /* try to get F-Curve */ + fcu= list_find_fcurve(&strip->fcurves, "influence", 0); + + /* add one if not found */ + if (fcu == NULL) { + /* make new F-Curve */ + fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve"); + BLI_addtail(&strip->fcurves, fcu); + + /* set default flags */ + fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED); + + /* store path - make copy, and store that */ + fcu->rna_path= BLI_strdupn("influence", 9); + + // TODO: insert a few keyframes to ensure default behaviour? + } + } + + /* if controlling time... */ + if (strip->flag & NLASTRIP_FLAG_USR_TIME) { + /* try to get F-Curve */ + fcu= list_find_fcurve(&strip->fcurves, "strip_time", 0); + + /* add one if not found */ + if (fcu == NULL) { + /* make new F-Curve */ + fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve"); + BLI_addtail(&strip->fcurves, fcu); + + /* set default flags */ + fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED); + + /* store path - make copy, and store that */ + fcu->rna_path= BLI_strdupn("strip_time", 10); + + // TODO: insert a few keyframes to ensure default behaviour? + } + } +} + +/* Sanity Validation ------------------------------------ */ + +/* Find (and set) a unique name for a strip from the whole AnimData block + * Uses a similar method to the BLI method, but is implemented differently + * as we need to ensure that the name is unique over several lists of tracks, + * not just a single track. + */ +void BKE_nlastrip_validate_name (AnimData *adt, NlaStrip *strip) +{ + GHash *gh; + NlaStrip *tstrip; + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, adt, strip) + return; + + /* give strip a default name if none already */ + if (strip->name[0]==0) { + switch (strip->type) { + case NLASTRIP_TYPE_CLIP: /* act-clip */ + sprintf(strip->name, "Act: %s", (strip->act)?(strip->act->id.name+2):("")); + break; + case NLASTRIP_TYPE_TRANSITION: /* transition */ + sprintf(strip->name, "Transition"); + break; + case NLASTRIP_TYPE_META: /* meta */ + sprintf(strip->name, "Meta"); + break; + default: + sprintf(strip->name, "NLA Strip"); + break; + } + } + + /* build a hash-table of all the strips in the tracks + * - this is easier than iterating over all the tracks+strips hierarchy everytime + * (and probably faster) + */ + gh= BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp); + + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + for (tstrip= nlt->strips.first; tstrip; tstrip= tstrip->next) { + /* don't add the strip of interest */ + if (tstrip == strip) + continue; + + /* use the name of the strip as the key, and the strip as the value, since we're mostly interested in the keys */ + BLI_ghash_insert(gh, tstrip->name, tstrip); + } + } + + /* if the hash-table has a match for this name, try other names... + * - in an extreme case, it might not be able to find a name, but then everything else in Blender would fail too :) + */ + if (BLI_ghash_haskey(gh, strip->name)) { + char tempname[128]; + int number = 1; + char *dot; + + /* Strip off the suffix */ + dot = strchr(strip->name, '.'); + if (dot) *dot=0; + + /* Try different possibilities */ + for (number = 1; number <= 999; number++) { + /* assemble alternative name */ + BLI_snprintf(tempname, 128, "%s%c%03d", strip->name, ".", number); + + /* if hash doesn't have this, set it */ + if (BLI_ghash_haskey(gh, tempname) == 0) { + BLI_strncpy(strip->name, tempname, sizeof(strip->name)); + break; + } + } + } + + /* free the hash... */ + BLI_ghash_free(gh, NULL, NULL); +} + +/* ---- */ + +/* Determine auto-blending for the given strip */ +void BKE_nlastrip_validate_autoblends (NlaTrack *nlt, NlaStrip *nls) +{ + NlaTrack *track; + NlaStrip *strip; + //float *ps=NULL, *pe=NULL; + //float *ns=NULL, *ne=NULL; + + /* sanity checks */ + if ELEM(NULL, nls, nlt) + return; + if ((nlt->prev == NULL) && (nlt->next == NULL)) + return; + if ((nls->flag & NLASTRIP_FLAG_AUTO_BLENDS)==0) + return; + + /* get test ranges */ + if (nlt->prev) { + /* find strips that overlap over the start/end of the given strip, + * but which don't cover the entire length + */ + track= nlt->prev; + for (strip= track->strips.first; strip; strip= strip->next) { + + } + } + if (nlt->next) { + /* find strips that overlap over the start/end of the given strip, + * but which don't cover the entire length + */ + track= nlt->next; + for (strip= track->strips.first; strip; strip= strip->next) { + + } + } +} + +/* Ensure that auto-blending and other settings are set correctly */ +void BKE_nla_validate_state (AnimData *adt) +{ + NlaStrip *strip; + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return; + + /* adjust blending values for auto-blending */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + for (strip= nlt->strips.first; strip; strip= strip->next) { + BKE_nlastrip_validate_autoblends(nlt, strip); + } + } +} + +/* Core Tools ------------------------------------------- */ + +/* For the given AnimData block, add the active action to the NLA + * stack (i.e. 'push-down' action). The UI should only allow this + * for normal editing only (i.e. not in editmode for some strip's action), + * so no checks for this are performed. + */ +// TODO: maybe we should have checks for this too... +void BKE_nla_action_pushdown (AnimData *adt) +{ + NlaStrip *strip; + + /* sanity checks */ + // TODO: need to report the error for this + if ELEM(NULL, adt, adt->action) + return; + + /* if the action is empty, we also shouldn't try to add to stack, + * as that will cause us grief down the track + */ + // TODO: what about modifiers? + if (action_has_motion(adt->action) == 0) { + printf("BKE_nla_action_pushdown(): action has no data \n"); + return; + } + + /* add a new NLA strip to the track, which references the active action */ + strip= add_nlastrip_to_stack(adt, adt->action); + + /* do other necessary work on strip */ + if (strip) { + /* clear reference to action now that we've pushed it onto the stack */ + adt->action->id.us--; + adt->action= NULL; + + /* if the strip is the first one in the track it lives in, check if there + * are strips in any other tracks that may be before this, and set the extend + * mode accordingly + */ + if (nlastrip_is_first(adt, strip) == 0) { + /* not first, so extend mode can only be NLASTRIP_EXTEND_HOLD_FORWARD not NLASTRIP_EXTEND_HOLD, + * so that it doesn't override strips in previous tracks + */ + // FIXME: this needs to be more automated, since user can rearrange strips + strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD; + } + } +} + + +/* Find the active strip + track combo, and set them up as the tweaking track, + * and return if successful or not. + */ +short BKE_nla_tweakmode_enter (AnimData *adt) +{ + NlaTrack *nlt, *activeTrack=NULL; + NlaStrip *strip, *activeStrip=NULL; + + /* verify that data is valid */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return 0; + + /* if block is already in tweakmode, just leave, but we should report + * that this block is in tweakmode (as our returncode) + */ + if (adt->flag & ADT_NLA_EDIT_ON) + return 1; + + /* go over the tracks, finding the active one, and its active strip + * - if we cannot find both, then there's nothing to do + */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + /* check if active */ + if (nlt->flag & NLATRACK_ACTIVE) { + /* store reference to this active track */ + activeTrack= nlt; + + /* now try to find active strip */ + activeStrip= BKE_nlastrip_find_active(nlt); + break; + } + } + if ELEM3(NULL, activeTrack, activeStrip, activeStrip->act) { + printf("NLA tweakmode enter - neither active requirement found \n"); + return 0; + } + + /* go over all the tracks up to the active one, tagging each strip that uses the same + * action as the active strip, but leaving everything else alone + */ + for (nlt= activeTrack->prev; nlt; nlt= nlt->prev) { + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->act == activeStrip->act) + strip->flag |= NLASTRIP_FLAG_TWEAKUSER; + else + strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER; + } + } + + + /* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled + * - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on + */ + for (nlt= activeTrack; nlt; nlt= nlt->next) + nlt->flag |= NLATRACK_DISABLED; + + /* handle AnimData level changes: + * - 'real' active action to temp storage (no need to change user-counts) + * - action of active strip set to be the 'active action', and have its usercount incremented + * - editing-flag for this AnimData block should also get turned on (for more efficient restoring) + * - take note of the active strip for mapping-correction of keyframes in the action being edited + */ + adt->tmpact= adt->action; + adt->action= activeStrip->act; + adt->actstrip= activeStrip; + id_us_plus(&activeStrip->act->id); + adt->flag |= ADT_NLA_EDIT_ON; + + /* done! */ + return 1; +} + +/* Exit tweakmode for this AnimData block */ +void BKE_nla_tweakmode_exit (AnimData *adt) +{ + NlaStrip *strip; + NlaTrack *nlt; + + /* verify that data is valid */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return; + + /* hopefully the flag is correct - skip if not on */ + if ((adt->flag & ADT_NLA_EDIT_ON) == 0) + return; + + // TODO: need to sync the user-strip with the new state of the action! + + /* for all Tracks, clear the 'disabled' flag + * for all Strips, clear the 'tweak-user' flag + */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + nlt->flag &= ~NLATRACK_DISABLED; + + for (strip= nlt->strips.first; strip; strip= strip->next) + strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER; + } + + /* handle AnimData level changes: + * - 'temporary' active action needs its usercount decreased, since we're removing this reference + * - 'real' active action is restored from storage + * - storage pointer gets cleared (to avoid having bad notes hanging around) + * - editing-flag for this AnimData block should also get turned off + * - clear pointer to active strip + */ + if (adt->action) adt->action->id.us--; + adt->action= adt->tmpact; + adt->tmpact= NULL; + adt->actstrip= NULL; + adt->flag &= ~ADT_NLA_EDIT_ON; +} + +/* *************************************************** */ diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 81bd78f1851..6490ff3c724 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1198,18 +1198,12 @@ Object *copy_object(Object *ob) armature_rebuild_pose(obn, obn->data); } copy_defgroups(&obn->defbase, &ob->defbase); -#if 0 // XXX old animation system - copy_nlastrips(&obn->nlastrips, &ob->nlastrips); -#endif // XXX old animation system copy_constraints(&obn->constraints, &ob->constraints); /* increase user numbers */ id_us_plus((ID *)obn->data); -#if 0 // XXX old animation system - id_us_plus((ID *)obn->ipo); - id_us_plus((ID *)obn->action); -#endif // XXX old animation system id_us_plus((ID *)obn->dup_group); + // FIXME: add this for animdata too... for(a=0; atotcol; a++) id_us_plus((ID *)obn->mat[a]); @@ -1575,14 +1569,14 @@ static void ob_parcurve(Scene *scene, Object *ob, Object *par, float mat[][4]) } /* catch exceptions: curve paths used as a duplicator */ else if(enable_cu_speed) { - ctime= bsystem_time(scene, ob, (float)scene->r.cfra, 0.0); - -#if 0 // XXX old animation system - if(calc_ipo_spec(cu->ipo, CU_SPEED, &ctime)==0) { - ctime /= cu->pathlen; - CLAMP(ctime, 0.0, 1.0); - } -#endif // XXX old animation system + /* ctime is now a proper var setting of Curve which gets set by Animato like any other var that's animated, + * but this will only work if it actually is animated... + * + * we firstly calculate the modulus of cu->ctime/cu->pathlen to clamp ctime within the 0.0 to 1.0 times pathlen + * range, then divide this (the modulus) by pathlen to get a value between 0.0 and 1.0 + */ + ctime= fmod(cu->ctime, cu->pathlen) / cu->pathlen; + CLAMP(ctime, 0.0, 1.0); } else { ctime= scene->r.cfra - give_timeoffset(ob); diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h new file mode 100644 index 00000000000..df7ffaa3064 --- /dev/null +++ b/source/blender/blenkernel/nla_private.h @@ -0,0 +1,85 @@ +/** + * $Id: BKE_nla.h 20999 2009-06-19 04:45:56Z 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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef NLA_PRIVATE +#define NLA_PRIVATE + +/* --------------- NLA Evaluation DataTypes ----------------------- */ + +/* used for list of strips to accumulate at current time */ +typedef struct NlaEvalStrip { + struct NlaEvalStrip *next, *prev; + + NlaTrack *track; /* track that this strip belongs to */ + NlaStrip *strip; /* strip that's being used */ + + short track_index; /* the index of the track within the list */ + short strip_mode; /* which end of the strip are we looking at */ + + float strip_time; /* time at which which strip is being evaluated */ +} NlaEvalStrip; + +/* NlaEvalStrip->strip_mode */ +enum { + /* standard evaluation */ + NES_TIME_BEFORE = -1, + NES_TIME_WITHIN, + NES_TIME_AFTER, + + /* transition-strip evaluations */ + NES_TIME_TRANSITION_START, + NES_TIME_TRANSITION_END, +} eNlaEvalStrip_StripMode; + + +/* temp channel for accumulating data from NLA (avoids needing to clear all values first) */ +// TODO: maybe this will be used as the 'cache' stuff needed for editable values too? +typedef struct NlaEvalChannel { + struct NlaEvalChannel *next, *prev; + + PointerRNA ptr; /* pointer to struct containing property to use */ + PropertyRNA *prop; /* RNA-property type to use (should be in the struct given) */ + int index; /* array index (where applicable) */ + + float value; /* value of this channel */ +} NlaEvalChannel; + +/* --------------- NLA Functions (not to be used as a proper API) ----------------------- */ + +/* convert from strip time <-> global time */ +float nlastrip_get_frame(NlaStrip *strip, float cframe, short mode); + +/* --------------- NLA Evaluation (very-private stuff) ----------------------- */ +/* these functions are only defined here to avoid problems with the order in which they get defined... */ + +NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list, ListBase *strips, short index, float ctime); +void nlastrip_evaluate(PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes); +void nladata_flush_channels(ListBase *channels); + +#endif // NLA_PRIVATE diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 460a1dcb2f5..846e54526b8 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1667,10 +1667,26 @@ static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbas /* Data Linking ----------------------------- */ +static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list) +{ + FModifier *fcm; + + for (fcm= list->first; fcm; fcm= fcm->next) { + /* data for specific modifiers */ + switch (fcm->type) { + case FMODIFIER_TYPE_PYTHON: + { + FMod_Python *data= (FMod_Python *)fcm->data; + data->script = newlibadr(fd, id->lib, data->script); + } + break; + } + } +} + static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list) { FCurve *fcu; - FModifier *fcm; /* relink ID-block references... */ for (fcu= list->first; fcu; fcu= fcu->next) { @@ -1684,16 +1700,45 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list) } /* modifiers */ - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { - /* data for specific modifiers */ - switch (fcm->type) { - case FMODIFIER_TYPE_PYTHON: - { - FMod_Python *data= (FMod_Python *)fcm->data; - data->script = newlibadr(fd, id->lib, data->script); - } - break; + lib_link_fmodifiers(fd, id, &fcu->modifiers); + } +} + + +/* NOTE: this assumes that link_list has already been called on the list */ +static void direct_link_fmodifiers(FileData *fd, ListBase *list) +{ + FModifier *fcm; + + for (fcm= list->first; fcm; fcm= fcm->next) { + /* relink general data */ + fcm->data = newdataadr(fd, fcm->data); + fcm->edata= NULL; + + /* do relinking of data for specific types */ + switch (fcm->type) { + case FMODIFIER_TYPE_GENERATOR: + { + FMod_Generator *data= (FMod_Generator *)fcm->data; + + data->coefficients= newdataadr(fd, data->coefficients); } + break; + case FMODIFIER_TYPE_ENVELOPE: + { + FMod_Envelope *data= (FMod_Envelope *)fcm->data; + + data->data= newdataadr(fd, data->data); + } + break; + case FMODIFIER_TYPE_PYTHON: + { + FMod_Python *data= (FMod_Python *)fcm->data; + + data->prop = newdataadr(fd, data->prop); + IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + } + break; } } } @@ -1702,7 +1747,6 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list) static void direct_link_fcurves(FileData *fd, ListBase *list) { FCurve *fcu; - FModifier *fcm; /* link F-Curve data to F-Curve again (non ID-libs) */ for (fcu= list->first; fcu; fcu= fcu->next) { @@ -1730,37 +1774,7 @@ static void direct_link_fcurves(FileData *fd, ListBase *list) /* modifiers */ link_list(fd, &fcu->modifiers); - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { - /* relink general data */ - fcm->data = newdataadr(fd, fcm->data); - fcm->edata= NULL; - - /* do relinking of data for specific types */ - switch (fcm->type) { - case FMODIFIER_TYPE_GENERATOR: - { - FMod_Generator *data= (FMod_Generator *)fcm->data; - - data->coefficients= newdataadr(fd, data->coefficients); - } - break; - case FMODIFIER_TYPE_ENVELOPE: - { - FMod_Envelope *data= (FMod_Envelope *)fcm->data; - - data->data= newdataadr(fd, data->data); - } - break; - case FMODIFIER_TYPE_PYTHON: - { - FMod_Python *data= (FMod_Python *)fcm->data; - - data->prop = newdataadr(fd, data->prop); - IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); - } - break; - } - } + direct_link_fmodifiers(fd, &fcu->modifiers); } } @@ -1812,6 +1826,65 @@ static void direct_link_action(FileData *fd, bAction *act) } } +static void lib_link_nladata_strips(FileData *fd, ID *id, ListBase *list) +{ + NlaStrip *strip; + + for (strip= list->first; strip; strip= strip->next) { + /* check strip's children */ + lib_link_nladata_strips(fd, id, &strip->strips); + + /* reassign the counted-reference to action */ + strip->act = newlibadr_us(fd, id->lib, strip->act); + } +} + +static void lib_link_nladata(FileData *fd, ID *id, ListBase *list) +{ + NlaTrack *nlt; + + /* we only care about the NLA strips inside the tracks */ + for (nlt= list->first; nlt; nlt= nlt->next) { + lib_link_nladata_strips(fd, id, &nlt->strips); + } +} + +/* This handles Animato NLA-Strips linking + * NOTE: this assumes that link_list has already been called on the list + */ +static void direct_link_nladata_strips(FileData *fd, ListBase *list) +{ + NlaStrip *strip; + + for (strip= list->first; strip; strip= strip->next) { + /* strip's child strips */ + link_list(fd, &strip->strips); + direct_link_nladata_strips(fd, &strip->strips); + + /* strip's F-Curves */ + link_list(fd, &strip->fcurves); + direct_link_fcurves(fd, &strip->fcurves); + + /* strip's F-Modifiers */ + link_list(fd, &strip->modifiers); + direct_link_fcurves(fd, &strip->modifiers); + } +} + +/* NOTE: this assumes that link_list has already been called on the list */ +static void direct_link_nladata(FileData *fd, ListBase *list) +{ + NlaTrack *nlt; + + for (nlt= list->first; nlt; nlt= nlt->next) { + /* relink list of strips */ + link_list(fd, &nlt->strips); + + /* relink strip data */ + direct_link_nladata_strips(fd, &nlt->strips); + } +} + /* ------- */ static void lib_link_keyingsets(FileData *fd, ID *id, ListBase *list) @@ -1854,6 +1927,7 @@ static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt) /* link action data */ adt->action= newlibadr_us(fd, id->lib, adt->action); + adt->tmpact= newlibadr_us(fd, id->lib, adt->tmpact); /* link drivers */ lib_link_fcurves(fd, id, &adt->drivers); @@ -1861,7 +1935,7 @@ static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt) /* overrides don't have lib-link for now, so no need to do anything */ /* link NLA-data */ - // TODO... + lib_link_nladata(fd, id, &adt->nla_tracks); } static void direct_link_animdata(FileData *fd, AnimData *adt) @@ -1878,7 +1952,12 @@ static void direct_link_animdata(FileData *fd, AnimData *adt) // TODO... /* link NLA-data */ - // TODO... + link_list(fd, &adt->nla_tracks); + direct_link_nladata(fd, &adt->nla_tracks); + + /* clear temp pointers that may have been set... */ + // TODO: it's probably only a small cost to reload this anyway... + adt->actstrip= NULL; } /* ************ READ NODE TREE *************** */ @@ -4708,6 +4787,11 @@ static void direct_link_screen(FileData *fd, bScreen *sc) sipo->ads= newdataadr(fd, sipo->ads); sipo->ghostCurves.first= sipo->ghostCurves.last= NULL; } + else if (sl->spacetype==SPACE_NLA) { + SpaceNla *snla= (SpaceNla*)sl; + + snla->ads= newdataadr(fd, snla->ads); + } else if (sl->spacetype==SPACE_OUTLINER) { SpaceOops *soops= (SpaceOops*) sl; @@ -9150,7 +9234,14 @@ static void do_versions(FileData *fd, Library *lib, Main *main) if(ts->normalsize == 0.0 || !ts->uv_selectmode || ts->vgroup_weight == 0.0) { ts->normalsize= 0.1f; ts->selectmode= SCE_SELECT_VERTEX; + + /* autokeying - setting should be taken from the user-prefs + * but the userprefs version may not have correct flags set + * (i.e. will result in blank box when enabled) + */ ts->autokey_mode= U.autokey_mode; + if (ts->autokey_mode == 0) + ts->autokey_mode= 2; /* 'add/replace' but not on */ ts->uv_selectmode= UV_SELECT_VERTEX; ts->vgroup_weight= 1.0f; } @@ -9485,12 +9576,27 @@ static void expand_keyingsets(FileData *fd, Main *mainvar, ListBase *list) } } +static void expand_animdata_nlastrips(FileData *fd, Main *mainvar, ListBase *list) +{ + NlaStrip *strip; + + for (strip= list->first; strip; strip= strip->next) { + /* check child strips */ + expand_animdata_nlastrips(fd, mainvar, &strip->strips); + + /* relink referenced action */ + expand_doit(fd, mainvar, strip->act); + } +} + static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt) { FCurve *fcd; + NlaTrack *nlt; /* own action */ expand_doit(fd, mainvar, adt->action); + expand_doit(fd, mainvar, adt->tmpact); /* drivers - assume that these F-Curves have driver data to be in this list... */ for (fcd= adt->drivers.first; fcd; fcd= fcd->next) { @@ -9500,6 +9606,10 @@ static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt) for (dtar= driver->targets.first; dtar; dtar= dtar->next) expand_doit(fd, mainvar, dtar->id); } + + /* nla-data - referenced actions */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) + expand_animdata_nlastrips(fd, mainvar, &nlt->strips); } static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSettings *part) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 9d35967c95d..4b52da83019 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -781,10 +781,59 @@ static void write_actuators(WriteData *wd, ListBase *lb) } } +static void write_fmodifiers(WriteData *wd, ListBase *fmodifiers) +{ + FModifier *fcm; + + /* Modifiers */ + for (fcm= fmodifiers->first; fcm; fcm= fcm->next) { + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + + /* Write the specific data */ + if (fmi && fcm->data) { + /* firstly, just write the plain fmi->data struct */ + writestruct(wd, DATA, fmi->structName, 1, fcm->data); + + /* do any modifier specific stuff */ + switch (fcm->type) { + case FMODIFIER_TYPE_GENERATOR: + { + FMod_Generator *data= (FMod_Generator *)fcm->data; + + /* write coefficients array */ + if (data->coefficients) + writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients); + } + break; + case FMODIFIER_TYPE_ENVELOPE: + { + FMod_Envelope *data= (FMod_Envelope *)fcm->data; + + /* write envelope data */ + if (data->data) + writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data); + } + break; + case FMODIFIER_TYPE_PYTHON: + { + FMod_Python *data = (FMod_Python *)fcm->data; + + /* Write ID Properties -- and copy this comment EXACTLY for easy finding + of library blocks that implement this.*/ + IDP_WriteProperty(data->prop, wd); + } + break; + } + } + + /* Write the modifier */ + writestruct(wd, DATA, "FModifier", 1, fcm); + } +} + static void write_fcurves(WriteData *wd, ListBase *fcurves) { FCurve *fcu; - FModifier *fcm; for (fcu=fcurves->first; fcu; fcu=fcu->next) { /* F-Curve */ @@ -815,50 +864,8 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves) } } - /* Modifiers */ - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* Write the specific data */ - if (fmi && fcm->data) { - /* firstly, just write the plain fmi->data struct */ - writestruct(wd, DATA, fmi->structName, 1, fcm->data); - - /* do any modifier specific stuff */ - switch (fcm->type) { - case FMODIFIER_TYPE_GENERATOR: - { - FMod_Generator *data= (FMod_Generator *)fcm->data; - - /* write coefficients array */ - if (data->coefficients) - writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients); - } - break; - case FMODIFIER_TYPE_ENVELOPE: - { - FMod_Envelope *data= (FMod_Envelope *)fcm->data; - - /* write envelope data */ - if (data->data) - writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data); - } - break; - case FMODIFIER_TYPE_PYTHON: - { - FMod_Python *data = (FMod_Python *)fcm->data; - - /* Write ID Properties -- and copy this comment EXACTLY for easy finding - of library blocks that implement this.*/ - IDP_WriteProperty(data->prop, wd); - } - break; - } - } - - /* Write the modifier */ - writestruct(wd, DATA, "FModifier", 1, fcm); - } + /* write F-Modifiers */ + write_fmodifiers(wd, &fcu->modifiers); } } @@ -909,6 +916,37 @@ static void write_keyingsets(WriteData *wd, ListBase *list) } } +static void write_nlastrips(WriteData *wd, ListBase *strips) +{ + NlaStrip *strip; + + for (strip= strips->first; strip; strip= strip->next) { + /* write the strip first */ + writestruct(wd, DATA, "NlaStrip", 1, strip); + + /* write the strip's F-Curves and modifiers */ + write_fcurves(wd, &strip->fcurves); + write_fmodifiers(wd, &strip->modifiers); + + /* write the strip's children */ + write_nlastrips(wd, &strip->strips); + } +} + +static void write_nladata(WriteData *wd, ListBase *nlabase) +{ + NlaTrack *nlt; + + /* write all the tracks */ + for (nlt= nlabase->first; nlt; nlt= nlt->next) { + /* write the track first */ + writestruct(wd, DATA, "NlaTrack", 1, nlt); + + /* write the track's strips */ + write_nlastrips(wd, &nlt->strips); + } +} + static void write_animdata(WriteData *wd, AnimData *adt) { AnimOverride *aor; @@ -920,14 +958,17 @@ static void write_animdata(WriteData *wd, AnimData *adt) write_fcurves(wd, &adt->drivers); /* write overrides */ + // FIXME: are these needed? for (aor= adt->overrides.first; aor; aor= aor->next) { /* overrides consist of base data + rna_path */ writestruct(wd, DATA, "AnimOverride", 1, aor); writedata(wd, DATA, strlen(aor->rna_path)+1, aor->rna_path); } + // TODO write the remaps (if they are needed) + /* write NLA data */ - // XXX todo... + write_nladata(wd, &adt->nla_tracks); } static void write_constraints(WriteData *wd, ListBase *conlist) @@ -1899,7 +1940,10 @@ static void write_screens(WriteData *wd, ListBase *scrbase) writestruct(wd, DATA, "SpaceSound", 1, sl); } else if(sl->spacetype==SPACE_NLA){ - writestruct(wd, DATA, "SpaceNla", 1, sl); + SpaceNla *snla= (SpaceNla *)sl; + + writestruct(wd, DATA, "SpaceNla", 1, snla); + if(snla->ads) writestruct(wd, DATA, "bDopeSheet", 1, snla->ads); } else if(sl->spacetype==SPACE_TIME){ writestruct(wd, DATA, "SpaceTime", 1, sl); diff --git a/source/blender/editors/animation/anim_channels.c b/source/blender/editors/animation/anim_channels.c index 05d50f98e8e..5c31d007705 100644 --- a/source/blender/editors/animation/anim_channels.c +++ b/source/blender/editors/animation/anim_channels.c @@ -63,9 +63,10 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_depsgraph.h" -#include "BKE_ipo.h" +#include "BKE_fcurve.h" #include "BKE_key.h" #include "BKE_material.h" #include "BKE_object.h" @@ -87,43 +88,16 @@ /* ************************************************************************** */ /* CHANNELS API */ -/* -------------------------- Internal Macros ------------------------------- */ - -/* set/clear/toggle macro - * - channel - channel with a 'flag' member that we're setting - * - smode - 0=clear, 1=set, 2=toggle - * - sflag - bitflag to set - */ -#define ACHANNEL_SET_FLAG(channel, smode, sflag) \ - { \ - if (smode == ACHANNEL_SETFLAG_TOGGLE) (channel)->flag ^= (sflag); \ - else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag |= (sflag); \ - else (channel)->flag &= ~(sflag); \ - } - -/* set/clear/toggle macro, where the flag is negative - * - channel - channel with a 'flag' member that we're setting - * - smode - 0=clear, 1=set, 2=toggle - * - sflag - bitflag to set - */ -#define ACHANNEL_SET_FLAG_NEG(channel, smode, sflag) \ - { \ - if (smode == ACHANNEL_SETFLAG_TOGGLE) (channel)->flag ^= (sflag); \ - else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag &= ~(sflag); \ - else (channel)->flag |= (sflag); \ - } - /* -------------------------- Exposed API ----------------------------------- */ /* Set the given animation-channel as the active one for the active context */ -void ANIM_set_active_channel (void *data, short datatype, int filter, void *channel_data, short channel_type) +void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; /* try to build list of filtered items */ - // XXX we don't need/supply animcontext for now, since in this case, there's nothing really essential there that isn't already covered - ANIM_animdata_filter(NULL, &anim_data, filter, data, datatype); + ANIM_animdata_filter(ac, &anim_data, filter, data, datatype); if (anim_data.first == NULL) return; @@ -149,11 +123,18 @@ void ANIM_set_active_channel (void *data, short datatype, int filter, void *chan ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE); } break; + case ANIMTYPE_NLATRACK: + { + NlaTrack *nlt= (NlaTrack *)ale->data; + + ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE); + } + break; } } /* set active flag */ - if (channel_data) { + if (channel_data != NULL) { switch (channel_type) { case ANIMTYPE_GROUP: { @@ -167,6 +148,12 @@ void ANIM_set_active_channel (void *data, short datatype, int filter, void *chan fcu->flag |= FCURVE_ACTIVE; } break; + case ANIMTYPE_NLATRACK: + { + NlaTrack *nlt= (NlaTrack *)channel_data; + nlt->flag |= NLATRACK_ACTIVE; + } + break; } } @@ -217,6 +204,10 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short if (ale->flag & FCURVE_SELECTED) sel= ACHANNEL_SETFLAG_CLEAR; break; + case ANIMTYPE_NLATRACK: + if (ale->flag & NLATRACK_SELECTED) + sel= ACHANNEL_SETFLAG_CLEAR; + break; } } } @@ -263,6 +254,14 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short fcu->flag &= ~FCURVE_ACTIVE; } break; + case ANIMTYPE_NLATRACK: + { + NlaTrack *nlt= (NlaTrack *)ale->data; + + ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED); + nlt->flag &= ~NLATRACK_ACTIVE; + } + break; } } @@ -273,6 +272,47 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short /* ************************************************************************** */ /* OPERATORS */ +/* ****************** Operator Utilities ********************************** */ + +/* poll callback for being in an Animation Editor channels list region */ +int animedit_poll_channels_active (bContext *C) +{ + ScrArea *sa= CTX_wm_area(C); + + /* channels region test */ + // TODO: could enhance with actually testing if channels region? + if (ELEM(NULL, sa, CTX_wm_region(C))) + return 0; + /* animation editor test */ + if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0) + return 0; + + return 1; +} + +/* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */ +int animedit_poll_channels_nla_tweakmode_off (bContext *C) +{ + ScrArea *sa= CTX_wm_area(C); + Scene *scene = CTX_data_scene(C); + + /* channels region test */ + // TODO: could enhance with actually testing if channels region? + if (ELEM(NULL, sa, CTX_wm_region(C))) + return 0; + /* animation editor test */ + if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0) + return 0; + + /* NLA TweakMode test */ + if (sa->spacetype == SPACE_NLA) { + if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON)) + return 0; + } + + return 1; +} + /* ****************** Rearrange Channels Operator ******************* */ /* This operator only works for Action Editor mode for now, as having it elsewhere makes things difficult */ @@ -575,8 +615,8 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op) mode= RNA_enum_get(op->ptr, "direction"); rearrange_action_channels(&ac, mode); - /* set notifier tha things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS); + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL); return OPERATOR_FINISHED; } @@ -652,6 +692,121 @@ void ANIM_OT_channels_move_bottom (wmOperatorType *ot) #endif // XXX old animation system - needs to be updated for new system... +/* ******************** Delete Channel Operator *********************** */ + +static int animchannels_delete_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* cannot delete in shapekey */ + if (ac.datatype == ANIMCONT_SHAPEKEY) + return OPERATOR_CANCELLED; + + + /* do groups only first (unless in Drivers mode, where there are none) */ + if (ac.datatype != ANIMCONT_DRIVERS) { + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CHANNELS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* delete selected groups and their associated channels */ + for (ale= anim_data.first; ale; ale= ale->next) { + /* only groups - don't check other types yet, since they may no-longer exist */ + if (ale->type == ANIMTYPE_GROUP) { + bActionGroup *agrp= (bActionGroup *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + FCurve *fcu, *fcn; + + /* skip this group if no AnimData available, as we can't safely remove the F-Curves */ + if (adt == NULL) + continue; + + /* delete all of the Group's F-Curves, but no others */ + for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcn) { + fcn= fcu->next; + + /* remove from group and action, then free */ + action_groups_remove_channel(adt->action, fcu); + free_fcurve(fcu); + } + + /* free the group itself */ + if (adt->action) + BLI_freelinkN(&adt->action->groups, agrp); + else + MEM_freeN(agrp); + } + } + + /* cleanup */ + BLI_freelistN(&anim_data); + } + + /* now do F-Curves */ + if (ac.datatype != ANIMCONT_GPENCIL) { + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* delete selected F-Curves */ + for (ale= anim_data.first; ale; ale= ale->next) { + /* only F-Curves, and only if we can identify its parent */ + if (ale->type == ANIMTYPE_FCURVE) { + AnimData *adt= BKE_animdata_from_id(ale->id); + FCurve *fcu= (FCurve *)ale->data; + + /* if no AnimData, we've got nowhere to remove the F-Curve from */ + if (adt == NULL) + continue; + + /* remove from whatever list it came from + * - Action Group + * - Action + * - Drivers + * - TODO... some others? + */ + if (fcu->grp) + action_groups_remove_channel(adt->action, fcu); + else if (adt->action) + BLI_remlink(&adt->action->curves, fcu); + else if (ac.datatype == ANIMCONT_DRIVERS) + BLI_remlink(&adt->drivers, fcu); + + /* free the F-Curve itself */ + free_fcurve(fcu); + } + } + + /* cleanup */ + BLI_freelistN(&anim_data); + } + + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL); + + return OPERATOR_FINISHED; +} + +void ANIM_OT_channels_delete (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Delete Channels"; + ot->idname= "ANIM_OT_channels_delete"; + + /* api callbacks */ + ot->exec= animchannels_delete_exec; + ot->poll= animedit_poll_channels_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} /* ******************** Toggle Channel Visibility Operator *********************** */ @@ -668,7 +823,7 @@ static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; /* filter data */ - filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVESONLY); + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* See if we should be making showing all selected or hiding */ @@ -676,21 +831,35 @@ static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *op) if (vis == ACHANNEL_SETFLAG_CLEAR) break; - if (ale->flag & FCURVE_VISIBLE) + if ((ale->type == ANIMTYPE_FCURVE) && (ale->flag & FCURVE_VISIBLE)) + vis= ACHANNEL_SETFLAG_CLEAR; + else if ((ale->type == ANIMTYPE_GROUP) && !(ale->flag & AGRP_NOTVISIBLE)) vis= ACHANNEL_SETFLAG_CLEAR; } /* Now set the flags */ for (ale= anim_data.first; ale; ale= ale->next) { - FCurve *fcu= (FCurve *)ale->data; - ACHANNEL_SET_FLAG(fcu, vis, FCURVE_VISIBLE); + switch (ale->type) { + case ANIMTYPE_FCURVE: /* F-Curve */ + { + FCurve *fcu= (FCurve *)ale->data; + ACHANNEL_SET_FLAG(fcu, vis, FCURVE_VISIBLE); + } + break; + case ANIMTYPE_GROUP: /* Group */ + { + bActionGroup *agrp= (bActionGroup *)ale->data; + ACHANNEL_SET_FLAG_NEG(agrp, vis, AGRP_NOTVISIBLE); + } + break; + } } /* cleanup */ BLI_freelistN(&anim_data); - /* set notifier tha things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS); + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL); return OPERATOR_FINISHED; } @@ -889,6 +1058,12 @@ static void setflag_anim_channels (bAnimContext *ac, short setting, short mode, case ACHANNEL_SETTING_EXPAND: ACHANNEL_SET_FLAG(agrp, mode, AGRP_EXPANDED); break; + case ACHANNEL_SETTING_MUTE: + ACHANNEL_SET_FLAG(agrp, mode, AGRP_MUTED); + break; + case ACHANNEL_SETTING_VISIBLE: + ACHANNEL_SET_FLAG_NEG(agrp, mode, AGRP_NOTVISIBLE); + break; } } break; @@ -947,8 +1122,8 @@ static int animchannels_setflag_exec(bContext *C, wmOperator *op) /* modify setting */ setflag_anim_channels(&ac, setting, mode, 1); - /* set notifier tha things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS); + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL); return OPERATOR_FINISHED; } @@ -963,7 +1138,7 @@ void ANIM_OT_channels_setting_enable (wmOperatorType *ot) /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= animchannels_setflag_exec; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -984,7 +1159,7 @@ void ANIM_OT_channels_setting_disable (wmOperatorType *ot) /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= animchannels_setflag_exec; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1005,7 +1180,7 @@ void ANIM_OT_channels_setting_toggle (wmOperatorType *ot) /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= animchannels_setflag_exec; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1026,7 +1201,7 @@ void ANIM_OT_channels_editable_toggle (wmOperatorType *ot) /* api callbacks */ ot->exec= animchannels_setflag_exec; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1056,8 +1231,8 @@ static int animchannels_expand_exec (bContext *C, wmOperator *op) /* modify setting */ setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS); + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL); return OPERATOR_FINISHED; } @@ -1070,7 +1245,7 @@ void ANIM_OT_channels_expand (wmOperatorType *ot) /* api callbacks */ ot->exec= animchannels_expand_exec; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1097,8 +1272,8 @@ static int animchannels_collapse_exec (bContext *C, wmOperator *op) /* modify setting */ setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS); + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL); return OPERATOR_FINISHED; } @@ -1111,7 +1286,7 @@ void ANIM_OT_channels_collapse (wmOperatorType *ot) /* api callbacks */ ot->exec= animchannels_collapse_exec; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1136,8 +1311,8 @@ static int animchannels_deselectall_exec(bContext *C, wmOperator *op) else ANIM_deselect_anim_channels(ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD); - /* set notifier tha things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS); + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_SELECT, NULL); return OPERATOR_FINISHED; } @@ -1150,7 +1325,7 @@ void ANIM_OT_channels_select_all_toggle (wmOperatorType *ot) /* api callbacks */ ot->exec= animchannels_deselectall_exec; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_nla_tweakmode_off; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1218,6 +1393,14 @@ static void borderselect_anim_channels (bAnimContext *ac, rcti *rect, short sele ACHANNEL_SET_FLAG(gpl, selectmode, GP_LAYER_SELECT); } break; + + case ANIMTYPE_NLATRACK: /* nla-track */ + { + NlaTrack *nlt= (NlaTrack *)ale->data; + + ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED); + } + break; } } @@ -1257,6 +1440,9 @@ static int animchannels_borderselect_exec(bContext *C, wmOperator *op) /* apply borderselect animation channels */ borderselect_anim_channels(&ac, &rect, selectmode); + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_SELECT, NULL); + return OPERATOR_FINISHED; } @@ -1271,7 +1457,7 @@ void ANIM_OT_channels_select_border(wmOperatorType *ot) ot->exec= animchannels_borderselect_exec; ot->modal= WM_border_select_modal; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_nla_tweakmode_off; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1291,11 +1477,12 @@ void ANIM_OT_channels_select_border(wmOperatorType *ot) * NOTE: eventually, this should probably be phased out when many of these things are replaced with buttons */ -static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, short selectmode) +static int mouse_anim_channels (bAnimContext *ac, float x, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; + int notifierFlags = 0; /* get the channel that was clicked on */ /* filter channels */ @@ -1309,7 +1496,7 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s printf("Error: animation channel (index = %d) not found in mouse_anim_channels() \n", channel_index); BLI_freelistN(&anim_data); - return; + return 0; } /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */ @@ -1317,7 +1504,7 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) { /* normal channels should not behave normally in this case */ BLI_freelistN(&anim_data); - return; + return 0; } /* action to take depends on what channel we've got */ @@ -1329,6 +1516,8 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s if (x < 16) { /* toggle expand */ sce->flag ^= SCE_DS_COLLAPSED; + + notifierFlags |= ND_ANIMCHAN_EDIT; } else { /* set selection status */ @@ -1339,6 +1528,8 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s else { sce->flag |= SCE_DS_SELECTED; } + + notifierFlags |= ND_ANIMCHAN_SELECT; } } break; @@ -1352,6 +1543,8 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s if (x < 16) { /* toggle expand */ ob->nlaflag ^= OB_ADS_COLLAPSED; // XXX + + notifierFlags |= ND_ANIMCHAN_EDIT; } else { /* set selection status */ @@ -1376,6 +1569,8 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s /* xxx should be ED_base_object_activate(), but we need context pointer for that... */ //set_active_base(base); + + notifierFlags |= ND_ANIMCHAN_SELECT; } } break; @@ -1383,18 +1578,21 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s { bAction *act= (bAction *)ale->data; act->flag ^= ACT_COLLAPSED; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_FILLDRIVERS: { AnimData *adt= (AnimData* )ale->data; adt->flag ^= ADT_DRIVERS_COLLAPSED; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_FILLMATD: { Object *ob= (Object *)ale->data; ob->nlaflag ^= OB_ADS_SHOWMATS; // XXX + notifierFlags |= ND_ANIMCHAN_EDIT; } break; @@ -1402,36 +1600,42 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s { Material *ma= (Material *)ale->data; ma->flag ^= MA_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_DSLAM: { Lamp *la= (Lamp *)ale->data; la->flag ^= LA_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_DSCAM: { Camera *ca= (Camera *)ale->data; ca->flag ^= CAM_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_DSCUR: { Curve *cu= (Curve *)ale->data; cu->flag ^= CU_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_DSSKEY: { Key *key= (Key *)ale->data; key->flag ^= KEYBLOCK_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_DSWOR: { World *wo= (World *)ale->data; wo->flag ^= WO_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; } break; @@ -1443,10 +1647,22 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s if ((x < (offset+17)) && (agrp->channels.first)) { /* toggle expand */ agrp->flag ^= AGRP_EXPANDED; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + else if ((x < (offset+32)) && (ac->spacetype==SPACE_IPO)) { + /* toggle visibility (of grouped F-Curves in Graph editor) */ + agrp->flag ^= AGRP_NOTVISIBLE; + notifierFlags |= ND_ANIMCHAN_EDIT; } else if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) { /* toggle protection/locking */ agrp->flag ^= AGRP_PROTECTED; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + else if (x >= (ACHANNEL_NAMEWIDTH-2*ACHANNEL_BUTTON_WIDTH)) { + /* toggle mute */ + agrp->flag ^= AGRP_MUTED; + notifierFlags |= ND_ANIMCHAN_EDIT; } else { /* select/deselect group */ @@ -1474,7 +1690,9 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s /* if group is selected now, make group the 'active' one in the visible list */ if (agrp->flag & AGRP_SELECTED) - ANIM_set_active_channel(ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); + + notifierFlags |= ND_ANIMCHAN_SELECT; } } break; @@ -1495,16 +1713,20 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) { /* toggle protection (only if there's a toggle there) */ - if (fcu->bezt) + if (fcu->bezt) { fcu->flag ^= FCURVE_PROTECTED; + notifierFlags |= ND_ANIMCHAN_EDIT; + } } else if (x >= (ACHANNEL_NAMEWIDTH-2*ACHANNEL_BUTTON_WIDTH)) { /* toggle mute */ fcu->flag ^= FCURVE_MUTED; + notifierFlags |= ND_ANIMCHAN_EDIT; } else if ((x < (offset+17)) && (ac->spacetype==SPACE_IPO)) { /* toggle visibility */ fcu->flag ^= FCURVE_VISIBLE; + notifierFlags |= ND_ANIMCHAN_EDIT; } else { /* select/deselect */ @@ -1520,7 +1742,9 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */ if (fcu->flag & FCURVE_SELECTED) - ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); + + notifierFlags |= ND_ANIMCHAN_SELECT; } } break; @@ -1530,6 +1754,8 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s /* toggle expand */ gpd->flag ^= GP_DATA_EXPAND; + + notifierFlags |= ND_ANIMCHAN_EDIT; } break; case ANIMTYPE_GPLAYER: @@ -1568,6 +1794,9 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s /* free channels */ BLI_freelistN(&anim_data); + + /* return notifier flags */ + return notifierFlags; } /* ------------------- */ @@ -1580,6 +1809,7 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent * ARegion *ar; View2D *v2d; int mval[2], channel_index; + int notifierFlags = 0; short selectmode; float x, y; @@ -1614,10 +1844,10 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent * UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index); /* handle mouse-click in the relevant channel then */ - mouse_anim_channels(&ac, x, channel_index, selectmode); + notifierFlags= mouse_anim_channels(&ac, x, channel_index, selectmode); - /* set notifier tha things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS); + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|notifierFlags, NULL); return OPERATOR_FINISHED; } @@ -1630,7 +1860,7 @@ void ANIM_OT_channels_click (wmOperatorType *ot) /* api callbacks */ ot->invoke= animchannels_mouseclick_invoke; - ot->poll= ED_operator_areaactive; + ot->poll= animedit_poll_channels_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1653,6 +1883,8 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channels_setting_disable); WM_operatortype_append(ANIM_OT_channels_setting_toggle); + WM_operatortype_append(ANIM_OT_channels_delete); + // XXX does this need to be a separate operator? WM_operatortype_append(ANIM_OT_channels_editable_toggle); @@ -1686,6 +1918,10 @@ void ED_keymap_animchannels(wmWindowManager *wm) /* borderselect */ WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0); + /* delete */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", DELKEY, KM_PRESS, 0, 0); + /* settings */ WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 1ff2169bf61..13667159fe0 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -77,72 +77,6 @@ void ED_anim_object_flush_update(const bContext *C, Object *ob) } -/* **************************** animation tool notifiers ******************************** */ - -/* Send notifiers on behalf of animation editing tools, based on various context info - * - data_changed: eAnimData_Changed - */ -void ANIM_animdata_send_notifiers (bContext *C, bAnimContext *ac, short data_changed) -{ - /* types of notifiers to send, depends on the editor context */ - switch (ac->datatype) { - case ANIMCONT_DOPESHEET: /* dopesheet */ - case ANIMCONT_FCURVES: /* fcurve editor */ - case ANIMCONT_DRIVERS: /* drivers editor */ // XXX probably this will need separate handling, since these are part of dependency system - { - /* what action was taken */ - switch (data_changed) { - case ANIM_CHANGED_KEYFRAMES_VALUES: - /* keyframe values changed, so transform may have changed */ - // XXX what about other cases? maybe we need general ND_KEYFRAMES or ND_ANIMATION? - WM_event_add_notifier(C, NC_OBJECT|ND_KEYS|ND_TRANSFORM, NULL); - break; - case ANIM_CHANGED_KEYFRAMES_SELECT: - WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, NULL); - break; - case ANIM_CHANGED_CHANNELS: - // XXX err... check available datatypes in dopesheet first? - // FIXME: this currently doesn't work (to update own view) - WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE|ND_OB_SELECT, ac->scene); - WM_event_add_notifier(C, NC_OBJECT|ND_BONE_ACTIVE|ND_BONE_SELECT, NULL); - break; - } - - // XXX for now, at least update own editor! - ED_area_tag_redraw(CTX_wm_area(C)); - } - break; - - case ANIMCONT_ACTION: /* action editor */ - { - Object *obact= CTX_data_active_object(C); - - switch (data_changed) { - case ANIM_CHANGED_KEYFRAMES_VALUES: - /* keyframe values changed, so transform may have changed */ - // XXX what about other cases? maybe we need general ND_KEYFRAMES or ND_ANIMATION? - WM_event_add_notifier(C, NC_OBJECT|ND_KEYS|ND_TRANSFORM, obact); - break; - case ANIM_CHANGED_KEYFRAMES_SELECT: - WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obact); - break; - case ANIM_CHANGED_CHANNELS: - // XXX err... check available datatypes in dopesheet first? - // FIXME: this currently doesn't work (to update own view) - WM_event_add_notifier(C, NC_OBJECT|ND_BONE_ACTIVE|ND_BONE_SELECT, obact); - break; - } - - // XXX for now, at least update own editor! - ED_area_tag_redraw(CTX_wm_area(C)); - } - break; - - default: /* some other data... just update area for now */ - ED_area_tag_redraw(CTX_wm_area(C)); - } -} - /* **************************** pose <-> action syncing ******************************** */ /* Summary of what needs to be synced between poses and actions: * 1) Flags @@ -152,6 +86,10 @@ void ANIM_animdata_send_notifiers (bContext *C, bAnimContext *ac, short data_cha * 3) Grouping (only for pose to action for now) */ +/* XXX OBSOLETE CODE WARNING: + * With the Animato system, the code below is somewhat obsolete now... + */ + /* Notifier from Action/Dopesheet (this may be extended to include other things such as Python...) * Channels in action changed, so update pose channels/groups to reflect changes. * diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 128c6b078e7..6c39e670f5c 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -43,10 +43,12 @@ #include "BLI_blenlib.h" +#include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_fcurve.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_screen.h" #include "BKE_utildefines.h" @@ -232,37 +234,16 @@ void ANIM_draw_previewrange (const bContext *C, View2D *v2d) /* *************************************************** */ /* NLA-MAPPING UTILITIES (required for drawing and also editing keyframes) */ -/* Obtain the Object providing NLA-scaling for the given channel (if applicable) */ -Object *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale) +/* Obtain the AnimData block providing NLA-mapping for the given channel (if applicable) */ +AnimData *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale) { /* sanity checks */ if (ac == NULL) return NULL; /* handling depends on the type of animation-context we've got */ - if (ac->datatype == ANIMCONT_ACTION) { - /* Action Editor (action mode) or Graph Editor (ipo mode): - * Only use if editor is not pinned, and active object has action - */ - if (ac->obact && ac->obact->action) { - SpaceAction *saction= (SpaceAction *)ac->sa->spacedata.first; - - if (saction->pin == 0) - return ac->obact; - } - } - else if ((ac->datatype == ANIMCONT_DOPESHEET) && (ale)) { - /* Dopesheet: - * Only if channel is available, and is owned by an Object with an Action - */ - if ((ale->id) && (GS(ale->id->name) == ID_OB)) { - Object *ob= (Object *)ale->id; - - if (ob->action) - return ob; - } - } - // XXX todo: add F-Curves mode (Graph Editor) ... + if (ale && ale->id) + return BKE_animdata_from_id(ale->id); /* no appropriate object found */ return NULL; @@ -273,7 +254,8 @@ Object *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale) * (where this is called) is single-threaded anyway */ // XXX was called: map_active_strip() -void ANIM_nla_mapping_draw(gla2DDrawInfo *di, Object *ob, short restore) +// TODO: should this be depreceated? +void ANIM_nla_mapping_draw(gla2DDrawInfo *di, AnimData *adt, short restore) { static rctf stored; @@ -288,8 +270,8 @@ void ANIM_nla_mapping_draw(gla2DDrawInfo *di, Object *ob, short restore) gla2DGetMap(di, &stored); map= stored; - map.xmin= get_action_frame(ob, map.xmin); - map.xmax= get_action_frame(ob, map.xmax); + map.xmin= BKE_nla_tweakedit_remap(adt, map.xmin, NLATIME_CONVERT_MAP); + map.xmax= BKE_nla_tweakedit_remap(adt, map.xmax, NLATIME_CONVERT_MAP); if (map.xmin == map.xmax) map.xmax += 1.0f; gla2DSetMap(di, &map); @@ -298,36 +280,38 @@ void ANIM_nla_mapping_draw(gla2DDrawInfo *di, Object *ob, short restore) /* ------------------- */ -/* helper function for ANIM_nla_mapping_apply_ipocurve() -> "restore", i.e. mapping points back to IPO-time */ +/* helper function for ANIM_nla_mapping_apply_fcurve() -> "restore", i.e. mapping points back to action-time */ static short bezt_nlamapping_restore(BeztEditData *bed, BezTriple *bezt) { - /* object providing scaling is stored in 'data', only_keys option is stored in i1 */ - Object *ob= (Object *)bed->data; + /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */ + AnimData *adt= (AnimData *)bed->data; short only_keys= (short)bed->i1; /* adjust BezTriple handles only if allowed to */ 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]); + bezt->vec[0][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_UNMAP); + bezt->vec[2][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_UNMAP); + } + + bezt->vec[1][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_UNMAP); return 0; } -/* helper function for ANIM_nla_mapping_apply_ipocurve() -> "apply", i.e. mapping points to NLA-mapped global time */ +/* helper function for ANIM_nla_mapping_apply_fcurve() -> "apply", i.e. mapping points to NLA-mapped global time */ static short bezt_nlamapping_apply(BeztEditData *bed, BezTriple *bezt) { - /* object providing scaling is stored in 'data', only_keys option is stored in i1 */ - Object *ob= (Object *)bed->data; + /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */ + AnimData *adt= (AnimData *)bed->data; short only_keys= (short)bed->i1; /* adjust BezTriple handles only if allowed to */ 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[0][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_MAP); + bezt->vec[2][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_MAP); } - bezt->vec[1][0]= get_action_frame_inv(ob, bezt->vec[1][0]); + + bezt->vec[1][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_MAP); return 0; } @@ -338,17 +322,17 @@ static short bezt_nlamapping_apply(BeztEditData *bed, BezTriple *bezt) * - restore = whether to map points back to non-mapped time * - only_keys = whether to only adjust the location of the center point of beztriples */ -void ANIM_nla_mapping_apply_fcurve(Object *ob, FCurve *fcu, short restore, short only_keys) +void ANIM_nla_mapping_apply_fcurve (AnimData *adt, FCurve *fcu, short restore, short only_keys) { BeztEditData bed; BeztEditFunc map_cb; /* init edit data - * - ob is stored in 'data' + * - AnimData is stored in 'data' * - only_keys is stored in 'i1' */ memset(&bed, 0, sizeof(BeztEditData)); - bed.data= (void *)ob; + bed.data= (void *)adt; bed.i1= (int)only_keys; /* get editing callback */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index afad396607b..42943475a57 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -195,7 +195,7 @@ static short actedit_get_context (bAnimContext *ac, SpaceAction *saction) } } -/* ----------- Private Stuff - IPO Editor ------------- */ +/* ----------- Private Stuff - Graph Editor ------------- */ /* Get data being edited in Graph Editor (depending on current 'mode') */ static short graphedit_get_context (bAnimContext *ac, SpaceIpo *sipo) @@ -237,6 +237,26 @@ static short graphedit_get_context (bAnimContext *ac, SpaceIpo *sipo) } } +/* ----------- Private Stuff - NLA Editor ------------- */ + +/* Get data being edited in Graph Editor (depending on current 'mode') */ +static short nlaedit_get_context (bAnimContext *ac, SpaceNla *snla) +{ + /* init dopesheet data if non-existant (i.e. for old files) */ + if (snla->ads == NULL) + snla->ads= MEM_callocN(sizeof(bDopeSheet), "NlaEdit DopeSheet"); + + /* sync settings with current view status, then return appropriate data */ + /* update scene-pointer (no need to check for pinning yet, as not implemented) */ + snla->ads->source= (ID *)ac->scene; + snla->ads->filterflag |= ADS_FILTER_ONLYNLA; + + ac->datatype= ANIMCONT_NLA; + ac->data= snla->ads; + + return 1; +} + /* ----------- Public API --------------- */ /* Obtain current anim-data context, given that context info from Blender context has already been set @@ -264,6 +284,13 @@ short ANIM_animdata_context_getdata (bAnimContext *ac) ok= graphedit_get_context(ac, sipo); } break; + + case SPACE_NLA: + { + SpaceNla *snla= (SpaceNla *)sa->spacedata.first; + ok= nlaedit_get_context(ac, snla); + } + break; } } @@ -313,6 +340,68 @@ short ANIM_animdata_get_context (const bContext *C, bAnimContext *ac) /* quick macro to test if AnimData is usable for drivers */ #define ANIMDATA_HAS_DRIVERS(id) ((id)->adt && (id)->adt->drivers.first) +/* quick macro to test if AnimData is usable for NLA */ +#define ANIMDATA_HAS_NLA(id) ((id)->adt && (id)->adt->nla_tracks.first) + + +/* Quick macro to test for all three avove usability tests, performing the appropriate provided + * action for each when the AnimData context is appropriate. + * + * Priority order for this goes (most important, to least): AnimData blocks, NLA, Drivers, Keyframes. + * + * For this to work correctly, a standard set of data needs to be available within the scope that this + * gets called in: + * - ListBase anim_data; + * - bDopeSheet *ads; + * - bAnimListElem *ale; + * - int items; + * + * - id: ID block which should have an AnimData pointer following it immediately, to use + * - adtOk: line or block of code to execute for AnimData-blocks case (usually ANIMDATA_ADD_ANIMDATA) + * - nlaOk: line or block of code to execute for NLA case + * - driversOk: line or block of code to execute for Drivers case + * - keysOk: line or block of code for Keyframes case + */ +#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, keysOk) \ + {\ + if (filter_mode & ANIMFILTER_ANIMDATA) {\ + if ((id)->adt) {\ + adtOk\ + }\ + }\ + else if (ads->filterflag & ADS_FILTER_ONLYNLA) {\ + if (ANIMDATA_HAS_NLA(id)) {\ + nlaOk\ + }\ + else if (!(ads->filterflag & ADS_FILTER_NLA_NOACT) && ANIMDATA_HAS_KEYS(id)) {\ + nlaOk\ + }\ + }\ + else if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) {\ + if (ANIMDATA_HAS_DRIVERS(id)) {\ + driversOk\ + }\ + }\ + else {\ + if (ANIMDATA_HAS_KEYS(id)) {\ + keysOk\ + }\ + }\ + } + + +/* quick macro to add a pointer to an AnimData block as a channel */ +#define ANIMDATA_ADD_ANIMDATA(id) \ + {\ + ale= make_new_animlistelem((id)->adt, ANIMTYPE_ANIMDATA, NULL, ANIMTYPE_NONE, (ID *)id);\ + if (ale) {\ + BLI_addtail(anim_data, ale);\ + items++;\ + }\ + } + + + /* quick macro to test if a anim-channel (F-Curve, Group, etc.) is selected in an acceptable way */ #define ANIMCHANNEL_SELOK(test_func) \ ( !(filter_mode & (ANIMFILTER_SEL|ANIMFILTER_UNSEL)) || \ @@ -494,6 +583,25 @@ bAnimListElem *make_new_animlistelem (void *data, short datatype, void *owner, s ale->datatype= ALE_GPFRAME; } break; + + case ANIMTYPE_NLATRACK: + { + NlaTrack *nlt= (NlaTrack *)data; + + ale->flag= nlt->flag; + + // XXX or should this be done some other way? + ale->key_data= &nlt->strips; + ale->datatype= ALE_NLASTRIP; + } + break; + case ANIMTYPE_NLAACTION: + { + /* nothing to include for now... nothing editable from NLA-perspective here */ + ale->key_data= NULL; + ale->datatype= ALE_NONE; + } + break; } } @@ -522,7 +630,6 @@ static int animdata_filter_fcurves (ListBase *anim_data, FCurve *first, bActionG if ( ANIMCHANNEL_SELOK(SEL_FCU(fcu)) ) { /* only include if this curve is active */ if (!(filter_mode & ANIMFILTER_ACTIVE) || (fcu->flag & FCURVE_ACTIVE)) { - /* owner/ownertype will be either object or action-channel, depending if it was dopesheet or part of an action */ ale= make_new_animlistelem(fcu, ANIMTYPE_FCURVE, owner, ownertype, owner_id); if (ale) { @@ -575,25 +682,30 @@ static int animdata_filter_action (ListBase *anim_data, bAction *act, int filter * cases when we should include F-Curves inside group: * - we don't care about visibility * - group is expanded - * - we're interested in keyframes, but not if they appear in selected channels + * - we just need the F-Curves present */ - // XXX what was the selection check here for again? - if ( (!(filter_mode & ANIMFILTER_VISIBLE) || EXPANDED_AGRP(agrp)) || - ( /*ANIMCHANNEL_SELOK(SEL_AGRP(agrp)) &&*/ (filter_mode & ANIMFILTER_CURVESONLY) ) ) + if ( (!(filter_mode & ANIMFILTER_VISIBLE) || EXPANDED_AGRP(agrp)) || (filter_mode & ANIMFILTER_CURVESONLY) ) { - if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) { - // XXX the 'owner' info here needs review... - items += animdata_filter_fcurves(anim_data, agrp->channels.first, agrp, owner, ownertype, filter_mode, owner_id); - - /* remove group from filtered list if last element is group - * (i.e. only if group had channels, which were all hidden) - */ - // XXX this is really hacky... it should be fixed in a much more elegant way! - if ( (ale) && (anim_data->last == ale) && - (ale->data == agrp) && (agrp->channels.first) ) - { - BLI_freelinkN(anim_data, ale); - items--; + /* for the Graph Editor, curves may be set to not be visible in the view to lessen clutter, + * but to do this, we need to check that the group doesn't have it's not-visible flag set preventing + * all its sub-curves to be shown + */ + if ( !(filter_mode & ANIMFILTER_CURVEVISIBLE) || !(agrp->flag & AGRP_NOTVISIBLE) ) + { + if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) { + // XXX the 'owner' info here needs review... + items += animdata_filter_fcurves(anim_data, agrp->channels.first, agrp, owner, ownertype, filter_mode, owner_id); + + /* remove group from filtered list if last element is group + * (i.e. only if group had channels, which were all hidden) + */ + // XXX this is really hacky... it should be fixed in a much more elegant way! + if ( (ale) && (anim_data->last == ale) && + (ale->data == agrp) && (agrp->channels.first) ) + { + BLI_freelinkN(anim_data, ale); + items--; + } } } } @@ -610,6 +722,83 @@ static int animdata_filter_action (ListBase *anim_data, bAction *act, int filter return items; } +/* Include NLA-Data for NLA-Editor: + * - when ANIMFILTER_CHANNELS is used, that means we should be filtering the list for display + * Although the evaluation order is from the first track to the last and then apply the Action on top, + * we present this in the UI as the Active Action followed by the last track to the first so that we + * get the evaluation order presented as per a stack. + * - for normal filtering (i.e. for editing), we only need the NLA-tracks but they can be in 'normal' evaluation + * order, i.e. first to last. Otherwise, some tools may get screwed up. + */ +static int animdata_filter_nla (ListBase *anim_data, AnimData *adt, int filter_mode, void *owner, short ownertype, ID *owner_id) +{ + bAnimListElem *ale; + NlaTrack *nlt; + NlaTrack *first=NULL, *next=NULL; + int items = 0; + + /* if showing channels, include active action */ + if (filter_mode & ANIMFILTER_CHANNELS) { + /* there isn't really anything editable here, so skip if need editable */ + // TODO: currently, selection isn't checked since it doesn't matter + if ((filter_mode & ANIMFILTER_FOREDIT) == 0) { + /* just add the action track now (this MUST appear for drawing) + * - as AnimData may not have an action, we pass a dummy pointer just to get the list elem created, then + * overwrite this with the real value - REVIEW THIS... + */ + ale= make_new_animlistelem((void *)(&adt->action), ANIMTYPE_NLAACTION, owner, ownertype, owner_id); + ale->data= (adt->action) ? adt->action : NULL; + + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + + /* first track to include will be the last one if we're filtering by channels */ + first= adt->nla_tracks.last; + } + else { + /* first track to include will the the first one (as per normal) */ + first= adt->nla_tracks.first; + } + + /* loop over NLA Tracks - assume that the caller of this has already checked that these should be included */ + for (nlt= first; nlt; nlt= next) { + /* 'next' NLA-Track to use depends on whether we're filtering for drawing or not */ + if (filter_mode & ANIMFILTER_CHANNELS) + next= nlt->prev; + else + next= nlt->next; + + /* if we're in NLA-tweakmode, don't show this track if it was disabled (due to tweaking) for now + * - active track should still get shown though (even though it has disabled flag set) + */ + // FIXME: the channels after should still get drawn, just 'differently', and after an active-action channel + if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED) && !(nlt->flag & NLATRACK_ACTIVE)) + continue; + + /* only work with this channel and its subchannels if it is editable */ + if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_NLT(nlt)) { + /* only include this track if selected in a way consistent with the filtering requirements */ + if ( ANIMCHANNEL_SELOK(SEL_NLT(nlt)) ) { + /* only include if this track is active */ + if (!(filter_mode & ANIMFILTER_ACTIVE) || (nlt->flag & NLATRACK_ACTIVE)) { + ale= make_new_animlistelem(nlt, ANIMTYPE_NLATRACK, owner, ownertype, owner_id); + + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + } + } + } + + /* return the number of items added to the list */ + return items; +} + static int animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_mode, void *owner, short ownertype, ID *owner_id) { bAnimListElem *ale; @@ -752,19 +941,19 @@ static int animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads, /* firstly check that we actuallly have some materials, by gathering all materials in a temp list */ for (a=0; a < ob->totcol; a++) { Material *ma= give_current_material(ob, a); + short ok = 0; /* for now, if no material returned, skip (this shouldn't confuse the user I hope) */ if (ELEM(NULL, ma, ma->adt)) continue; - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) { - if (ANIMDATA_HAS_KEYS(ma) == 0) - continue; - } - else { - if (ANIMDATA_HAS_DRIVERS(ma) == 0) - continue; - } + /* check if ok */ + ANIMDATA_FILTER_CASES(ma, + { /* AnimData blocks - do nothing... */ }, + ok=1;, + ok=1;, + ok=1;) + if (ok == 0) continue; /* make a temp list elem for this */ ld= MEM_callocN(sizeof(LinkData), "DopeSheet-MaterialCache"); @@ -801,16 +990,13 @@ static int animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads, } } - /* add material's F-Curve or Driver channels? */ + /* add material's animation data */ if (FILTER_MAT_OBJD(ma) || (filter_mode & ANIMFILTER_CURVESONLY)) { - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) { - // XXX the 'owner' info here is still subject to improvement - items += animdata_filter_action(anim_data, ma->adt->action, filter_mode, ma, ANIMTYPE_DSMAT, (ID *)ma); - } - else { - // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?) - items += animdata_filter_fcurves(anim_data, ma->adt->drivers.first, NULL, ma, ANIMTYPE_DSMAT, filter_mode, (ID *)ma); - } + ANIMDATA_FILTER_CASES(ma, + { /* AnimData blocks - do nothing... */ }, + items += animdata_filter_nla(anim_data, ma->adt, filter_mode, ma, ANIMTYPE_DSMAT, (ID *)ma);, + items += animdata_filter_fcurves(anim_data, ma->adt->drivers.first, NULL, ma, ANIMTYPE_DSMAT, filter_mode, (ID *)ma);, + items += animdata_filter_action(anim_data, ma->adt->action, filter_mode, ma, ANIMTYPE_DSMAT, (ID *)ma);) } } } @@ -871,15 +1057,12 @@ static int animdata_filter_dopesheet_obdata (ListBase *anim_data, bDopeSheet *ad /* add object-data animation channels? */ if ((expanded) || (filter_mode & ANIMFILTER_CURVESONLY)) { - /* Action or Drivers? */ - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) { - // XXX the 'owner' info here is still subject to improvement - items += animdata_filter_action(anim_data, iat->adt->action, filter_mode, iat, type, (ID *)iat); - } - else { - // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?) - items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, iat, type, filter_mode, (ID *)iat); - } + /* filtering for channels - nla, drivers, keyframes */ + ANIMDATA_FILTER_CASES(iat, + { /* AnimData blocks - do nothing... */ }, + items+= animdata_filter_nla(anim_data, iat->adt, filter_mode, iat, type, (ID *)iat);, + items+= animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, iat, type, filter_mode, (ID *)iat);, + items += animdata_filter_action(anim_data, iat->adt->action, filter_mode, iat, type, (ID *)iat);) } /* return the number of items added to the list */ @@ -889,12 +1072,14 @@ static int animdata_filter_dopesheet_obdata (ListBase *anim_data, bDopeSheet *ad static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode) { bAnimListElem *ale=NULL; + AnimData *adt = NULL; Object *ob= base->object; Key *key= ob_get_key(ob); + short obdata_ok = 0; int items = 0; /* add this object as a channel first */ - if ((filter_mode & ANIMFILTER_CURVESONLY) == 0) { + if ((filter_mode & (ANIMFILTER_CURVESONLY|ANIMFILTER_NLATRACKS)) == 0) { /* check if filtering by selection */ if ANIMCHANNEL_SELOK((base->flag & SELECT)) { ale= make_new_animlistelem(base, ANIMTYPE_OBJECT, NULL, ANIMTYPE_NONE, NULL); @@ -906,76 +1091,64 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B } /* if collapsed, don't go any further (unless adding keyframes only) */ - if ( (EXPANDED_OBJC(ob) == 0) && !(filter_mode & ANIMFILTER_CURVESONLY) ) + if ( (EXPANDED_OBJC(ob) == 0) && !(filter_mode & (ANIMFILTER_CURVESONLY|ANIMFILTER_NLATRACKS)) ) return items; - /* Action or Drivers */ - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) { - /* Action? */ - if (ANIMDATA_HAS_KEYS(ob) /*&& !(ads->filterflag & ADS_FILTER_NOACTS)*/) { - AnimData *adt= ob->adt; - - /* include action-expand widget? */ - if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { - ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, base, ANIMTYPE_OBJECT, (ID *)ob); - if (ale) { - BLI_addtail(anim_data, ale); - items++; + /* Action, Drivers, or NLA */ + if (ob->adt) { + adt= ob->adt; + ANIMDATA_FILTER_CASES(ob, + { /* AnimData blocks - do nothing... */ }, + { /* nla */ + /* add NLA tracks */ + items += animdata_filter_nla(anim_data, adt, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); + }, + { /* drivers */ + /* include drivers-expand widget? */ + if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { + ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, base, ANIMTYPE_OBJECT, (ID *)ob); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + + /* add F-Curve channels (drivers are F-Curves) */ + if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) { + // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?) + items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, ob, ANIMTYPE_OBJECT, filter_mode, (ID *)ob); + } + }, + { /* action (keyframes) */ + /* include action-expand widget? */ + if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { + ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, base, ANIMTYPE_OBJECT, (ID *)ob); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + + /* add F-Curve channels? */ + if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) { + // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?) + items += animdata_filter_action(anim_data, adt->action, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); } } - - /* add F-Curve channels? */ - if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) { - // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?) - items += animdata_filter_action(anim_data, adt->action, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); - } - } - } - else { - /* Drivers */ - if (ANIMDATA_HAS_DRIVERS(ob)) { - AnimData *adt= ob->adt; - - /* include drivers-expand widget? */ - if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { - ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, base, ANIMTYPE_OBJECT, (ID *)ob); - if (ale) { - BLI_addtail(anim_data, ale); - items++; - } - } - - /* add F-Curve channels (drivers are F-Curves) */ - if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) { - // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?) - items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, ob, ANIMTYPE_OBJECT, filter_mode, (ID *)ob); - } - } + ); } + /* ShapeKeys? */ if ((key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) { - /* Animation or Drivers */ - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) { - /* include shapekey-expand widget? */ - if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { - ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT, (ID *)ob); - if (ale) { - BLI_addtail(anim_data, ale); - items++; - } - } - - /* add channels */ - if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_CURVESONLY)) { - items += animdata_filter_shapekey(anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); - } - } - else { - /* Drivers */ - if (ANIMDATA_HAS_DRIVERS(key)) { - AnimData *adt= key->adt; - + adt= key->adt; + ANIMDATA_FILTER_CASES(key, + { /* AnimData blocks - do nothing... */ }, + { /* nla */ + /* add NLA tracks */ + items += animdata_filter_nla(anim_data, adt, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); + }, + { /* drivers */ /* include shapekey-expand widget? */ if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT, (ID *)ob); @@ -985,15 +1158,28 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B } } - /* add F-Curve channels (drivers are F-Curves) */ - if (FILTER_SKE_OBJD(key)/*EXPANDED_DRVD(adt)*/ || !(filter_mode & ANIMFILTER_CHANNELS)) { - // XXX owner info is messed up now... - items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, ob, ANIMTYPE_OBJECT, filter_mode, (ID *)key); + /* add channels */ + if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_CURVESONLY)) { + items += animdata_filter_shapekey(anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); + } + }, + { /* action (keyframes) */ + /* include shapekey-expand widget? */ + if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { + ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT, (ID *)ob); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + + /* add channels */ + if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_CURVESONLY)) { + items += animdata_filter_shapekey(anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); } } - } + ); } - /* Materials? */ if ((ob->totcol) && !(ads->filterflag & ADS_FILTER_NOMAT)) @@ -1006,14 +1192,11 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B Camera *ca= (Camera *)ob->data; if ((ads->filterflag & ADS_FILTER_NOCAM) == 0) { - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) { - if (ANIMDATA_HAS_KEYS(ca)) - items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode); - } - else { - if (ANIMDATA_HAS_DRIVERS(ca)) - items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode); - } + ANIMDATA_FILTER_CASES(ca, + { /* AnimData blocks - do nothing... */ }, + obdata_ok= 1;, + obdata_ok= 1;, + obdata_ok= 1;) } } break; @@ -1022,14 +1205,11 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B Lamp *la= (Lamp *)ob->data; if ((ads->filterflag & ADS_FILTER_NOLAM) == 0) { - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) { - if (ANIMDATA_HAS_KEYS(la)) - items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode); - } - else { - if (ANIMDATA_HAS_DRIVERS(la)) - items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode); - } + ANIMDATA_FILTER_CASES(la, + { /* AnimData blocks - do nothing... */ }, + obdata_ok= 1;, + obdata_ok= 1;, + obdata_ok= 1;) } } break; @@ -1038,18 +1218,17 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B Curve *cu= (Curve *)ob->data; if ((ads->filterflag & ADS_FILTER_NOCUR) == 0) { - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) { - if (ANIMDATA_HAS_KEYS(cu)) - items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode); - } - else { - if (ANIMDATA_HAS_DRIVERS(cu)) - items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode); - } + ANIMDATA_FILTER_CASES(cu, + { /* AnimData blocks - do nothing... */ }, + obdata_ok= 1;, + obdata_ok= 1;, + obdata_ok= 1;) } } break; } + if (obdata_ok) + items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode); /* return the number of items added to the list */ return items; @@ -1058,11 +1237,12 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B static int animdata_filter_dopesheet_scene (ListBase *anim_data, bDopeSheet *ads, Scene *sce, int filter_mode) { World *wo= sce->world; + AnimData *adt= NULL; bAnimListElem *ale; int items = 0; /* add scene as a channel first (even if we aren't showing scenes we still need to show the scene's sub-data */ - if ((filter_mode & ANIMFILTER_CURVESONLY) == 0) { + if ((filter_mode & (ANIMFILTER_CURVESONLY|ANIMFILTER_NLATRACKS)) == 0) { /* check if filtering by selection */ if (ANIMCHANNEL_SELOK( (sce->flag & SCE_DS_SELECTED) )) { ale= make_new_animlistelem(sce, ANIMTYPE_SCENE, NULL, ANIMTYPE_NONE, NULL); @@ -1074,77 +1254,63 @@ static int animdata_filter_dopesheet_scene (ListBase *anim_data, bDopeSheet *ads } /* if collapsed, don't go any further (unless adding keyframes only) */ - if ( (EXPANDED_SCEC(sce) == 0) && !(filter_mode & ANIMFILTER_CURVESONLY) ) + if ( (EXPANDED_SCEC(sce) == 0) && !(filter_mode & (ANIMFILTER_CURVESONLY|ANIMFILTER_NLATRACKS)) ) return items; - /* Action or Drivers */ - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) { - /* Action? */ - if (ANIMDATA_HAS_KEYS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE)) { - AnimData *adt= sce->adt; - - /* include action-expand widget? */ - if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { - ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, sce, ANIMTYPE_SCENE, (ID *)sce); - if (ale) { - BLI_addtail(anim_data, ale); - items++; + /* Action, Drivers, or NLA for Scene */ + if ((ads->filterflag & ADS_FILTER_NOSCE) == 0) { + adt= sce->adt; + ANIMDATA_FILTER_CASES(sce, + { /* AnimData blocks - do nothing... */ }, + { /* nla */ + /* add NLA tracks */ + items += animdata_filter_nla(anim_data, adt, filter_mode, sce, ANIMTYPE_SCENE, (ID *)sce); + }, + { /* drivers */ + /* include drivers-expand widget? */ + if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { + ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, sce, ANIMTYPE_SCENE, (ID *)sce); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + + /* add F-Curve channels (drivers are F-Curves) */ + if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) { + items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, sce, ANIMTYPE_SCENE, filter_mode, (ID *)sce); + } + }, + { /* action */ + /* include action-expand widget? */ + if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { + ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, sce, ANIMTYPE_SCENE, (ID *)sce); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + + /* add F-Curve channels? */ + if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) { + items += animdata_filter_action(anim_data, adt->action, filter_mode, sce, ANIMTYPE_SCENE, (ID *)sce); } } - - /* add F-Curve channels? */ - if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) { - items += animdata_filter_action(anim_data, adt->action, filter_mode, sce, ANIMTYPE_SCENE, (ID *)sce); - } - } + ) } - else { - /* Drivers */ - if (ANIMDATA_HAS_DRIVERS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE)) { - AnimData *adt= sce->adt; - - /* include drivers-expand widget? */ - if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { - ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, sce, ANIMTYPE_SCENE, (ID *)sce); - if (ale) { - BLI_addtail(anim_data, ale); - items++; - } - } - - /* add F-Curve channels (drivers are F-Curves) */ - if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) { - items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, sce, ANIMTYPE_SCENE, filter_mode, (ID *)sce); - } - } - } - + /* world */ if ((wo && wo->adt) && !(ads->filterflag & ADS_FILTER_NOWOR)) { - /* Animation or Drivers */ - if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) { - AnimData *adt= wo->adt; - - /* include world-expand widget? */ - if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { - ale= make_new_animlistelem(wo, ANIMTYPE_DSWOR, sce, ANIMTYPE_SCENE, (ID *)sce); - if (ale) { - BLI_addtail(anim_data, ale); - items++; - } - } - - /* add channels */ - if (FILTER_WOR_SCED(wo) || (filter_mode & ANIMFILTER_CURVESONLY)) { - items += animdata_filter_action(anim_data, adt->action, filter_mode, wo, ANIMTYPE_DSWOR, (ID *)wo); - } - } - else { - /* Drivers */ - if (ANIMDATA_HAS_DRIVERS(wo)) { - AnimData *adt= wo->adt; - - /* include shapekey-expand widget? */ + /* Action, Drivers, or NLA for World */ + adt= wo->adt; + ANIMDATA_FILTER_CASES(wo, + { /* AnimData blocks - do nothing... */ }, + { /* nla */ + /* add NLA tracks */ + items += animdata_filter_nla(anim_data, adt, filter_mode, wo, ANIMTYPE_DSWOR, (ID *)wo); + }, + { /* drivers */ + /* include world-expand widget? */ if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { ale= make_new_animlistelem(wo, ANIMTYPE_DSWOR, sce, ANIMTYPE_SCENE, (ID *)wo); if (ale) { @@ -1158,8 +1324,23 @@ static int animdata_filter_dopesheet_scene (ListBase *anim_data, bDopeSheet *ads // XXX owner info is messed up now... items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, wo, ANIMTYPE_DSWOR, filter_mode, (ID *)wo); } + }, + { /* action */ + /* include world-expand widget? */ + if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) { + ale= make_new_animlistelem(wo, ANIMTYPE_DSWOR, sce, ANIMTYPE_SCENE, (ID *)sce); + if (ale) { + BLI_addtail(anim_data, ale); + items++; + } + } + + /* add channels */ + if (FILTER_WOR_SCED(wo) || (filter_mode & ANIMFILTER_CURVESONLY)) { + items += animdata_filter_action(anim_data, adt->action, filter_mode, wo, ANIMTYPE_DSWOR, (ID *)wo); + } } - } + ) } /* return the number of items added to the list */ @@ -1171,6 +1352,7 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int { Scene *sce= (Scene *)ads->source; Base *base; + bAnimListElem *ale; int items = 0; /* check that we do indeed have a scene */ @@ -1182,22 +1364,32 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int /* scene-linked animation */ // TODO: sequencer, composite nodes - are we to include those here too? { - short sceOk, worOk; + short sceOk= 0, worOk= 0; /* check filtering-flags if ok */ - if (ads->filterflag) { - if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) { - sceOk= (ANIMDATA_HAS_DRIVERS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE)); - worOk= ((sce->world) && ANIMDATA_HAS_DRIVERS(sce->world) && !(ads->filterflag & ADS_FILTER_NOWOR)); - } - else { - sceOk= (ANIMDATA_HAS_KEYS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE)); - worOk= ((sce->world) && ANIMDATA_HAS_KEYS(sce->world) && !(ads->filterflag & ADS_FILTER_NOWOR)); - } - } - else { - sceOk= (ANIMDATA_HAS_KEYS(sce)); - worOk= ((sce->world) && ANIMDATA_HAS_KEYS(sce->world)); + ANIMDATA_FILTER_CASES(sce, + { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(sce); + sceOk=0; + }, + sceOk= !(ads->filterflag & ADS_FILTER_NOSCE);, + sceOk= !(ads->filterflag & ADS_FILTER_NOSCE);, + sceOk= !(ads->filterflag & ADS_FILTER_NOSCE);) + if (sce->world) { + ANIMDATA_FILTER_CASES(sce->world, + { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(sce->world); + worOk=0; + }, + worOk= !(ads->filterflag & ADS_FILTER_NOWOR);, + worOk= !(ads->filterflag & ADS_FILTER_NOWOR);, + worOk= !(ads->filterflag & ADS_FILTER_NOWOR);) } /* check if not all bad (i.e. so there is something to show) */ @@ -1239,13 +1431,33 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int } /* check filters for datatypes */ - if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) { - actOk= (ANIMDATA_HAS_DRIVERS(ob)); - keyOk= ((key) && ANIMDATA_HAS_DRIVERS(key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)); - } - else { - actOk= ANIMDATA_HAS_KEYS(ob); - keyOk= ((key) && ANIMDATA_HAS_KEYS(key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)); + /* object */ + actOk= 0; + keyOk= 0; + ANIMDATA_FILTER_CASES(ob, + { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(ob); + actOk=0; + }, + actOk= 1;, + actOk= 1;, + actOk= 1;) + if (key) { + /* shapekeys */ + ANIMDATA_FILTER_CASES(key, + { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(key); + keyOk=0; + }, + keyOk= 1;, + keyOk= 1;, + keyOk= 1;) } /* materials - only for geometric types */ @@ -1260,18 +1472,20 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int Material *ma= give_current_material(ob, a); /* if material has relevant animation data, break */ - if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) { - if (ANIMDATA_HAS_DRIVERS(ma)) { - matOk= 1; - break; - } - } - else { - if (ANIMDATA_HAS_KEYS(ma)) { - matOk= 1; - break; - } - } + ANIMDATA_FILTER_CASES(ma, + { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(ma); + matOk=0; + }, + matOk= 1;, + matOk= 1;, + matOk= 1;) + + if (matOk) + break; } } @@ -1280,19 +1494,52 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int case OB_CAMERA: /* ------- Camera ------------ */ { Camera *ca= (Camera *)ob->data; - if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) - dataOk= (ANIMDATA_HAS_DRIVERS(ca) && !(ads->filterflag & ADS_FILTER_NOCAM)); - else - dataOk= (ANIMDATA_HAS_KEYS(ca) && !(ads->filterflag & ADS_FILTER_NOCAM)); + dataOk= 0; + ANIMDATA_FILTER_CASES(ca, + if ((ads->filterflag & ADS_FILTER_NOCAM)==0) { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(ca); + dataOk=0; + }, + dataOk= !(ads->filterflag & ADS_FILTER_NOCAM);, + dataOk= !(ads->filterflag & ADS_FILTER_NOCAM);, + dataOk= !(ads->filterflag & ADS_FILTER_NOCAM);) } break; case OB_LAMP: /* ---------- Lamp ----------- */ { Lamp *la= (Lamp *)ob->data; - if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) - dataOk= (ANIMDATA_HAS_DRIVERS(la) && !(ads->filterflag & ADS_FILTER_NOLAM)); - else - dataOk= (ANIMDATA_HAS_KEYS(la) && !(ads->filterflag & ADS_FILTER_NOLAM)); + dataOk= 0; + ANIMDATA_FILTER_CASES(la, + if ((ads->filterflag & ADS_FILTER_NOLAM)==0) { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(la); + dataOk=0; + }, + dataOk= !(ads->filterflag & ADS_FILTER_NOLAM);, + dataOk= !(ads->filterflag & ADS_FILTER_NOLAM);, + dataOk= !(ads->filterflag & ADS_FILTER_NOLAM);) + } + break; + case OB_CURVE: /* ------- Curve ---------- */ + { + Curve *cu= (Curve *)ob->data; + dataOk= 0; + ANIMDATA_FILTER_CASES(cu, + if ((ads->filterflag & ADS_FILTER_NOCUR)==0) { + /* for the special AnimData blocks only case, we only need to add + * the block if it is valid... then other cases just get skipped (hence ok=0) + */ + ANIMDATA_ADD_ANIMDATA(cu); + dataOk=0; + }, + dataOk= !(ads->filterflag & ADS_FILTER_NOCUR);, + dataOk= !(ads->filterflag & ADS_FILTER_NOCUR);, + dataOk= !(ads->filterflag & ADS_FILTER_NOCUR);) } break; default: /* --- other --- */ @@ -1400,6 +1647,7 @@ int ANIM_animdata_filter (bAnimContext *ac, ListBase *anim_data, int filter_mode case ANIMCONT_DOPESHEET: case ANIMCONT_FCURVES: case ANIMCONT_DRIVERS: + case ANIMCONT_NLA: items= animdata_filter_dopesheet(anim_data, data, filter_mode); break; } diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index e899cc1d520..10691c558bf 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -83,10 +83,12 @@ static void change_frame_apply(bContext *C, wmOperator *op) Scene *scene= CTX_data_scene(C); int cfra; - /* get frame, and clamp to MINFRAME */ + /* get frame, and clamp to MINAFRAME + * - not MINFRAME, since it's useful to be able to key a few-frames back + */ cfra= RNA_int_get(op->ptr, "frame"); - if (cfra < MINFRAME) cfra= MINFRAME; + if (cfra < MINAFRAME) cfra= MINAFRAME; CFRA= cfra; WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene); @@ -209,7 +211,7 @@ void ANIM_OT_change_frame(wmOperatorType *ot) ot->modal= change_frame_modal; /* rna */ - RNA_def_int(ot->srna, "frame", 0, 1, MAXFRAME, "Frame", "", 1, MAXFRAME); + RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME); } /* ****************** set preview range operator ****************************/ diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 9c401289011..fdce0965ce3 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -1,5 +1,30 @@ -/* Testing code for 2.5 animation system - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #include @@ -94,7 +119,7 @@ FCurve *verify_driver_fcurve (ID *id, const char rna_path[], const int array_ind fcu->driver= MEM_callocN(sizeof(ChannelDriver), "ChannelDriver"); /* add simple generator modifier for driver so that there is some visible representation */ - fcurve_add_modifier(fcu, FMODIFIER_TYPE_GENERATOR); + add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR); /* just add F-Curve to end of driver list */ BLI_addtail(&adt->drivers, fcu); diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c new file mode 100644 index 00000000000..7a618f4d222 --- /dev/null +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -0,0 +1,679 @@ +/** + * $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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* User-Interface Stuff for F-Modifiers: + * This file defines the (C-Coded) templates + editing callbacks needed + * by the interface stuff or F-Modifiers, as used by F-Curves in the Graph Editor, + * and NLA-Strips in the NLA Editor. + */ + +#include +#include +#include +#include + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_object.h" +#include "BKE_global.h" +#include "BKE_nla.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "BIF_gl.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_util.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +// XXX! -------------------------------- +/* temporary definition for limits of float number buttons (FLT_MAX tends to infinity with old system) */ +#define UI_FLT_MAX 10000.0f + +/* ********************************************** */ + +#define B_REDR 1 +#define B_FMODIFIER_REDRAW 20 + +/* macro for use here to draw background box and set height */ +// XXX for now, roundbox has it's callback func set to NULL to not intercept events +#define DRAW_BACKDROP(height) \ + { \ + uiDefBut(block, ROUNDBOX, B_REDR, "", -3, yco-height, width+3, height-1, NULL, 5.0, 0.0, 12.0, (float)rb_col, ""); \ + } + +/* callback to verify modifier data */ +static void validate_fmodifier_cb (bContext *C, void *fcm_v, void *dummy) +{ + FModifier *fcm= (FModifier *)fcm_v; + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + + /* call the verify callback on the modifier if applicable */ + if (fmi && fmi->verify_data) + fmi->verify_data(fcm); +} + +/* callback to set the active modifier */ +static void activate_fmodifier_cb (bContext *C, void *fmods_v, void *fcm_v) +{ + ListBase *modifiers = (ListBase *)fmods_v; + FModifier *fcm= (FModifier *)fcm_v; + + /* call API function to set the active modifier for active modifier-stack */ + set_active_fmodifier(modifiers, fcm); +} + +/* callback to remove the given modifier */ +static void delete_fmodifier_cb (bContext *C, void *fmods_v, void *fcm_v) +{ + ListBase *modifiers = (ListBase *)fmods_v; + FModifier *fcm= (FModifier *)fcm_v; + + /* remove the given F-Modifier from the active modifier-stack */ + remove_fmodifier(modifiers, fcm); +} + +/* --------------- */ + +/* draw settings for generator modifier */ +static void draw_modifier__generator(uiLayout *layout, ID *id, FModifier *fcm, short width) +{ + FMod_Generator *data= (FMod_Generator *)fcm->data; + uiLayout *col, *row; + uiBlock *block; + uiBut *but; + PointerRNA ptr; + + /* init the RNA-pointer */ + RNA_pointer_create(id, &RNA_FModifierFunctionGenerator, fcm, &ptr); + + /* basic settings (backdrop + mode selector + some padding) */ + col= uiLayoutColumn(layout, 1); + block= uiLayoutGetBlock(layout); + uiBlockBeginAlign(block); + but= uiDefButR(block, MENU, B_FMODIFIER_REDRAW, NULL, 0, 0, width-30, UI_UNIT_Y, &ptr, "mode", -1, 0, 0, -1, -1, NULL); + uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL); + + uiDefButR(block, TOG, B_FMODIFIER_REDRAW, NULL, 0, 0, width-30, UI_UNIT_Y, &ptr, "additive", -1, 0, 0, -1, -1, NULL); + uiBlockEndAlign(block); + + /* now add settings for individual modes */ + switch (data->mode) { + case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */ + { + float *cp = NULL; + char xval[32]; + unsigned int i; + + /* draw polynomial order selector */ + row= uiLayoutRow(layout, 0); + block= uiLayoutGetBlock(row); + but= uiDefButI(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 10,0,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1"); + uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL); + + + /* draw controls for each coefficient and a + sign at end of row */ + row= uiLayoutRow(layout, 1); + block= uiLayoutGetBlock(row); + uiDefBut(block, LABEL, 1, "y = ", 0, 0, 50, 20, NULL, 0.0, 0.0, 0, 0, ""); + + cp= data->coefficients; + for (i=0; (i < data->arraysize) && (cp); i++, cp++) { + /* coefficient */ + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 0, 0, 150, 20, cp, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient for polynomial"); + + /* 'x' param (and '+' if necessary) */ + if (i) { + if (i == 1) + strcpy(xval, "x"); + else + sprintf(xval, "x^%d", i); + uiDefBut(block, LABEL, 1, xval, 0, 0, 50, 20, NULL, 0.0, 0.0, 0, 0, "Power of x"); + } + + if ( (i != (data->arraysize - 1)) || ((i==0) && data->arraysize==2) ) { + uiDefBut(block, LABEL, 1, "+", 0,0 , 30, 20, NULL, 0.0, 0.0, 0, 0, ""); + + /* next coefficient on a new row */ + row= uiLayoutRow(layout, 1); + block= uiLayoutGetBlock(row); + } + } + } + break; + + case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial expression */ + { + float *cp = NULL; + unsigned int i; + + /* draw polynomial order selector */ + row= uiLayoutRow(layout, 0); + block= uiLayoutGetBlock(row); + but= uiDefButI(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 0,0,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1"); + uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL); + + + /* draw controls for each pair of coefficients */ + row= uiLayoutRow(layout, 1); + block= uiLayoutGetBlock(row); + uiDefBut(block, LABEL, 1, "y=", 0, 0, 50, 20, NULL, 0.0, 0.0, 0, 0, ""); + + cp= data->coefficients; + for (i=0; (i < data->poly_order) && (cp); i++, cp+=2) { + /* opening bracket */ + uiDefBut(block, LABEL, 1, "(", 0, 0, 20, 20, NULL, 0.0, 0.0, 0, 0, ""); + + /* coefficients */ + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 0, 0, 100, 20, cp, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient of x"); + + uiDefBut(block, LABEL, 1, "x+", 0, 0, 40, 20, NULL, 0.0, 0.0, 0, 0, ""); + + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 0, 0, 100, 20, cp+1, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Second coefficient"); + + /* closing bracket and '+' sign */ + if ( (i != (data->poly_order - 1)) || ((i==0) && data->poly_order==2) ) { + uiDefBut(block, LABEL, 1, ") +", 0, 0, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); + + /* set up new row for the next pair of coefficients*/ + row= uiLayoutRow(layout, 1); + block= uiLayoutGetBlock(row); + } + else + uiDefBut(block, LABEL, 1, ")", 0, 0, 20, 20, NULL, 0.0, 0.0, 0, 0, ""); + } + } + break; + } +} + +/* --------------- */ + +/* draw settings for noise modifier */ +static void draw_modifier__fn_generator(uiLayout *layout, ID *id, FModifier *fcm, short width) +{ + uiLayout *col; + PointerRNA ptr; + + /* init the RNA-pointer */ + RNA_pointer_create(id, &RNA_FModifierFunctionGenerator, fcm, &ptr); + + /* add the settings */ + col= uiLayoutColumn(layout, 1); + uiItemR(col, "", 0, &ptr, "type", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "additive", 0, 0, 1); + + col= uiLayoutColumn(layout, 0); // no grouping for now + uiItemR(col, NULL, 0, &ptr, "amplitude", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "phase_multiplier", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "phase_offset", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "value_offset", 0, 0, 0); +} + +/* --------------- */ + +/* draw settings for cycles modifier */ +static void draw_modifier__cycles(uiLayout *layout, ID *id, FModifier *fcm, short width) +{ + uiLayout *split, *col; + PointerRNA ptr; + + /* init the RNA-pointer */ + RNA_pointer_create(id, &RNA_FModifierCycles, fcm, &ptr); + + /* split into 2 columns + * NOTE: the mode comboboxes shouldn't get labels, otherwise there isn't enough room + */ + split= uiLayoutSplit(layout, 0.5f); + + /* before range */ + col= uiLayoutColumn(split, 1); + uiItemL(col, "Before:", 0); + uiItemR(col, "", 0, &ptr, "before_mode", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "before_cycles", 0, 0, 0); + + /* after range */ + col= uiLayoutColumn(split, 1); + uiItemL(col, "After:", 0); + uiItemR(col, "", 0, &ptr, "after_mode", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "after_cycles", 0, 0, 0); +} + +/* --------------- */ + +/* draw settings for noise modifier */ +static void draw_modifier__noise(uiLayout *layout, ID *id, FModifier *fcm, short width) +{ + uiLayout *split, *col; + PointerRNA ptr; + + /* init the RNA-pointer */ + RNA_pointer_create(id, &RNA_FModifierNoise, fcm, &ptr); + + /* blending mode */ + uiItemR(layout, NULL, 0, &ptr, "modification", 0, 0, 0); + + /* split into 2 columns */ + split= uiLayoutSplit(layout, 0.5f); + + /* col 1 */ + col= uiLayoutColumn(split, 0); + uiItemR(col, NULL, 0, &ptr, "size", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "strength", 0, 0, 0); + + /* col 2 */ + col= uiLayoutColumn(split, 0); + uiItemR(col, NULL, 0, &ptr, "phase", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "depth", 0, 0, 0); +} + +/* --------------- */ + +#define BINARYSEARCH_FRAMEEQ_THRESH 0.0001 + +/* Binary search algorithm for finding where to insert Envelope Data Point. + * Returns the index to insert at (data already at that index will be offset if replace is 0) + */ +static int binarysearch_fcm_envelopedata_index (FCM_EnvelopeData array[], float frame, int arraylen, short *exists) +{ + int start=0, end=arraylen; + int loopbreaker= 0, maxloop= arraylen * 2; + + /* initialise exists-flag first */ + *exists= 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_fcm_envelopedata_index() encountered invalid array \n"); + return 0; + } + else { + /* check whether to add before/after/on */ + float framenum; + + /* 'First' Point (when only one point, this case is used) */ + framenum= array[0].time; + if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) { + *exists = 1; + return 0; + } + else if (frame < framenum) + return 0; + + /* 'Last' Point */ + framenum= array[(arraylen-1)].time; + if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) { + *exists= 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 - start) / 2); /* we calculate the midpoint this way to avoid int overflows... */ + float midfra= array[mid].time; + + /* check if exactly equal to midpoint */ + if (IS_EQT(frame, midfra, BINARYSEARCH_FRAMEEQ_THRESH)) { + *exists = 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_fcm_envelopedata_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; +} + +/* callback to add new envelope data point */ +// TODO: should we have a separate file for things like this? +static void fmod_envelope_addpoint_cb (bContext *C, void *fcm_dv, void *dummy) +{ + Scene *scene= CTX_data_scene(C); + FMod_Envelope *env= (FMod_Envelope *)fcm_dv; + FCM_EnvelopeData *fedn; + FCM_EnvelopeData fed; + + /* init template data */ + fed.min= -1.0f; + fed.max= 1.0f; + fed.time= (float)scene->r.cfra; // XXX make this int for ease of use? + fed.f1= fed.f2= 0; + + /* check that no data exists for the current frame... */ + if (env->data) { + short exists = -1; + int i= binarysearch_fcm_envelopedata_index(env->data, (float)(scene->r.cfra), env->totvert, &exists); + + /* binarysearch_...() will set exists by default to 0, so if it is non-zero, that means that the point exists already */ + if (exists) + return; + + /* add new */ + fedn= MEM_callocN((env->totvert+1)*sizeof(FCM_EnvelopeData), "FCM_EnvelopeData"); + + /* add the points that should occur before the point to be pasted */ + if (i > 0) + memcpy(fedn, env->data, i*sizeof(FCM_EnvelopeData)); + + /* add point to paste at index i */ + *(fedn + i)= fed; + + /* add the points that occur after the point to be pasted */ + if (i < env->totvert) + memcpy(fedn+i+1, env->data+i, (env->totvert-i)*sizeof(FCM_EnvelopeData)); + + /* replace (+ free) old with new */ + MEM_freeN(env->data); + env->data= fedn; + + env->totvert++; + } + else { + env->data= MEM_callocN(sizeof(FCM_EnvelopeData), "FCM_EnvelopeData"); + *(env->data)= fed; + + env->totvert= 1; + } +} + +/* callback to remove envelope data point */ +// TODO: should we have a separate file for things like this? +static void fmod_envelope_deletepoint_cb (bContext *C, void *fcm_dv, void *ind_v) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm_dv; + FCM_EnvelopeData *fedn; + int index= GET_INT_FROM_POINTER(ind_v); + + /* check that no data exists for the current frame... */ + if (env->totvert > 1) { + /* allocate a new smaller array */ + fedn= MEM_callocN(sizeof(FCM_EnvelopeData)*(env->totvert-1), "FCM_EnvelopeData"); + + memcpy(fedn, &env->data, sizeof(FCM_EnvelopeData)*(index)); + memcpy(&fedn[index], &env->data[index+1], sizeof(FCM_EnvelopeData)*(env->totvert-index-1)); + + /* free old array, and set the new */ + MEM_freeN(env->data); + env->data= fedn; + env->totvert--; + } + else { + /* just free array, since the only vert was deleted */ + if (env->data) + MEM_freeN(env->data); + env->totvert= 0; + } +} + +/* draw settings for envelope modifier */ +static void draw_modifier__envelope(uiLayout *layout, ID *id, FModifier *fcm, short width) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + FCM_EnvelopeData *fed; + uiLayout *col, *row; + uiBlock *block; + uiBut *but; + PointerRNA ptr; + int i; + + /* init the RNA-pointer */ + RNA_pointer_create(id, &RNA_FModifierEnvelope, fcm, &ptr); + + /* general settings */ + col= uiLayoutColumn(layout, 1); + uiItemL(col, "Envelope:", 0); + uiItemR(col, NULL, 0, &ptr, "reference_value", 0, 0, 0); + + row= uiLayoutRow(col, 1); + uiItemR(row, "Min", 0, &ptr, "default_minimum", 0, 0, 0); + uiItemR(row, "Max", 0, &ptr, "default_maximum", 0, 0, 0); + + /* control points header */ + // TODO: move this control-point control stuff to using the new special widgets for lists + // the current way is far too cramped + row= uiLayoutRow(layout, 0); + block= uiLayoutGetBlock(row); + + uiDefBut(block, LABEL, 1, "Control Points:", 0, 0, 150, 20, NULL, 0.0, 0.0, 0, 0, ""); + + but= uiDefBut(block, BUT, B_FMODIFIER_REDRAW, "Add Point", 0,0,150,19, NULL, 0, 0, 0, 0, "Adds a new control-point to the envelope on the current frame"); + uiButSetFunc(but, fmod_envelope_addpoint_cb, env, NULL); + + /* control points list */ + for (i=0, fed=env->data; i < env->totvert; i++, fed++) { + /* get a new row to operate on */ + row= uiLayoutRow(layout, 1); + block= uiLayoutGetBlock(row); + + uiBlockBeginAlign(block); + but=uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Fra:", 0, 0, 90, 20, &fed->time, -UI_FLT_MAX, UI_FLT_MAX, 10, 1, "Frame that envelope point occurs"); + uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL); + + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 0, 0, 100, 20, &fed->min, -UI_FLT_MAX, UI_FLT_MAX, 10, 2, "Minimum bound of envelope at this point"); + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 0, 0, 100, 20, &fed->max, -UI_FLT_MAX, UI_FLT_MAX, 10, 2, "Maximum bound of envelope at this point"); + + but= uiDefIconBut(block, BUT, B_FMODIFIER_REDRAW, ICON_X, 0, 0, 18, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Delete envelope control point"); + uiButSetFunc(but, fmod_envelope_deletepoint_cb, env, SET_INT_IN_POINTER(i)); + uiBlockBeginAlign(block); + } +} + +/* --------------- */ + +/* draw settings for limits modifier */ +static void draw_modifier__limits(uiLayout *layout, ID *id, FModifier *fcm, short width) +{ + uiLayout *split, *col, *row; + PointerRNA ptr; + + /* init the RNA-pointer */ + RNA_pointer_create(id, &RNA_FModifierLimits, fcm, &ptr); + + /* row 1: minimum */ + { + row= uiLayoutRow(layout, 0); + + /* split into 2 columns */ + split= uiLayoutSplit(layout, 0.5f); + + /* x-minimum */ + col= uiLayoutColumn(split, 1); + uiItemR(col, NULL, 0, &ptr, "use_minimum_x", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "minimum_x", 0, 0, 0); + + /* y-minimum*/ + col= uiLayoutColumn(split, 1); + uiItemR(col, NULL, 0, &ptr, "use_minimum_y", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "minimum_y", 0, 0, 0); + } + + /* row 2: minimum */ + { + row= uiLayoutRow(layout, 0); + + /* split into 2 columns */ + split= uiLayoutSplit(layout, 0.5f); + + /* x-minimum */ + col= uiLayoutColumn(split, 1); + uiItemR(col, NULL, 0, &ptr, "use_maximum_x", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "maximum_x", 0, 0, 0); + + /* y-minimum*/ + col= uiLayoutColumn(split, 1); + uiItemR(col, NULL, 0, &ptr, "use_maximum_y", 0, 0, 0); + uiItemR(col, NULL, 0, &ptr, "maximum_y", 0, 0, 0); + } +} + +/* --------------- */ + + +void ANIM_uiTemplate_fmodifier_draw (uiLayout *layout, ID *id, ListBase *modifiers, FModifier *fcm) +{ + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + uiLayout *box, *row, *subrow; + uiBlock *block; + uiBut *but; + short width= 314; + + /* draw header */ + { + /* get layout-row + UI-block for this */ + box= uiLayoutBox(layout); + + row= uiLayoutRow(box, 0); + block= uiLayoutGetBlock(row); // err... + + uiBlockSetEmboss(block, UI_EMBOSSN); + + /* left-align -------------------------------------------- */ + subrow= uiLayoutRow(row, 0); + uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_LEFT); + + /* expand */ + uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_EXPANDED, B_REDR, ICON_TRIA_RIGHT, 0, -1, UI_UNIT_X, UI_UNIT_Y, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is expanded."); + + /* checkbox for 'active' status (for now) */ + but= uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_ACTIVE, B_REDR, ICON_RADIOBUT_OFF, 0, -1, UI_UNIT_X, UI_UNIT_Y, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is active one."); + uiButSetFunc(but, activate_fmodifier_cb, modifiers, fcm); + + /* name */ + if (fmi) + uiDefBut(block, LABEL, 1, fmi->name, 0, 0, 150, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); + else + uiDefBut(block, LABEL, 1, "", 0, 0, 150, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); + + /* right-align ------------------------------------------- */ + subrow= uiLayoutRow(row, 0); + uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_RIGHT); + + /* 'mute' button */ + uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_MUTED, B_REDR, ICON_MUTE_IPO_OFF, 0, 0, UI_UNIT_X, UI_UNIT_Y, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is temporarily muted (not evaluated)."); + + /* delete button */ + but= uiDefIconBut(block, BUT, B_REDR, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, "Delete F-Curve Modifier."); + uiButSetFunc(but, delete_fmodifier_cb, modifiers, fcm); + + uiBlockSetEmboss(block, UI_EMBOSS); + } + + /* when modifier is expanded, draw settings */ + if (fcm->flag & FMODIFIER_FLAG_EXPANDED) { + /* set up the flexible-box layout which acts as the backdrop for the modifier settings */ + box= uiLayoutBox(layout); + + /* draw settings for individual modifiers */ + switch (fcm->type) { + case FMODIFIER_TYPE_GENERATOR: /* Generator */ + draw_modifier__generator(box, id, fcm, width); + break; + + case FMODIFIER_TYPE_FN_GENERATOR: /* Built-In Function Generator */ + draw_modifier__fn_generator(box, id, fcm, width); + break; + + case FMODIFIER_TYPE_CYCLES: /* Cycles */ + draw_modifier__cycles(box, id, fcm, width); + break; + + case FMODIFIER_TYPE_ENVELOPE: /* Envelope */ + draw_modifier__envelope(box, id, fcm, width); + break; + + case FMODIFIER_TYPE_LIMITS: /* Limits */ + draw_modifier__limits(box, id, fcm, width); + break; + + case FMODIFIER_TYPE_NOISE: /* Noise */ + draw_modifier__noise(box, id, fcm, width); + break; + + default: /* unknown type */ + break; + } + } +} + +/* ********************************************** */ diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index d0e83eeaec7..1db78beb2cb 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -51,7 +51,6 @@ #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" @@ -64,6 +63,7 @@ #include "DNA_gpencil_types.h" #include "DNA_windowmanager_types.h" #include "DNA_world_types.h" +#include "DNA_view2d_types.h" #include "BKE_action.h" #include "BKE_depsgraph.h" @@ -234,45 +234,87 @@ static ActKeyColumn *cfra_find_actkeycolumn (ListBase *keys, float cframe) 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 +/* coordinates for diamond shape */ +static const float _unit_diamond_shape[4][2] = { + {0.0f, 1.0f}, /* top vert */ + {1.0f, 0.0f}, /* mid-right */ + {0.0f, -1.0f}, /* bottom vert */ + {-1.0f, 0.0f} /* mid-left */ +}; -static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, float ypos) +/* draw a simple diamond shape with OpenGL */ +void draw_keyframe_shape (float x, float y, float xscale, float hsize, short sel, short mode) +{ + static GLuint displist1=0; + static GLuint displist2=0; + + /* initialise 2 display lists for diamond shape - one empty, one filled */ + if (displist1 == 0) { + displist1= glGenLists(1); + glNewList(displist1, GL_COMPILE); + + glBegin(GL_LINE_LOOP); + glVertex2fv(_unit_diamond_shape[0]); + glVertex2fv(_unit_diamond_shape[1]); + glVertex2fv(_unit_diamond_shape[2]); + glVertex2fv(_unit_diamond_shape[3]); + glEnd(); + glEndList(); + } + if (displist2 == 0) { + displist2= glGenLists(1); + glNewList(displist2, GL_COMPILE); + + glBegin(GL_QUADS); + glVertex2fv(_unit_diamond_shape[0]); + glVertex2fv(_unit_diamond_shape[1]); + glVertex2fv(_unit_diamond_shape[2]); + glVertex2fv(_unit_diamond_shape[3]); + glEnd(); + glEndList(); + } + + /* adjust view transform before starting */ + glTranslatef(x, y, 0.0f); + glScalef(1.0f/xscale*hsize, hsize, 1.0f); + + /* anti-aliased lines for more consistent appearance */ + glEnable(GL_LINE_SMOOTH); + + /* draw! */ + if ELEM(mode, KEYFRAME_SHAPE_INSIDE, KEYFRAME_SHAPE_BOTH) { + /* interior - hardcoded colors (for selected and unselected only) */ + if (sel) UI_ThemeColorShade(TH_STRIP_SELECT, 50); + else glColor3ub(0xE9, 0xE9, 0xE9); + + glCallList(displist2); + } + + if ELEM(mode, KEYFRAME_SHAPE_FRAME, KEYFRAME_SHAPE_BOTH) { + /* exterior - black frame */ + glColor3ub(0, 0, 0); + + glCallList(displist1); + } + + glDisable(GL_LINE_SMOOTH); + + /* restore view transform */ + glScalef(xscale/hsize, 1.0f/hsize, 1.0); + glTranslatef(-x, -y, 0.0f); +} + +static void draw_keylist(View2D *v2d, ListBase *keys, ListBase *blocks, float ypos) { ActKeyColumn *ak; ActKeyBlock *ab; + float xscale; glEnable(GL_BLEND); + /* get View2D scaling factor */ + UI_view2d_getscale(v2d, &xscale, NULL); + /* draw keyblocks */ if (blocks) { for (ab= blocks->first; ab; ab= ab->next) { @@ -292,18 +334,13 @@ static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, fl 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); + + glRectf(ab->start, ypos-5, ab->end, ypos+5); } } } @@ -311,18 +348,28 @@ static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, fl /* draw keys */ if (keys) { for (ak= keys->first; ak; ak= ak->next) { - int sc_x, sc_y; + /* draw using OpenGL - uglier but faster */ + // NOTE: a previous version of this didn't work nice for some intel cards + draw_keyframe_shape(ak->cfra, ypos, xscale, 5.0f, (ak->sel & SELECT), KEYFRAME_SHAPE_BOTH); + +#if 0 // OLD CODE + //int sc_x, sc_y; /* get co-ordinate to draw at */ - gla2DDrawTranslatePt(di, ak->cfra, ypos, &sc_x, &sc_y); + //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); + //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); +#endif // OLD CODE +#if 0 // NEW NON-WORKING CODE + /* draw icon */ + // FIXME: this draws slightly wrong, as we need to apply some offset for icon, but that depends on scaling + // so for now disabled + //int icon = (ak->sel & SELECT) ? ICON_SPACE2 : ICON_SPACE3; + //UI_icon_draw_aspect(ak->cfra, ypos-6, icon, 1.0f); +#endif // NEW NON-WORKING CODE - /* 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)); } } @@ -331,89 +378,86 @@ static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, fl /* *************************** Channel Drawing Funcs *************************** */ -void draw_scene_channel(gla2DDrawInfo *di, ActKeysInc *aki, Scene *sce, float ypos) +void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos) { ListBase keys = {0, 0}; ListBase blocks = {0, 0}; - scene_to_keylist(sce, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); + scene_to_keylist(ads, sce, &keys, &blocks); + draw_keylist(v2d, &keys, &blocks, ypos); BLI_freelistN(&keys); BLI_freelistN(&blocks); } -void draw_object_channel(gla2DDrawInfo *di, ActKeysInc *aki, Object *ob, float ypos) +void draw_object_channel(View2D *v2d, bDopeSheet *ads, 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); + ob_to_keylist(ads, ob, &keys, &blocks); + draw_keylist(v2d, &keys, &blocks, ypos); BLI_freelistN(&keys); BLI_freelistN(&blocks); } -void draw_fcurve_channel(gla2DDrawInfo *di, ActKeysInc *aki, FCurve *fcu, float ypos) +void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos) { ListBase keys = {0, 0}; ListBase blocks = {0, 0}; - fcurve_to_keylist(fcu, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); + fcurve_to_keylist(adt, fcu, &keys, &blocks); + draw_keylist(v2d, &keys, &blocks, ypos); BLI_freelistN(&keys); BLI_freelistN(&blocks); } -void draw_agroup_channel(gla2DDrawInfo *di, ActKeysInc *aki, bActionGroup *agrp, float ypos) +void draw_agroup_channel(View2D *v2d, AnimData *adt, 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); + agroup_to_keylist(adt, agrp, &keys, &blocks); + draw_keylist(v2d, &keys, &blocks, ypos); BLI_freelistN(&keys); BLI_freelistN(&blocks); } -void draw_action_channel(gla2DDrawInfo *di, ActKeysInc *aki, bAction *act, float ypos) +void draw_action_channel(View2D *v2d, AnimData *adt, 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); + action_to_keylist(adt, act, &keys, &blocks); + draw_keylist(v2d, &keys, &blocks, ypos); BLI_freelistN(&keys); BLI_freelistN(&blocks); } -void draw_gpl_channel(gla2DDrawInfo *di, ActKeysInc *aki, bGPDlayer *gpl, float ypos) +void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos) { ListBase keys = {0, 0}; - gpl_to_keylist(gpl, &keys, NULL, aki); - draw_keylist(di, &keys, NULL, ypos); + gpl_to_keylist(ads, gpl, &keys, NULL); + draw_keylist(v2d, &keys, NULL, ypos); BLI_freelistN(&keys); } /* *************************** Keyframe List Conversions *************************** */ -void scene_to_keylist(Scene *sce, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +void scene_to_keylist(bDopeSheet *ads, Scene *sce, ListBase *keys, ListBase *blocks) { if (sce) { - bDopeSheet *ads= (aki)? (aki->ads) : NULL; AnimData *adt; 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; @@ -423,7 +467,7 @@ void scene_to_keylist(Scene *sce, ListBase *keys, ListBase *blocks, ActKeysInc * // TODO: when we adapt NLA system, this needs to be the NLA-scaled version if (adt->action) - action_to_keylist(adt->action, keys, blocks, aki); + action_to_keylist(adt, adt->action, keys, blocks); } /* world animdata */ @@ -432,17 +476,16 @@ void scene_to_keylist(Scene *sce, ListBase *keys, ListBase *blocks, ActKeysInc * // TODO: when we adapt NLA system, this needs to be the NLA-scaled version if (adt->action) - action_to_keylist(adt->action, keys, blocks, aki); + action_to_keylist(adt, adt->action, keys, blocks); } } } -void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +void ob_to_keylist(bDopeSheet *ads, Object *ob, ListBase *keys, ListBase *blocks) { Key *key= ob_get_key(ob); if (ob) { - bDopeSheet *ads= (aki)? (aki->ads) : NULL; int filterflag; /* get filterflag */ @@ -453,79 +496,18 @@ void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki /* Add action keyframes */ if (ob->adt && ob->adt->action) - action_nlascaled_to_keylist(ob, ob->adt->action, keys, blocks, aki); + action_to_keylist(ob->adt, ob->adt->action, keys, blocks); /* Add shapekey keyframes (only if dopesheet allows, if it is available) */ - // TODO: when we adapt NLA system, this needs to be the NLA-scaled version if ((key && key->adt && key->adt->action) && !(filterflag & ADS_FILTER_NOSHAPEKEYS)) - action_to_keylist(key->adt->action, keys, blocks, aki); + action_to_keylist(key->adt, key->adt->action, keys, blocks); -#if 0 // XXX old animation system - /* 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) && !(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) && !(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) && !(filterflag & ADS_FILTER_NOCUR)) - ipo_to_keylist(cu->ipo, keys, blocks, aki); - } - break; - } -#endif // XXX old animation system + // TODO: restore materials, and object data, etc. } } -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 fcurve_to_keylist(FCurve *fcu, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +void fcurve_to_keylist(AnimData *adt, FCurve *fcu, ListBase *keys, ListBase *blocks) { BezTriple *bezt; ActKeyColumn *ak, *ak2; @@ -533,15 +515,17 @@ void fcurve_to_keylist(FCurve *fcu, ListBase *keys, ListBase *blocks, ActKeysInc int v; if (fcu && fcu->totvert && fcu->bezt) { + /* apply NLA-mapping (if applicable) */ + if (adt) + ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 1); + /* loop through beztriples, making ActKeys and ActKeyBlocks */ bezt= fcu->bezt; for (v=0; v < fcu->totvert; 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, fcu, v); - } + add_bezt_to_keycolumnslist(keys, bezt); + if (blocks) add_bezt_to_keyblockslist(blocks, fcu, v); } /* update the number of curves that elements have appeared in */ @@ -577,65 +561,38 @@ void fcurve_to_keylist(FCurve *fcu, ListBase *keys, ListBase *blocks, ActKeysInc } } } + + /* unapply NLA-mapping if applicable */ + ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 1); } } -void agroup_to_keylist(bActionGroup *agrp, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, ListBase *keys, ListBase *blocks) { FCurve *fcu; if (agrp) { /* loop through F-Curves */ for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next) { - fcurve_to_keylist(fcu, keys, blocks, aki); + fcurve_to_keylist(adt, fcu, keys, blocks); } } } -void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +void action_to_keylist(AnimData *adt, bAction *act, ListBase *keys, ListBase *blocks) { FCurve *fcu; if (act) { /* loop through F-Curves */ for (fcu= act->curves.first; fcu; fcu= fcu->next) { - fcurve_to_keylist(fcu, keys, blocks, aki); + fcurve_to_keylist(adt, fcu, keys, blocks); } } } -void action_nlascaled_to_keylist(Object *ob, bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) -{ - FCurve *fcu; - 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 F-Curves - * - scaling correction only does times for center-points, so should be faster - */ - for (fcu= act->curves.first; fcu; fcu= fcu->next) { - ANIM_nla_mapping_apply_fcurve(ob, fcu, 0, 1); - fcurve_to_keylist(fcu, keys, blocks, aki); - ANIM_nla_mapping_apply_fcurve(ob, fcu, 1, 1); - } - - /* if 'aki' is provided, restore ob */ - if (aki) - aki->ob= oldob; - } -} -void gpl_to_keylist(bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +void gpl_to_keylist(bDopeSheet *ads, bGPDlayer *gpl, ListBase *keys, ListBase *blocks) { bGPDframe *gpf; ActKeyColumn *ak; diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 8243629b4a6..77826eca87a 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -128,6 +128,10 @@ static short agrp_keys_bezier_loop(BeztEditData *bed, bActionGroup *agrp, BeztEd { FCurve *fcu; + /* sanity check */ + if (agrp == NULL) + return 0; + /* only iterate over the F-Curves that are in this group */ for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next) { if (ANIM_fcurve_keys_bezier_loop(bed, fcu, bezt_ok, bezt_cb, fcu_cb)) @@ -142,6 +146,10 @@ static short act_keys_bezier_loop(BeztEditData *bed, bAction *act, BeztEditFunc { FCurve *fcu; + /* sanity check */ + if (act == NULL) + return 0; + /* just loop through all F-Curves */ for (fcu= act->curves.first; fcu; fcu= fcu->next) { if (ANIM_fcurve_keys_bezier_loop(bed, fcu, bezt_ok, bezt_cb, fcu_cb)) @@ -154,6 +162,10 @@ static short act_keys_bezier_loop(BeztEditData *bed, bAction *act, BeztEditFunc /* This function is used to loop over the keyframe data of an AnimData block */ static short adt_keys_bezier_loop(BeztEditData *bed, AnimData *adt, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag) { + /* sanity check */ + if (adt == NULL) + return 0; + /* drivers or actions? */ if (filterflag & ADS_FILTER_ONLYDRIVERS) { FCurve *fcu; @@ -178,6 +190,10 @@ static short ob_keys_bezier_loop(BeztEditData *bed, Object *ob, BeztEditFunc bez { Key *key= ob_get_key(ob); + /* sanity check */ + if (ob == NULL) + return 0; + /* firstly, Object's own AnimData */ if (ob->adt) adt_keys_bezier_loop(bed, ob->adt, bezt_ok, bezt_cb, fcu_cb, filterflag); @@ -194,7 +210,11 @@ static short ob_keys_bezier_loop(BeztEditData *bed, Object *ob, BeztEditFunc bez /* This function is used to loop over the keyframe data in a Scene */ static short scene_keys_bezier_loop(BeztEditData *bed, Scene *sce, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag) { - World *wo= sce->world; + World *wo= (sce) ? sce->world : NULL; + + /* sanity check */ + if (sce == NULL) + return 0; /* Scene's own animation */ if (sce->adt) @@ -231,7 +251,7 @@ short ANIM_animchannel_keys_bezier_loop(BeztEditData *bed, bAnimListElem *ale, B return act_keys_bezier_loop(bed, (bAction *)ale->data, bezt_ok, bezt_cb, fcu_cb); case ALE_OB: /* object */ - return ob_keys_bezier_loop(bed, (Object *)ale->data, bezt_ok, bezt_cb, fcu_cb, filterflag); + return ob_keys_bezier_loop(bed, (Object *)ale->key_data, bezt_ok, bezt_cb, fcu_cb, filterflag); case ALE_SCE: /* scene */ return scene_keys_bezier_loop(bed, (Scene *)ale->data, bezt_ok, bezt_cb, fcu_cb, filterflag); } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 48ca06fb73d..6e62b163ca9 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -52,6 +52,8 @@ #include "ED_keyframing.h" #include "ED_keyframes_edit.h" +#include "RNA_access.h" + /* This file contains code for various keyframe-editing tools which are 'destructive' * (i.e. they will modify the order of the keyframes, and change the size of the array). * While some of these tools may eventually be moved out into blenkernel, for now, it is diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 90804052370..dd4c4c23f1e 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1,5 +1,30 @@ -/* Testing code for 2.5 animation system - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #include @@ -29,6 +54,7 @@ #include "BKE_action.h" #include "BKE_constraint.h" #include "BKE_fcurve.h" +#include "BKE_nla.h" #include "BKE_global.h" #include "BKE_utildefines.h" #include "BKE_context.h" @@ -697,9 +723,119 @@ static float visualkey_get_value (PointerRNA *ptr, PropertyRNA *prop, int array_ /* ------------------------- Insert Key API ------------------------- */ +/* Secondary Keyframing API call: + * Use this when validation of necessary animation data is not necessary, since an RNA-pointer to the necessary + * data being keyframed, and a pointer to the F-Curve to use have both been provided. + * + * 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 insert_keyframe_direct (PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, float cfra, short flag) +{ + float curval= 0.0f; + + /* no F-Curve to add keyframe to? */ + if (fcu == NULL) { + printf("ERROR: no F-Curve to add keyframes to \n"); + return 0; + } + + /* if no property given yet, try to validate from F-Curve info */ + if ((ptr.id.data == NULL) && (ptr.data==NULL)) { + printf("ERROR: no RNA-pointer available to retrieve values for keyframing from\n"); + return 0; + } + if (prop == NULL) { + PointerRNA tmp_ptr; + + /* try to get property we should be affecting */ + if ((RNA_path_resolve(&ptr, fcu->rna_path, &tmp_ptr, &prop) == 0) || (prop == NULL)) { + /* property not found... */ + char *idname= (ptr.id.data) ? ((ID *)ptr.id.data)->name : ""; + + printf("Insert Key: Could not insert keyframe, as RNA Path is invalid for the given ID (ID = %s, Path = %s)\n", idname, fcu->rna_path); + return 0; + } + else { + /* property found, so overwrite 'ptr' to make later code easier */ + ptr= tmp_ptr; + } + } + + /* set additional flags for the F-Curve (i.e. only integer values) */ + fcu->flag &= ~(FCURVE_INT_VALUES|FCURVE_DISCRETE_VALUES); + switch (RNA_property_type(prop)) { + case PROP_FLOAT: + /* do nothing */ + break; + case PROP_INT: + /* do integer (only 'whole' numbers) interpolation between all points */ + fcu->flag |= FCURVE_INT_VALUES; + break; + default: + /* do 'discrete' (i.e. enum, boolean values which cannot take any intermediate + * values at all) interpolation between all points + * - however, we must also ensure that evaluated values are only integers still + */ + fcu->flag |= (FCURVE_DISCRETE_VALUES|FCURVE_INT_VALUES); + break; + } + + /* obtain value to give keyframe */ + if ( (flag & INSERTKEY_MATRIX) && + (visualkey_can_use(&ptr, prop)) ) + { + /* 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(&ptr, prop, fcu->array_index); + } + else { + /* read value from system */ + curval= setting_get_rna_value(&ptr, prop, fcu->array_index); + } + + /* 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(fcu, cfra, curval); + + /* insert new keyframe at current frame */ + if (insert_mode) + insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* delete keyframe immediately before/after newly added */ + switch (insert_mode) { + case KEYNEEDED_DELPREV: + delete_fcurve_key(fcu, fcu->totvert-2, 1); + break; + case KEYNEEDED_DELNEXT: + delete_fcurve_key(fcu, 1, 1); + break; + } + + /* only return success if keyframe added */ + if (insert_mode) + return 1; + } + else { + /* just insert keyframe */ + insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* return success */ + return 1; + } + + /* failed */ + return 0; +} + /* 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. + * Use this when validation of necessary animation data is necessary, since it may not exist yet. * * The flag argument is used for special settings that alter the behaviour of * the keyframe insertion. These include the 'visual' keyframing modes, quick refresh, @@ -719,102 +855,31 @@ short insert_keyframe (ID *id, bAction *act, const char group[], const char rna_ } /* get F-Curve - if no action is provided, keyframe to the default one attached to this ID-block */ - if (act == NULL) + if (act == NULL) { + AnimData *adt= BKE_animdata_from_id(id); + + /* get action to add F-Curve+keyframe to */ act= verify_adt_action(id, 1); + + /* apply NLA-mapping to frame to use (if applicable) */ + cfra= BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); + } fcu= verify_fcurve(act, group, rna_path, array_index, 1); - /* only continue if we have an F-Curve to add keyframe to */ - if (fcu) { - float curval= 0.0f; + /* apply special time tweaking */ + // XXX check on this stuff... + if (GS(id->name) == ID_OB) { + //Object *ob= (Object *)id; - /* set additional flags for the F-Curve (i.e. only integer values) */ - fcu->flag &= ~(FCURVE_INT_VALUES|FCURVE_DISCRETE_VALUES); - switch (RNA_property_type(prop)) { - case PROP_FLOAT: - /* do nothing */ - break; - case PROP_INT: - /* do integer (only 'whole' numbers) interpolation between all points */ - fcu->flag |= FCURVE_INT_VALUES; - break; - default: - /* do 'discrete' (i.e. enum, boolean values which cannot take any intermediate - * values at all) interpolation between all points - * - however, we must also ensure that evaluated values are only integers still - */ - fcu->flag |= (FCURVE_DISCRETE_VALUES|FCURVE_INT_VALUES); - break; - } - - /* apply special time tweaking */ - // XXX check on this stuff... - if (GS(id->name) == ID_OB) { - //Object *ob= (Object *)id; - - /* apply NLA-scaling (if applicable) */ - //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)*scene->r.framelen; - //} - } - - /* obtain value to give keyframe */ - if ( (flag & INSERTKEY_MATRIX) && - (visualkey_can_use(&ptr, prop)) ) - { - /* 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(&ptr, prop, array_index); - } - else { - /* read value from system */ - curval= setting_get_rna_value(&ptr, prop, array_index); - } - - /* 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(fcu, cfra, curval); - - /* insert new keyframe at current frame */ - if (insert_mode) - insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST)); - - /* delete keyframe immediately before/after newly added */ - switch (insert_mode) { - case KEYNEEDED_DELPREV: - delete_fcurve_key(fcu, fcu->totvert-2, 1); - break; - case KEYNEEDED_DELNEXT: - delete_fcurve_key(fcu, 1, 1); - break; - } - - /* only return success if keyframe added */ - if (insert_mode) - return 1; - } - else { - /* just insert keyframe */ - insert_vert_fcurve(fcu, cfra, curval, (flag & INSERTKEY_FAST)); - - /* return success */ - return 1; - } + /* ancient time-offset cruft */ + //if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) { + // /* actually frametofloat calc again! */ + // cfra-= give_timeoffset(ob)*scene->r.framelen; + //} } - /* no F-Curve to add keyframes to */ - printf("ERROR: no F-Curve to add keyframes to \n"); - - /* return failure */ - return 0; + /* insert keyframe */ + return insert_keyframe_direct(ptr, prop, fcu, cfra, flag); } /* ************************************************** */ @@ -839,6 +904,9 @@ short delete_keyframe (ID *id, bAction *act, const char group[], const char rna_ /* if no action is provided, use the default one attached to this ID-block */ AnimData *adt= BKE_animdata_from_id(id); act= adt->action; + + /* apply NLA-mapping to frame to use (if applicable) */ + cfra= BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); } /* we don't check the validity of the path here yet, but it should be ok... */ fcu= verify_fcurve(act, group, rna_path, array_index, 0); @@ -852,9 +920,6 @@ short delete_keyframe (ID *id, bAction *act, const char group[], const char rna_ if (GS(id->name) == ID_OB) { //Object *ob= (Object *)id; - /* apply NLA-scaling (if applicable) */ - // cfra= get_action_frame(ob, cfra); - /* ancient time-offset cruft */ //if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) { // /* actually frametofloat calc again! */ @@ -1241,6 +1306,13 @@ static int insert_key_button_exec (bContext *C, wmOperator *op) MEM_freeN(path); } + else if (ptr.type == &RNA_NlaStrip) { + /* handle special vars for NLA-strips */ + NlaStrip *strip= (NlaStrip *)ptr.data; + FCurve *fcu= list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), 0); + + success+= insert_keyframe_direct(ptr, prop, fcu, cfra, 0); + } else { if (G.f & G_DEBUG) printf("Button Insert-Key: no path to property \n"); @@ -1358,6 +1430,31 @@ void ANIM_OT_delete_keyframe_button (wmOperatorType *ot) /* --------------- API/Per-Datablock Handling ------------------- */ +/* Checks if some F-Curve has a keyframe for a given frame */ +short fcurve_frame_has_keyframe (FCurve *fcu, float frame, short filter) +{ + /* quick sanity check */ + if (fcu == NULL) + return 0; + + /* we either include all regardless of muting, or only non-muted */ + if ((filter & ANIMFILTER_KEYS_MUTED) || (fcu->flag & FCURVE_MUTED)==0) { + short replace = -1; + int i = binarysearch_bezt_index(fcu->bezt, frame, fcu->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 < fcu->totvert)) + return 1; + } + } + + return 0; +} + /* Checks whether an Action 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... */ @@ -1379,20 +1476,8 @@ short action_frame_has_keyframe (bAction *act, float frame, short filter) for (fcu= act->curves.first; fcu; fcu= fcu->next) { /* only check if there are keyframes (currently only of type BezTriple) */ if (fcu->bezt && fcu->totvert) { - /* we either include all regardless of muting, or only non-muted */ - if ((filter & ANIMFILTER_KEYS_MUTED) || (fcu->flag & FCURVE_MUTED)==0) { - short replace = -1; - int i = binarysearch_bezt_index(fcu->bezt, frame, fcu->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 < fcu->totvert)) - return 1; - } - } + if (fcurve_frame_has_keyframe(fcu, frame, filter)) + return 1; } } diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 1813c76d0c4..0f8de7f607d 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -1,5 +1,30 @@ -/* Testing code for 2.5 animation system - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #include @@ -1060,6 +1085,9 @@ int modify_keyframes (bContext *C, ListBase *dsources, bAction *act, KeyingSet * case ID_MA: /* Material Keyframes */ WM_event_add_notifier(C, NC_MATERIAL|ND_KEYS, ksp->id); break; + default: /* Any keyframes */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + break; } } } @@ -1166,6 +1194,9 @@ int modify_keyframes (bContext *C, ListBase *dsources, bAction *act, KeyingSet * case ID_MA: /* Material Keyframes */ WM_event_add_notifier(C, NC_MATERIAL|ND_KEYS, cks->id); break; + default: /* Any keyframes */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + break; } } } diff --git a/source/blender/editors/armature/poselib.c b/source/blender/editors/armature/poselib.c index 8cbfebebff6..fcc1e8f9644 100644 --- a/source/blender/editors/armature/poselib.c +++ b/source/blender/editors/armature/poselib.c @@ -227,7 +227,7 @@ void poselib_validate_act (bAction *act) } /* determine which frames have keys */ - action_to_keylist(act, &keys, NULL, NULL); + action_to_keylist(NULL, act, &keys, NULL); /* for each key, make sure there is a correspnding pose */ for (ak= keys.first; ak; ak= ak->next) { diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index efc0a0b9a57..cc18c81fe51 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -41,7 +41,7 @@ struct gla2DDrawInfo; struct Object; struct bActionGroup; struct FCurve; -struct IpoCurve; // xxx +struct FModifier; /* ************************************************ */ /* ANIMATION CHANNEL FILTERING */ @@ -75,8 +75,9 @@ typedef enum eAnimCont_Types { ANIMCONT_SHAPEKEY, /* shapekey (Key) */ ANIMCONT_GPENCIL, /* grease pencil (screen) */ ANIMCONT_DOPESHEET, /* dopesheet (bDopesheet) */ - ANIMCONT_FCURVES, /* animation F-Curves (bDopesheet) */ // XXX + ANIMCONT_FCURVES, /* animation F-Curves (bDopesheet) */ ANIMCONT_DRIVERS, /* drivers (bDopesheet) */ + ANIMCONT_NLA, /* nla (bDopesheet) */ } eAnimCont_Types; /* --------------- Channels -------------------- */ @@ -92,10 +93,10 @@ typedef struct bAnimListElem { int flag; /* copy of elem's flags for quick access */ int index; /* copy of adrcode where applicable */ - void *key_data; /* motion data - ipo or ipo-curve */ + void *key_data; /* motion data - mostly F-Curves, but can be other types too */ short datatype; /* type of motion data to expect */ - struct ID *id; /* ID block that channel is attached to (may be used */ + struct ID *id; /* ID block that channel is attached to (may be used */ void *owner; /* group or channel which acts as this channel's owner */ short ownertype; /* type of owner */ @@ -106,6 +107,7 @@ typedef struct bAnimListElem { // XXX was ACTTYPE_* typedef enum eAnim_ChannelType { ANIMTYPE_NONE= 0, + ANIMTYPE_ANIMDATA, ANIMTYPE_SPECIALDATA, ANIMTYPE_SCENE, @@ -128,6 +130,9 @@ typedef enum eAnim_ChannelType { ANIMTYPE_GPDATABLOCK, ANIMTYPE_GPLAYER, + + ANIMTYPE_NLATRACK, + ANIMTYPE_NLAACTION, } eAnim_ChannelType; /* types of keyframe data in bAnimListElem */ @@ -135,8 +140,8 @@ typedef enum eAnim_KeyType { ALE_NONE = 0, /* no keyframe data */ ALE_FCURVE, /* F-Curve */ ALE_GPFRAME, /* Grease Pencil Frames */ + ALE_NLASTRIP, /* NLA Strips */ - // XXX the following are for summaries... should these be kept? ALE_SCE, /* Scene summary */ ALE_OB, /* Object summary */ ALE_ACT, /* Action summary */ @@ -156,7 +161,9 @@ typedef enum eAnimFilter_Flags { ANIMFILTER_CHANNELS = (1<<5), /* make list for interface drawing */ ANIMFILTER_ACTGROUPED = (1<<6), /* belongs to the active actiongroup */ ANIMFILTER_CURVEVISIBLE = (1<<7), /* F-Curve is visible for editing/viewing in Graph Editor */ - ANIMFILTER_ACTIVE = (1<<8), /* channel should be 'active' */ // FIXME: this is only relevant for F-Curves for now + ANIMFILTER_ACTIVE = (1<<8), /* channel should be 'active' */ + ANIMFILTER_ANIMDATA = (1<<9), /* only return the underlying AnimData blocks (not the tracks, etc.) data comes from */ + ANIMFILTER_NLATRACKS = (1<<10), /* only include NLA-tracks */ } eAnimFilter_Flags; @@ -202,6 +209,10 @@ typedef enum eAnimFilter_Flags { #define EDITABLE_GPL(gpl) ((gpl->flag & GP_LAYER_LOCKED)==0) #define SEL_GPL(gpl) ((gpl->flag & GP_LAYER_ACTIVE) || (gpl->flag & GP_LAYER_SELECT)) +/* NLA only */ +#define SEL_NLT(nlt) (nlt->flag & NLATRACK_SELECTED) +#define EDITABLE_NLT(nlt) ((nlt->flag & NLATRACK_PROTECTED)==0) + /* -------------- Channel Defines -------------- */ /* channel heights */ @@ -217,6 +228,22 @@ typedef enum eAnimFilter_Flags { /* channel toggle-buttons */ #define ACHANNEL_BUTTON_WIDTH 16 + +/* -------------- NLA Channel Defines -------------- */ + +/* NLA channel heights */ +#define NLACHANNEL_FIRST -16 +#define NLACHANNEL_HEIGHT 24 +#define NLACHANNEL_HEIGHT_HALF 12 +#define NLACHANNEL_SKIP 2 +#define NLACHANNEL_STEP (NLACHANNEL_HEIGHT + NLACHANNEL_SKIP) + +/* channel widths */ +#define NLACHANNEL_NAMEWIDTH 200 + +/* channel toggle-buttons */ +#define NLACHANNEL_BUTTON_WIDTH 16 + /* ---------------- API -------------------- */ /* Obtain list of filtered Animation channels to operate on. @@ -245,7 +272,7 @@ short ANIM_animdata_context_getdata(bAnimContext *ac); void ANIM_deselect_anim_channels(void *data, short datatype, short test, short sel); /* Set the 'active' channel of type channel_type, in the given action */ -void ANIM_set_active_channel(void *data, short datatype, int filter, void *channel_data, short channel_type); +void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type); /* --------------- Settings and/or Defines -------------- */ @@ -282,6 +309,14 @@ void ANIM_draw_cfra(const struct bContext *C, struct View2D *v2d, short flag); /* main call to draw preview range curtains */ void ANIM_draw_previewrange(const struct bContext *C, struct View2D *v2d); +/* ************************************************* */ +/* F-MODIFIER TOOLS */ + +struct uiLayout; + +/* draw a given F-Modifier for some layout/UI-Block */ +void ANIM_uiTemplate_fmodifier_draw(struct uiLayout *layout, struct ID *id, ListBase *modifiers, struct FModifier *fcm); + /* ************************************************* */ /* ASSORTED TOOLS */ @@ -299,19 +334,44 @@ void ipo_rainbow(int cur, int tot, float *out); /* ------------- NLA-Mapping ----------------------- */ /* anim_draw.c */ -/* Obtain the Object providing NLA-scaling for the given channel if applicable */ -struct Object *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale); +/* Obtain the AnimData block providing NLA-scaling for the given channel if applicable */ +struct AnimData *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale); -/* 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); +/* Set/clear temporary mapping of coordinates from 'local-action' time to 'global-nla-mapped' time */ +void ANIM_nla_mapping_draw(struct gla2DDrawInfo *di, struct AnimData *adt, short restore); -/* Apply/Unapply NLA mapping to all keyframes in the nominated IPO block */ -void ANIM_nla_mapping_apply_fcurve(struct Object *ob, struct FCurve *fcu, short restore, short only_keys); +/* Apply/Unapply NLA mapping to all keyframes in the nominated F-Curve */ +void ANIM_nla_mapping_apply_fcurve(struct AnimData *adt, struct FCurve *fcu, short restore, short only_keys); -/* ------------- xxx macros ----------------------- */ +/* ------------- Utility macros ----------------------- */ +/* checks if the given BezTriple is selected */ #define BEZSELECTED(bezt) ((bezt->f2 & SELECT) || (bezt->f1 & SELECT) || (bezt->f3 & SELECT)) +/* set/clear/toggle macro + * - channel - channel with a 'flag' member that we're setting + * - smode - 0=clear, 1=set, 2=toggle + * - sflag - bitflag to set + */ +#define ACHANNEL_SET_FLAG(channel, smode, sflag) \ + { \ + if (smode == ACHANNEL_SETFLAG_TOGGLE) (channel)->flag ^= (sflag); \ + else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag |= (sflag); \ + else (channel)->flag &= ~(sflag); \ + } + +/* set/clear/toggle macro, where the flag is negative + * - channel - channel with a 'flag' member that we're setting + * - smode - 0=clear, 1=set, 2=toggle + * - sflag - bitflag to set + */ +#define ACHANNEL_SET_FLAG_NEG(channel, smode, sflag) \ + { \ + if (smode == ACHANNEL_SETFLAG_TOGGLE) (channel)->flag ^= (sflag); \ + else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag &= ~(sflag); \ + else (channel)->flag |= (sflag); \ + } + /* --------- anim_deps.c, animation updates -------- */ @@ -324,18 +384,6 @@ void ED_anim_object_flush_update(const struct bContext *C, struct Object *ob); void ANIM_action_to_pose_sync(struct Object *ob); void ANIM_pose_to_action_sync(struct Object *ob, struct ScrArea *sa); - -/* what types of animation data was changed (for sending notifiers from animation tools) */ -enum { - ANIM_CHANGED_BOTH= 0, - ANIM_CHANGED_KEYFRAMES_VALUES, - ANIM_CHANGED_KEYFRAMES_SELECT, - ANIM_CHANGED_CHANNELS -} eAnimData_Changed; - -/* Send notifiers on behalf of animation editing tools, based on various context info */ -void ANIM_animdata_send_notifiers(struct bContext *C, bAnimContext *ac, short data_changed); - /* ************************************************* */ /* OPERATORS */ diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index e104bce90f6..63adcf39c12 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -30,15 +30,17 @@ #ifndef ED_KEYFRAMES_DRAW_H #define ED_KEYFRAMES_DRAW_H +struct AnimData; struct BezTriple; struct FCurve; -struct gla2DDrawInfo; +struct bDopeSheet; struct bAction; struct bActionGroup; struct Object; struct ListBase; struct bGPDlayer; struct Scene; +struct View2D; /* ****************************** Base Structs ****************************** */ @@ -66,33 +68,38 @@ typedef struct ActKeyBlock { } ActKeyBlock; -/* Inclusion-Range Limiting Struct (optional) */ -typedef struct ActKeysInc { - struct bDopeSheet *ads; /* dopesheet data (for dopesheet mode) */ - struct Object *ob; /* owner object for NLA-scaling info (if Object channels, is just Object) */ - short actmode; /* mode of the Action Editor (-1 is for NLA) */ - - float start, end; /* frames (global-time) to only consider keys between */ // XXX not used anymore! -} ActKeysInc; +/* *********************** Keyframe Drawing ****************************** */ + +/* options for keyframe shape drawing */ +typedef enum eKeyframeShapeDrawOpts { + /* only the border */ + KEYFRAME_SHAPE_FRAME = 0, + /* only the inside filling */ + KEYFRAME_SHAPE_INSIDE, + /* the whole thing */ + KEYFRAME_SHAPE_BOTH +} eKeyframeShapeDrawOpts; + +/* draw simple diamond-shape keyframe (with OpenGL) */ +void draw_keyframe_shape (float x, float y, float xscale, float hsize, short sel, short mode); /* ******************************* Methods ****************************** */ /* Channel Drawing */ -void draw_fcurve_channel(struct gla2DDrawInfo *di, ActKeysInc *aki, struct FCurve *fcu, float ypos); -void draw_agroup_channel(struct gla2DDrawInfo *di, ActKeysInc *aki, struct bActionGroup *agrp, float ypos); -void draw_action_channel(struct gla2DDrawInfo *di, ActKeysInc *aki, struct bAction *act, float ypos); -void draw_object_channel(struct gla2DDrawInfo *di, ActKeysInc *aki, struct Object *ob, float ypos); -void draw_scene_channel(struct gla2DDrawInfo *di, ActKeysInc *aki, struct Scene *sce, float ypos); -void draw_gpl_channel(struct gla2DDrawInfo *di, ActKeysInc *aki, struct bGPDlayer *gpl, float ypos); +void draw_fcurve_channel(struct View2D *v2d, struct AnimData *adt, struct FCurve *fcu, float ypos); +void draw_agroup_channel(struct View2D *v2d, struct AnimData *adt, struct bActionGroup *agrp, float ypos); +void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos); +void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos); +void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos); +void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos); /* Keydata Generation */ -void fcurve_to_keylist(struct FCurve *fcu, ListBase *keys, ListBase *blocks, ActKeysInc *aki); -void agroup_to_keylist(struct bActionGroup *agrp, ListBase *keys, ListBase *blocks, ActKeysInc *aki); -void action_to_keylist(struct bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki); -void action_nlascaled_to_keylist(struct Object *ob, struct bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki); -void ob_to_keylist(struct Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki); -void scene_to_keylist(struct Scene *sce, ListBase *keys, ListBase *blocks, ActKeysInc *aki); -void gpl_to_keylist(struct bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki); +void fcurve_to_keylist(struct AnimData *adt, struct FCurve *fcu, ListBase *keys, ListBase *blocks); +void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, ListBase *keys, ListBase *blocks); +void action_to_keylist(struct AnimData *adt, struct bAction *act, ListBase *keys, ListBase *blocks); +void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, ListBase *keys, ListBase *blocks); +void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, ListBase *keys, ListBase *blocks); +void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, ListBase *keys, ListBase *blocks); #endif /* ED_KEYFRAMES_DRAW_H */ diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 9d063910aa9..503d71b0d3e 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -43,6 +43,9 @@ struct bConstraint; struct bContext; struct wmOperatorType; +struct PointerRNA; +struct PropertyRNA; + /* ************ Keyframing Management **************** */ /* Get (or add relevant data to be able to do so) the Active Action for the given @@ -69,6 +72,16 @@ int insert_bezt_fcurve(struct FCurve *fcu, struct BezTriple *bezt); */ void insert_vert_fcurve(struct FCurve *fcu, float x, float y, short flag); +/* -------- */ + +/* Secondary Keyframing API calls: + * Use this to insert a keyframe using the current value being keyframed, in the + * nominated F-Curve (no creation of animation data performed). Returns success. + */ +short insert_keyframe_direct(struct PointerRNA ptr, struct PropertyRNA *prop, struct FCurve *fcu, float cfra, short flag); + + + /* -------- */ /* Main Keyframing API calls: @@ -186,6 +199,11 @@ void ANIM_OT_remove_driver_button(struct wmOperatorType *ot); /* ************ Keyframe Checking ******************** */ +/* Lesser Keyframe Checking API call: + * - Used for the buttons to check for keyframes... + */ +short fcurve_frame_has_keyframe(struct FCurve *fcu, float frame, short filter); + /* Main Keyframe Checking API call: * Checks whether a keyframe exists for the given ID-block one the given frame. * - It is recommended to call this method over the other keyframe-checkers directly, diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 4bb1dd65f85..3449458168e 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -118,6 +118,7 @@ int ED_operator_node_active(struct bContext *C); int ED_operator_ipo_active(struct bContext *C); int ED_operator_sequencer_active(struct bContext *C); int ED_operator_image_active(struct bContext *C); +int ED_operator_nla_active(struct bContext *C); int ED_operator_logic_active(struct bContext *C); int ED_operator_object_active(struct bContext *C); diff --git a/source/blender/editors/include/ED_screen_types.h b/source/blender/editors/include/ED_screen_types.h index 37f2f4f051d..3ea5dfba65c 100644 --- a/source/blender/editors/include/ED_screen_types.h +++ b/source/blender/editors/include/ED_screen_types.h @@ -33,6 +33,7 @@ typedef struct ScreenAnimData { ARegion *ar; /* do not read from this, only for comparing if region exists */ int redraws; + int reverse; } ScreenAnimData; diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index fde270040a9..d2185854a95 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -1,5 +1,5 @@ /** - * $Id$ + * $Id: ED_transform.h 21450 2009-07-09 02:45:48Z theeth $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 537c1b42b97..e3fbd906cf6 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -52,6 +52,8 @@ enum { V2D_COMMONVIEW_STANDARD, /* listview (i.e. Outliner) */ V2D_COMMONVIEW_LIST, + /* stackview (this is basically a list where new items are added at the top) */ + V2D_COMMONVIEW_STACK, /* headers (this is basically the same as listview, but no y-panning) */ V2D_COMMONVIEW_HEADER, /* ui region containing panels */ diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 4a26db29160..7c439f408ba 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -18,6 +18,8 @@ #include "RNA_access.h" #include "RNA_types.h" +#include "ED_keyframing.h" + #include "UI_interface.h" #include "WM_api.h" @@ -28,12 +30,15 @@ void ui_but_anim_flag(uiBut *but, float cfra) { but->flag &= ~(UI_BUT_ANIMATED|UI_BUT_ANIMATED_KEY|UI_BUT_DRIVEN); - - if(but->rnaprop && but->rnapoin.id.data) { + + /* there must be some RNA-pointer + property combo for this button */ + if (but->rnaprop && but->rnapoin.id.data && + RNA_property_animateable(&but->rnapoin, but->rnaprop)) + { AnimData *adt= BKE_animdata_from_id(but->rnapoin.id.data); FCurve *fcu; char *path; - + if (adt) { if ((adt->action && adt->action->curves.first) || (adt->drivers.first)) { /* XXX this function call can become a performance bottleneck */ @@ -47,7 +52,7 @@ void ui_but_anim_flag(uiBut *but, float cfra) if (fcu) { but->flag |= UI_BUT_ANIMATED; - if (on_keyframe_fcurve(fcu, cfra)) + if (fcurve_frame_has_keyframe(fcu, cfra, 0)) but->flag |= UI_BUT_ANIMATED_KEY; } } @@ -111,7 +116,6 @@ void ui_but_anim_remove_driver(bContext *C) WM_operator_name_call(C, "ANIM_OT_remove_driver_button", WM_OP_INVOKE_DEFAULT, NULL); } -// TODO: refine the logic for adding/removing drivers... void ui_but_anim_menu(bContext *C, uiBut *but) { uiPopupMenu *pup; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index f83dee23417..2798f7a473f 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -505,7 +505,10 @@ void ui_theme_init_userdef(void) btheme->tact= btheme->tipo; SETCOL(btheme->tact.strip, 12, 10, 10, 128); SETCOL(btheme->tact.strip_select, 255, 140, 0, 255); - + + /* space nla */ + btheme->tnla= btheme->tact; + /* space file */ /* to have something initialized */ btheme->tfile= btheme->tv3d; @@ -522,20 +525,6 @@ void ui_theme_init_userdef(void) SETCOL(btheme->tfile.scene, 250, 250, 250, 255); - - - /* space nla */ - btheme->tnla= btheme->tv3d; - SETCOL(btheme->tnla.back, 116, 116, 116, 255); - SETCOL(btheme->tnla.text, 0, 0, 0, 255); - SETCOL(btheme->tnla.text_hi, 255, 255, 255, 255); - SETCOL(btheme->tnla.grid, 94, 94, 94, 255); - SETCOL(btheme->tnla.shade1, 172, 172, 172, 255); // sliders - SETCOL(btheme->tnla.shade2, 84, 44, 31, 100); // bar - SETCOL(btheme->tnla.hilite, 17, 27, 60, 100); // bar - SETCOL(btheme->tnla.strip_select, 0xff, 0xff, 0xaa, 255); - SETCOL(btheme->tnla.strip, 0xe4, 0x9c, 0xc6, 255); - /* space seq */ btheme->tseq= btheme->tv3d; SETCOL(btheme->tseq.back, 116, 116, 116, 255); @@ -1086,19 +1075,6 @@ void init_userdef_do_versions(void) SETCOL(btheme->ttime.cframe, 0x60, 0xc0, 0x40, 255); } } - if ((G.main->versionfile < 245) || (G.main->versionfile == 245 && G.main->subversionfile < 11)) { - bTheme *btheme; - for (btheme= U.themes.first; btheme; btheme= btheme->next) { - /* these should all use the same color */ - SETCOL(btheme->tv3d.cframe, 0x60, 0xc0, 0x40, 255); - SETCOL(btheme->tipo.cframe, 0x60, 0xc0, 0x40, 255); - SETCOL(btheme->tact.cframe, 0x60, 0xc0, 0x40, 255); - SETCOL(btheme->tnla.cframe, 0x60, 0xc0, 0x40, 255); - SETCOL(btheme->tseq.cframe, 0x60, 0xc0, 0x40, 255); - SETCOL(btheme->tsnd.cframe, 0x60, 0xc0, 0x40, 255); - SETCOL(btheme->ttime.cframe, 0x60, 0xc0, 0x40, 255); - } - } if ((G.main->versionfile < 245) || (G.main->versionfile == 245 && G.main->subversionfile < 13)) { bTheme *btheme; for (btheme= U.themes.first; btheme; btheme= btheme->next) { @@ -1221,6 +1197,13 @@ void init_userdef_do_versions(void) /* Graph Editor - Group Channel color */ SETCOL(btheme->tipo.group, 79, 101, 73, 255); SETCOL(btheme->tipo.group_active, 135, 177, 125, 255); + + /* Nla Editor - (Object) Channel color */ + SETCOL(btheme->tnla.ds_channel, 82, 96, 110, 255); + SETCOL(btheme->tnla.ds_subchannel, 124, 137, 150, 255); + /* NLA Editor - New Strip colors */ + SETCOL(btheme->tnla.strip, 12, 10, 10, 128); + SETCOL(btheme->tnla.strip_select, 255, 140, 0, 255); } /* adjust grease-pencil distances */ diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index e0e2af5472d..6df7b1c8e28 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -209,6 +209,23 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) } break; + /* 'stack view' - practically the same as list/channel view, except is located in the pos y half instead. + * zoom, aspect ratio, and alignment restrictions are set here */ + case V2D_COMMONVIEW_STACK: + { + /* zoom + aspect ratio are locked */ + v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_KEEPZOOM|V2D_KEEPASPECT); + v2d->minzoom= v2d->maxzoom= 1.0f; + + /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */ + v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y); + v2d->keeptot = V2D_KEEPTOT_STRICT; + tot_changed= 1; + + /* scroller settings are currently not set here... that is left for regions... */ + } + break; + /* 'header' regions - zoom, aspect ratio, alignment, and panning restrictions are set here */ case V2D_COMMONVIEW_HEADER: { @@ -247,18 +264,21 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) v2d->tot.xmin= 0.0f; v2d->tot.xmax= winx; - + v2d->tot.ymax= 0.0f; v2d->tot.ymin= -winy; - + v2d->cur.xmin= 0.0f; v2d->cur.xmax= winx*style->panelzoom; v2d->cur.ymax= 0.0f; v2d->cur.ymin= -winy*style->panelzoom; + + v2d->cur.ymax= 0.0f; + v2d->cur.ymin= -winy*style->panelzoom; } break; - + /* other view types are completely defined using their own settings already */ default: /* we don't do anything here, as settings should be fine, but just make sure that rect */ diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 4e08310240c..6da00f12a25 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1460,7 +1460,9 @@ void ED_screen_full_prevspace(bContext *C) ed_screen_fullarea(C, sa); } -/* redraws: uses defines from stime->redraws */ +/* redraws: uses defines from stime->redraws + * enable: 1 - forward on, -1 - backwards on, 0 - off + */ void ED_screen_animation_timer(bContext *C, int redraws, int enable) { bScreen *screen= CTX_wm_screen(C); @@ -1477,6 +1479,7 @@ void ED_screen_animation_timer(bContext *C, int redraws, int enable) screen->animtimer= WM_event_add_window_timer(win, TIMER0, (1.0/FPS)); sad->ar= CTX_wm_region(C); sad->redraws= redraws; + sad->reverse= (enable < 0); screen->animtimer->customdata= sad; } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 01cb4427944..99bbe0514c9 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -166,6 +166,7 @@ int ED_operator_node_active(bContext *C) return 0; } +// XXX rename int ED_operator_ipo_active(bContext *C) { return ed_spacetype_test(C, SPACE_IPO); @@ -181,6 +182,11 @@ int ED_operator_image_active(bContext *C) return ed_spacetype_test(C, SPACE_IMAGE); } +int ED_operator_nla_active(bContext *C) +{ + return ed_spacetype_test(C, SPACE_NLA); +} + int ED_operator_logic_active(bContext *C) { return ed_spacetype_test(C, SPACE_LOGIC); @@ -2070,19 +2076,40 @@ static int screen_animation_step(bContext *C, wmOperator *op, wmEvent *event) if(scene->audio.flag & AUDIO_SYNC) { int step = floor(wt->duration * FPS); - scene->r.cfra += step; + if (sad->reverse) // XXX does this option work with audio? + scene->r.cfra -= step; + else + scene->r.cfra += step; wt->duration -= ((float)step)/FPS; } - else - scene->r.cfra++; + else { + if (sad->reverse) + scene->r.cfra--; + else + scene->r.cfra++; + } - if (scene->r.psfra) { - if(scene->r.cfra > scene->r.pefra) - scene->r.cfra= scene->r.psfra; + if (sad->reverse) { + /* jump back to end */ + if (scene->r.psfra) { + if(scene->r.cfra < scene->r.psfra) + scene->r.cfra= scene->r.pefra; + } + else { + if(scene->r.cfra < scene->r.sfra) + scene->r.cfra= scene->r.efra; + } } else { - if(scene->r.cfra > scene->r.efra) - scene->r.cfra= scene->r.sfra; + /* jump back to start */ + if (scene->r.psfra) { + if(scene->r.cfra > scene->r.pefra) + scene->r.cfra= scene->r.psfra; + } + else { + if(scene->r.cfra > scene->r.efra) + scene->r.cfra= scene->r.sfra; + } } /* since we follow drawflags, we can't send notifier but tag regions ourselves */ @@ -2130,8 +2157,9 @@ static int screen_animation_play(bContext *C, wmOperator *op, wmEvent *event) ED_screen_animation_timer(C, 0, 0); } else { - /* todo: RNA properties to define play types */ - ED_screen_animation_timer(C, TIME_REGION|TIME_ALL_3D_WIN, 1); + int mode= (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1; + + ED_screen_animation_timer(C, TIME_REGION|TIME_ALL_3D_WIN, mode); if(screen->animtimer) { wmTimer *wt= screen->animtimer; @@ -2155,7 +2183,7 @@ void SCREEN_OT_animation_play(wmOperatorType *ot) ot->poll= ED_operator_screenactive; - + RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards"); } /* ************** border select operator (template) ***************************** */ diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 2c9f91e0941..2fd5b9bbd93 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -422,6 +422,8 @@ void draw_channel_names(bAnimContext *ac, SpaceAction *saction, ARegion *ar) */ v2d->tot.ymin= (float)(-height); } + /* need to do a view-sync here, so that the keys area doesn't jump around */ + UI_view2d_sync(NULL, ac->sa, v2d, V2D_VIEWSYNC_AREA_VERTICAL); /* loop through channels, and set up drawing depending on their type */ y= (float)ACHANNEL_FIRST; @@ -645,6 +647,11 @@ void draw_channel_names(bAnimContext *ac, SpaceAction *saction, ARegion *ar) expand = ICON_TRIA_RIGHT; } + if (agrp->flag & AGRP_MUTED) + mute = ICON_MUTE_IPO_ON; + else + mute = ICON_MUTE_IPO_OFF; + if (EDITABLE_AGRP(agrp)) protect = ICON_UNLOCKED; else @@ -955,27 +962,8 @@ void draw_channel_names(bAnimContext *ac, SpaceAction *saction, ARegion *ar) /* ************************************************************************* */ /* Keyframes */ -ActKeysInc *init_aki_data(bAnimContext *ac, bAnimListElem *ale) -{ - static ActKeysInc aki; - - /* no need to set settings if wrong context */ - if ((ac->data == NULL) || ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)==0) - return NULL; - - /* if strip is mapped, store settings */ - aki.ob= ANIM_nla_mapping_get(ac, ale); - - if (ac->datatype == ANIMCONT_DOPESHEET) - aki.ads= (bDopeSheet *)ac->data; - else - aki.ads= NULL; - aki.actmode= ac->datatype; - - /* always return pointer... */ - return &aki; -} - +/* extra padding for lengths (to go under scrollers) */ +#define EXTRA_SCROLL_PAD 100.0f /* draw keyframes in each channel */ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) @@ -985,13 +973,11 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) int filter; View2D *v2d= &ar->v2d; - Object *nob= NULL; - gla2DDrawInfo *di; - rcti scr_rct; + bDopeSheet *ads= &saction->ads; + AnimData *adt= NULL; - int act_start, act_end, dummy; + float act_start, act_end, y; int height, items; - float y, sta, end; char col1[3], col2[3]; char col1a[3], col2a[3]; @@ -1001,6 +987,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) /* get theme colors */ UI_GetThemeColor3ubv(TH_BACK, col2); UI_GetThemeColor3ubv(TH_HILITE, col1); + UI_GetThemeColor3ubv(TH_GROUP, col2a); UI_GetThemeColor3ubv(TH_GROUP_ACTIVE, col1a); @@ -1008,26 +995,14 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) UI_GetThemeColor3ubv(TH_DOPESHEET_CHANNELSUBOB, col2b); /* set view-mapping rect (only used for x-axis), for NLA-scaling mapping with less calculation */ - scr_rct.xmin= ar->winrct.xmin + ar->v2d.mask.xmin; - scr_rct.ymin= ar->winrct.ymin + ar->v2d.mask.ymin; - scr_rct.xmax= ar->winrct.xmin + ar->v2d.hor.xmax; - scr_rct.ymax= ar->winrct.ymin + ar->v2d.mask.ymax; - di= glaBegin2DDraw(&scr_rct, &v2d->cur); /* if in NLA there's a strip active, map the view */ if (ac->datatype == ANIMCONT_ACTION) { - nob= ANIM_nla_mapping_get(ac, NULL); - - if (nob) - ANIM_nla_mapping_draw(di, nob, 0); + adt= ANIM_nla_mapping_get(ac, NULL); /* start and end of action itself */ - calc_action_range(ac->data, &sta, &end, 0); - gla2DDrawTranslatePt(di, sta, 0.0f, &act_start, &dummy); - gla2DDrawTranslatePt(di, end, 0.0f, &act_end, &dummy); - - if (nob) - ANIM_nla_mapping_draw(di, nob, 1); + // TODO: this has not had scaling applied + calc_action_range(ac->data, &act_start, &act_end, 0); } /* build list of channels to draw */ @@ -1058,7 +1033,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { - int frame1_x, channel_y, sel=0; + int sel=0; /* determine if any need to draw channel */ if (ale->datatype != ALE_NONE) { @@ -1097,8 +1072,6 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) } if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) { - gla2DDrawTranslatePt(di, v2d->cur.xmin, y, &frame1_x, &channel_y); - switch (ale->type) { case ANIMTYPE_SCENE: case ANIMTYPE_OBJECT: @@ -1134,36 +1107,32 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) } /* draw region twice: firstly backdrop, then the current range */ - glRectf((float)frame1_x, (float)channel_y-ACHANNEL_HEIGHT_HALF, (float)v2d->hor.xmax, (float)channel_y+ACHANNEL_HEIGHT_HALF); + glRectf(v2d->cur.xmin, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmax+EXTRA_SCROLL_PAD, (float)y+ACHANNEL_HEIGHT_HALF); if (ac->datatype == ANIMCONT_ACTION) - glRectf((float)act_start, (float)channel_y-ACHANNEL_HEIGHT_HALF, (float)act_end, (float)channel_y+ACHANNEL_HEIGHT_HALF); + glRectf(act_start, (float)y-ACHANNEL_HEIGHT_HALF, act_end, (float)y+ACHANNEL_HEIGHT_HALF); } else if (ac->datatype == ANIMCONT_SHAPEKEY) { - gla2DDrawTranslatePt(di, 1, y, &frame1_x, &channel_y); - /* all frames that have a frame number less than one * get a desaturated orange background */ glColor4ub(col2[0], col2[1], col2[2], 0x22); - glRectf(0.0f, (float)channel_y-ACHANNEL_HEIGHT_HALF, (float)frame1_x, (float)channel_y+ACHANNEL_HEIGHT_HALF); + glRectf(0.0f, (float)y-ACHANNEL_HEIGHT_HALF, 1.0f, (float)y+ACHANNEL_HEIGHT_HALF); /* frames one and higher get a saturated orange background */ glColor4ub(col2[0], col2[1], col2[2], 0x44); - glRectf((float)frame1_x, (float)channel_y-ACHANNEL_HEIGHT_HALF, (float)v2d->hor.xmax, (float)channel_y+ACHANNEL_HEIGHT_HALF); + glRectf(1.0f, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmax+EXTRA_SCROLL_PAD, (float)y+ACHANNEL_HEIGHT_HALF); } else if (ac->datatype == ANIMCONT_GPENCIL) { - gla2DDrawTranslatePt(di, v2d->cur.xmin, y, &frame1_x, &channel_y); - /* frames less than one get less saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); - glRectf(0.0f, (float)channel_y-ACHANNEL_HEIGHT_HALF, (float)frame1_x, (float)channel_y+ACHANNEL_HEIGHT_HALF); + glRectf(0.0f, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmin, (float)y+ACHANNEL_HEIGHT_HALF); /* frames one and higher get a saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); else glColor4ub(col2[0], col2[1], col2[2], 0x44); - glRectf((float)frame1_x, (float)channel_y-ACHANNEL_HEIGHT_HALF, (float)v2d->hor.xmax, (float)channel_y+ACHANNEL_HEIGHT_HALF); + glRectf(v2d->cur.xmin, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmax+EXTRA_SCROLL_PAD, (float)y+ACHANNEL_HEIGHT_HALF); } } } @@ -1190,36 +1159,29 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) { /* check if anything to show for this channel */ if (ale->datatype != ALE_NONE) { - ActKeysInc *aki= init_aki_data(ac, ale); - nob= ANIM_nla_mapping_get(ac, ale); - - if (nob) - ANIM_nla_mapping_draw(di, nob, 0); + adt= ANIM_nla_mapping_get(ac, ale); /* draw 'keyframes' for each specific datatype */ switch (ale->datatype) { case ALE_SCE: - draw_scene_channel(di, aki, ale->key_data, y); + draw_scene_channel(v2d, ads, ale->key_data, y); break; case ALE_OB: - draw_object_channel(di, aki, ale->key_data, y); + draw_object_channel(v2d, ads, ale->key_data, y); break; case ALE_ACT: - draw_action_channel(di, aki, ale->key_data, y); + draw_action_channel(v2d, adt, ale->key_data, y); break; case ALE_GROUP: - draw_agroup_channel(di, aki, ale->data, y); + draw_agroup_channel(v2d, adt, ale->data, y); break; case ALE_FCURVE: - draw_fcurve_channel(di, aki, ale->key_data, y); + draw_fcurve_channel(v2d, adt, ale->key_data, y); break; case ALE_GPFRAME: - draw_gpl_channel(di, aki, ale->data, y); + draw_gpl_channel(v2d, ads, ale->data, y); break; } - - if (nob) - ANIM_nla_mapping_draw(di, nob, 1); } } @@ -1231,16 +1193,11 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) /* black line marking 'current frame' for Time-Slide transform mode */ if (saction->flag & SACTION_MOVING) { - int frame1_x; - - gla2DDrawTranslatePt(di, saction->timeslide, 0, &frame1_x, &dummy); - cpack(0x0); + glColor3f(0.0f, 0.0f, 0.0f); glBegin(GL_LINES); - glVertex2f((float)frame1_x, (float)v2d->mask.ymin - 100); - glVertex2f((float)frame1_x, (float)v2d->mask.ymax); + glVertex2f(saction->timeslide, v2d->cur.ymin-EXTRA_SCROLL_PAD); + glVertex2f(saction->timeslide, v2d->cur.ymax); glEnd(); } - - glaEnd2DDraw(di); } diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 148021a6fa3..8d2d342e28a 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -67,6 +67,7 @@ #include "BKE_fcurve.h" #include "BKE_key.h" #include "BKE_material.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_context.h" #include "BKE_report.h" @@ -111,16 +112,16 @@ static void get_keyframe_extents (bAnimContext *ac, float *min, float *max) if (anim_data.first) { /* go through channels, finding max extents */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); FCurve *fcu= (FCurve *)ale->key_data; float tmin, tmax; /* get range and apply necessary scaling before */ calc_fcurve_range(fcu, &tmin, &tmax); - if (nob) { - tmin= get_action_frame_inv(nob, tmin); - tmax= get_action_frame_inv(nob, tmax); + if (adt) { + tmin= BKE_nla_tweakedit_remap(adt, tmin, NLATIME_CONVERT_MAP); + tmax= BKE_nla_tweakedit_remap(adt, tmax, NLATIME_CONVERT_MAP); } /* try to set cur using these values, if they're more extreme than previously set values */ @@ -180,7 +181,7 @@ void ACT_OT_previewrange_set (wmOperatorType *ot) /* api callbacks */ ot->exec= actkeys_previewrange_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -213,7 +214,7 @@ static int actkeys_viewall_exec(bContext *C, wmOperator *op) /* do View2D syncing */ UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); - /* set notifier that things have changed */ + /* just redraw this view */ ED_area_tag_redraw(CTX_wm_area(C)); return OPERATOR_FINISHED; @@ -227,7 +228,7 @@ void ACT_OT_view_all (wmOperatorType *ot) /* api callbacks */ ot->exec= actkeys_viewall_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -300,21 +301,18 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op) } } - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); - return OPERATOR_FINISHED; } -void ACT_OT_keyframes_copy (wmOperatorType *ot) +void ACT_OT_copy (wmOperatorType *ot) { /* identifiers */ ot->name= "Copy Keyframes"; - ot->idname= "ACT_OT_keyframes_copy"; + ot->idname= "ACT_OT_copy"; /* api callbacks */ ot->exec= actkeys_copy_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -344,21 +342,21 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_paste (wmOperatorType *ot) +void ACT_OT_paste (wmOperatorType *ot) { /* identifiers */ ot->name= "Paste Keyframes"; - ot->idname= "ACT_OT_keyframes_paste"; + ot->idname= "ACT_OT_paste"; /* api callbacks */ ot->exec= actkeys_paste_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -399,14 +397,14 @@ static void insert_action_keys(bAnimContext *ac, short mode) /* insert keyframes */ for (ale= anim_data.first; ale; ale= ale->next) { - //Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); FCurve *fcu= (FCurve *)ale->key_data; /* adjust current frame for NLA-scaling */ - //if (nob) - // cfra= get_action_frame(nob, CFRA); - //else - // cfra= (float)CFRA; + if (adt) + cfra= BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra= (float)CFRA; /* if there's an id */ if (ale->id) @@ -431,31 +429,31 @@ static int actkeys_insertkey_exec(bContext *C, wmOperator *op) if (ac.datatype == ANIMCONT_GPENCIL) return OPERATOR_CANCELLED; - /* get snapping mode */ + /* what channels to affect? */ mode= RNA_enum_get(op->ptr, "type"); - /* snap keyframes */ + /* insert keyframes */ insert_action_keys(&ac, mode); /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_insert (wmOperatorType *ot) +void ACT_OT_insert_keyframe (wmOperatorType *ot) { /* identifiers */ ot->name= "Insert Keyframes"; - ot->idname= "ACT_OT_keyframes_insert"; + ot->idname= "ACT_OT_insert_keyframe"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= actkeys_insertkey_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -507,8 +505,8 @@ static int actkeys_duplicate_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; // xxx - start transform } @@ -523,16 +521,16 @@ static int actkeys_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event) return OPERATOR_FINISHED; } -void ACT_OT_keyframes_duplicate (wmOperatorType *ot) +void ACT_OT_duplicate (wmOperatorType *ot) { /* identifiers */ ot->name= "Duplicate Keyframes"; - ot->idname= "ACT_OT_keyframes_duplicate"; + ot->idname= "ACT_OT_duplicate"; /* api callbacks */ ot->invoke= actkeys_duplicate_invoke; ot->exec= actkeys_duplicate_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -584,22 +582,22 @@ static int actkeys_delete_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_delete (wmOperatorType *ot) +void ACT_OT_delete (wmOperatorType *ot) { /* identifiers */ ot->name= "Delete Keyframes"; - ot->idname= "ACT_OT_keyframes_delete"; + ot->idname= "ACT_OT_delete"; /* api callbacks */ ot->invoke= WM_operator_confirm; ot->exec= actkeys_delete_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -647,22 +645,22 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_clean (wmOperatorType *ot) +void ACT_OT_clean (wmOperatorType *ot) { /* identifiers */ ot->name= "Clean Keyframes"; - ot->idname= "ACT_OT_keyframes_clean"; + ot->idname= "ACT_OT_clean"; /* api callbacks */ //ot->invoke= // XXX we need that number popup for this! ot->exec= actkeys_clean_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -771,21 +769,21 @@ static int actkeys_sample_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_sample (wmOperatorType *ot) +void ACT_OT_sample (wmOperatorType *ot) { /* identifiers */ ot->name= "Sample Keyframes"; - ot->idname= "ACT_OT_keyframes_sample"; + ot->idname= "ACT_OT_sample"; /* api callbacks */ ot->exec= actkeys_sample_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -846,22 +844,22 @@ static int actkeys_expo_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframe properties have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_extrapolation_type_set (wmOperatorType *ot) +void ACT_OT_extrapolation_type (wmOperatorType *ot) { /* identifiers */ ot->name= "Set Keyframe Extrapolation"; - ot->idname= "ACT_OT_keyframes_extrapolation_type_set"; + ot->idname= "ACT_OT_extrapolation_type"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= actkeys_expo_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -916,22 +914,22 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframe properties have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_interpolation_type (wmOperatorType *ot) +void ACT_OT_interpolation_type (wmOperatorType *ot) { /* identifiers */ ot->name= "Set Keyframe Interpolation"; - ot->idname= "ACT_OT_keyframes_interpolation_type"; + ot->idname= "ACT_OT_interpolation_type"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= actkeys_ipo_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1004,22 +1002,22 @@ static int actkeys_handletype_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframe properties have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_handle_type_set (wmOperatorType *ot) +void ACT_OT_handle_type (wmOperatorType *ot) { /* identifiers */ ot->name= "Set Keyframe Handle Type"; - ot->idname= "ACT_OT_keyframes_handle_type_set"; + ot->idname= "ACT_OT_handle_type"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= actkeys_handletype_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1031,10 +1029,10 @@ void ACT_OT_keyframes_handle_type_set (wmOperatorType *ot) /* ************************************************************************** */ /* TRANSFORM STUFF */ -/* ***************** Snap Current Frame Operator *********************** */ +/* ***************** Jump to Selected Frames Operator *********************** */ /* snap current-frame indicator to 'average time' of selected keyframe */ -static int actkeys_cfrasnap_exec(bContext *C, wmOperator *op) +static int actkeys_framejump_exec(bContext *C, wmOperator *op) { bAnimContext ac; ListBase anim_data= {NULL, NULL}; @@ -1053,8 +1051,17 @@ static int actkeys_cfrasnap_exec(bContext *C, wmOperator *op) filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - for (ale= anim_data.first; ale; ale= ale->next) - ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_calc_average, NULL); + for (ale= anim_data.first; ale; ale= ale->next) { + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); + + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_calc_average, NULL); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); + } + else + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_calc_average, NULL); + } BLI_freelistN(&anim_data); @@ -1070,15 +1077,15 @@ static int actkeys_cfrasnap_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void ACT_OT_keyframes_cfrasnap (wmOperatorType *ot) +void ACT_OT_frame_jump (wmOperatorType *ot) { /* identifiers */ - ot->name= "Snap Current Frame to Keys"; - ot->idname= "ACT_OT_keyframes_cfrasnap"; + ot->name= "Jump to Frame"; + ot->idname= "ACT_OT_frame_jump"; /* api callbacks */ - ot->exec= actkeys_cfrasnap_exec; - ot->poll= ED_operator_areaactive; + ot->exec= actkeys_framejump_exec; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1124,12 +1131,12 @@ static void snap_action_keys(bAnimContext *ac, short mode) /* snap keyframes */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, edit_cb, calchandles_fcurve); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } //else if (ale->type == ACTTYPE_GPLAYER) // snap_gplayer_frames(ale->data, mode); @@ -1159,22 +1166,22 @@ static int actkeys_snap_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_snap (wmOperatorType *ot) +void ACT_OT_snap (wmOperatorType *ot) { /* identifiers */ ot->name= "Snap Keys"; - ot->idname= "ACT_OT_keyframes_snap"; + ot->idname= "ACT_OT_snap"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= actkeys_snap_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1187,10 +1194,10 @@ void ACT_OT_keyframes_snap (wmOperatorType *ot) /* defines for mirror keyframes tool */ EnumPropertyItem prop_actkeys_mirror_types[] = { - {ACTKEYS_MIRROR_CFRA, "CFRA", 0, "Current frame", ""}, - {ACTKEYS_MIRROR_YAXIS, "YAXIS", 0, "Vertical Axis", ""}, - {ACTKEYS_MIRROR_XAXIS, "XAXIS", 0, "Horizontal Axis", ""}, - {ACTKEYS_MIRROR_MARKER, "MARKER", 0, "First Selected Marker", ""}, + {ACTKEYS_MIRROR_CFRA, "CFRA", 0, "By Times over Current frame", ""}, + {ACTKEYS_MIRROR_YAXIS, "YAXIS", 0, "By Times over Time=0", ""}, + {ACTKEYS_MIRROR_XAXIS, "XAXIS", 0, "By Values over Value=0", ""}, + {ACTKEYS_MIRROR_MARKER, "MARKER", 0, "By Times over First Selected Marker", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1240,12 +1247,12 @@ static void mirror_action_keys(bAnimContext *ac, short mode) /* mirror keyframes */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, edit_cb, calchandles_fcurve); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } //else if (ale->type == ACTTYPE_GPLAYER) // snap_gplayer_frames(ale->data, mode); @@ -1275,22 +1282,22 @@ static int actkeys_mirror_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_mirror (wmOperatorType *ot) +void ACT_OT_mirror (wmOperatorType *ot) { /* identifiers */ ot->name= "Mirror Keys"; - ot->idname= "ACT_OT_keyframes_mirror"; + ot->idname= "ACT_OT_mirror"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= actkeys_mirror_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; diff --git a/source/blender/editors/space_action/action_header.c b/source/blender/editors/space_action/action_header.c index fa96e1ea81f..f5c85d4d983 100644 --- a/source/blender/editors/space_action/action_header.c +++ b/source/blender/editors/space_action/action_header.c @@ -48,9 +48,12 @@ #include "ED_anim_api.h" #include "ED_screen.h" +#include "ED_transform.h" #include "ED_types.h" #include "ED_util.h" +#include "RNA_access.h" + #include "WM_api.h" #include "WM_types.h" @@ -63,1494 +66,183 @@ #include "action_intern.h" +enum { + B_REDR= 0, +} eActHeader_Events; + /* ********************************************************* */ /* Menu Defines... */ -/* button events */ -enum { - B_REDR = 0, - B_ACTCOPYKEYS, - B_ACTPASTEKEYS, -} eActHeader_ButEvents; - -/* ------------------------------- */ -/* enums declaring constants that are used as menu event codes */ - -enum { - ACTMENU_VIEW_CENTERVIEW= 0, - ACTMENU_VIEW_AUTOUPDATE, - ACTMENU_VIEW_PLAY3D, - ACTMENU_VIEW_PLAYALL, - ACTMENU_VIEW_ALL, - ACTMENU_VIEW_MAXIMIZE, - ACTMENU_VIEW_LOCK, - ACTMENU_VIEW_SLIDERS, - ACTMENU_VIEW_NEXTMARKER, - ACTMENU_VIEW_PREVMARKER, - ACTMENU_VIEW_NEXTKEYFRAME, - ACTMENU_VIEW_PREVKEYFRAME, - ACTMENU_VIEW_TIME, - ACTMENU_VIEW_NOHIDE, - ACTMENU_VIEW_FRANUM, - ACTMENU_VIEW_TRANSDELDUPS, - ACTMENU_VIEW_HORIZOPTIMISE, - ACTMENU_VIEW_GCOLORS, - ACTMENU_VIEW_PREVRANGESET, - ACTMENU_VIEW_PREVRANGECLEAR, - ACTMENU_VIEW_PREVRANGEAUTO -}; - -enum { - ACTMENU_SEL_BORDER = 0, - ACTMENU_SEL_BORDERC, - ACTMENU_SEL_BORDERM, - ACTMENU_SEL_ALL_KEYS, - ACTMENU_SEL_ALL_CHAN, - ACTMENU_SEL_ALL_MARKERS, - ACTMENU_SEL_INVERSE_KEYS, - ACTMENU_SEL_INVERSE_MARKERS, - ACTMENU_SEL_INVERSE_CHANNELS, - ACTMENU_SEL_LEFTKEYS, - ACTMENU_SEL_RIGHTKEYS -}; - -enum { - ACTMENU_SEL_COLUMN_KEYS = 1, - ACTMENU_SEL_COLUMN_CFRA, - ACTMENU_SEL_COLUMN_MARKERSCOLUMN, - ACTMENU_SEL_COLUMN_MARKERSBETWEEN -}; - -enum { - ACTMENU_CHANNELS_OPENLEVELS = 0, - ACTMENU_CHANNELS_CLOSELEVELS, - ACTMENU_CHANNELS_EXPANDALL, - ACTMENU_CHANNELS_SHOWACHANS, - ACTMENU_CHANNELS_DELETE -}; - -enum { - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_UP = 0, - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_DOWN, - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_TOP, - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_BOTTOM -}; - -enum { - ACTMENU_CHANNELS_GROUP_ADD_TOACTIVE = 0, - ACTMENU_CHANNELS_GROUP_ADD_TONEW, - ACTMENU_CHANNELS_GROUP_REMOVE, - ACTMENU_CHANNELS_GROUP_SYNCPOSE -}; - -enum { - ACTMENU_CHANNELS_SETTINGS_TOGGLE = 0, - ACTMENU_CHANNELS_SETTINGS_ENABLE, - ACTMENU_CHANNELS_SETTINGS_DISABLE, -}; - -enum { - ACTMENU_KEY_DUPLICATE = 0, - ACTMENU_KEY_DELETE, - ACTMENU_KEY_CLEAN, - ACTMENU_KEY_SAMPLEKEYS, - ACTMENU_KEY_INSERTKEY -}; - -enum { - ACTMENU_KEY_TRANSFORM_MOVE = 0, - ACTMENU_KEY_TRANSFORM_SCALE, - ACTMENU_KEY_TRANSFORM_SLIDE, - ACTMENU_KEY_TRANSFORM_EXTEND -}; - -enum { - ACTMENU_KEY_HANDLE_AUTO = 0, - ACTMENU_KEY_HANDLE_ALIGN, - ACTMENU_KEY_HANDLE_FREE, - ACTMENU_KEY_HANDLE_VECTOR -}; - -enum { - ACTMENU_KEY_INTERP_CONST = 0, - ACTMENU_KEY_INTERP_LINEAR, - ACTMENU_KEY_INTERP_BEZIER -}; - -enum { - ACTMENU_KEY_EXTEND_CONST = 0, - ACTMENU_KEY_EXTEND_EXTRAPOLATION, - ACTMENU_KEY_EXTEND_CYCLIC, - ACTMENU_KEY_EXTEND_CYCLICEXTRAPOLATION -}; - -enum { - ACTMENU_KEY_SNAP_NEARFRAME = 1, - ACTMENU_KEY_SNAP_CURFRAME, - ACTMENU_KEY_SNAP_NEARMARK, - ACTMENU_KEY_SNAP_NEARTIME, - ACTMENU_KEY_SNAP_CFRA2KEY, -}; - -enum { - ACTMENU_KEY_MIRROR_CURFRAME = 1, - ACTMENU_KEY_MIRROR_YAXIS, - ACTMENU_KEY_MIRROR_XAXIS, - ACTMENU_KEY_MIRROR_MARKER -}; - -enum { - ACTMENU_MARKERS_ADD = 0, - ACTMENU_MARKERS_DUPLICATE, - ACTMENU_MARKERS_DELETE, - ACTMENU_MARKERS_NAME, - ACTMENU_MARKERS_MOVE, - ACTMENU_MARKERS_LOCALADD, - ACTMENU_MARKERS_LOCALRENAME, - ACTMENU_MARKERS_LOCALDELETE, - ACTMENU_MARKERS_LOCALMOVE -}; - -/* ------------------------------- */ -/* macros for easier state testing (only for use here) */ - -/* test if active action editor is showing any markers */ -#if 0 - #define SACTION_HASMARKERS \ - ((saction->action && saction->action->markers.first) \ - || (scene->markers.first)) -#endif - -/* need to find out how to get scene from context */ -#define SACTION_HASMARKERS (saction->action && saction->action->markers.first) - -/* ------------------------------- */ - -/* *************************************************************** */ -/* menus */ - -/* Key menu --------------------------- */ - -static void do_keymenu_transformmenu(bContext *C, void *arg, int event) +static void act_viewmenu(bContext *C, uiLayout *layout, void *arg_unused) { - switch (event) - { - case ACTMENU_KEY_TRANSFORM_MOVE: - //transform_action_keys('g', 0); - break; - case ACTMENU_KEY_TRANSFORM_SCALE: - //transform_action_keys('s', 0); - break; - case ACTMENU_KEY_TRANSFORM_SLIDE: - //transform_action_keys('t', 0); - break; - case ACTMENU_KEY_TRANSFORM_EXTEND: - //transform_action_keys('e', 0); - break; - } -} - -static uiBlock *action_keymenu_transformmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa= CTX_wm_area(C); + SpaceAction *sact= (SpaceAction*)CTX_wm_space_data(C); + PointerRNA spaceptr; - block= uiBeginBlock(C, ar, "action_keymenu_transformmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_keymenu_transformmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Grab/Move|G", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_TRANSFORM_MOVE, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Grab/Extend from Frame|E", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_TRANSFORM_EXTEND, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Scale|S", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_TRANSFORM_SCALE, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Time Slide|T", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_TRANSFORM_SLIDE, ""); + /* retrieve state */ + RNA_pointer_create(&sc->id, &RNA_SpaceDopeSheetEditor, sact, &spaceptr); - uiBlockSetDirection(block, UI_RIGHT); + /* create menu */ + //uiItemO(layout, NULL, ICON_MENU_PANEL, "ACT_OT_properties"); - uiTextBoundsBlock(block, 60); - uiEndBlock(C, block); + //uiItemS(layout); - return block; -} - -static void do_keymenu_snapmenu(bContext *C, void *arg, int event) -{ - switch(event) - { - case ACTMENU_KEY_SNAP_NEARFRAME: - case ACTMENU_KEY_SNAP_CURFRAME: - case ACTMENU_KEY_SNAP_NEARMARK: - case ACTMENU_KEY_SNAP_NEARTIME: - //snap_action_keys(event); - break; - - case ACTMENU_KEY_SNAP_CFRA2KEY: - //snap_cfra_action(); - break; - } -} - -static uiBlock *action_keymenu_snapmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - uiBlock *block; - short yco= 0, menuwidth=120; - + uiItemR(layout, NULL, 0, &spaceptr, "show_cframe_indicator", 0, 0, 0); + uiItemR(layout, NULL, 0, &spaceptr, "show_sliders", 0, 0, 0); + uiItemR(layout, NULL, 0, &spaceptr, "automerge_keyframes", 0, 0, 0); - block= uiBeginBlock(C, ar, "action_keymenu_snapmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_keymenu_snapmenu, NULL); - - if (saction->flag & SACTION_DRAWTIME) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Key -> Nearest Second|Shift S, 1", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_SNAP_NEARTIME, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Key -> Current Time|Shift S, 2", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_SNAP_CURFRAME, ""); - - } - else { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Key -> Nearest Frame|Shift S, 1", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_SNAP_NEARFRAME, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Key -> Current Frame|Shift S, 2", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_SNAP_CURFRAME, ""); - } - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Key -> Nearest Marker|Shift S, 3", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_SNAP_NEARMARK, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Current Frame -> Key|Ctrl Shift S", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_SNAP_NEARMARK, ""); + if (sact->flag & SACTION_DRAWTIME) + uiItemO(layout, "Show Frames", 0, "ANIM_OT_time_toggle"); + else + uiItemO(layout, "Show Seconds", 0, "ANIM_OT_time_toggle"); - uiBlockSetDirection(block, UI_RIGHT); + uiItemS(layout); - uiTextBoundsBlock(block, 60); - uiEndBlock(C, block); + uiItemO(layout, NULL, 0, "ANIM_OT_previewrange_set"); + uiItemO(layout, NULL, 0, "ANIM_OT_previewrange_clear"); - return block; -} - -static void do_keymenu_mirrormenu(bContext *C, void *arg, int event) -{ - switch(event) - { - case ACTMENU_KEY_MIRROR_CURFRAME: - case ACTMENU_KEY_MIRROR_YAXIS: - //mirror_action_keys(event); - break; - } - -} - -static uiBlock *action_keymenu_mirrormenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; + uiItemO(layout, NULL, 0, "ACT_OT_previewrange_set"); - block= uiBeginBlock(C, ar, "action_keymenu_mirrormenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_keymenu_mirrormenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Current Frame|Shift M, 1", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_MIRROR_CURFRAME, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Vertical Axis|Shift M, 2", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_MIRROR_YAXIS, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Horizontal Axis|Shift M, 3", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_MIRROR_XAXIS, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Selected Marker|Shift M, 4", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_MIRROR_MARKER, ""); + uiItemS(layout); - uiBlockSetDirection(block, UI_RIGHT); + uiItemO(layout, NULL, 0, "ACT_OT_frame_jump"); - uiTextBoundsBlock(block, 60); - uiEndBlock(C, block); + uiItemO(layout, NULL, 0, "ACT_OT_view_all"); - return block; -} - -static void do_keymenu_handlemenu(bContext *C, void *arg, int event) -{ - switch (event) { - case ACTMENU_KEY_HANDLE_AUTO: - //sethandles_action_keys(HD_AUTO); - break; - - case ACTMENU_KEY_HANDLE_ALIGN: - case ACTMENU_KEY_HANDLE_FREE: - /* OK, this is kinda dumb, need to fix the - * toggle crap in sethandles_ipo_keys() - */ - //sethandles_action_keys(HD_ALIGN); - break; - - case ACTMENU_KEY_HANDLE_VECTOR: - //sethandles_action_keys(HD_VECT); - break; - } -} - -static uiBlock *action_keymenu_handlemenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_keymenu_handlemenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_keymenu_handlemenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Auto|Shift H", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_HANDLE_AUTO, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Aligned|H", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_HANDLE_ALIGN, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Free|H", 0, yco-=20, menuwidth, - 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_HANDLE_FREE, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Vector|V", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_HANDLE_VECTOR, ""); - - uiBlockSetDirection(block, UI_RIGHT); - - uiTextBoundsBlock(block, 60); - uiEndBlock(C, block); - - return block; -} - -static void do_keymenu_extendmenu(bContext *C, void *arg, int event) -{ - switch(event) - { - case ACTMENU_KEY_EXTEND_CONST: - //action_set_ipo_flags(SET_EXTEND_MENU, SET_EXTEND_CONSTANT); - break; - case ACTMENU_KEY_EXTEND_EXTRAPOLATION: - //action_set_ipo_flags(SET_EXTEND_MENU, SET_EXTEND_EXTRAPOLATION); - break; - case ACTMENU_KEY_EXTEND_CYCLIC: - //action_set_ipo_flags(SET_EXTEND_MENU, SET_EXTEND_CYCLIC); - break; - case ACTMENU_KEY_EXTEND_CYCLICEXTRAPOLATION: - //action_set_ipo_flags(SET_EXTEND_MENU, SET_EXTEND_CYCLICEXTRAPOLATION); - break; - } -} - -static uiBlock *action_keymenu_extendmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_keymenu_extendmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_keymenu_extendmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Constant", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_EXTEND_CONST, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Extrapolation", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_EXTEND_EXTRAPOLATION, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Cyclic", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_EXTEND_CYCLIC, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Cyclic Extrapolation", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_EXTEND_CYCLICEXTRAPOLATION, ""); - - uiBlockSetDirection(block, UI_RIGHT); - - uiTextBoundsBlock(block, 60); - uiEndBlock(C, block); - - return block; -} - -static void do_keymenu_intpolmenu(bContext *C, void *arg, int event) -{ - switch(event) - { - case ACTMENU_KEY_INTERP_CONST: - //action_set_ipo_flags(SET_IPO_MENU, SET_IPO_CONSTANT); - break; - case ACTMENU_KEY_INTERP_LINEAR: - //action_set_ipo_flags(SET_IPO_MENU, SET_IPO_LINEAR); - break; - case ACTMENU_KEY_INTERP_BEZIER: - //action_set_ipo_flags(SET_IPO_MENU, SET_IPO_BEZIER); - break; - } -} - -static uiBlock *action_keymenu_intpolmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_keymenu_intpolmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_keymenu_intpolmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Constant|Shift T, 1", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_INTERP_CONST, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Linear|Shift T, 2", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_INTERP_LINEAR, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Bezier|Shift T, 3", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_INTERP_BEZIER, ""); - - uiBlockSetDirection(block, UI_RIGHT); - - uiTextBoundsBlock(block, 60); - uiEndBlock(C, block); - - return block; -} - -static void do_action_keymenu(bContext *C, void *arg, int event) -{ - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - bAction *act; - //Key *key; - - if (!saction) return; - - act = saction->action; - //key = get_action_mesh_key(); - - switch(event) - { - case ACTMENU_KEY_DUPLICATE: - //duplicate_action_keys(); - break; - case ACTMENU_KEY_DELETE: - //delete_action_keys(); - break; - case ACTMENU_KEY_CLEAN: - //clean_action(); - break; - case ACTMENU_KEY_SAMPLEKEYS: - //sample_action_keys(); - break; - case ACTMENU_KEY_INSERTKEY: - //insertkey_action(); - break; - } -} - -static uiBlock *action_keymenu(bContext *C, ARegion *ar, void *arg_unused) -{ - ScrArea *curarea= CTX_wm_area(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_keymenu", UI_EMBOSSP); - - - uiBlockSetButmFunc(block, do_action_keymenu, NULL); - - uiDefIconTextBlockBut(block, action_keymenu_transformmenu, - NULL, ICON_RIGHTARROW_THIN, "Transform", 0, yco-=20, 120, 20, ""); - - uiDefIconTextBlockBut(block, action_keymenu_snapmenu, - NULL, ICON_RIGHTARROW_THIN, "Snap", 0, yco-=20, 120, 20, ""); - - uiDefIconTextBlockBut(block, action_keymenu_mirrormenu, - NULL, ICON_RIGHTARROW_THIN, "Mirror", 0, yco-=20, 120, 20, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Insert Key|I", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_INSERTKEY, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Duplicate|Shift D", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_DUPLICATE, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Delete|X", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_DELETE, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Clean Action|O", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_CLEAN, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Sample Keys|Alt O", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_SAMPLEKEYS, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBlockBut(block, action_keymenu_handlemenu, - NULL, ICON_RIGHTARROW_THIN, - "Handle Type", 0, yco-=20, 120, 20, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBlockBut(block, action_keymenu_extendmenu, - NULL, ICON_RIGHTARROW_THIN, - "Extend Mode", 0, yco-=20, 120, 20, ""); - uiDefIconTextBlockBut(block, action_keymenu_intpolmenu, - NULL, ICON_RIGHTARROW_THIN, - "Interpolation Mode", 0, yco-=20, 120, 20, ""); - - if(curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; -} - -/* Frame menu --------------------------- */ - - -// framemenu uses functions from keymenu -static uiBlock *action_framemenu(bContext *C, ARegion *ar, void *arg_unused) -{ - ScrArea *curarea= CTX_wm_area(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_framemenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_action_keymenu, NULL); - - uiDefIconTextBlockBut(block, action_keymenu_transformmenu, - NULL, ICON_RIGHTARROW_THIN, "Transform", 0, yco-=20, 120, 20, ""); - - uiDefIconTextBlockBut(block, action_keymenu_snapmenu, - NULL, ICON_RIGHTARROW_THIN, "Snap", 0, yco-=20, 120, 20, ""); - - uiDefIconTextBlockBut(block, action_keymenu_mirrormenu, - NULL, ICON_RIGHTARROW_THIN, "Mirror", 0, yco-=20, 120, 20, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Duplicate|Shift D", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_DUPLICATE, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Delete|X", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_KEY_DELETE, ""); - - if(curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; -} - -/* Marker menu --------------------------- */ - -static void do_markermenu(bContext *C, void *arg, int event) -{ - switch(event) - { - case ACTMENU_MARKERS_ADD: - //add_marker(CFRA); - break; - case ACTMENU_MARKERS_DUPLICATE: - //duplicate_marker(); - break; - case ACTMENU_MARKERS_DELETE: - //remove_marker(); - break; - case ACTMENU_MARKERS_NAME: - //rename_marker(); - break; - case ACTMENU_MARKERS_MOVE: - //transform_markers('g', 0); - break; - case ACTMENU_MARKERS_LOCALADD: - //action_add_localmarker(G.saction->action, CFRA); - break; - case ACTMENU_MARKERS_LOCALDELETE: - //action_remove_localmarkers(G.saction->action); - break; - case ACTMENU_MARKERS_LOCALRENAME: - //action_rename_localmarker(G.saction->action); - break; - case ACTMENU_MARKERS_LOCALMOVE: - /*G.saction->flag |= SACTION_POSEMARKERS_MOVE; - transform_markers('g', 0); - G.saction->flag &= ~SACTION_POSEMARKERS_MOVE;*/ - break; - } -} - -static uiBlock *action_markermenu(bContext *C, ARegion *ar, void *arg_unused) -{ - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - ScrArea *curarea= CTX_wm_area(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_markermenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_markermenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Add Marker|M", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_ADD, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Duplicate Marker|Ctrl Shift D", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_DUPLICATE, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete Marker|Shift X", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_DELETE, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "(Re)Name Marker|Ctrl M", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_NAME, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Marker|Ctrl G", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_MOVE, ""); - - if (saction->mode == SACTCONT_ACTION) { - uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Add Pose Marker|Shift L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALADD, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Rename Pose Marker|Ctrl Shift L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALRENAME, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete Pose Marker|Alt L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALDELETE, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Pose Marker|Ctrl L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALMOVE, ""); - } - - if(curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; -} - - -/* Channel menu --------------------------- */ - -static void do_channelmenu_posmenu(bContext *C, void *arg, int event) -{ - switch(event) - { - case ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_DOWN: - //rearrange_action_channels(REARRANGE_ACTCHAN_DOWN); - break; - case ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_UP: - //rearrange_action_channels(REARRANGE_ACTCHAN_UP); - break; - case ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_TOP: - //rearrange_action_channels(REARRANGE_ACTCHAN_TOP); - break; - case ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_BOTTOM: - //rearrange_action_channels(REARRANGE_ACTCHAN_BOTTOM); - break; - } -} - -static uiBlock *action_channelmenu_posmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_channelmenu_posmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_channelmenu_posmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move Up|Shift Page Up", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_UP, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move Down|Shift Page Down", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_DOWN, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move to Top|Ctrl Shift Page Up", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_TOP, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move to Bottom|Ctrl Shift Page Down", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_CHANPOS_MOVE_CHANNEL_BOTTOM, ""); - - uiBlockSetDirection(block, UI_RIGHT); - uiTextBoundsBlock(block, 60); - - return block; -} - -static void do_channelmenu_groupmenu(bContext *C, void *arg, int event) -{ - switch(event) - { - case ACTMENU_CHANNELS_GROUP_ADD_TOACTIVE: - //action_groups_group(0); - break; - case ACTMENU_CHANNELS_GROUP_ADD_TONEW: - //action_groups_group(1); - break; - case ACTMENU_CHANNELS_GROUP_REMOVE: - //action_groups_ungroup(); - break; - case ACTMENU_CHANNELS_GROUP_SYNCPOSE: /* Syncronise Pose-data and Action-data */ - //sync_pchan2achan_grouping(); - break; - } -} - -static uiBlock *action_channelmenu_groupmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_channelmenu_groupmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_channelmenu_groupmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Add to Active Group|Shift G", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_GROUP_ADD_TOACTIVE, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Add to New Group|Ctrl Shift G", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_GROUP_ADD_TONEW, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Remove From Group|Alt G", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_GROUP_REMOVE, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Synchronise with Armature", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_GROUP_SYNCPOSE, ""); - - uiBlockSetDirection(block, UI_RIGHT); - uiTextBoundsBlock(block, 60); - - return block; -} - -static void do_channelmenu_settingsmenu(bContext *C, void *arg, int event) -{ - //setflag_action_channels(event); -} - -static uiBlock *action_channelmenu_settingsmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_channelmenu_settingsmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_channelmenu_settingsmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Toggle a Setting|Shift W", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_SETTINGS_TOGGLE, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Enable a Setting|Ctrl Shift W", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_SETTINGS_ENABLE, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Disable a Setting|Alt W", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_CHANNELS_SETTINGS_DISABLE, ""); - - uiBlockSetDirection(block, UI_RIGHT); - uiTextBoundsBlock(block, 60); - - return block; -} - -static void do_channelmenu(bContext *C, void *arg, int event) -{ - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - - if (saction == NULL) return; - - switch(event) - { - case ACTMENU_CHANNELS_OPENLEVELS: /* Unfold selected channels one step */ - //openclose_level_action(1); - break; - case ACTMENU_CHANNELS_CLOSELEVELS: /* Fold selected channels one step */ - //openclose_level_action(-1); - break; - case ACTMENU_CHANNELS_EXPANDALL: /* Expands all channels */ - //expand_all_action(); - break; - case ACTMENU_CHANNELS_SHOWACHANS: /* Unfold groups that are hiding selected achans */ - //expand_obscuregroups_action(); - break; - case ACTMENU_CHANNELS_DELETE: /* Deletes selected channels */ - //delete_action_channels(); - break; - } -} - -static uiBlock *action_channelmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - ScrArea *curarea= CTX_wm_area(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_channelmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_channelmenu, NULL); - - uiDefIconTextBlockBut(block, action_channelmenu_groupmenu, - NULL, ICON_RIGHTARROW_THIN, - "Grouping", 0, yco-=20, 120, 20, ""); - - uiDefIconTextBlockBut(block, action_channelmenu_posmenu, - NULL, ICON_RIGHTARROW_THIN, - "Ordering", 0, yco-=20, 120, 20, ""); - - uiDefIconTextBlockBut(block, action_channelmenu_settingsmenu, - NULL, ICON_RIGHTARROW_THIN, - "Settings", 0, yco-=20, 120, 20, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Delete|X", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_DELETE, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Toggle Show Hierachy|~", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_EXPANDALL, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Show Group-Hidden Channels|Shift ~", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_SHOWACHANS, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Expand One Level|Ctrl NumPad+", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_OPENLEVELS, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Collapse One Level|Ctrl NumPad-", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_CLOSELEVELS, ""); - - if(curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; -} - -/* Grease Pencil --------------------------- */ - -/* Uses channelmenu functions */ -static uiBlock *action_gplayermenu(bContext *C, ARegion *ar, void *arg_unused) -{ - ScrArea *curarea= CTX_wm_area(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_gplayermenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_channelmenu, NULL); - - uiDefIconTextBlockBut(block, action_channelmenu_settingsmenu, - NULL, ICON_RIGHTARROW_THIN, - "Settings", 0, yco-=20, 120, 20, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Delete|X", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_DELETE, ""); - - if(curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; -} - -/* Select menu --------------------------- */ - -static void do_selectmenu_columnmenu(bContext *C, void *arg, int event) -{ - switch (event) { - case ACTMENU_SEL_COLUMN_MARKERSBETWEEN: - //markers_selectkeys_between(); - break; - case ACTMENU_SEL_COLUMN_KEYS: - //column_select_action_keys(1); - break; - case ACTMENU_SEL_COLUMN_MARKERSCOLUMN: - //column_select_action_keys(2); - break; - case ACTMENU_SEL_COLUMN_CFRA: - //column_select_action_keys(3); - break; - } -} - -static uiBlock *action_selectmenu_columnmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_selectmenu_columnmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_selectmenu_columnmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "On Selected Keys|K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_COLUMN_KEYS, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "On Current Frame|Ctrl K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_COLUMN_CFRA, ""); - - if (SACTION_HASMARKERS) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "On Selected Markers|Shift K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_COLUMN_MARKERSCOLUMN, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Between Selected Markers|Alt K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_COLUMN_MARKERSBETWEEN, ""); - } - - uiBlockSetDirection(block, UI_RIGHT); - uiTextBoundsBlock(block, 60); - - return block; -} - -static void do_selectmenu(bContext *C, void *arg, int event) -{ - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - //Key *key; - - if (saction == NULL) return; - - //key = get_action_mesh_key(); - - switch(event) - { - case ACTMENU_SEL_BORDER: /* Border Select */ - //borderselect_action(); - break; - - case ACTMENU_SEL_BORDERC: /* Border Select */ - //borderselect_actionchannels(); - break; - - case ACTMENU_SEL_BORDERM: /* Border Select */ - //borderselect_markers(); - break; - - case ACTMENU_SEL_ALL_KEYS: /* Select/Deselect All Keys */ - /*deselect_action_keys(1, 1); - BIF_undo_push("(De)Select Keys"); - allqueue(REDRAWACTION, 0); - allqueue(REDRAWNLA, 0); - allqueue(REDRAWIPO, 0);*/ - break; - - case ACTMENU_SEL_ALL_CHAN: /* Select/Deselect All Channels */ - /*deselect_action_channels(1); - BIF_undo_push("(De)Select Action Channels"); - allqueue(REDRAWVIEW3D, 0); - allqueue(REDRAWACTION, 0); - allqueue(REDRAWNLA, 0); - allqueue(REDRAWIPO, 0);*/ - break; - - case ACTMENU_SEL_ALL_MARKERS: /* select/deselect all markers */ - /*deselect_markers(1, 0); - BIF_undo_push("(De)Select Markers"); - allqueue(REDRAWMARKER, 0);*/ - break; - - case ACTMENU_SEL_INVERSE_KEYS: /* invert selection status of keys */ - /*deselect_action_keys(0, 2); - BIF_undo_push("Inverse Keys"); - allqueue(REDRAWACTION, 0); - allqueue(REDRAWNLA, 0); - allqueue(REDRAWIPO, 0);*/ - break; - - case ACTMENU_SEL_INVERSE_CHANNELS: /* invert selection status of channels */ - /*deselect_action_channels(2); - BIF_undo_push("Inverse Action Channels"); - allqueue(REDRAWVIEW3D, 0); - allqueue(REDRAWACTION, 0); - allqueue(REDRAWNLA, 0); - allqueue(REDRAWIPO, 0);*/ - break; - - case ACTMENU_SEL_INVERSE_MARKERS: /* invert selection of markers */ - /*deselect_markers(0, 2); - BIF_undo_push("Inverse Action Channels"); - allqueue(REDRAWMARKER, 0);*/ - break; - - case ACTMENU_SEL_LEFTKEYS: - //selectkeys_leftright(1, SELECT_REPLACE); - break; - - case ACTMENU_SEL_RIGHTKEYS: - //selectkeys_leftright(0, SELECT_REPLACE); - break; - } -} - -static uiBlock *action_selectmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - ScrArea *curarea= CTX_wm_area(C); - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "action_selectmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_selectmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Border Select Keys|B", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_BORDER, ""); - if (SACTION_HASMARKERS) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Border Select Markers|Ctrl B", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_BORDERM, ""); - } - if (saction->mode != SACTCONT_SHAPEKEY) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Border Select Channels|B", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_BORDERC, ""); - } - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Select/Deselect All Keys|A", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_ALL_KEYS, ""); - if (SACTION_HASMARKERS) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Select/Deselect All Markers|Ctrl A", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_ALL_MARKERS, ""); - } - if (saction->mode != SACTCONT_SHAPEKEY) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Select/Deselect All Channels|A", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_ALL_CHAN, ""); - } - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Inverse Keys|Ctrl I", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_INVERSE_KEYS, ""); - if (SACTION_HASMARKERS) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Inverse Markers|Ctrl Shift I", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_INVERSE_MARKERS, ""); - } - if (saction->mode != SACTCONT_SHAPEKEY) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Inverse All Channels|Ctrl I", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_INVERSE_CHANNELS, ""); - } - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Back In Time|Alt RMB", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_LEFTKEYS, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Ahead In Time|Alt RMB", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_RIGHTKEYS, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBlockBut(block, action_selectmenu_columnmenu, - NULL, ICON_RIGHTARROW_THIN, "Column Select Keys", 0, yco-=20, 120, 20, ""); - - if(curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; -} - -/* View menu --------------------------- */ - -static void do_viewmenu(bContext *C, void *arg, int event) -{ - switch(event) { - case ACTMENU_VIEW_CENTERVIEW: /* Center View to Current Frame */ - //center_currframe(); - break; - case ACTMENU_VIEW_AUTOUPDATE: /* Update Automatically */ - /*if (BTST(G.saction->lock, 0)) - G.saction->lock = BCLR(G.saction->lock, 0); - else - G.saction->lock = BSET(G.saction->lock, 0);*/ - break; - case ACTMENU_VIEW_PLAY3D: /* Play Back Animation */ - //play_anim(0); - break; - case ACTMENU_VIEW_PLAYALL: /* Play Back Animation in All */ - //play_anim(1); - break; - case ACTMENU_VIEW_ALL: /* View All */ - //do_action_buttons(B_ACTHOME); - break; - case ACTMENU_VIEW_LOCK: - /*G.v2d->flag ^= V2D_VIEWLOCK; - if (G.v2d->flag & V2D_VIEWLOCK) - view2d_do_locks(curarea, 0);*/ - break; - case ACTMENU_VIEW_SLIDERS: /* Show sliders (when applicable) */ - //G.saction->flag ^= SACTION_SLIDERS; - break; - case ACTMENU_VIEW_MAXIMIZE: /* Maximize Window */ - /* using event B_FULL */ - break; - case ACTMENU_VIEW_NEXTMARKER: /* Jump to next marker */ - //nextprev_marker(1); - break; - case ACTMENU_VIEW_PREVMARKER: /* Jump to previous marker */ - //nextprev_marker(-1); - break; - case ACTMENU_VIEW_TIME: /* switch between frames and seconds display */ - //G.saction->flag ^= SACTION_DRAWTIME; - break; - case ACTMENU_VIEW_NOHIDE: /* Show hidden channels */ - //G.saction->flag ^= SACTION_NOHIDE; - break; - case ACTMENU_VIEW_NEXTKEYFRAME: /* Jump to next keyframe */ - //nextprev_action_keyframe(1); - break; - case ACTMENU_VIEW_PREVKEYFRAME: /* Jump to previous keyframe */ - //nextprev_action_keyframe(-1); - break; - case ACTMENU_VIEW_TRANSDELDUPS: /* Don't delete duplicate/overlapping keyframes after transform */ - //G.saction->flag ^= SACTION_NOTRANSKEYCULL; - break; - case ACTMENU_VIEW_HORIZOPTIMISE: /* Include keyframes not in view (horizontally) when preparing to draw */ - //G.saction->flag ^= SACTION_HORIZOPTIMISEON; - break; - case ACTMENU_VIEW_GCOLORS: /* Draw grouped-action channels using its group's color */ - //G.saction->flag ^= SACTION_NODRAWGCOLORS; - break; - case ACTMENU_VIEW_PREVRANGESET: /* Set preview range */ - //anim_previewrange_set(); - break; - case ACTMENU_VIEW_PREVRANGECLEAR: /* Clear preview range */ - //anim_previewrange_clear(); - break; - case ACTMENU_VIEW_PREVRANGEAUTO: /* Auto preview-range length */ - //action_previewrange_set(G.saction->action); - break; - } -} - -static uiBlock *action_viewmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - ScrArea *curarea= CTX_wm_area(C); - SpaceAction *saction= (SpaceAction*)CTX_wm_space_data(C); - View2D *v2d= UI_view2d_fromcontext_rwin(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "viewmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_viewmenu, NULL); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Center View to Current Frame|C", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_CENTERVIEW, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - if (saction->flag & SACTION_DRAWTIME) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Show Frames|Ctrl T", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_TIME, ""); - } - else { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Show Seconds|Ctrl T", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_TIME, ""); - } - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - if (saction->mode == SACTCONT_GPENCIL) { - // this option may get removed in future - uiDefIconTextBut(block, BUTM, 1, (saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Cull Out-of-View Keys (Time)|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_HORIZOPTIMISE, ""); - } - else { - uiDefIconTextBut(block, BUTM, 1, (saction->flag & SACTION_SLIDERS)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Show Sliders|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_SLIDERS, ""); - - uiDefIconTextBut(block, BUTM, 1, (saction->flag & SACTION_NOHIDE)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Show Hidden Channels|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_NOHIDE, ""); - - uiDefIconTextBut(block, BUTM, 1, (saction->flag & SACTION_NODRAWGCOLORS)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, - "Use Group Colors|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_GCOLORS, ""); - - // this option may get removed in future - uiDefIconTextBut(block, BUTM, 1, (saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Cull Out-of-View Keys (Time)|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_HORIZOPTIMISE, ""); - - uiDefIconTextBut(block, BUTM, 1, (saction->flag & SACTION_NOTRANSKEYCULL)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, - "AutoMerge Keyframes|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_TRANSDELDUPS, ""); - } - - - uiDefIconTextBut(block, BUTM, 1, (v2d->flag & V2D_VIEWSYNC_SCREEN_TIME)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Lock Time to Other Windows|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_LOCK, ""); - - /*uiDefIconTextBut(block, BUTM, 1, BTST(saction->lock, 0)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Update Automatically|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_AUTOUPDATE, "");*/ - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Jump To Next Marker|PageUp", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_NEXTMARKER, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Jump To Prev Marker|PageDown", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_PREVMARKER, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Jump To Next Keyframe|Ctrl PageUp", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_NEXTKEYFRAME, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Jump To Prev Keyframe|Ctrl PageDown", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_PREVKEYFRAME, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Play Back Animation|Alt A", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_PLAY3D, ""); - //uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - // "Play Back Animation in 3D View|Alt Shift A", 0, yco-=20, - // menuwidth, 19, NULL, 0.0, 0.0, 1, - // ACTMENU_VIEW_PLAYALL, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Set Preview Range|Ctrl P", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_PREVRANGESET, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Clear Preview Range|Alt P", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_PREVRANGECLEAR, ""); - - if ((saction->mode == SACTCONT_ACTION) && (saction->action)) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Preview Range from Action Length|Ctrl Alt P", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_PREVRANGEAUTO, ""); - } - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, - menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "View All|Home", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_ALL, ""); - -/* if (!curarea->full) - uiDefIconTextBut(block, BUTM, B_FULL, ICON_BLANK1, - "Maximize Window|Ctrl UpArrow", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_VIEW_MAXIMIZE, ""); + if (sa->full) + uiItemO(layout, NULL, 0, "SCREEN_OT_screen_full_area"); // "Tile Window", Ctrl UpArrow else - uiDefIconTextBut(block, BUTM, B_FULL, ICON_BLANK1, - "Tile Window|Ctrl DownArrow", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_VIEW_MAXIMIZE, ""); -*/ + uiItemO(layout, NULL, 0, "SCREEN_OT_screen_full_area"); // "Maximize Window", Ctrl DownArrow +} + +static void act_selectmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemO(layout, NULL, 0, "ACT_OT_select_all_toggle"); + uiItemBooleanO(layout, "Invert All", 0, "ACT_OT_select_all_toggle", "invert", 1); - if (curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } + uiItemS(layout); - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); + uiItemO(layout, NULL, 0, "ACT_OT_select_border"); + uiItemBooleanO(layout, "Border Axis Range", 0, "ACT_OT_select_border", "axis_range", 1); - return block; + uiItemS(layout); + + uiItemEnumO(layout, "Columns on Selected Keys", 0, "ACT_OT_select_column", "mode", ACTKEYS_COLUMNSEL_KEYS); + uiItemEnumO(layout, "Column on Current Frame", 0, "ACT_OT_select_column", "mode", ACTKEYS_COLUMNSEL_CFRA); + + uiItemEnumO(layout, "Columns on Selected Markers", 0, "ACT_OT_select_column", "mode", ACTKEYS_COLUMNSEL_MARKERS_COLUMN); + uiItemEnumO(layout, "Between Selected Markers", 0, "ACT_OT_select_column", "mode", ACTKEYS_COLUMNSEL_MARKERS_BETWEEN); +} + +static void act_channelmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemO(layout, NULL, 0, "ANIM_OT_channels_setting_toggle"); + uiItemO(layout, NULL, 0, "ANIM_OT_channels_setting_enable"); + uiItemO(layout, NULL, 0, "ANIM_OT_channels_setting_disable"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ANIM_OT_channels_editable_toggle"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ANIM_OT_channels_expand"); + uiItemO(layout, NULL, 0, "ANIM_OT_channels_collapse"); +} + +static void act_gplayermenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + //uiItemMenuF(layout, "Transform", 0, nla_edit_transformmenu); + //uiItemS(layout); + //uiItemO(layout, NULL, 0, "NLAEDIT_OT_duplicate"); +} + +static void act_edit_transformmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, "Grab/Move", 0, "TFM_OT_transform", "mode", TFM_TIME_TRANSLATE); + uiItemEnumO(layout, "Extend", 0, "TFM_OT_transform", "mode", TFM_TIME_EXTEND); + uiItemEnumO(layout, "Scale", 0, "TFM_OT_transform", "mode", TFM_TIME_SCALE); +} + +static void act_edit_snapmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "ACT_OT_snap", "type", ACTKEYS_SNAP_CFRA); + uiItemEnumO(layout, NULL, 0, "ACT_OT_snap", "type", ACTKEYS_SNAP_NEAREST_FRAME); + uiItemEnumO(layout, NULL, 0, "ACT_OT_snap", "type", ACTKEYS_SNAP_NEAREST_SECOND); + uiItemEnumO(layout, NULL, 0, "ACT_OT_snap", "type", ACTKEYS_SNAP_NEAREST_MARKER); +} + +static void act_edit_mirrormenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "ACT_OT_mirror", "type", ACTKEYS_MIRROR_CFRA); + uiItemEnumO(layout, NULL, 0, "ACT_OT_mirror", "type", ACTKEYS_MIRROR_YAXIS); + uiItemEnumO(layout, NULL, 0, "ACT_OT_mirror", "type", ACTKEYS_MIRROR_XAXIS); + uiItemEnumO(layout, NULL, 0, "ACT_OT_mirror", "type", ACTKEYS_MIRROR_MARKER); +} + +static void act_edit_handlesmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "ACT_OT_handle_type", "type", HD_FREE); + uiItemEnumO(layout, NULL, 0, "ACT_OT_handle_type", "type", HD_AUTO); + uiItemEnumO(layout, NULL, 0, "ACT_OT_handle_type", "type", HD_VECT); + uiItemEnumO(layout, NULL, 0, "ACT_OT_handle_type", "type", HD_ALIGN); + uiItemEnumO(layout, NULL, 0, "ACT_OT_handle_type", "type", HD_AUTO_ANIM); // xxx? +} + +static void act_edit_ipomenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "ACT_OT_interpolation_type", "type", BEZT_IPO_CONST); + uiItemEnumO(layout, NULL, 0, "ACT_OT_interpolation_type", "type", BEZT_IPO_LIN); + uiItemEnumO(layout, NULL, 0, "ACT_OT_interpolation_type", "type", BEZT_IPO_BEZ); +} + +static void act_edit_expomenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "ACT_OT_extrapolation_type", "type", FCURVE_EXTRAPOLATE_CONSTANT); + uiItemEnumO(layout, NULL, 0, "ACT_OT_extrapolation_type", "type", FCURVE_EXTRAPOLATE_LINEAR); +} + +static void act_editmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemMenuF(layout, "Transform", 0, act_edit_transformmenu); + uiItemMenuF(layout, "Snap", 0, act_edit_snapmenu); + uiItemMenuF(layout, "Mirror", 0, act_edit_mirrormenu); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ACT_OT_insert_keyframe"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ACT_OT_duplicate"); + uiItemO(layout, NULL, 0, "ACT_OT_delete"); + + uiItemS(layout); + + uiItemMenuF(layout, "Handle Type", 0, act_edit_handlesmenu); + uiItemMenuF(layout, "Interpolation Mode", 0, act_edit_ipomenu); + uiItemMenuF(layout, "Extrapolation Mode", 0, act_edit_expomenu); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ACT_OT_clean"); + uiItemO(layout, NULL, 0, "ACT_OT_sample"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ACT_OT_copy"); + uiItemO(layout, NULL, 0, "ACT_OT_paste"); } /* ************************ header area region *********************** */ static void do_action_buttons(bContext *C, void *arg, int event) { - switch(event) { + switch (event) { case B_REDR: ED_area_tag_redraw(CTX_wm_area(C)); break; - - case B_ACTCOPYKEYS: - WM_operator_name_call(C, "ACT_OT_keyframes_copy", WM_OP_EXEC_REGION_WIN, NULL); - break; - case B_ACTPASTEKEYS: - WM_operator_name_call(C, "ACT_OT_keyframes_paste", WM_OP_EXEC_REGION_WIN, NULL); - break; } } @@ -1622,45 +314,38 @@ void action_header_buttons(const bContext *C, ARegion *ar) if ((sa->flag & HEADER_NO_PULLDOWN)==0) { xmax= GetButStringLength("View"); - uiDefPulldownBut(block, action_viewmenu, CTX_wm_area(C), - "View", xco, yco-2, xmax-3, 24, ""); + uiDefMenuBut(block, act_viewmenu, NULL, "View", xco, yco, xmax-3, 20, ""); xco+= xmax; xmax= GetButStringLength("Select"); - uiDefPulldownBut(block, action_selectmenu, CTX_wm_area(C), - "Select", xco, yco-2, xmax-3, 24, ""); + uiDefMenuBut(block, act_selectmenu, NULL, "Select", xco, yco, xmax-3, 20, ""); xco+= xmax; if ( (saction->mode == SACTCONT_DOPESHEET) || ((saction->action) && (saction->mode==SACTCONT_ACTION)) ) { xmax= GetButStringLength("Channel"); - uiDefPulldownBut(block, action_channelmenu, CTX_wm_area(C), - "Channel", xco, yco-2, xmax-3, 24, ""); + uiDefMenuBut(block, act_channelmenu, NULL, "Channel", xco, yco, xmax-3, 20, ""); xco+= xmax; } else if (saction->mode==SACTCONT_GPENCIL) { xmax= GetButStringLength("Channel"); - uiDefPulldownBut(block, action_gplayermenu, CTX_wm_area(C), - "Channel", xco, yco-2, xmax-3, 24, ""); + uiDefMenuBut(block, act_gplayermenu, NULL, "Channel", xco, yco, xmax-3, 20, ""); xco+= xmax; } - xmax= GetButStringLength("Marker"); - uiDefPulldownBut(block, action_markermenu, CTX_wm_area(C), - "Marker", xco, yco-2, xmax-3, 24, ""); - xco+= xmax; + //xmax= GetButStringLength("Marker"); + //uiDefMenuBut(block, act_markermenu, NULL, "Marker", xco, yco, xmax-3, 20, ""); + //xco+= xmax; if (saction->mode == SACTCONT_GPENCIL) { - xmax= GetButStringLength("Frame"); - uiDefPulldownBut(block, action_framemenu, CTX_wm_area(C), - "Frame", xco, yco-2, xmax-3, 24, ""); - xco+= xmax; + //xmax= GetButStringLength("Frame"); + //uiDefMenuBut(block, act_selectmenu, NULL, "Frame", xco, yco, xmax-3, 20, ""); + //xco+= xmax; } else { xmax= GetButStringLength("Key"); - uiDefPulldownBut(block, action_keymenu, CTX_wm_area(C), - "Key", xco, yco-2, xmax-3, 24, ""); + uiDefMenuBut(block, act_editmenu, NULL, "Key", xco, yco, xmax-3, 20, ""); xco+= xmax; } } @@ -1712,8 +397,9 @@ void action_header_buttons(const bContext *C, ARegion *ar) /* COPY PASTE */ uiBlockBeginAlign(block); - uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN, xco,yco,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); - uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN, xco+=XIC,yco,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); + uiDefIconButO(block, BUT, "ACT_OT_copy", WM_OP_INVOKE_REGION_WIN, ICON_COPYDOWN, xco,yco,XIC,YIC, "Copies the selected keyframes to the buffer."); + xco += XIC; + uiDefIconButO(block, BUT, "ACT_OT_paste", WM_OP_INVOKE_REGION_WIN, ICON_PASTEDOWN, xco,yco,XIC,YIC, "Pastes the keyframes from the buffer into the selected channels."); uiBlockEndAlign(block); xco += (XIC + 8); diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index b4d2528b3b4..26655892176 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -53,10 +53,10 @@ void action_header_buttons(const struct bContext *C, struct ARegion *ar); /* ***************************************** */ /* action_select.c */ -void ACT_OT_keyframes_select_all_toggle(struct wmOperatorType *ot); -void ACT_OT_keyframes_select_border(struct wmOperatorType *ot); -void ACT_OT_keyframes_select_column(struct wmOperatorType *ot); -void ACT_OT_keyframes_clickselect(struct wmOperatorType *ot); +void ACT_OT_select_all_toggle(struct wmOperatorType *ot); +void ACT_OT_select_border(struct wmOperatorType *ot); +void ACT_OT_select_column(struct wmOperatorType *ot); +void ACT_OT_clickselect(struct wmOperatorType *ot); /* defines for left-right select tool */ enum { @@ -80,22 +80,23 @@ enum { void ACT_OT_previewrange_set(struct wmOperatorType *ot); void ACT_OT_view_all(struct wmOperatorType *ot); -void ACT_OT_keyframes_copy(struct wmOperatorType *ot); -void ACT_OT_keyframes_paste(struct wmOperatorType *ot); +void ACT_OT_copy(struct wmOperatorType *ot); +void ACT_OT_paste(struct wmOperatorType *ot); -void ACT_OT_keyframes_insert(struct wmOperatorType *ot); -void ACT_OT_keyframes_duplicate(struct wmOperatorType *ot); -void ACT_OT_keyframes_delete(struct wmOperatorType *ot); -void ACT_OT_keyframes_clean(struct wmOperatorType *ot); -void ACT_OT_keyframes_sample(struct wmOperatorType *ot); +void ACT_OT_insert_keyframe(struct wmOperatorType *ot); +void ACT_OT_duplicate(struct wmOperatorType *ot); +void ACT_OT_delete(struct wmOperatorType *ot); +void ACT_OT_clean(struct wmOperatorType *ot); +void ACT_OT_sample(struct wmOperatorType *ot); -void ACT_OT_keyframes_handle_type_set(struct wmOperatorType *ot); -void ACT_OT_keyframes_interpolation_type(struct wmOperatorType *ot); -void ACT_OT_keyframes_extrapolation_type_set(struct wmOperatorType *ot); +void ACT_OT_handle_type(struct wmOperatorType *ot); +void ACT_OT_interpolation_type(struct wmOperatorType *ot); +void ACT_OT_extrapolation_type(struct wmOperatorType *ot); -void ACT_OT_keyframes_cfrasnap(struct wmOperatorType *ot); -void ACT_OT_keyframes_snap(struct wmOperatorType *ot); -void ACT_OT_keyframes_mirror(struct wmOperatorType *ot); +void ACT_OT_frame_jump(struct wmOperatorType *ot); + +void ACT_OT_snap(struct wmOperatorType *ot); +void ACT_OT_mirror(struct wmOperatorType *ot); /* defines for snap keyframes * NOTE: keep in sync with eEditKeyframes_Snap (in ED_keyframes_edit.h) diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index 8ca2bf40979..d1c9e1deac3 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -63,25 +63,25 @@ void action_operatortypes(void) { /* keyframes */ /* selection */ - WM_operatortype_append(ACT_OT_keyframes_clickselect); - WM_operatortype_append(ACT_OT_keyframes_select_all_toggle); - WM_operatortype_append(ACT_OT_keyframes_select_border); - WM_operatortype_append(ACT_OT_keyframes_select_column); + WM_operatortype_append(ACT_OT_clickselect); + WM_operatortype_append(ACT_OT_select_all_toggle); + WM_operatortype_append(ACT_OT_select_border); + WM_operatortype_append(ACT_OT_select_column); /* editing */ - WM_operatortype_append(ACT_OT_keyframes_snap); - WM_operatortype_append(ACT_OT_keyframes_mirror); - WM_operatortype_append(ACT_OT_keyframes_cfrasnap); - WM_operatortype_append(ACT_OT_keyframes_handle_type_set); - WM_operatortype_append(ACT_OT_keyframes_interpolation_type); - WM_operatortype_append(ACT_OT_keyframes_extrapolation_type_set); - WM_operatortype_append(ACT_OT_keyframes_sample); - WM_operatortype_append(ACT_OT_keyframes_clean); - WM_operatortype_append(ACT_OT_keyframes_delete); - WM_operatortype_append(ACT_OT_keyframes_duplicate); - WM_operatortype_append(ACT_OT_keyframes_insert); - WM_operatortype_append(ACT_OT_keyframes_copy); - WM_operatortype_append(ACT_OT_keyframes_paste); + WM_operatortype_append(ACT_OT_snap); + WM_operatortype_append(ACT_OT_mirror); + WM_operatortype_append(ACT_OT_frame_jump); + WM_operatortype_append(ACT_OT_handle_type); + WM_operatortype_append(ACT_OT_interpolation_type); + WM_operatortype_append(ACT_OT_extrapolation_type); + WM_operatortype_append(ACT_OT_sample); + WM_operatortype_append(ACT_OT_clean); + WM_operatortype_append(ACT_OT_delete); + WM_operatortype_append(ACT_OT_duplicate); + WM_operatortype_append(ACT_OT_insert_keyframe); + WM_operatortype_append(ACT_OT_copy); + WM_operatortype_append(ACT_OT_paste); WM_operatortype_append(ACT_OT_previewrange_set); WM_operatortype_append(ACT_OT_view_all); @@ -95,57 +95,58 @@ static void action_keymap_keyframes (wmWindowManager *wm, ListBase *keymap) /* action_select.c - selection tools */ /* click-select */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, 0, 0); - kmi= WM_keymap_add_item(keymap, "ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "ACT_OT_clickselect", SELECTMOUSE, KM_PRESS, 0, 0); + kmi= WM_keymap_add_item(keymap, "ACT_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "column", 1); - kmi= WM_keymap_add_item(keymap, "ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + kmi= WM_keymap_add_item(keymap, "ACT_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", 1); - kmi= WM_keymap_add_item(keymap, "ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT|KM_SHIFT, 0); + kmi= WM_keymap_add_item(keymap, "ACT_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT|KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", 1); RNA_boolean_set(kmi->ptr, "column", 1); - kmi= WM_keymap_add_item(keymap, "ACT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); + kmi= WM_keymap_add_item(keymap, "ACT_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); RNA_enum_set(kmi->ptr, "left_right", ACTKEYS_LRSEL_TEST); /* deselect all */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_all_toggle", AKEY, KM_PRESS, 0, 0); - RNA_boolean_set(WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1); + WM_keymap_add_item(keymap, "ACT_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "ACT_OT_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1); /* borderselect */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_border", BKEY, KM_PRESS, 0, 0); - RNA_boolean_set(WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_border", BKEY, KM_PRESS, KM_ALT, 0)->ptr, "axis_range", 1); + WM_keymap_add_item(keymap, "ACT_OT_select_border", BKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "ACT_OT_select_border", BKEY, KM_PRESS, KM_ALT, 0)->ptr, "axis_range", 1); /* column select */ - RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_column", KKEY, KM_PRESS, 0, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_KEYS); - RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_column", KKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_CFRA); - RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_column", KKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_MARKERS_COLUMN); - RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_keyframes_select_column", KKEY, KM_PRESS, KM_ALT, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_MARKERS_BETWEEN); + RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_select_column", KKEY, KM_PRESS, 0, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_KEYS); + RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_select_column", KKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_CFRA); + RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_select_column", KKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_MARKERS_COLUMN); + RNA_enum_set(WM_keymap_add_item(keymap, "ACT_OT_select_column", KKEY, KM_PRESS, KM_ALT, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_MARKERS_BETWEEN); /* action_edit.c */ /* snap - current frame to selected keys */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_cfrasnap", SKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); + // TODO: maybe since this is called jump, we're better to have it on -J? + WM_keymap_add_item(keymap, "ACT_OT_frame_jump", SKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); /* menu + single-step transform */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_snap", SKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_mirror", MKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ACT_OT_snap", SKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ACT_OT_mirror", MKEY, KM_PRESS, KM_SHIFT, 0); /* menu + set setting */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_handle_type_set", HKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_interpolation_type", TKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_extrapolation_type_set", EKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ACT_OT_handle_type", HKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ACT_OT_interpolation_type", TKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ACT_OT_extrapolation_type", EKEY, KM_PRESS, KM_SHIFT, 0); /* destructive */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_clean", OKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_sample", OKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ACT_OT_clean", OKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ACT_OT_sample", OKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_delete", XKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_delete", DELKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ACT_OT_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ACT_OT_delete", DELKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_insert", IKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ACT_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ACT_OT_insert_keyframe", IKEY, KM_PRESS, 0, 0); /* copy/paste */ - WM_keymap_add_item(keymap, "ACT_OT_keyframes_copy", CKEY, KM_PRESS, KM_CTRL, 0); - WM_keymap_add_item(keymap, "ACT_OT_keyframes_paste", VKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "ACT_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "ACT_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0); /* auto-set range */ WM_keymap_add_item(keymap, "ACT_OT_previewrange_set", PKEY, KM_PRESS, KM_CTRL|KM_ALT, 0); diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 06f35f5cf05..d9e6bb10ceb 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -64,6 +64,7 @@ #include "BKE_fcurve.h" #include "BKE_key.h" #include "BKE_material.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_context.h" #include "BKE_utildefines.h" @@ -173,21 +174,21 @@ static int actkeys_deselectall_exec(bContext *C, wmOperator *op) else deselect_action_keys(&ac, 1, SELECT_ADD); - /* set notifier that things have changed */ - ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead! + /* set notifier that keyframe selection have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_select_all_toggle (wmOperatorType *ot) +void ACT_OT_select_all_toggle (wmOperatorType *ot) { /* identifiers */ ot->name= "Select All"; - ot->idname= "ACT_OT_keyframes_select_all_toggle"; + ot->idname= "ACT_OT_select_all_toggle"; /* api callbacks */ ot->exec= actkeys_deselectall_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -222,7 +223,8 @@ static void borderselect_action (bAnimContext *ac, rcti rect, short mode, short BeztEditFunc ok_cb, select_cb; View2D *v2d= &ac->ar->v2d; rctf rectf; - float ymin=0, ymax=(float)(-ACHANNEL_HEIGHT); + //float ymin=0, ymax=(float)(-ACHANNEL_HEIGHT); + float ymin=0, ymax=(float)(-ACHANNEL_HEIGHT_HALF); /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */ UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin+2, &rectf.xmin, &rectf.ymin); @@ -245,7 +247,7 @@ static void borderselect_action (bAnimContext *ac, rcti rect, short mode, short /* loop over data, doing border select */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* get new vertical minimum extent of channel */ ymin= ymax - ACHANNEL_STEP; @@ -253,9 +255,9 @@ static void borderselect_action (bAnimContext *ac, rcti rect, short mode, short /* set horizontal range (if applicable) */ if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) { /* if channel is mapped in NLA, apply correction */ - if (nob) { - bed.f1= get_action_frame(nob, rectf.xmin); - bed.f2= get_action_frame(nob, rectf.xmax); + if (adt) { + bed.f1= BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP); + bed.f2= BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP); } else { bed.f1= rectf.xmin; @@ -335,21 +337,24 @@ static int actkeys_borderselect_exec(bContext *C, wmOperator *op) /* apply borderselect action */ borderselect_action(&ac, rect, mode, selectmode); + /* set notifier that keyframe selection have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL); + return OPERATOR_FINISHED; } -void ACT_OT_keyframes_select_border(wmOperatorType *ot) +void ACT_OT_select_border(wmOperatorType *ot) { /* identifiers */ ot->name= "Border Select"; - ot->idname= "ACT_OT_keyframes_select_border"; + ot->idname= "ACT_OT_select_border"; /* api callbacks */ ot->invoke= WM_border_select_invoke; ot->exec= actkeys_borderselect_exec; ot->modal= WM_border_select_modal; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -413,12 +418,12 @@ static void markers_selectkeys_between (bAnimContext *ac) /* select keys in-between */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } else { ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); @@ -495,15 +500,15 @@ static void columnselect_action_keys (bAnimContext *ac, short mode) ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* loop over cfraelems (stored in the BeztEditData->list) * - we need to do this here, as we can apply fewer NLA-mapping conversions */ for (ce= bed.list.first; ce; ce= ce->next) { /* set frame for validation callback to refer to */ - if (nob) - bed.f1= get_action_frame(nob, ce->cfra); + if (adt) + bed.f1= BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP); else bed.f1= ce->cfra; @@ -549,21 +554,21 @@ static int actkeys_columnselect_exec(bContext *C, wmOperator *op) else columnselect_action_keys(&ac, mode); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_SELECT); + /* set notifier that keyframe selection have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL); return OPERATOR_FINISHED; } -void ACT_OT_keyframes_select_column (wmOperatorType *ot) +void ACT_OT_select_column (wmOperatorType *ot) { /* identifiers */ ot->name= "Select All"; - ot->idname= "ACT_OT_keyframes_select_column"; + ot->idname= "ACT_OT_select_column"; /* api callbacks */ ot->exec= actkeys_columnselect_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -641,7 +646,7 @@ static void actkeys_mselect_leftright (bAnimContext *ac, short leftright, short memset(&bed, 0, sizeof(BeztEditFunc)); if (leftright == ACTKEYS_LRSEL_LEFT) { - bed.f1 = -MAXFRAMEF; + bed.f1 = MINAFRAMEF; bed.f2 = (float)(CFRA + FRAME_CLICK_THRESH); } else { @@ -658,12 +663,12 @@ static void actkeys_mselect_leftright (bAnimContext *ac, short leftright, short /* select keys on the side where most data occurs */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } //else if (ale->type == ANIMTYPE_GPLAYER) // borderselect_gplayer_frames(ale->data, min, max, SELECT_ADD); @@ -702,11 +707,11 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* set frame for validation callback to refer to */ - if (nob) - bed.f1= get_action_frame(nob, selx); + if (adt) + bed.f1= BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP); else bed.f1= selx; @@ -742,12 +747,16 @@ static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode, int filter; View2D *v2d= &ac->ar->v2d; + bDopeSheet *ads = NULL; int channel_index; short found = 0; float selx = 0.0f; float x, y; rctf rectf; + /* get dopesheet info */ + if (ac->datatype == ANIMCONT_DOPESHEET) + ads= ac->data; /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */ UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); @@ -766,51 +775,41 @@ static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode, if (ale == NULL) { /* channel not found */ printf("Error: animation channel (index = %d) not found in mouse_action_keys() \n", channel_index); + BLI_freelistN(&anim_data); return; } else { /* found match - must return here... */ - Object *nob= ANIM_nla_mapping_get(ac, ale); - ActKeysInc *aki= init_aki_data(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); ActKeyColumn *ak; - float xmin, xmax; - - /* 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 */ + // TODO: it would be great if we didn't have to apply this to all the keyframes to do this... if (ale->key_data) { switch (ale->datatype) { case ALE_OB: { Object *ob= (Object *)ale->key_data; - ob_to_keylist(ob, &anim_keys, NULL, aki); + ob_to_keylist(ads, ob, &anim_keys, NULL); } break; case ALE_ACT: { bAction *act= (bAction *)ale->key_data; - action_to_keylist(act, &anim_keys, NULL, aki); + action_to_keylist(adt, act, &anim_keys, NULL); } break; case ALE_FCURVE: { FCurve *fcu= (FCurve *)ale->key_data; - fcurve_to_keylist(fcu, &anim_keys, NULL, aki); + fcurve_to_keylist(adt, fcu, &anim_keys, NULL); } break; } } else if (ale->type == ANIMTYPE_GROUP) { bActionGroup *agrp= (bActionGroup *)ale->data; - agroup_to_keylist(agrp, &anim_keys, NULL, aki); + agroup_to_keylist(adt, agrp, &anim_keys, NULL); } else if (ale->type == ANIMTYPE_GPDATABLOCK) { /* cleanup */ @@ -820,13 +819,17 @@ static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode, } else if (ale->type == ANIMTYPE_GPLAYER) { bGPDlayer *gpl= (bGPDlayer *)ale->data; - gpl_to_keylist(gpl, &anim_keys, NULL, aki); + gpl_to_keylist(ads, gpl, &anim_keys, 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; + if (IN_RANGE(ak->cfra, rectf.xmin, rectf.xmax)) { + /* set the frame to use, and apply inverse-correction for NLA-mapping + * so that the frame will get selected by the selection functiosn without + * requiring to map each frame once again... + */ + selx= BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); found= 1; break; } @@ -857,17 +860,19 @@ static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode, ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); /* Highlight Action-Group or F-Curve? */ - if (ale->type == ANIMTYPE_GROUP) { - bActionGroup *agrp= ale->data; - - agrp->flag |= AGRP_SELECTED; - ANIM_set_active_channel(ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); - } - else if (ale->type == ANIMTYPE_FCURVE) { - FCurve *fcu= ale->data; - - fcu->flag |= FCURVE_SELECTED; - ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); + if (ale && ale->data) { + if (ale->type == ANIMTYPE_GROUP) { + bActionGroup *agrp= ale->data; + + agrp->flag |= AGRP_SELECTED; + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); + } + else if (ale->type == ANIMTYPE_FCURVE) { + FCurve *fcu= ale->data; + + fcu->flag |= FCURVE_SELECTED; + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); + } } } else if (ac->datatype == ANIMCONT_GPENCIL) { @@ -881,18 +886,20 @@ static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode, /* only select keyframes if we clicked on a valid channel and hit something */ if (ale) { - /* apply selection to keyframes */ - if (/*gpl*/0) { - /* grease pencil */ - //select_gpencil_frame(gpl, (int)selx, selectmode); - } - else if (column) { - /* select all keyframes in the same frame as the one we hit on the active channel */ - actkeys_mselect_column(ac, select_mode, selx); - } - else { - /* select the nominated keyframe on the given frame */ - actkeys_mselect_single(ac, ale, select_mode, selx); + if (found) { + /* apply selection to keyframes */ + if (/*gpl*/0) { + /* grease pencil */ + //select_gpencil_frame(gpl, (int)selx, selectmode); + } + else if (column) { + /* select all keyframes in the same frame as the one we hit on the active channel */ + actkeys_mselect_column(ac, select_mode, selx); + } + else { + /* select the nominated keyframe on the given frame */ + actkeys_mselect_single(ac, ale, select_mode, selx); + } } /* free this channel */ @@ -950,22 +957,22 @@ static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *even mouse_action_keys(&ac, mval, selectmode, column); } - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH); + /* set notifier that keyframe selection (and channels too) have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT|ND_ANIMCHAN_SELECT, NULL); /* for tweak grab to work */ return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH; } -void ACT_OT_keyframes_clickselect (wmOperatorType *ot) +void ACT_OT_clickselect (wmOperatorType *ot) { /* identifiers */ ot->name= "Mouse Select Keys"; - ot->idname= "ACT_OT_keyframes_clickselect"; + ot->idname= "ACT_OT_clickselect"; /* api callbacks - absolutely no exec() this yet... */ ot->invoke= actkeys_clickselect_invoke; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_action_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index b4e43c29c3d..0b06499693c 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -288,6 +288,9 @@ static void action_channel_area_listener(ARegion *ar, wmNotifier *wmn) { /* context changes */ switch(wmn->category) { + case NC_ANIMATION: + ED_region_tag_redraw(ar); + break; case NC_SCENE: switch(wmn->data) { case ND_OB_ACTIVE: @@ -314,6 +317,9 @@ static void action_main_area_listener(ARegion *ar, wmNotifier *wmn) { /* context changes */ switch(wmn->category) { + case NC_ANIMATION: + ED_region_tag_redraw(ar); + break; case NC_SCENE: switch(wmn->data) { case ND_OB_ACTIVE: @@ -344,6 +350,9 @@ static void action_listener(ScrArea *sa, wmNotifier *wmn) { /* context changes */ switch (wmn->category) { + case NC_ANIMATION: + ED_area_tag_refresh(sa); + break; case NC_SCENE: /*switch (wmn->data) { case ND_OB_ACTIVE: diff --git a/source/blender/editors/space_buttons/buttons_header.c b/source/blender/editors/space_buttons/buttons_header.c index 7c622f172a2..99cc85d9a52 100644 --- a/source/blender/editors/space_buttons/buttons_header.c +++ b/source/blender/editors/space_buttons/buttons_header.c @@ -193,7 +193,7 @@ void buttons_header_buttons(const bContext *C, ARegion *ar) uiBlockEndAlign(block); xco+=XIC; - uiDefButI(block, NUM, B_NEWFRAME, "", (xco+20),yco,60,YIC, &(CTX_data_scene(C)->r.cfra), 1.0, MAXFRAMEF, 0, 0, "Displays Current Frame of animation. Click to change."); + uiDefButI(block, NUM, B_NEWFRAME, "", (xco+20),yco,60,YIC, &(CTX_data_scene(C)->r.cfra), MINAFRAMEF, MAXFRAMEF, 0, 0, "Displays Current Frame of animation. Click to change."); xco+= 80; /* always as last */ diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 6c8e84f0db8..d34eb29a78d 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -147,6 +147,9 @@ int ED_fileselect_layout_offset(FileLayout* layout, int x, int y) int offsetx, offsety; int active_file; + if (layout == NULL) + return NULL; + offsetx = (x)/(layout->tile_w + 2*layout->tile_border_x); offsety = (y)/(layout->tile_h + 2*layout->tile_border_y); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index a4babaad74c..0e48d56ac90 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -347,6 +347,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) } /* ******************* f-modifiers ******************************** */ +/* all the drawing code is in editors/animation/fmodifier_ui.c */ #define B_FMODIFIER_REDRAW 20 @@ -360,671 +361,41 @@ static void do_graph_region_modifier_buttons(bContext *C, void *arg, int event) } } -/* macro for use here to draw background box and set height */ -// XXX for now, roundbox has it's callback func set to NULL to not intercept events -#define DRAW_BACKDROP(height) \ - { \ - uiDefBut(block, ROUNDBOX, B_REDR, "", -3, *yco-height, width+3, height-1, NULL, 5.0, 0.0, 12.0, (float)rb_col, ""); \ - } - -/* callback to verify modifier data */ -static void validate_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v) -{ - FModifier *fcm= (FModifier *)fcm_v; - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* call the verify callback on the modifier if applicable */ - if (fmi && fmi->verify_data) - fmi->verify_data(fcm); -} - -/* callback to set the active modifier */ -static void activate_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v) -{ - FCurve *fcu= (FCurve *)fcu_v; - FModifier *fcm= (FModifier *)fcm_v; - - /* call API function to set the active modifier for active F-Curve */ - fcurve_set_active_modifier(fcu, fcm); -} - -/* callback to remove the given modifier */ -static void delete_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v) -{ - FCurve *fcu= (FCurve *)fcu_v; - FModifier *fcm= (FModifier *)fcm_v; - - /* remove the given F-Modifier from the F-Curve */ - fcurve_remove_modifier(fcu, fcm); -} - -/* --------------- */ - -/* draw settings for generator modifier */ -static void draw_modifier__generator(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) -{ - FMod_Generator *data= (FMod_Generator *)fcm->data; - char gen_mode[]="Generator Type%t|Expanded Polynomial%x0|Factorised Polynomial%x1|Built-In Function%x2|Expression%x3"; - char fn_type[]="Built-In Function%t|Sin%x0|Cos%x1|Tan%x2|Square Root%x3|Natural Log%x4"; - int cy= *yco - 30; - uiBut *but; - - /* set the height */ - (*height) = 90; - switch (data->mode) { - case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */ - (*height) += 20*(data->poly_order+1) + 20; - break; - case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */ - (*height) += 20 * data->poly_order + 15; - break; - case FCM_GENERATOR_FUNCTION: /* builtin function */ - (*height) += 55; // xxx - break; - case FCM_GENERATOR_EXPRESSION: /* py-expression */ - // xxx nothing to draw - break; - } - - /* basic settings (backdrop + mode selector + some padding) */ - DRAW_BACKDROP((*height)); - uiBlockBeginAlign(block); - but= uiDefButS(block, MENU, B_FMODIFIER_REDRAW, gen_mode, 10,cy,width-30,19, &data->mode, 0, 0, 0, 0, "Selects type of generator algorithm."); - uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm); - cy -= 20; - - uiDefButBitS(block, TOG, FCM_GENERATOR_ADDITIVE, B_FMODIFIER_REDRAW, "Additive", 10,cy,width-30,19, &data->flag, 0, 0, 0, 0, "Values generated by this modifier are applied on top of the existing values instead of overwriting them"); - cy -= 35; - uiBlockEndAlign(block); - - /* now add settings for individual modes */ - switch (data->mode) { - case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */ - { - float *cp = NULL; - char xval[32]; - unsigned int i; - - /* draw polynomial order selector */ - but= uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 10,cy,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1"); - uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm); - cy -= 35; - - /* draw controls for each coefficient and a + sign at end of row */ - uiDefBut(block, LABEL, 1, "y = ", 0, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, ""); - - cp= data->coefficients; - for (i=0; (i < data->arraysize) && (cp); i++, cp++) { - /* coefficient */ - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 50, cy, 150, 20, cp, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient for polynomial"); - - /* 'x' param (and '+' if necessary) */ - if (i == 0) - strcpy(xval, ""); - else if (i == 1) - strcpy(xval, "x"); - else - sprintf(xval, "x^%d", i); - uiDefBut(block, LABEL, 1, xval, 200, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, "Power of x"); - - if ( (i != (data->arraysize - 1)) || ((i==0) && data->arraysize==2) ) - uiDefBut(block, LABEL, 1, "+", 250, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); - - cy -= 20; - } - } - break; - - case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial expression */ - { - float *cp = NULL; - unsigned int i; - - /* draw polynomial order selector */ - but= uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 10,cy,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1"); - uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm); - cy -= 35; - - /* draw controls for each pair of coefficients */ - uiDefBut(block, LABEL, 1, "y = ", 0, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, ""); - - cp= data->coefficients; - for (i=0; (i < data->poly_order) && (cp); i++, cp+=2) { - /* opening bracket */ - uiDefBut(block, LABEL, 1, "(", 40, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, ""); - - /* coefficients */ - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 50, cy, 100, 20, cp, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient of x"); - - uiDefBut(block, LABEL, 1, "x + ", 150, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 180, cy, 100, 20, cp+1, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Second coefficient"); - - /* closing bracket and '+' sign */ - if ( (i != (data->poly_order - 1)) || ((i==0) && data->poly_order==2) ) - uiDefBut(block, LABEL, 1, ") â—Š", 280, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); - else - uiDefBut(block, LABEL, 1, ")", 280, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); - - cy -= 20; - } - } - break; - - case FCM_GENERATOR_FUNCTION: /* built-in function */ - { - float *cp= data->coefficients; - - /* draw function selector */ - but= uiDefButS(block, MENU, B_FMODIFIER_REDRAW, fn_type, 10,cy,width-30,19, &data->func_type, 0, 0, 0, 0, "Built-In Function to use"); - uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm); - cy -= 35; - - /* draw controls for equation of coefficients */ - /* row 1 */ - { - uiDefBut(block, LABEL, 1, "y = ", 0, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 50, cy, 150, 20, cp+3, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient (D) for function"); - uiDefBut(block, LABEL, 1, "+", 200, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); - cy -= 20; - } - - /* row 2 */ - { - char func_name[32]; - - /* coefficient outside bracket */ - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 5, cy, 80, 20, cp, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient (A) for function"); - - /* opening bracket */ - switch (data->func_type) - { - case FCM_GENERATOR_FN_SIN: /* sine wave */ - sprintf(func_name, "sin("); - break; - case FCM_GENERATOR_FN_COS: /* cosine wave */ - sprintf(func_name, "cos("); - break; - case FCM_GENERATOR_FN_TAN: /* tangent wave */ - sprintf(func_name, "tan("); - break; - case FCM_GENERATOR_FN_LN: /* natural log */ - sprintf(func_name, "ln("); - break; - case FCM_GENERATOR_FN_SQRT: /* square root */ - sprintf(func_name, "sqrt("); - break; - default: /* unknown */ - sprintf(func_name, "("); - break; - } - uiDefBut(block, LABEL, 1, func_name, 85, cy, 40, 20, NULL, 0.0, 0.0, 0, 0, ""); - - /* coefficients inside bracket */ - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 120, cy, 75, 20, cp+1, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient (B) of x"); - - uiDefBut(block, LABEL, 1, "x+", 195, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 225, cy, 80, 20, cp+2, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient (C) of function"); - - /* closing bracket */ - uiDefBut(block, LABEL, 1, ")", 300, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, ""); - cy -= 20; - } - } - break; - - case FCM_GENERATOR_EXPRESSION: /* py-expression */ - // TODO... - break; - } -} - -/* --------------- */ - -/* draw settings for cycles modifier */ -static void draw_modifier__cycles(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) -{ - FMod_Cycles *data= (FMod_Cycles *)fcm->data; - char cyc_mode[]="Cycling Mode%t|No Cycles%x0|Repeat Motion%x1|Repeat with Offset%x2|Repeat Mirrored%x3"; - int cy= (*yco - 30), cy1= (*yco - 50), cy2= (*yco - 70); - - /* set the height */ - (*height) = 80; - - /* basic settings (backdrop + some padding) */ - DRAW_BACKDROP((*height)); - - /* 'before' range */ - uiDefBut(block, LABEL, 1, "Before:", 4, cy, 80, 20, NULL, 0.0, 0.0, 0, 0, "Settings for cycling before first keyframe"); - uiBlockBeginAlign(block); - uiDefButS(block, MENU, B_FMODIFIER_REDRAW, cyc_mode, 3,cy1,150,20, &data->before_mode, 0, 0, 0, 0, "Cycling mode to use before first keyframe"); - uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Max Cycles:", 3, cy2, 150, 20, &data->before_cycles, 0, 10000, 10, 3, "Maximum number of cycles to allow (0 = infinite)"); - uiBlockEndAlign(block); - - /* 'after' range */ - uiDefBut(block, LABEL, 1, "After:", 155, cy, 80, 20, NULL, 0.0, 0.0, 0, 0, "Settings for cycling after last keyframe"); - uiBlockBeginAlign(block); - uiDefButS(block, MENU, B_FMODIFIER_REDRAW, cyc_mode, 157,cy1,150,20, &data->after_mode, 0, 0, 0, 0, "Cycling mode to use after first keyframe"); - uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Max Cycles:", 157, cy2, 150, 20, &data->after_cycles, 0, 10000, 10, 3, "Maximum number of cycles to allow (0 = infinite)"); - uiBlockEndAlign(block); -} - -/* --------------- */ - -/* draw settings for noise modifier */ -static void draw_modifier__noise(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) -{ - FMod_Noise *data= (FMod_Noise *)fcm->data; - int cy= (*yco - 30), cy1= (*yco - 50), cy2= (*yco - 70); - char cyc_mode[]="Modification %t|Replace %x0|Add %x1|Subtract %x2|Multiply %x3"; - - /* set the height */ - (*height) = 80; - - /* basic settings (backdrop + some padding) */ - DRAW_BACKDROP((*height)); - - uiDefButS(block, MENU, B_FMODIFIER_REDRAW, cyc_mode, - 3, cy, 150, 20, &data->modification, 0, 0, 0, 0, "Method of modifying the existing F-Curve use before first keyframe"); - - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Size:", - 3, cy1, 150, 20, &data->size, 0.000001, 10000.0, 0.01, 3, ""); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Strength:", - 3, cy2, 150, 20, &data->strength, 0.0, 10000.0, 0.01, 3, ""); - - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Phase:", - 155, cy1, 150, 20, &data->phase, 0.0, 100000.0, 0.1, 3, ""); - uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Depth:", - 155, cy2, 150, 20, &data->depth, 0, 128, 1, 3, ""); - -} - -/* --------------- */ - -#define BINARYSEARCH_FRAMEEQ_THRESH 0.0001 - -/* Binary search algorithm for finding where to insert Envelope Data Point. - * Returns the index to insert at (data already at that index will be offset if replace is 0) - */ -static int binarysearch_fcm_envelopedata_index (FCM_EnvelopeData array[], float frame, int arraylen, short *exists) -{ - int start=0, end=arraylen; - int loopbreaker= 0, maxloop= arraylen * 2; - - /* initialise exists-flag first */ - *exists= 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_fcm_envelopedata_index() encountered invalid array \n"); - return 0; - } - else { - /* check whether to add before/after/on */ - float framenum; - - /* 'First' Point (when only one point, this case is used) */ - framenum= array[0].time; - if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) { - *exists = 1; - return 0; - } - else if (frame < framenum) - return 0; - - /* 'Last' Point */ - framenum= array[(arraylen-1)].time; - if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) { - *exists= 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 - start) / 2); /* we calculate the midpoint this way to avoid int overflows... */ - float midfra= array[mid].time; - - /* check if exactly equal to midpoint */ - if (IS_EQT(frame, midfra, BINARYSEARCH_FRAMEEQ_THRESH)) { - *exists = 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_fcm_envelopedata_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; -} - -/* callback to add new envelope data point */ -// TODO: should we have a separate file for things like this? -static void fmod_envelope_addpoint_cb (bContext *C, void *fcm_dv, void *dummy) -{ - Scene *scene= CTX_data_scene(C); - FMod_Envelope *env= (FMod_Envelope *)fcm_dv; - FCM_EnvelopeData *fedn; - FCM_EnvelopeData fed; - - /* init template data */ - fed.min= -1.0f; - fed.max= 1.0f; - fed.time= (float)scene->r.cfra; // XXX make this int for ease of use? - fed.f1= fed.f2= 0; - - /* check that no data exists for the current frame... */ - if (env->data) { - short exists = -1; - int i= binarysearch_fcm_envelopedata_index(env->data, (float)(scene->r.cfra), env->totvert, &exists); - - /* binarysearch_...() will set exists by default to 0, so if it is non-zero, that means that the point exists already */ - if (exists) - return; - - /* add new */ - fedn= MEM_callocN((env->totvert+1)*sizeof(FCM_EnvelopeData), "FCM_EnvelopeData"); - - /* add the points that should occur before the point to be pasted */ - if (i > 0) - memcpy(fedn, env->data, i*sizeof(FCM_EnvelopeData)); - - /* add point to paste at index i */ - *(fedn + i)= fed; - - /* add the points that occur after the point to be pasted */ - if (i < env->totvert) - memcpy(fedn+i+1, env->data+i, (env->totvert-i)*sizeof(FCM_EnvelopeData)); - - /* replace (+ free) old with new */ - MEM_freeN(env->data); - env->data= fedn; - - env->totvert++; - } - else { - env->data= MEM_callocN(sizeof(FCM_EnvelopeData), "FCM_EnvelopeData"); - *(env->data)= fed; - - env->totvert= 1; - } -} - -/* callback to remove envelope data point */ -// TODO: should we have a separate file for things like this? -static void fmod_envelope_deletepoint_cb (bContext *C, void *fcm_dv, void *ind_v) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm_dv; - FCM_EnvelopeData *fedn; - int index= GET_INT_FROM_POINTER(ind_v); - - /* check that no data exists for the current frame... */ - if (env->totvert > 1) { - /* allocate a new smaller array */ - fedn= MEM_callocN(sizeof(FCM_EnvelopeData)*(env->totvert-1), "FCM_EnvelopeData"); - - memcpy(fedn, &env->data, sizeof(FCM_EnvelopeData)*(index)); - memcpy(&fedn[index], &env->data[index+1], sizeof(FCM_EnvelopeData)*(env->totvert-index-1)); - - /* free old array, and set the new */ - MEM_freeN(env->data); - env->data= fedn; - env->totvert--; - } - else { - /* just free array, since the only vert was deleted */ - if (env->data) - MEM_freeN(env->data); - env->totvert= 0; - } -} - -/* draw settings for envelope modifier */ -static void draw_modifier__envelope(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - FCM_EnvelopeData *fed; - uiBut *but; - int cy= (*yco - 28); - int i; - - /* set the height: - * - basic settings + variable height from envelope controls - */ - (*height) = 115 + (35 * env->totvert); - - /* basic settings (backdrop + general settings + some padding) */ - DRAW_BACKDROP((*height)); - - /* General Settings */ - uiDefBut(block, LABEL, 1, "Envelope:", 10, cy, 100, 20, NULL, 0.0, 0.0, 0, 0, "Settings for cycling before first keyframe"); - cy -= 20; - - uiBlockBeginAlign(block); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Reference Val:", 10, cy, 300, 20, &env->midval, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, ""); - cy -= 20; - - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 10, cy, 150, 20, &env->min, -UI_FLT_MAX, env->max, 10, 3, "Minimum value (relative to Reference Value) that is used as the 'normal' minimum value"); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 160, cy, 150, 20, &env->max, env->min, UI_FLT_MAX, 10, 3, "Maximum value (relative to Reference Value) that is used as the 'normal' maximum value"); - cy -= 35; - uiBlockEndAlign(block); - - - /* Points header */ - uiDefBut(block, LABEL, 1, "Control Points:", 10, cy, 150, 20, NULL, 0.0, 0.0, 0, 0, ""); - - but= uiDefBut(block, BUT, B_FMODIFIER_REDRAW, "Add Point", 160,cy,150,19, NULL, 0, 0, 0, 0, "Adds a new control-point to the envelope on the current frame"); - uiButSetFunc(but, fmod_envelope_addpoint_cb, env, NULL); - cy -= 35; - - /* Points List */ - for (i=0, fed=env->data; i < env->totvert; i++, fed++) { - uiBlockBeginAlign(block); - but=uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Fra:", 2, cy, 90, 20, &fed->time, -UI_FLT_MAX, UI_FLT_MAX, 10, 1, "Frame that envelope point occurs"); - uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm); - - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 92, cy, 100, 20, &fed->min, -UI_FLT_MAX, UI_FLT_MAX, 10, 2, "Minimum bound of envelope at this point"); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 192, cy, 100, 20, &fed->max, -UI_FLT_MAX, UI_FLT_MAX, 10, 2, "Maximum bound of envelope at this point"); - - but= uiDefIconBut(block, BUT, B_FMODIFIER_REDRAW, ICON_X, 292, cy, 18, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Delete envelope control point"); - uiButSetFunc(but, fmod_envelope_deletepoint_cb, env, SET_INT_IN_POINTER(i)); - uiBlockBeginAlign(block); - cy -= 25; - } -} - -/* --------------- */ - -/* draw settings for limits modifier */ -static void draw_modifier__limits(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) -{ - FMod_Limits *data= (FMod_Limits *)fcm->data; - const int togButWidth = 50; - const int textButWidth = ((width/2)-togButWidth); - - /* set the height */ - (*height) = 60; - - /* basic settings (backdrop + some padding) */ - DRAW_BACKDROP((*height)); - - /* Draw Pairs of LimitToggle+LimitValue */ - uiBlockBeginAlign(block); - uiDefButBitI(block, TOGBUT, FCM_LIMIT_XMIN, B_FMODIFIER_REDRAW, "xMin", 5, *yco-30, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum x value"); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 5+togButWidth, *yco-30, (textButWidth-5), 18, &data->rect.xmin, -UI_FLT_MAX, UI_FLT_MAX, 0.1,0.5,"Lowest x value to allow"); - uiBlockEndAlign(block); - - uiBlockBeginAlign(block); - uiDefButBitI(block, TOGBUT, FCM_LIMIT_XMAX, B_FMODIFIER_REDRAW, "XMax", 5+(width-(textButWidth-5)-togButWidth), *yco-30, 50, 18, &data->flag, 0, 24, 0, 0, "Use maximum x value"); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 5+(width-textButWidth-5), *yco-30, (textButWidth-5), 18, &data->rect.xmax, -UI_FLT_MAX, UI_FLT_MAX, 0.1,0.5,"Highest x value to allow"); - uiBlockEndAlign(block); - - uiBlockBeginAlign(block); - uiDefButBitI(block, TOGBUT, FCM_LIMIT_YMIN, B_FMODIFIER_REDRAW, "yMin", 5, *yco-52, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum y value"); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 5+togButWidth, *yco-52, (textButWidth-5), 18, &data->rect.ymin, -UI_FLT_MAX, UI_FLT_MAX, 0.1,0.5,"Lowest y value to allow"); - uiBlockEndAlign(block); - - uiBlockBeginAlign(block); - uiDefButBitI(block, TOGBUT, FCM_LIMIT_YMAX, B_FMODIFIER_REDRAW, "YMax", 5+(width-(textButWidth-5)-togButWidth), *yco-52, 50, 18, &data->flag, 0, 24, 0, 0, "Use maximum y value"); - uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 5+(width-textButWidth-5), *yco-52, (textButWidth-5), 18, &data->rect.ymax, -UI_FLT_MAX, UI_FLT_MAX, 0.1,0.5,"Highest y value to allow"); - uiBlockEndAlign(block); -} - -/* --------------- */ - -static void graph_panel_modifier_draw(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco) -{ - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - uiBut *but; - short active= (fcm->flag & FMODIFIER_FLAG_ACTIVE); - short width= 314; - short height = 0; - int rb_col; - - /* draw header */ - { - uiBlockSetEmboss(block, UI_EMBOSSN); - - /* rounded header */ - rb_col= (active)?-20:20; - but= uiDefBut(block, ROUNDBOX, B_REDR, "", 0, *yco-2, width, 24, NULL, 5.0, 0.0, 15.0, (float)(rb_col-20), ""); - - /* expand */ - uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_EXPANDED, B_REDR, ICON_TRIA_RIGHT, 5, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is expanded."); - - /* checkbox for 'active' status (for now) */ - but= uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_ACTIVE, B_REDR, ICON_RADIOBUT_OFF, 25, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is active one."); - uiButSetFunc(but, activate_fmodifier_cb, fcu, fcm); - - /* name */ - if (fmi) - uiDefBut(block, LABEL, 1, fmi->name, 10+40, *yco, 150, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); - else - uiDefBut(block, LABEL, 1, "", 10+40, *yco, 150, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); - - /* 'mute' button */ - uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_MUTED, B_REDR, ICON_MUTE_IPO_OFF, 10+(width-60), *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is temporarily muted (not evaluated)."); - - /* delete button */ - but= uiDefIconBut(block, BUT, B_REDR, ICON_X, 10+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete F-Curve Modifier."); - uiButSetFunc(but, delete_fmodifier_cb, fcu, fcm); - - uiBlockSetEmboss(block, UI_EMBOSS); - } - - /* when modifier is expanded, draw settings */ - if (fcm->flag & FMODIFIER_FLAG_EXPANDED) { - /* draw settings for individual modifiers */ - switch (fcm->type) { - case FMODIFIER_TYPE_GENERATOR: /* Generator */ - draw_modifier__generator(block, fcu, fcm, yco, &height, width, active, rb_col); - break; - - case FMODIFIER_TYPE_CYCLES: /* Cycles */ - draw_modifier__cycles(block, fcu, fcm, yco, &height, width, active, rb_col); - break; - - case FMODIFIER_TYPE_ENVELOPE: /* Envelope */ - draw_modifier__envelope(block, fcu, fcm, yco, &height, width, active, rb_col); - break; - - case FMODIFIER_TYPE_LIMITS: /* Limits */ - draw_modifier__limits(block, fcu, fcm, yco, &height, width, active, rb_col); - break; - - case FMODIFIER_TYPE_NOISE: /* Noise */ - draw_modifier__noise(block, fcu, fcm, yco, &height, width, active, rb_col); - break; - - default: /* unknown type */ - height= 96; - //DRAW_BACKDROP(height); // XXX buggy... - break; - } - } - - /* adjust height for new to start */ - (*yco) -= (height + 27); -} - static void graph_panel_modifiers(const bContext *C, Panel *pa) { bAnimListElem *ale; FCurve *fcu; FModifier *fcm; + uiLayout *col, *row; uiBlock *block; - int yco= 190; - if(!graph_panel_context(C, &ale, &fcu)) + if (!graph_panel_context(C, &ale, &fcu)) return; - block= uiLayoutFreeBlock(pa->layout); + block= uiLayoutGetBlock(pa->layout); uiBlockSetHandleFunc(block, do_graph_region_modifier_buttons, NULL); /* 'add modifier' button at top of panel */ - // XXX for now, this will be a operator button which calls a temporary 'add modifier' operator - uiDefButO(block, BUT, "GRAPHEDIT_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, "Add Modifier", 10, 225, 150, 20, "Adds a new F-Curve Modifier for the active F-Curve"); + { + row= uiLayoutRow(pa->layout, 0); + block= uiLayoutGetBlock(row); + + // XXX for now, this will be a operator button which calls a temporary 'add modifier' operator + uiDefButO(block, BUT, "GRAPH_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, "Add Modifier", 10, 0, 150, 20, "Adds a new F-Curve Modifier for the active F-Curve"); + } /* draw each modifier */ - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) - graph_panel_modifier_draw(block, fcu, fcm, &yco); + for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { + col= uiLayoutColumn(pa->layout, 1); + + ANIM_uiTemplate_fmodifier_draw(col, ale->id, &fcu->modifiers, fcm); + } MEM_freeN(ale); } /* ******************* general ******************************** */ -/* Find 'active' F-Curve. It must be editable, since that's the purpose of these buttons (subject to change). - * We return the 'wrapper' since it contains valuable context info (about hierarchy), which will need to be freed - * when the caller is done with it. - */ -// TODO: move this to anim api with another name? -bAnimListElem *get_active_fcurve_channel (bAnimContext *ac) -{ - ListBase anim_data = {NULL, NULL}; - int filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE | ANIMFILTER_CURVESONLY); - int items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* We take the first F-Curve only, since some other ones may have had 'active' flag set - * if they were from linked data. - */ - if (items) { - bAnimListElem *ale= (bAnimListElem *)anim_data.first; - - /* remove first item from list, then free the rest of the list and return the stored one */ - BLI_remlink(&anim_data, ale); - BLI_freelistN(&anim_data); - - return ale; - } - - /* no active F-Curve */ - return NULL; -} - void graph_buttons_register(ARegionType *art) { PanelType *pt; @@ -1066,10 +437,10 @@ static int graph_properties(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_properties(wmOperatorType *ot) +void GRAPH_OT_properties(wmOperatorType *ot) { ot->name= "Properties"; - ot->idname= "GRAPHEDIT_OT_properties"; + ot->idname= "GRAPH_OT_properties"; ot->exec= graph_properties; ot->poll= ED_operator_ipo_active; // xxx diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index ddf4105fdf4..8b242794ca3 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -835,12 +835,12 @@ void graph_draw_curves (bAnimContext *ac, SpaceIpo *sipo, ARegion *ar, View2DGri */ for (ale=anim_data.first; ale; ale=ale->next) { FCurve *fcu= (FCurve *)ale->key_data; - FModifier *fcm= fcurve_find_active_modifier(fcu); - //Object *nob= ANIM_nla_mapping_get(ac, ale); + FModifier *fcm= find_active_fmodifier(&fcu->modifiers); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* map keyframes for drawing if scaled F-Curve */ - //if (nob) - // ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 0); + if (adt) + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); /* draw curve: * - curve line may be result of one or more destructive modifiers or just the raw data, @@ -918,8 +918,8 @@ void graph_draw_curves (bAnimContext *ac, SpaceIpo *sipo, ARegion *ar, View2DGri } /* undo mapping of keyframes for drawing if scaled F-Curve */ - //if (nob) - // ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 0); + if (adt) + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } /* free list of curves */ @@ -1206,6 +1206,17 @@ void graph_draw_channel_names(bAnimContext *ac, SpaceIpo *sipo, ARegion *ar) expand = ICON_TRIA_RIGHT; } + /* for now, 'special' (i.e. in front of name) is used to show visibility status */ + if (agrp->flag & AGRP_NOTVISIBLE) + special= ICON_CHECKBOX_DEHLT; + else + special= ICON_CHECKBOX_HLT; + + if (agrp->flag & AGRP_MUTED) + mute = ICON_MUTE_IPO_ON; + else + mute = ICON_MUTE_IPO_OFF; + if (EDITABLE_AGRP(agrp)) protect = ICON_UNLOCKED; else diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 71f2eb2a8de..1837b6d4ecd 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -67,11 +67,13 @@ #include "BKE_fcurve.h" #include "BKE_key.h" #include "BKE_material.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_context.h" #include "BKE_report.h" #include "BKE_utildefines.h" +#include "UI_interface.h" #include "UI_view2d.h" #include "ED_anim_api.h" @@ -114,16 +116,16 @@ void get_graph_keyframe_extents (bAnimContext *ac, float *xmin, float *xmax, flo if (anim_data.first) { /* go through channels, finding max extents */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= NULL; //ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); FCurve *fcu= (FCurve *)ale->key_data; float txmin, txmax, tymin, tymax; /* get range and apply necessary scaling before */ calc_fcurve_bounds(fcu, &txmin, &txmax, &tymin, &tymax); - if (nob) { - txmin= get_action_frame_inv(nob, txmin); - txmax= get_action_frame_inv(nob, txmax); + if (adt) { + txmin= BKE_nla_tweakedit_remap(adt, txmin, NLATIME_CONVERT_MAP); + txmax= BKE_nla_tweakedit_remap(adt, txmax, NLATIME_CONVERT_MAP); } /* try to set cur using these values, if they're more extreme than previously set values */ @@ -180,15 +182,15 @@ static int graphkeys_previewrange_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_previewrange_set (wmOperatorType *ot) +void GRAPH_OT_previewrange_set (wmOperatorType *ot) { /* identifiers */ ot->name= "Auto-Set Preview Range"; - ot->idname= "GRAPHEDIT_OT_previewrange_set"; + ot->idname= "GRAPH_OT_previewrange_set"; /* api callbacks */ ot->exec= graphkeys_previewrange_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_visible_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -227,15 +229,15 @@ static int graphkeys_viewall_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_view_all (wmOperatorType *ot) +void GRAPH_OT_view_all (wmOperatorType *ot) { /* identifiers */ ot->name= "View All"; - ot->idname= "GRAPHEDIT_OT_view_all"; + ot->idname= "GRAPH_OT_view_all"; /* api callbacks */ ot->exec= graphkeys_viewall_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_visible_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -271,6 +273,7 @@ static void create_ghost_curves (bAnimContext *ac, int start, int end) for (ale= anim_data.first; ale; ale= ale->next) { FCurve *fcu= (FCurve *)ale->key_data; FCurve *gcu= MEM_callocN(sizeof(FCurve), "Ghost FCurve"); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); ChannelDriver *driver= fcu->driver; FPoint *fpt; int cfra; @@ -286,8 +289,10 @@ static void create_ghost_curves (bAnimContext *ac, int start, int end) /* use the sampling callback at 1-frame intervals from start to end frames */ for (cfra= start; cfra <= end; cfra++, fpt++) { - fpt->vec[0]= (float)cfra; - fpt->vec[1]= fcurve_samplingcb_evalcurve(fcu, NULL, (float)cfra); + float cfrae= BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); + + fpt->vec[0]= cfrae; + fpt->vec[1]= fcurve_samplingcb_evalcurve(fcu, NULL, cfrae); } /* set color of ghost curve @@ -334,16 +339,16 @@ static int graphkeys_create_ghostcurves_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_ghost_curves_create (wmOperatorType *ot) +void GRAPH_OT_ghost_curves_create (wmOperatorType *ot) { /* identifiers */ ot->name= "Create Ghost Curves"; - ot->idname= "GRAPHEDIT_OT_ghost_curves_create"; + ot->idname= "GRAPH_OT_ghost_curves_create"; ot->description= "Create snapshot (Ghosts) of selected F-Curves as background aid for active Graph Editor."; /* api callbacks */ ot->exec= graphkeys_create_ghostcurves_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_visible_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -377,16 +382,16 @@ static int graphkeys_clear_ghostcurves_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_ghost_curves_clear (wmOperatorType *ot) +void GRAPH_OT_ghost_curves_clear (wmOperatorType *ot) { /* identifiers */ ot->name= "Create Ghost Curves"; - ot->idname= "GRAPHEDIT_OT_ghost_curves_clear"; + ot->idname= "GRAPH_OT_ghost_curves_clear"; ot->description= "Clear F-Curve snapshots (Ghosts) for active Graph Editor."; /* api callbacks */ ot->exec= graphkeys_clear_ghostcurves_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_ipo_active; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -395,7 +400,103 @@ void GRAPHEDIT_OT_ghost_curves_clear (wmOperatorType *ot) /* ************************************************************************** */ /* GENERAL STUFF */ -// TODO: insertkey +/* ******************** Insert Keyframes Operator ************************* */ + +/* defines for insert keyframes tool */ +EnumPropertyItem prop_graphkeys_insertkey_types[] = { + {1, "ALL", 0, "All Channels", ""}, + {2, "SEL", 0, "Only Selected Channels", ""}, + {0, NULL, 0, NULL, NULL} +}; + +/* this function is responsible for snapping keyframes to frame-times */ +static void insert_graph_keys(bAnimContext *ac, short mode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + Scene *scene= ac->scene; + float cfra= (float)CFRA; + short flag = 0; + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY); + if (mode == 2) filter |= ANIMFILTER_SEL; + + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* init keyframing 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 keyframes */ + for (ale= anim_data.first; ale; ale= ale->next) { + AnimData *adt= ANIM_nla_mapping_get(ac, ale); + FCurve *fcu= (FCurve *)ale->key_data; + + /* adjust current frame for NLA-mapping */ + if (adt) + cfra= BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra= (float)CFRA; + + /* if there's an id */ + if (ale->id) + insert_keyframe(ale->id, NULL, ((fcu->grp)?(fcu->grp->name):(NULL)), fcu->rna_path, fcu->array_index, cfra, flag); + else + insert_vert_fcurve(fcu, cfra, fcu->curval, 0); + } + + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +static int graphkeys_insertkey_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + short mode; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + if (ac.datatype == ANIMCONT_GPENCIL) + return OPERATOR_CANCELLED; + + /* which channels to affect? */ + mode= RNA_enum_get(op->ptr, "type"); + + /* insert keyframes */ + insert_graph_keys(&ac, mode); + + /* validate keyframes after editing */ + ANIM_editkeyframes_refresh(&ac); + + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + + return OPERATOR_FINISHED; +} + +void GRAPH_OT_insert_keyframe (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Insert Keyframes"; + ot->idname= "GRAPH_OT_insert_keyframe"; + + /* api callbacks */ + ot->invoke= WM_menu_invoke; + ot->exec= graphkeys_insertkey_exec; + ot->poll= graphop_editable_keyframes_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* id-props */ + RNA_def_enum(ot->srna, "type", prop_graphkeys_insertkey_types, 0, "Type", ""); +} /* ******************** Click-Insert Keyframes Operator ************************* */ @@ -403,6 +504,7 @@ static int graphkeys_click_insert_exec (bContext *C, wmOperator *op) { bAnimContext ac; bAnimListElem *ale; + AnimData *adt; float frame, val; /* get animation context */ @@ -420,14 +522,18 @@ static int graphkeys_click_insert_exec (bContext *C, wmOperator *op) frame= RNA_float_get(op->ptr, "frame"); val= RNA_float_get(op->ptr, "value"); + /* apply inverse NLA-mapping to frame to get correct time in un-scaled action */ + adt= ANIM_nla_mapping_get(&ac, ale); + frame= BKE_nla_tweakedit_remap(adt, frame, NLATIME_CONVERT_UNMAP); + /* insert keyframe on the specified frame + value */ insert_vert_fcurve((FCurve *)ale->data, frame, val, 0); /* free temp data */ MEM_freeN(ale); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); /* done */ return OPERATOR_FINISHED; @@ -461,16 +567,16 @@ static int graphkeys_click_insert_invoke (bContext *C, wmOperator *op, wmEvent * return graphkeys_click_insert_exec(C, op); } -void GRAPHEDIT_OT_keyframes_click_insert (wmOperatorType *ot) +void GRAPH_OT_click_insert (wmOperatorType *ot) { /* identifiers */ ot->name= "Click-Insert Keyframes"; - ot->idname= "GRAPHEDIT_OT_keyframes_click_insert"; + ot->idname= "GRAPH_OT_click_insert"; /* api callbacks */ ot->invoke= graphkeys_click_insert_invoke; ot->exec= graphkeys_click_insert_exec; - ot->poll= ED_operator_areaactive; // XXX active + editable poll + ot->poll= graphop_active_fcurve_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -538,21 +644,19 @@ static int graphkeys_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); - + /* just return - no operator needed here (no changes) */ return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_copy (wmOperatorType *ot) +void GRAPH_OT_copy (wmOperatorType *ot) { /* identifiers */ ot->name= "Copy Keyframes"; - ot->idname= "GRAPHEDIT_OT_keyframes_copy"; + ot->idname= "GRAPH_OT_copy"; /* api callbacks */ ot->exec= graphkeys_copy_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -577,21 +681,21 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_paste (wmOperatorType *ot) +void GRAPH_OT_paste (wmOperatorType *ot) { /* identifiers */ ot->name= "Paste Keyframes"; - ot->idname= "GRAPHEDIT_OT_keyframes_paste"; + ot->idname= "GRAPH_OT_paste"; /* api callbacks */ ot->exec= graphkeys_paste_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -634,8 +738,8 @@ static int graphkeys_duplicate_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } @@ -650,16 +754,16 @@ static int graphkeys_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *even return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_duplicate (wmOperatorType *ot) +void GRAPH_OT_duplicate (wmOperatorType *ot) { /* identifiers */ ot->name= "Duplicate Keyframes"; - ot->idname= "GRAPHEDIT_OT_keyframes_duplicate"; + ot->idname= "GRAPH_OT_duplicate"; /* api callbacks */ ot->invoke= graphkeys_duplicate_invoke; ot->exec= graphkeys_duplicate_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -705,22 +809,22 @@ static int graphkeys_delete_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_delete (wmOperatorType *ot) +void GRAPH_OT_delete (wmOperatorType *ot) { /* identifiers */ ot->name= "Delete Keyframes"; - ot->idname= "GRAPHEDIT_OT_keyframes_delete"; + ot->idname= "GRAPH_OT_delete"; /* api callbacks */ ot->invoke= WM_operator_confirm; ot->exec= graphkeys_delete_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -766,22 +870,22 @@ static int graphkeys_clean_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_clean (wmOperatorType *ot) +void GRAPH_OT_clean (wmOperatorType *ot) { /* identifiers */ ot->name= "Clean Keyframes"; - ot->idname= "GRAPHEDIT_OT_keyframes_clean"; + ot->idname= "GRAPH_OT_clean"; /* api callbacks */ //ot->invoke= // XXX we need that number popup for this! ot->exec= graphkeys_clean_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -847,22 +951,23 @@ static int graphkeys_bake_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + // NOTE: some distinction between order/number of keyframes and type should be made? + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_bake (wmOperatorType *ot) +void GRAPH_OT_bake (wmOperatorType *ot) { /* identifiers */ ot->name= "Bake Curve"; - ot->idname= "GRAPHEDIT_OT_keyframes_bake"; + ot->idname= "GRAPH_OT_bake"; /* api callbacks */ ot->invoke= WM_operator_confirm; // FIXME... ot->exec= graphkeys_bake_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -973,21 +1078,21 @@ static int graphkeys_sample_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_sample (wmOperatorType *ot) +void GRAPH_OT_sample (wmOperatorType *ot) { /* identifiers */ ot->name= "Sample Keyframes"; - ot->idname= "GRAPHEDIT_OT_keyframes_sample"; + ot->idname= "GRAPH_OT_sample"; /* api callbacks */ ot->exec= graphkeys_sample_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1047,22 +1152,22 @@ static int graphkeys_expo_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframe properties have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_extrapolation_type (wmOperatorType *ot) +void GRAPH_OT_extrapolation_type (wmOperatorType *ot) { /* identifiers */ ot->name= "Set Keyframe Extrapolation"; - ot->idname= "GRAPHEDIT_OT_keyframes_extrapolation_type"; + ot->idname= "GRAPH_OT_extrapolation_type"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= graphkeys_expo_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1115,22 +1220,22 @@ static int graphkeys_ipo_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframe properties have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_interpolation_type (wmOperatorType *ot) +void GRAPH_OT_interpolation_type (wmOperatorType *ot) { /* identifiers */ ot->name= "Set Keyframe Interpolation"; - ot->idname= "GRAPHEDIT_OT_keyframes_interpolation_type"; + ot->idname= "GRAPH_OT_interpolation_type"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= graphkeys_ipo_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1203,21 +1308,21 @@ static int graphkeys_handletype_exec(bContext *C, wmOperator *op) ANIM_editkeyframes_refresh(&ac); /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_handletype (wmOperatorType *ot) +void GRAPH_OT_handletype (wmOperatorType *ot) { /* identifiers */ ot->name= "Set Keyframe Handle Type"; - ot->idname= "GRAPHEDIT_OT_keyframes_handletype"; + ot->idname= "GRAPH_OT_handletype"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= graphkeys_handletype_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1298,15 +1403,15 @@ static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -void GRAPHEDIT_OT_keyframes_euler_filter (wmOperatorType *ot) +void GRAPH_OT_euler_filter (wmOperatorType *ot) { /* identifiers */ ot->name= "Euler Filter"; - ot->idname= "GRAPHEDIT_OT_keyframes_euler_filter"; + ot->idname= "GRAPH_OT_euler_filter"; /* api callbacks */ ot->exec= graphkeys_euler_filter_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1314,10 +1419,10 @@ void GRAPHEDIT_OT_keyframes_euler_filter (wmOperatorType *ot) #endif // XXX this is not ready for the primetime yet -/* ***************** Snap Current Frame Operator *********************** */ +/* ***************** Jump to Selected Frames Operator *********************** */ /* snap current-frame indicator to 'average time' of selected keyframe */ -static int graphkeys_cfrasnap_exec(bContext *C, wmOperator *op) +static int graphkeys_framejump_exec(bContext *C, wmOperator *op) { bAnimContext ac; ListBase anim_data= {NULL, NULL}; @@ -1333,11 +1438,21 @@ static int graphkeys_cfrasnap_exec(bContext *C, wmOperator *op) memset(&bed, 0, sizeof(BeztEditData)); /* loop over action data, averaging values */ - filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE| ANIMFILTER_CURVESONLY); + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - for (ale= anim_data.first; ale; ale= ale->next) - ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_calc_average, NULL); + for (ale= anim_data.first; ale; ale= ale->next) { + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); + + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_calc_average, NULL); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); + } + else + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_calc_average, NULL); + + } BLI_freelistN(&anim_data); @@ -1353,15 +1468,15 @@ static int graphkeys_cfrasnap_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_cfrasnap (wmOperatorType *ot) +void GRAPH_OT_frame_jump (wmOperatorType *ot) { /* identifiers */ - ot->name= "Snap Current Frame to Keys"; - ot->idname= "GRAPHEDIT_OT_keyframes_cfrasnap"; + ot->name= "Jump to Frame"; + ot->idname= "GRAPH_OT_frame_jump"; /* api callbacks */ - ot->exec= graphkeys_cfrasnap_exec; - ot->poll= ED_operator_areaactive; + ot->exec= graphkeys_framejump_exec; + ot->poll= graphop_visible_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1405,12 +1520,12 @@ static void snap_graph_keys(bAnimContext *ac, short mode) /* snap keyframes */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, edit_cb, calchandles_fcurve); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } else ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, edit_cb, calchandles_fcurve); @@ -1438,22 +1553,22 @@ static int graphkeys_snap_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_snap (wmOperatorType *ot) +void GRAPH_OT_snap (wmOperatorType *ot) { /* identifiers */ ot->name= "Snap Keys"; - ot->idname= "GRAPHEDIT_OT_keyframes_snap"; + ot->idname= "GRAPH_OT_snap"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= graphkeys_snap_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1466,10 +1581,10 @@ void GRAPHEDIT_OT_keyframes_snap (wmOperatorType *ot) /* defines for mirror keyframes tool */ EnumPropertyItem prop_graphkeys_mirror_types[] = { - {GRAPHKEYS_MIRROR_CFRA, "CFRA", 0, "Current frame", ""}, - {GRAPHKEYS_MIRROR_YAXIS, "YAXIS", 0, "Vertical Axis", ""}, - {GRAPHKEYS_MIRROR_XAXIS, "XAXIS", 0, "Horizontal Axis", ""}, - {GRAPHKEYS_MIRROR_MARKER, "MARKER", 0, "First Selected Marker", ""}, + {GRAPHKEYS_MIRROR_CFRA, "CFRA", 0, "By Times over Current frame", ""}, + {GRAPHKEYS_MIRROR_YAXIS, "YAXIS", 0, "By Times over Time=0", ""}, + {GRAPHKEYS_MIRROR_XAXIS, "XAXIS", 0, "By Values over Value=0", ""}, + {GRAPHKEYS_MIRROR_MARKER, "MARKER", 0, "By Times over First Selected Marker", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1516,12 +1631,12 @@ static void mirror_graph_keys(bAnimContext *ac, short mode) /* mirror keyframes */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, edit_cb, calchandles_fcurve); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } else ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, edit_cb, calchandles_fcurve); @@ -1549,22 +1664,22 @@ static int graphkeys_mirror_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_mirror (wmOperatorType *ot) +void GRAPH_OT_mirror (wmOperatorType *ot) { /* identifiers */ ot->name= "Mirror Keys"; - ot->idname= "GRAPHEDIT_OT_keyframes_mirror"; + ot->idname= "GRAPH_OT_mirror"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= graphkeys_mirror_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1603,21 +1718,21 @@ static int graphkeys_smooth_exec(bContext *C, wmOperator *op) /* validate keyframes after editing */ ANIM_editkeyframes_refresh(&ac); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_VALUES); + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_smooth (wmOperatorType *ot) +void GRAPH_OT_smooth (wmOperatorType *ot) { /* identifiers */ ot->name= "Smooth Keys"; - ot->idname= "GRAPHEDIT_OT_keyframes_smooth"; + ot->idname= "GRAPH_OT_smooth"; /* api callbacks */ ot->exec= graphkeys_smooth_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_editable_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -1626,7 +1741,35 @@ void GRAPHEDIT_OT_keyframes_smooth (wmOperatorType *ot) /* ************************************************************************** */ /* F-CURVE MODIFIERS */ -/* ******************** Add F-Curve Modifier Operator *********************** */ +/* ******************** Add F-Modifier Operator *********************** */ + +/* present a special customised popup menu for this, with some filtering */ +static int graph_fmodifier_add_invoke (bContext *C, wmOperator *op, wmEvent *event) +{ + uiPopupMenu *pup; + uiLayout *layout; + int i; + + pup= uiPupMenuBegin(C, "Add F-Curve Modifier", 0); + layout= uiPupMenuLayout(pup); + + /* start from 1 to skip the 'Invalid' modifier type */ + for (i = 1; i < FMODIFIER_NUM_TYPES; i++) { + FModifierTypeInfo *fmi= get_fmodifier_typeinfo(i); + + /* check if modifier is valid for this context */ + if (fmi == NULL) + continue; + + /* add entry to add this type of modifier */ + uiItemEnumO(layout, fmi->name, 0, "GRAPH_OT_fmodifier_add", "type", i); + } + uiItemS(layout); + + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; +} static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) { @@ -1654,9 +1797,9 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) type= RNA_enum_get(op->ptr, "type"); /* add F-Modifier of specified type to active F-Curve, and make it the active one */ - fcm= fcurve_add_modifier(fcu, type); + fcm= add_fmodifier(&fcu->modifiers, type); if (fcm) - fcurve_set_active_modifier(fcu, fcm); + set_active_fmodifier(&fcu->modifiers, fcm); else { BKE_report(op->reports, RPT_ERROR, "Modifier couldn't be added. See console for details."); return OPERATOR_CANCELLED; @@ -1666,21 +1809,22 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) ANIM_editkeyframes_refresh(&ac); /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH); + // FIXME: this really isn't the best description for it... + WM_event_add_notifier(C, NC_ANIMATION, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_fmodifier_add (wmOperatorType *ot) +void GRAPH_OT_fmodifier_add (wmOperatorType *ot) { /* identifiers */ ot->name= "Add F-Curve Modifier"; - ot->idname= "GRAPHEDIT_OT_fmodifier_add"; + ot->idname= "GRAPH_OT_fmodifier_add"; /* api callbacks */ - ot->invoke= WM_menu_invoke; + ot->invoke= graph_fmodifier_add_invoke; ot->exec= graph_fmodifier_add_exec; - ot->poll= ED_operator_areaactive; // XXX need active F-Curve + ot->poll= graphop_active_fcurve_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; diff --git a/source/blender/editors/space_graph/graph_header.c b/source/blender/editors/space_graph/graph_header.c index 178b4b4562f..05d9b2f7e6c 100644 --- a/source/blender/editors/space_graph/graph_header.c +++ b/source/blender/editors/space_graph/graph_header.c @@ -43,10 +43,13 @@ #include "BKE_screen.h" #include "ED_anim_api.h" +#include "ED_transform.h" #include "ED_screen.h" #include "ED_types.h" #include "ED_util.h" +#include "RNA_access.h" + #include "WM_api.h" #include "WM_types.h" @@ -62,76 +65,173 @@ /* ********************************************************* */ /* Menu Defines... */ -/* button events */ +static void graph_viewmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa= CTX_wm_area(C); + SpaceIpo *sipo= (SpaceIpo*)CTX_wm_space_data(C); + PointerRNA spaceptr; + + /* retrieve state */ + RNA_pointer_create(&sc->id, &RNA_SpaceGraphEditor, sipo, &spaceptr); + + /* create menu */ + uiItemO(layout, NULL, ICON_MENU_PANEL, "GRAPH_OT_properties"); + + uiItemS(layout); + + uiItemR(layout, NULL, 0, &spaceptr, "show_cframe_indicator", 0, 0, 0); + + if (sipo->flag & SIPO_NOHANDLES) + uiItemO(layout, "Show Handles", ICON_CHECKBOX_DEHLT, "GRAPH_OT_handles_view_toggle"); + else + uiItemO(layout, "Show Handles", ICON_CHECKBOX_HLT, "GRAPH_OT_handles_view_toggle"); + + uiItemR(layout, NULL, 0, &spaceptr, "automerge_keyframes", 0, 0, 0); + + if (sipo->flag & SIPO_DRAWTIME) + uiItemO(layout, "Show Frames", 0, "ANIM_OT_time_toggle"); + else + uiItemO(layout, "Show Seconds", 0, "ANIM_OT_time_toggle"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ANIM_OT_previewrange_set"); + uiItemO(layout, NULL, 0, "ANIM_OT_previewrange_clear"); + + uiItemO(layout, NULL, 0, "GRAPH_OT_previewrange_set"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "GRAPH_OT_frame_jump"); + + uiItemO(layout, NULL, 0, "GRAPH_OT_view_all"); + + if (sa->full) + uiItemO(layout, NULL, 0, "SCREEN_OT_screen_full_area"); // "Tile Window", Ctrl UpArrow + else + uiItemO(layout, NULL, 0, "SCREEN_OT_screen_full_area"); // "Maximize Window", Ctrl DownArrow +} + +static void graph_selectmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemO(layout, NULL, 0, "GRAPH_OT_select_all_toggle"); + uiItemBooleanO(layout, "Invert All", 0, "GRAPH_OT_select_all_toggle", "invert", 1); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "GRAPH_OT_select_border"); + uiItemBooleanO(layout, "Border Axis Range", 0, "GRAPH_OT_select_border", "axis_range", 1); + + uiItemS(layout); + + uiItemEnumO(layout, "Columns on Selected Keys", 0, "GRAPH_OT_select_column", "mode", GRAPHKEYS_COLUMNSEL_KEYS); + uiItemEnumO(layout, "Column on Current Frame", 0, "GRAPH_OT_select_column", "mode", GRAPHKEYS_COLUMNSEL_CFRA); + + uiItemEnumO(layout, "Columns on Selected Markers", 0, "GRAPH_OT_select_column", "mode", GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN); + uiItemEnumO(layout, "Between Selected Markers", 0, "GRAPH_OT_select_column", "mode", GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN); +} + +static void graph_channelmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemO(layout, NULL, 0, "ANIM_OT_channels_setting_toggle"); + uiItemO(layout, NULL, 0, "ANIM_OT_channels_setting_enable"); + uiItemO(layout, NULL, 0, "ANIM_OT_channels_setting_disable"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ANIM_OT_channels_editable_toggle"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ANIM_OT_channels_expand"); + uiItemO(layout, NULL, 0, "ANIM_OT_channels_collapse"); +} + +static void graph_edit_transformmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, "Grab/Move", 0, "TFM_OT_transform", "mode", TFM_TIME_TRANSLATE); + uiItemEnumO(layout, "Extend", 0, "TFM_OT_transform", "mode", TFM_TIME_EXTEND); + uiItemEnumO(layout, "Scale", 0, "TFM_OT_transform", "mode", TFM_TIME_SCALE); +} + +static void graph_edit_snapmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_snap", "type", GRAPHKEYS_SNAP_CFRA); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_snap", "type", GRAPHKEYS_SNAP_NEAREST_FRAME); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_snap", "type", GRAPHKEYS_SNAP_NEAREST_SECOND); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_snap", "type", GRAPHKEYS_SNAP_NEAREST_MARKER); +} + +static void graph_edit_mirrormenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_mirror", "type", GRAPHKEYS_MIRROR_CFRA); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_mirror", "type", GRAPHKEYS_MIRROR_YAXIS); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_mirror", "type", GRAPHKEYS_MIRROR_XAXIS); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_mirror", "type", GRAPHKEYS_MIRROR_MARKER); +} + +static void graph_edit_handlesmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_handle_type", "type", HD_FREE); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_handle_type", "type", HD_AUTO); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_handle_type", "type", HD_VECT); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_handle_type", "type", HD_ALIGN); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_handle_type", "type", HD_AUTO_ANIM); // xxx? +} + +static void graph_edit_ipomenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_interpolation_type", "type", BEZT_IPO_CONST); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_interpolation_type", "type", BEZT_IPO_LIN); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_interpolation_type", "type", BEZT_IPO_BEZ); +} + +static void graph_edit_expomenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_extrapolation_type", "type", FCURVE_EXTRAPOLATE_CONSTANT); + uiItemEnumO(layout, NULL, 0, "GRAPH_OT_extrapolation_type", "type", FCURVE_EXTRAPOLATE_LINEAR); +} + +static void graph_editmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemMenuF(layout, "Transform", 0, graph_edit_transformmenu); + uiItemMenuF(layout, "Snap", 0, graph_edit_snapmenu); + uiItemMenuF(layout, "Mirror", 0, graph_edit_mirrormenu); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "GRAPH_OT_insert_keyframe"); + uiItemO(layout, NULL, 0, "GRAPH_OT_fmodifier_add"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "GRAPH_OT_duplicate"); + uiItemO(layout, NULL, 0, "GRAPH_OT_delete"); + + uiItemS(layout); + + uiItemMenuF(layout, "Handle Type", 0, graph_edit_handlesmenu); + uiItemMenuF(layout, "Interpolation Mode", 0, graph_edit_ipomenu); + uiItemMenuF(layout, "Extrapolation Mode", 0, graph_edit_expomenu); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "GRAPH_OT_clean"); + uiItemO(layout, NULL, 0, "GRAPH_OT_sample"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "GRAPH_OT_copy"); + uiItemO(layout, NULL, 0, "GRAPH_OT_paste"); +} + +/* ********************************************************* */ + enum { - B_REDR = 0, + B_REDR = 0, B_MODECHANGE, -} eActHeader_ButEvents; - -/* ************************ header area region *********************** */ - -static void do_viewmenu(bContext *C, void *arg, int event) -{ - SpaceIpo *sipo= (SpaceIpo *)CTX_wm_space_data(C); - - switch (event) { - case 1: /* Show time/frames */ - sipo->flag ^= SIPO_DRAWTIME; - break; - case 2: /* AutoMerge Keyframes */ - sipo->flag ^= SIPO_NOTRANSKEYCULL; - break; - case 3: /* Show/Hide handles */ - sipo->flag ^= SIPO_NOHANDLES; - break; - case 4: /* Show current frame number beside indicator */ - sipo->flag ^= SIPO_NODRAWCFRANUM; - break; - } -} - -static uiBlock *graph_viewmenu(bContext *C, ARegion *ar, void *arg_unused) -{ - ScrArea *curarea= CTX_wm_area(C); - SpaceIpo *sipo= (SpaceIpo *)CTX_wm_space_data(C); - uiBlock *block; - short yco= 0, menuwidth=120; - - block= uiBeginBlock(C, ar, "graph_viewmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_viewmenu, NULL); - - // XXX these options should use new menu-options - - if (sipo->flag & SIPO_DRAWTIME) { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Show Frames|Ctrl T", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, ""); - } - else { - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Show Seconds|Ctrl T", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, ""); - } - - - uiDefIconTextBut(block, BUTM, 1, (sipo->flag & SIPO_NOTRANSKEYCULL)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, - "AutoMerge Keyframes|", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, ""); - uiDefIconTextBut(block, BUTM, 1, (sipo->flag & SIPO_NOHANDLES)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, - "Show Handles|Ctrl H", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, ""); - uiDefIconTextBut(block, BUTM, 1, (sipo->flag & SIPO_NODRAWCFRANUM)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, - "Show Current Frame Number|", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, ""); - - if (curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; -} +} eGraphEdit_Events; static void do_graph_buttons(bContext *C, void *arg, int event) { @@ -163,8 +263,19 @@ void graph_header_buttons(const bContext *C, ARegion *ar) int xmax; xmax= GetButStringLength("View"); - uiDefPulldownBut(block, graph_viewmenu, CTX_wm_area(C), - "View", xco, yco-2, xmax-3, 24, ""); + uiDefMenuBut(block, graph_viewmenu, NULL, "View", xco, yco, xmax-3, 20, ""); + xco+= xmax; + + xmax= GetButStringLength("Select"); + uiDefMenuBut(block, graph_selectmenu, NULL, "Select", xco, yco, xmax-3, 20, ""); + xco+= xmax; + + xmax= GetButStringLength("Channel"); + uiDefMenuBut(block, graph_channelmenu, NULL, "Channel", xco, yco, xmax-3, 20, ""); + xco+= xmax; + + xmax= GetButStringLength("Key"); + uiDefMenuBut(block, graph_editmenu, NULL, "Key", xco, yco, xmax-3, 20, ""); xco+= xmax; } @@ -202,8 +313,8 @@ void graph_header_buttons(const bContext *C, ARegion *ar) /* copy + paste */ uiBlockBeginAlign(block); - uiDefIconButO(block, BUT, "GRAPHEDIT_OT_keyframes_copy", WM_OP_INVOKE_REGION_WIN, ICON_COPYDOWN, xco+=XIC,yco,XIC,YIC, "Copies the selected keyframes from the selected channel(s) to the buffer"); - uiDefIconButO(block, BUT, "GRAPHEDIT_OT_keyframes_paste", WM_OP_INVOKE_REGION_WIN, ICON_PASTEDOWN, xco+=XIC,yco,XIC,YIC, "Pastes the keyframes from the buffer"); + uiDefIconButO(block, BUT, "GRAPH_OT_copy", WM_OP_INVOKE_REGION_WIN, ICON_COPYDOWN, xco+=XIC,yco,XIC,YIC, "Copies the selected keyframes from the selected channel(s) to the buffer"); + uiDefIconButO(block, BUT, "GRAPH_OT_paste", WM_OP_INVOKE_REGION_WIN, ICON_PASTEDOWN, xco+=XIC,yco,XIC,YIC, "Pastes the keyframes from the buffer"); uiBlockEndAlign(block); xco += (XIC + 8); @@ -225,9 +336,9 @@ void graph_header_buttons(const bContext *C, ARegion *ar) /* ghost curves */ // XXX these icons need to be changed if (sipo->ghostCurves.first) - uiDefIconButO(block, BUT, "GRAPHEDIT_OT_ghost_curves_clear", WM_OP_INVOKE_REGION_WIN, ICON_OUTLINER_DATA_CURVE, xco,yco,XIC,YIC, "Clear F-Curve snapshots (Ghosts) for this Graph Editor instance"); + uiDefIconButO(block, BUT, "GRAPH_OT_ghost_curves_clear", WM_OP_INVOKE_REGION_WIN, ICON_OUTLINER_DATA_CURVE, xco,yco,XIC,YIC, "Clear F-Curve snapshots (Ghosts) for this Graph Editor instance"); else - uiDefIconButO(block, BUT, "GRAPHEDIT_OT_ghost_curves_create", WM_OP_INVOKE_REGION_WIN, ICON_OUTLINER_OB_CURVE, xco,yco,XIC,YIC, "Create snapshot (Ghosts) of selected F-Curves as background aid for this Graph Editor instance"); + uiDefIconButO(block, BUT, "GRAPH_OT_ghost_curves_create", WM_OP_INVOKE_REGION_WIN, ICON_OUTLINER_OB_CURVE, xco,yco,XIC,YIC, "Create snapshot (Ghosts) of selected F-Curves as background aid for this Graph Editor instance"); xco+= XIC; diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index e31adf7a7c8..697c31eaa98 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -32,6 +32,8 @@ struct bContext; struct wmWindowManager; struct bAnimContext; struct bAnimListElem; +struct FCurve; +struct FModifier; struct SpaceIpo; struct ScrArea; struct ARegion; @@ -58,10 +60,10 @@ void graph_header_buttons(const bContext *C, struct ARegion *ar); /* ***************************************** */ /* graph_select.c */ -void GRAPHEDIT_OT_keyframes_select_all_toggle(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_select_border(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_columnselect(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_clickselect(struct wmOperatorType *ot); +void GRAPH_OT_select_all_toggle(struct wmOperatorType *ot); +void GRAPH_OT_select_border(struct wmOperatorType *ot); +void GRAPH_OT_select_column(struct wmOperatorType *ot); +void GRAPH_OT_clickselect(struct wmOperatorType *ot); /* defines for left-right select tool */ enum { @@ -84,28 +86,29 @@ enum { void get_graph_keyframe_extents (struct bAnimContext *ac, float *xmin, float *xmax, float *ymin, float *ymax); -void GRAPHEDIT_OT_previewrange_set(struct wmOperatorType *ot); -void GRAPHEDIT_OT_view_all(struct wmOperatorType *ot); +void GRAPH_OT_previewrange_set(struct wmOperatorType *ot); +void GRAPH_OT_view_all(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_click_insert(struct wmOperatorType *ot); +void GRAPH_OT_click_insert(struct wmOperatorType *ot); +void GRAPH_OT_insert_keyframe(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_copy(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_paste(struct wmOperatorType *ot); +void GRAPH_OT_copy(struct wmOperatorType *ot); +void GRAPH_OT_paste(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_duplicate(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_delete(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_clean(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_sample(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_bake(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_smooth(struct wmOperatorType *ot); +void GRAPH_OT_duplicate(struct wmOperatorType *ot); +void GRAPH_OT_delete(struct wmOperatorType *ot); +void GRAPH_OT_clean(struct wmOperatorType *ot); +void GRAPH_OT_sample(struct wmOperatorType *ot); +void GRAPH_OT_bake(struct wmOperatorType *ot); +void GRAPH_OT_smooth(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_handletype(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_interpolation_type(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_extrapolation_type(struct wmOperatorType *ot); +void GRAPH_OT_handletype(struct wmOperatorType *ot); +void GRAPH_OT_interpolation_type(struct wmOperatorType *ot); +void GRAPH_OT_extrapolation_type(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_cfrasnap(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_snap(struct wmOperatorType *ot); -void GRAPHEDIT_OT_keyframes_mirror(struct wmOperatorType *ot); +void GRAPH_OT_frame_jump(struct wmOperatorType *ot); +void GRAPH_OT_snap(struct wmOperatorType *ot); +void GRAPH_OT_mirror(struct wmOperatorType *ot); /* defines for snap keyframes * NOTE: keep in sync with eEditKeyframes_Snap (in ED_keyframes_edit.h) @@ -130,20 +133,29 @@ enum { /* ----------- */ -void GRAPHEDIT_OT_fmodifier_add(struct wmOperatorType *ot); +void GRAPH_OT_fmodifier_add(struct wmOperatorType *ot); /* ----------- */ -void GRAPHEDIT_OT_ghost_curves_create(struct wmOperatorType *ot); -void GRAPHEDIT_OT_ghost_curves_clear(struct wmOperatorType *ot); +void GRAPH_OT_ghost_curves_create(struct wmOperatorType *ot); +void GRAPH_OT_ghost_curves_clear(struct wmOperatorType *ot); /* ***************************************** */ /* graph_buttons.c */ -void GRAPHEDIT_OT_properties(struct wmOperatorType *ot); +void GRAPH_OT_properties(struct wmOperatorType *ot); void graph_buttons_register(struct ARegionType *art); +/* ***************************************** */ +/* graph_utils.c */ + struct bAnimListElem *get_active_fcurve_channel(struct bAnimContext *ac); +short fcurve_needs_draw_fmodifier_controls(struct FCurve *fcu, struct FModifier *fcm); + +int graphop_visible_keyframes_poll(struct bContext *C); +int graphop_editable_keyframes_poll(struct bContext *C); +int graphop_active_fcurve_poll(struct bContext *C); + /* ***************************************** */ /* graph_ops.c */ void graphedit_keymap(struct wmWindowManager *wm); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index d1a678f91d4..305ad825030 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -57,6 +57,10 @@ #include "WM_api.h" #include "WM_types.h" +/* ************************** poll callbacks **********************************/ + + + /* ************************** view-based operators **********************************/ // XXX this probably shouldn't be here.. @@ -79,15 +83,15 @@ static int view_toggle_handles_exec (bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_view_togglehandles (wmOperatorType *ot) +void GRAPH_OT_view_togglehandles (wmOperatorType *ot) { /* identification */ ot->name= "Show/Hide All Handles"; - ot->idname= "GRAPHEDIT_OT_handles_view_toggle"; + ot->idname= "GRAPH_OT_handles_view_toggle"; /* callbacks */ ot->exec= view_toggle_handles_exec; - ot->poll= ED_operator_areaactive; + ot->poll= ED_operator_ipo_active; } /* ************************** registration - operator types **********************************/ @@ -95,45 +99,44 @@ void GRAPHEDIT_OT_view_togglehandles (wmOperatorType *ot) void graphedit_operatortypes(void) { /* view */ - WM_operatortype_append(GRAPHEDIT_OT_view_togglehandles); - WM_operatortype_append(GRAPHEDIT_OT_previewrange_set); - WM_operatortype_append(GRAPHEDIT_OT_view_all); - WM_operatortype_append(GRAPHEDIT_OT_properties); + WM_operatortype_append(GRAPH_OT_view_togglehandles); + WM_operatortype_append(GRAPH_OT_previewrange_set); + WM_operatortype_append(GRAPH_OT_view_all); + WM_operatortype_append(GRAPH_OT_properties); - WM_operatortype_append(GRAPHEDIT_OT_ghost_curves_create); - WM_operatortype_append(GRAPHEDIT_OT_ghost_curves_clear); + WM_operatortype_append(GRAPH_OT_ghost_curves_create); + WM_operatortype_append(GRAPH_OT_ghost_curves_clear); /* keyframes */ /* selection */ - WM_operatortype_append(GRAPHEDIT_OT_keyframes_clickselect); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_select_all_toggle); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_select_border); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_columnselect); + WM_operatortype_append(GRAPH_OT_clickselect); + WM_operatortype_append(GRAPH_OT_select_all_toggle); + WM_operatortype_append(GRAPH_OT_select_border); + WM_operatortype_append(GRAPH_OT_select_column); /* editing */ - WM_operatortype_append(GRAPHEDIT_OT_keyframes_snap); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_mirror); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_cfrasnap); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_handletype); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_interpolation_type); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_extrapolation_type); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_sample); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_bake); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_smooth); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_clean); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_delete); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_duplicate); + WM_operatortype_append(GRAPH_OT_snap); + WM_operatortype_append(GRAPH_OT_mirror); + WM_operatortype_append(GRAPH_OT_frame_jump); + WM_operatortype_append(GRAPH_OT_handletype); + WM_operatortype_append(GRAPH_OT_interpolation_type); + WM_operatortype_append(GRAPH_OT_extrapolation_type); + WM_operatortype_append(GRAPH_OT_sample); + WM_operatortype_append(GRAPH_OT_bake); + WM_operatortype_append(GRAPH_OT_smooth); + WM_operatortype_append(GRAPH_OT_clean); + WM_operatortype_append(GRAPH_OT_delete); + WM_operatortype_append(GRAPH_OT_duplicate); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_copy); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_paste); + WM_operatortype_append(GRAPH_OT_copy); + WM_operatortype_append(GRAPH_OT_paste); - WM_operatortype_append(GRAPHEDIT_OT_keyframes_click_insert); - - //TODO: insertkey... + WM_operatortype_append(GRAPH_OT_insert_keyframe); + WM_operatortype_append(GRAPH_OT_click_insert); /* F-Curve Modifiers */ // XXX temporary? - WM_operatortype_append(GRAPHEDIT_OT_fmodifier_add); + WM_operatortype_append(GRAPH_OT_fmodifier_add); } /* ************************** registration - keymaps **********************************/ @@ -143,81 +146,82 @@ static void graphedit_keymap_keyframes (wmWindowManager *wm, ListBase *keymap) wmKeymapItem *kmi; /* view */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_handles_view_toggle", HKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_handles_view_toggle", HKEY, KM_PRESS, KM_CTRL, 0); /* graph_select.c - selection tools */ /* click-select */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, 0, 0); - kmi= WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_clickselect", SELECTMOUSE, KM_PRESS, 0, 0); + kmi= WM_keymap_add_item(keymap, "GRAPH_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "column", 1); - kmi= WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + kmi= WM_keymap_add_item(keymap, "GRAPH_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", 1); - kmi= WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT|KM_SHIFT, 0); + kmi= WM_keymap_add_item(keymap, "GRAPH_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_ALT|KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", 1); RNA_boolean_set(kmi->ptr, "column", 1); - kmi= WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); + kmi= WM_keymap_add_item(keymap, "GRAPH_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); RNA_enum_set(kmi->ptr, "left_right", GRAPHKEYS_LRSEL_TEST); - kmi= WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL|KM_ALT, 0); + kmi= WM_keymap_add_item(keymap, "GRAPH_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL|KM_ALT, 0); RNA_boolean_set(kmi->ptr, "curves", 1); - kmi= WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL|KM_ALT|KM_SHIFT, 0); + kmi= WM_keymap_add_item(keymap, "GRAPH_OT_clickselect", SELECTMOUSE, KM_PRESS, KM_CTRL|KM_ALT|KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "curves", 1); RNA_boolean_set(kmi->ptr, "extend", 1); /* deselect all */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_select_all_toggle", AKEY, KM_PRESS, 0, 0); - RNA_boolean_set(WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1); + WM_keymap_add_item(keymap, "GRAPH_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1); /* borderselect */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_select_border", BKEY, KM_PRESS, 0, 0); - RNA_boolean_set(WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_select_border", BKEY, KM_PRESS, KM_ALT, 0)->ptr, "axis_range", 1); + WM_keymap_add_item(keymap, "GRAPH_OT_select_border", BKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_border", BKEY, KM_PRESS, KM_ALT, 0)->ptr, "axis_range", 1); /* column select */ // XXX KKEY would be nice to keep for 'keyframe' lines - RNA_enum_set(WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_columnselect", KKEY, KM_PRESS, 0, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_KEYS); - RNA_enum_set(WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_columnselect", KKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_CFRA); - RNA_enum_set(WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_columnselect", KKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN); - RNA_enum_set(WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_columnselect", KKEY, KM_PRESS, KM_ALT, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN); + RNA_enum_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_column", KKEY, KM_PRESS, 0, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_KEYS); + RNA_enum_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_column", KKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_CFRA); + RNA_enum_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_column", KKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN); + RNA_enum_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_column", KKEY, KM_PRESS, KM_ALT, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN); /* graph_edit.c */ /* snap - current frame to selected keys */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_cfrasnap", SKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); + // TODO: maybe since this is called jump, we're better to have it on -J? + WM_keymap_add_item(keymap, "GRAPH_OT_frame_jump", SKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); /* menu + single-step transform */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_snap", SKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_mirror", MKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_snap", SKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_mirror", MKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_handletype", HKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_interpolation_type", TKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_extrapolation_type", EKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_handletype", HKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_interpolation_type", TKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_extrapolation_type", EKEY, KM_PRESS, KM_SHIFT, 0); /* destructive */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_clean", OKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_smooth", OKEY, KM_PRESS, KM_ALT, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_sample", OKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_clean", OKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_smooth", OKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_sample", OKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_bake", CKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_bake", CKEY, KM_PRESS, KM_ALT, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_delete", XKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_delete", DELKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_delete", DELKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); /* insertkey */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_click_insert", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_insert_keyframe", IKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); /* copy/paste */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_copy", CKEY, KM_PRESS, KM_CTRL, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_keyframes_paste", VKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0); /* auto-set range */ - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_previewrange_set", PKEY, KM_PRESS, KM_CTRL|KM_ALT, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_previewrange_set", PKEY, KM_PRESS, KM_CTRL|KM_ALT, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); - /* F-Curve Modifiers */ - // XXX these are temporary? operators... - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_fmodifier_add", MKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); + /* F-Modifiers */ + WM_keymap_add_item(keymap, "GRAPH_OT_fmodifier_add", MKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); /* transform system */ @@ -232,7 +236,7 @@ void graphedit_keymap(wmWindowManager *wm) /* keymap for all regions */ keymap= WM_keymap_listbase(wm, "GraphEdit Generic", SPACE_IPO, 0); - WM_keymap_add_item(keymap, "GRAPHEDIT_OT_properties", NKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_properties", NKEY, KM_PRESS, 0, 0); /* channels */ /* Channels are not directly handled by the Graph Editor module, but are inherited from the Animation module. diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 7746f49d135..a78cacb42ef 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -63,6 +63,7 @@ #include "BKE_fcurve.h" #include "BKE_key.h" #include "BKE_material.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_context.h" #include "BKE_utildefines.h" @@ -168,20 +169,20 @@ static int graphkeys_deselectall_exec(bContext *C, wmOperator *op) deselect_graph_keys(&ac, 1, SELECT_ADD); /* set notifier that things have changed */ - ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead! + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_select_all_toggle (wmOperatorType *ot) +void GRAPH_OT_select_all_toggle (wmOperatorType *ot) { /* identifiers */ ot->name= "Select All"; - ot->idname= "GRAPHEDIT_OT_keyframes_select_all_toggle"; + ot->idname= "GRAPH_OT_select_all_toggle"; /* api callbacks */ ot->exec= graphkeys_deselectall_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_visible_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; @@ -231,14 +232,14 @@ static void borderselect_graphkeys (bAnimContext *ac, rcti rect, short mode, sho /* loop over data, doing border select */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* set horizontal range (if applicable) */ if (mode != BEZT_OK_VALUERANGE) { /* if channel is mapped in NLA, apply correction */ - if (nob) { - bed.f1= get_action_frame(nob, rectf.xmin); - bed.f2= get_action_frame(nob, rectf.xmax); + if (adt) { + bed.f1= BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP); + bed.f2= BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP); } else { bed.f1= rectf.xmin; @@ -301,21 +302,24 @@ static int graphkeys_borderselect_exec(bContext *C, wmOperator *op) /* apply borderselect action */ borderselect_graphkeys(&ac, rect, mode, selectmode); + /* send notifier that keyframe selection has changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL); + return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_select_border(wmOperatorType *ot) +void GRAPH_OT_select_border(wmOperatorType *ot) { /* identifiers */ ot->name= "Border Select"; - ot->idname= "GRAPHEDIT_OT_keyframes_select_border"; + ot->idname= "GRAPH_OT_select_border"; /* api callbacks */ ot->invoke= WM_border_select_invoke; ot->exec= graphkeys_borderselect_exec; ot->modal= WM_border_select_modal; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_visible_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; @@ -379,12 +383,12 @@ static void markers_selectkeys_between (bAnimContext *ac) /* select keys in-between */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } else { ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); @@ -450,15 +454,15 @@ static void columnselect_graph_keys (bAnimContext *ac, short mode) ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* loop over cfraelems (stored in the BeztEditData->list) * - we need to do this here, as we can apply fewer NLA-mapping conversions */ for (ce= bed.list.first; ce; ce= ce->next) { /* set frame for validation callback to refer to */ - if (nob) - bed.f1= get_action_frame(nob, ce->cfra); + if (ale) + bed.f1= BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP); else bed.f1= ce->cfra; @@ -491,21 +495,21 @@ static int graphkeys_columnselect_exec(bContext *C, wmOperator *op) else columnselect_graph_keys(&ac, mode); - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_SELECT); + /* set notifier that keyframe selection has changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL); return OPERATOR_FINISHED; } -void GRAPHEDIT_OT_keyframes_columnselect (wmOperatorType *ot) +void GRAPH_OT_select_column (wmOperatorType *ot) { /* identifiers */ ot->name= "Select All"; - ot->idname= "GRAPHEDIT_OT_keyframes_columnselect"; + ot->idname= "GRAPH_OT_select_column"; /* api callbacks */ ot->exec= graphkeys_columnselect_exec; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_visible_keyframes_poll; /* flags */ ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; @@ -566,11 +570,16 @@ static short findnearest_fcurve_vert (bAnimContext *ac, int mval[2], FCurve **fc for (ale= anim_data.first; ale; ale= ale->next) { FCurve *fcu= (FCurve *)ale->key_data; + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* try to progressively get closer to the right point... */ if (fcu->bezt) { BezTriple *bezt1=fcu->bezt, *prevbezt=NULL; + /* apply NLA mapping to all the keyframes */ + if (adt) + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); + for (i=0; i < fcu->totvert; i++, prevbezt=bezt1, bezt1++) { /* convert beztriple points to screen-space */ UI_view2d_to_region_no_clip(v2d, bezt1->vec[0][0], bezt1->vec[0][1], &sco[0][0], &sco[0][1]); @@ -624,6 +633,10 @@ static short findnearest_fcurve_vert (bAnimContext *ac, int mval[2], FCurve **fc } } } + + /* un-apply NLA mapping from all the keyframes */ + if (adt) + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } } @@ -722,7 +735,7 @@ static void mouse_graph_keys (bAnimContext *ac, int mval[], short select_mode, s /* set active F-Curve (NOTE: sync the filter flags with findnearest_fcurve_vert) */ if (fcu->flag & FCURVE_SELECTED) { filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); - ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); } } @@ -753,7 +766,7 @@ static void graphkeys_mselect_leftright (bAnimContext *ac, short leftright, shor memset(&bed, 0, sizeof(BeztEditFunc)); if (leftright == GRAPHKEYS_LRSEL_LEFT) { - bed.f1 = -MAXFRAMEF; + bed.f1 = MINAFRAMEF; bed.f2 = (float)(CFRA + 0.1f); } else { @@ -767,12 +780,12 @@ static void graphkeys_mselect_leftright (bAnimContext *ac, short leftright, shor /* select keys on the side where most data occurs */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); - ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } else ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); @@ -827,11 +840,11 @@ static void graphkeys_mselect_column (bAnimContext *ac, int mval[2], short selec ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); /* set frame for validation callback to refer to */ - if (nob) - bed.f1= get_action_frame(nob, selx); + if (adt) + bed.f1= BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP); else bed.f1= selx; @@ -901,22 +914,22 @@ static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *ev mouse_graph_keys(&ac, mval, selectmode, 0); } - /* set notifier that things have changed */ - ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH); + /* set notifier that keyframe selection (and also channel selection in some cases) has changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT|ND_ANIMCHAN_SELECT, NULL); /* for tweak grab to work */ return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH; } -void GRAPHEDIT_OT_keyframes_clickselect (wmOperatorType *ot) +void GRAPH_OT_clickselect (wmOperatorType *ot) { /* identifiers */ ot->name= "Mouse Select Keys"; - ot->idname= "GRAPHEDIT_OT_keyframes_clickselect"; + ot->idname= "GRAPH_OT_clickselect"; /* api callbacks */ ot->invoke= graphkeys_clickselect_invoke; - ot->poll= ED_operator_areaactive; + ot->poll= graphop_visible_keyframes_poll; /* id-props */ // XXX should we make this into separate operators? diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c new file mode 100644 index 00000000000..f00e7845549 --- /dev/null +++ b/source/blender/editors/space_graph/graph_utils.c @@ -0,0 +1,288 @@ +/** + * $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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_object.h" +#include "BKE_global.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "BIF_gl.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_util.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "graph_intern.h" // own include + +/* ************************************************************** */ +/* Active F-Curve */ + +/* Find 'active' F-Curve. It must be editable, since that's the purpose of these buttons (subject to change). + * We return the 'wrapper' since it contains valuable context info (about hierarchy), which will need to be freed + * when the caller is done with it. + */ +bAnimListElem *get_active_fcurve_channel (bAnimContext *ac) +{ + ListBase anim_data = {NULL, NULL}; + int filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE | ANIMFILTER_CURVESONLY); + int items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* We take the first F-Curve only, since some other ones may have had 'active' flag set + * if they were from linked data. + */ + if (items) { + bAnimListElem *ale= (bAnimListElem *)anim_data.first; + + /* remove first item from list, then free the rest of the list and return the stored one */ + BLI_remlink(&anim_data, ale); + BLI_freelistN(&anim_data); + + return ale; + } + + /* no active F-Curve */ + return NULL; +} + +/* ************************************************************** */ +/* Operator Polling Callbacks */ + +/* check if any FModifiers to draw controls for - fcm is 'active' modifier + * used for the polling callbacks + also for drawing + */ +short fcurve_needs_draw_fmodifier_controls (FCurve *fcu, FModifier *fcm) +{ + /* don't draw if there aren't any modifiers at all */ + if (fcu->modifiers.first == NULL) + return 0; + + /* if there's an active modifier - don't draw if it doesn't drastically + * alter the curve... + */ + if (fcm) { + switch (fcm->type) { + /* clearly harmless */ + case FMODIFIER_TYPE_CYCLES: + return 0; + + /* borderline... */ + case FMODIFIER_TYPE_NOISE: + return 0; + } + } + + /* if only one modifier - don't draw if it is muted or disabled */ + if (fcu->modifiers.first == fcu->modifiers.last) { + fcm= fcu->modifiers.first; + if (fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) + return 0; + } + + /* if only active modifier - don't draw if it is muted or disabled */ + if (fcm) { + if (fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) + return 0; + } + + /* if we're still here, this means that there are modifiers with controls to be drawn */ + // FIXME: what happens if all the modifiers were muted/disabled + return 1; +} + +/* ------------------- */ + +/* Check if there are any visible keyframes (for selection tools) */ +int graphop_visible_keyframes_poll (bContext *C) +{ + bAnimContext ac; + bAnimListElem *ale; + ListBase anim_data = {NULL, NULL}; + ScrArea *sa= CTX_wm_area(C); + int filter, items; + short found = 0; + + /* firstly, check if in Graph Editor */ + // TODO: also check for region? + if ((sa == NULL) || (sa->spacetype != SPACE_IPO)) + return 0; + + /* try to init Anim-Context stuff ourselves and check */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return 0; + + /* loop over the visible (selection doesn't matter) F-Curves, and see if they're suitable + * stopping on the first successful match + */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY); + items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + if (items == 0) + return 0; + + for (ale = anim_data.first; ale; ale= ale->next) { + FCurve *fcu= (FCurve *)ale->data; + FModifier *fcm; + + /* visible curves for selection must fulfull the following criteria: + * - it has bezier keyframes + * - F-Curve modifiers do not interfere with the result too much + * (i.e. the modifier-control drawing check returns false) + */ + if (fcu->bezt == NULL) + continue; + fcm= find_active_fmodifier(&fcu->modifiers); + + found= (fcurve_needs_draw_fmodifier_controls(fcu, fcm) == 0); + if (found) break; + } + + /* cleanup and return findings */ + BLI_freelistN(&anim_data); + return found; +} + +/* Check if there are any visible + editable keyframes (for editing tools) */ +int graphop_editable_keyframes_poll (bContext *C) +{ + bAnimContext ac; + bAnimListElem *ale; + ListBase anim_data = {NULL, NULL}; + ScrArea *sa= CTX_wm_area(C); + int filter, items; + short found = 0; + + /* firstly, check if in Graph Editor */ + // TODO: also check for region? + if ((sa == NULL) || (sa->spacetype != SPACE_IPO)) + return 0; + + /* try to init Anim-Context stuff ourselves and check */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return 0; + + /* loop over the editable (selected + editable) F-Curves, and see if they're suitable + * stopping on the first successful match + */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY); + items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + if (items == 0) + return 0; + + for (ale = anim_data.first; ale; ale= ale->next) { + FCurve *fcu= (FCurve *)ale->data; + FModifier *fcm; + + /* editable curves must fulfull the following criteria: + * - it has bezier keyframes + * - it must not be protected from editing (this is already checked for with the foredit flag + * - F-Curve modifiers do not interfere with the result too much + * (i.e. the modifier-control drawing check returns false) + */ + if (fcu->bezt == NULL) + continue; + fcm= find_active_fmodifier(&fcu->modifiers); + + found= (fcurve_needs_draw_fmodifier_controls(fcu, fcm) == 0); + if (found) break; + } + + /* cleanup and return findings */ + BLI_freelistN(&anim_data); + return found; +} + +/* has active F-Curve that's editable */ +int graphop_active_fcurve_poll (bContext *C) +{ + bAnimContext ac; + bAnimListElem *ale; + ScrArea *sa= CTX_wm_area(C); + short has_fcurve= 0; + + /* firstly, check if in Graph Editor */ + // TODO: also check for region? + if ((sa == NULL) || (sa->spacetype != SPACE_IPO)) + return 0; + + /* try to init Anim-Context stuff ourselves and check */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return 0; + + /* try to get the Active F-Curve */ + ale= get_active_fcurve_channel(&ac); + if (ale == NULL) + return 0; + + /* free temp data... */ + has_fcurve= ((ale->data) && (ale->type == ANIMTYPE_FCURVE)); + MEM_freeN(ale); + + /* return success */ + return has_fcurve; +} + +/* ************************************************************** */ diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 1c977724767..a5578e88076 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -72,19 +72,19 @@ ARegion *graph_has_buttons_region(ScrArea *sa) if(ar->regiontype==RGN_TYPE_UI) return ar; - /* add subdiv level; after channel */ + /* add subdiv level; after main window */ for(ar= sa->regionbase.first; ar; ar= ar->next) - if(ar->regiontype==RGN_TYPE_CHANNELS) + if(ar->regiontype==RGN_TYPE_WINDOW) break; /* is error! */ if(ar==NULL) return NULL; - arnew= MEM_callocN(sizeof(ARegion), "buttons for view3d"); + arnew= MEM_callocN(sizeof(ARegion), "buttons for nla"); BLI_insertlinkafter(&sa->regionbase, ar, arnew); arnew->regiontype= RGN_TYPE_UI; - arnew->alignment= RGN_ALIGN_BOTTOM|RGN_SPLIT_PREV; + arnew->alignment= RGN_ALIGN_RIGHT; arnew->flag = RGN_FLAG_HIDDEN; @@ -117,7 +117,7 @@ static SpaceLink *graph_new(const bContext *C) ar->alignment= RGN_ALIGN_BOTTOM; /* channels */ - ar= MEM_callocN(sizeof(ARegion), "main area for graphedit"); + ar= MEM_callocN(sizeof(ARegion), "channels area for graphedit"); BLI_addtail(&sipo->regionbase, ar); ar->regiontype= RGN_TYPE_CHANNELS; @@ -126,11 +126,11 @@ static SpaceLink *graph_new(const bContext *C) ar->v2d.scroll = (V2D_SCROLL_RIGHT|V2D_SCROLL_BOTTOM); /* ui buttons */ - ar= MEM_callocN(sizeof(ARegion), "main area for graphedit"); + ar= MEM_callocN(sizeof(ARegion), "buttons area for graphedit"); BLI_addtail(&sipo->regionbase, ar); ar->regiontype= RGN_TYPE_UI; - ar->alignment= RGN_ALIGN_TOP|RGN_SPLIT_PREV; + ar->alignment= RGN_ALIGN_RIGHT; ar->flag = RGN_FLAG_HIDDEN; /* main area */ @@ -192,7 +192,7 @@ static SpaceLink *graph_duplicate(SpaceLink *sl) SpaceIpo *sipon= MEM_dupallocN(sl); /* clear or remove stuff from old */ - //sipon->ipokey.first= sipon->ipokey.last= NULL; + BLI_duplicatelist(&sipon->ghostCurves, &((SpaceIpo *)sl)->ghostCurves); sipon->ads= MEM_dupallocN(sipon->ads); return (SpaceLink *)sipon; @@ -365,6 +365,9 @@ static void graph_region_listener(ARegion *ar, wmNotifier *wmn) { /* context changes */ switch(wmn->category) { + case NC_ANIMATION: + ED_region_tag_redraw(ar); + break; case NC_SCENE: switch(wmn->data) { case ND_OB_ACTIVE: @@ -395,6 +398,9 @@ static void graph_listener(ScrArea *sa, wmNotifier *wmn) { /* context changes */ switch (wmn->category) { + case NC_ANIMATION: + ED_area_tag_refresh(sa); + break; case NC_SCENE: /*switch (wmn->data) { case ND_OB_ACTIVE: @@ -566,7 +572,7 @@ void ED_spacetype_ipo(void) /* regions: UI buttons */ art= MEM_callocN(sizeof(ARegionType), "spacetype graphedit region"); art->regionid = RGN_TYPE_UI; - art->minsizey= 200; + art->minsizex= 200; art->keymapflag= ED_KEYMAP_UI; art->listener= graph_region_listener; art->init= graph_buttons_area_init; diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c new file mode 100644 index 00000000000..a74037d1ace --- /dev/null +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -0,0 +1,426 @@ +/** + * $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) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_nla.h" +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_object.h" +#include "BKE_global.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "BIF_gl.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_util.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "nla_intern.h" // own include + + +/* ******************* nla editor space & buttons ************** */ + +#define B_NOP 1 +#define B_REDR 2 + +/* -------------- */ + +static void do_nla_region_buttons(bContext *C, void *arg, int event) +{ + //Scene *scene= CTX_data_scene(C); + + switch(event) { + + } + + /* default for now */ + WM_event_add_notifier(C, NC_SCENE|NC_OBJECT|ND_TRANSFORM, NULL); +} + +static int nla_panel_context(const bContext *C, PointerRNA *nlt_ptr, PointerRNA *strip_ptr) +{ + bAnimContext ac; + bAnimListElem *ale= NULL; + ListBase anim_data = {NULL, NULL}; + short found=0; + int filter; + + /* for now, only draw if we could init the anim-context info (necessary for all animation-related tools) + * to work correctly is able to be correctly retrieved. There's no point showing empty panels? + */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return 0; + + /* extract list of active channel(s), of which we should only take the first one (expecting it to be an NLA track) */ + filter= (ANIMFILTER_VISIBLE|ANIMFILTER_ACTIVE); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + for (ale= anim_data.first; ale; ale= ale->next) { + if (ale->type == ANIMTYPE_NLATRACK) { + NlaTrack *nlt= (NlaTrack *)ale->data; + + /* found it, now set the pointers */ + if (nlt_ptr) { + /* NLA-Track pointer */ + RNA_pointer_create(ale->id, &RNA_NlaTrack, nlt, nlt_ptr); + } + if (strip_ptr) { + /* NLA-Strip pointer */ + NlaStrip *strip= BKE_nlastrip_find_active(nlt); + RNA_pointer_create(ale->id, &RNA_NlaStrip, strip, strip_ptr); + } + + found= 1; + break; + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + return found; +} + +#if 0 +static int nla_panel_poll(const bContext *C, PanelType *pt) +{ + return nla_panel_context(C, NULL, NULL); +} +#endif + +static int nla_track_panel_poll(const bContext *C, PanelType *pt) +{ + PointerRNA ptr; + return (nla_panel_context(C, &ptr, NULL) && (ptr.data != NULL)); +} + +static int nla_strip_panel_poll(const bContext *C, PanelType *pt) +{ + PointerRNA ptr; + return (nla_panel_context(C, NULL, &ptr) && (ptr.data != NULL)); +} + +static int nla_strip_actclip_panel_poll(const bContext *C, PanelType *pt) +{ + PointerRNA ptr; + NlaStrip *strip; + + if (!nla_panel_context(C, NULL, &ptr)) + return 0; + if (ptr.data == NULL) + return 0; + + strip= ptr.data; + return (strip->type == NLASTRIP_TYPE_CLIP); +} + +/* -------------- */ + +/* active NLA-Track */ +static void nla_panel_track (const bContext *C, Panel *pa) +{ + PointerRNA nlt_ptr; + uiLayout *layout= pa->layout; + uiLayout *row; + uiBlock *block; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, &nlt_ptr, NULL)) + return; + + block= uiLayoutGetBlock(layout); + uiBlockSetHandleFunc(block, do_nla_region_buttons, NULL); + + /* Info - Active NLA-Context:Track ---------------------- */ + row= uiLayoutRow(layout, 1); + uiItemR(row, NULL, ICON_NLA, &nlt_ptr, "name", 0, 0, 0); +} + +/* generic settings for active NLA-Strip */ +static void nla_panel_properties(const bContext *C, Panel *pa) +{ + PointerRNA strip_ptr; + uiLayout *layout= pa->layout; + uiLayout *column, *row, *subcol; + uiBlock *block; + + if (!nla_panel_context(C, NULL, &strip_ptr)) + return; + + block= uiLayoutGetBlock(layout); + uiBlockSetHandleFunc(block, do_nla_region_buttons, NULL); + + /* Strip Properties ------------------------------------- */ + /* strip type */ + row= uiLayoutColumn(layout, 1); + uiItemR(row, NULL, ICON_NLA, &strip_ptr, "name", 0, 0, 0); // XXX icon? + uiItemR(row, NULL, 0, &strip_ptr, "type", 0, 0, 0); + + /* strip extents */ + column= uiLayoutColumn(layout, 1); + uiItemL(column, "Strip Extents:", 0); + uiItemR(column, NULL, 0, &strip_ptr, "start_frame", 0, 0, 0); + uiItemR(column, NULL, 0, &strip_ptr, "end_frame", 0, 0, 0); + + /* extrapolation */ + row= uiLayoutRow(layout, 1); + uiItemR(row, NULL, 0, &strip_ptr, "extrapolation", 0, 0, 0); + + /* blending */ + row= uiLayoutRow(layout, 1); + uiItemR(row, NULL, 0, &strip_ptr, "blending", 0, 0, 0); + + /* blend in/out + autoblending + * - blend in/out can only be set when autoblending is off + */ + column= uiLayoutColumn(layout, 1); + uiLayoutSetActive(column, RNA_boolean_get(&strip_ptr, "animated_influence")==0); + uiItemR(column, NULL, 0, &strip_ptr, "auto_blending", 0, 0, 0); // XXX as toggle? + + subcol= uiLayoutColumn(column, 1); + uiLayoutSetActive(subcol, RNA_boolean_get(&strip_ptr, "auto_blending")==0); + uiItemR(subcol, NULL, 0, &strip_ptr, "blend_in", 0, 0, 0); + uiItemR(subcol, NULL, 0, &strip_ptr, "blend_out", 0, 0, 0); + + /* settings */ + column= uiLayoutColumn(layout, 1); + uiLayoutSetActive(column, !(RNA_boolean_get(&strip_ptr, "animated_influence") || RNA_boolean_get(&strip_ptr, "animated_time"))); + uiItemL(column, "Playback Settings:", 0); + uiItemR(column, NULL, 0, &strip_ptr, "muted", 0, 0, 0); + uiItemR(column, NULL, 0, &strip_ptr, "reversed", 0, 0, 0); +} + + +/* action-clip only settings for active NLA-Strip */ +static void nla_panel_actclip(const bContext *C, Panel *pa) +{ + PointerRNA strip_ptr; + uiLayout *layout= pa->layout; + uiLayout *column, *row; + uiBlock *block; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, NULL, &strip_ptr)) + return; + + block= uiLayoutGetBlock(layout); + uiBlockSetHandleFunc(block, do_nla_region_buttons, NULL); + + /* Strip Properties ------------------------------------- */ + /* action pointer */ + row= uiLayoutRow(layout, 1); + uiItemR(row, NULL, ICON_ACTION, &strip_ptr, "action", 0, 0, 0); + + /* action extents */ + // XXX custom names were used here (to avoid the prefixes)... probably not necessary in future? + column= uiLayoutColumn(layout, 1); + uiItemL(column, "Action Extents:", 0); + uiItemR(column, "Start Frame", 0, &strip_ptr, "action_start_frame", 0, 0, 0); + uiItemR(column, "End Frame", 0, &strip_ptr, "action_end_frame", 0, 0, 0); + + /* action usage */ + column= uiLayoutColumn(layout, 1); + uiLayoutSetActive(column, RNA_boolean_get(&strip_ptr, "animated_time")==0); + uiItemL(column, "Playback Settings:", 0); + uiItemR(column, NULL, 0, &strip_ptr, "scale", 0, 0, 0); + uiItemR(column, NULL, 0, &strip_ptr, "repeat", 0, 0, 0); +} + +/* evaluation settings for active NLA-Strip */ +static void nla_panel_evaluation(const bContext *C, Panel *pa) +{ + PointerRNA strip_ptr; + uiLayout *layout= pa->layout; + uiLayout *column, *subcolumn; + uiBlock *block; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, NULL, &strip_ptr)) + return; + + block= uiLayoutGetBlock(layout); + uiBlockSetHandleFunc(block, do_nla_region_buttons, NULL); + + column= uiLayoutColumn(layout, 1); + uiItemR(column, NULL, 0, &strip_ptr, "animated_influence", 0, 0, 0); + + subcolumn= uiLayoutColumn(column, 1); + uiLayoutSetEnabled(subcolumn, RNA_boolean_get(&strip_ptr, "animated_influence")); + uiItemR(subcolumn, NULL, 0, &strip_ptr, "influence", 0, 0, 0); + + + column= uiLayoutColumn(layout, 1); + uiItemR(column, NULL, 0, &strip_ptr, "animated_time", 0, 0, 0); + + subcolumn= uiLayoutColumn(column, 1); + uiLayoutSetEnabled(subcolumn, RNA_boolean_get(&strip_ptr, "animated_time")); + uiItemR(subcolumn, NULL, 0, &strip_ptr, "strip_time", 0, 0, 0); +} + +/* F-Modifiers for active NLA-Strip */ +static void nla_panel_modifiers(const bContext *C, Panel *pa) +{ + PointerRNA strip_ptr; + NlaStrip *strip; + FModifier *fcm; + uiLayout *col, *row; + uiBlock *block; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, NULL, &strip_ptr)) + return; + strip= strip_ptr.data; + + block= uiLayoutGetBlock(pa->layout); + uiBlockSetHandleFunc(block, do_nla_region_buttons, NULL); + + /* 'add modifier' button at top of panel */ + { + row= uiLayoutRow(pa->layout, 0); + block= uiLayoutGetBlock(row); + + // XXX for now, this will be a operator button which calls a temporary 'add modifier' operator + // FIXME: we need to set the only-active property so that this will only add modifiers for the active strip (not all selected) + uiDefButO(block, BUT, "NLA_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, "Add Modifier", 10, 0, 150, 20, "Adds a new F-Modifier for the active NLA Strip"); + } + + /* draw each modifier */ + for (fcm= strip->modifiers.first; fcm; fcm= fcm->next) { + col= uiLayoutColumn(pa->layout, 1); + + ANIM_uiTemplate_fmodifier_draw(col, strip_ptr.id.data, &strip->modifiers, fcm); + } +} + +/* ******************* general ******************************** */ + + +void nla_buttons_register(ARegionType *art) +{ + PanelType *pt; + + pt= MEM_callocN(sizeof(PanelType), "spacetype nla panel track"); + strcpy(pt->idname, "NLA_PT_track"); + strcpy(pt->label, "Active Track"); + pt->draw= nla_panel_track; + pt->poll= nla_track_panel_poll; + BLI_addtail(&art->paneltypes, pt); + + pt= MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); + strcpy(pt->idname, "NLA_PT_properties"); + strcpy(pt->label, "Active Strip"); + pt->draw= nla_panel_properties; + pt->poll= nla_strip_panel_poll; + BLI_addtail(&art->paneltypes, pt); + + pt= MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); + strcpy(pt->idname, "NLA_PT_actionclip"); + strcpy(pt->label, "Action Clip"); + pt->draw= nla_panel_actclip; + pt->poll= nla_strip_actclip_panel_poll; + BLI_addtail(&art->paneltypes, pt); + + pt= MEM_callocN(sizeof(PanelType), "spacetype nla panel evaluation"); + strcpy(pt->idname, "NLA_PT_evaluation"); + strcpy(pt->label, "Evaluation"); + pt->draw= nla_panel_evaluation; + pt->poll= nla_strip_panel_poll; + BLI_addtail(&art->paneltypes, pt); + + pt= MEM_callocN(sizeof(PanelType), "spacetype nla panel modifiers"); + strcpy(pt->idname, "NLA_PT_modifiers"); + strcpy(pt->label, "Modifiers"); + pt->draw= nla_panel_modifiers; + pt->poll= nla_strip_panel_poll; + BLI_addtail(&art->paneltypes, pt); +} + +static int nla_properties(bContext *C, wmOperator *op) +{ + ScrArea *sa= CTX_wm_area(C); + ARegion *ar= nla_has_buttons_region(sa); + + if(ar) { + ar->flag ^= RGN_FLAG_HIDDEN; + ar->v2d.flag &= ~V2D_IS_INITIALISED; /* XXX should become hide/unhide api? */ + + ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa); + ED_area_tag_redraw(sa); + } + return OPERATOR_FINISHED; +} + +void NLA_OT_properties(wmOperatorType *ot) +{ + ot->name= "Properties"; + ot->idname= "NLA_OT_properties"; + + ot->exec= nla_properties; + ot->poll= ED_operator_nla_active; + + /* flags */ + ot->flag= 0; +} diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c new file mode 100644 index 00000000000..1ab348eb28e --- /dev/null +++ b/source/blender/editors/space_nla/nla_channels.c @@ -0,0 +1,516 @@ +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * + * Contributor(s): Joshua Leung (major recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "DNA_listBase.h" +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_material_types.h" +#include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_world_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_nla.h" +#include "BKE_context.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_edit.h" +#include "ED_markers.h" +#include "ED_space_api.h" +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "UI_interface_icons.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "nla_intern.h" // own include + +/* *********************************************** */ +/* Operators for NLA channels-list which need to be different from the standard Animation Editor ones */ + +/* ******************** Mouse-Click Operator *********************** */ +/* Depending on the channel that was clicked on, the mouse click will activate whichever + * part of the channel is relevant. + * + * NOTE: eventually, this should probably be phased out when many of these things are replaced with buttons + */ + +static int mouse_nla_channels (bAnimContext *ac, float x, int channel_index, short selectmode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + int notifierFlags = 0; + + /* get the channel that was clicked on */ + /* filter channels */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS); + filter= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* get channel from index */ + ale= BLI_findlink(&anim_data, channel_index); + if (ale == NULL) { + /* channel not found */ + printf("Error: animation channel (index = %d) not found in mouse_anim_channels() \n", channel_index); + + BLI_freelistN(&anim_data); + return 0; + } + + /* action to take depends on what channel we've got */ + switch (ale->type) { + case ANIMTYPE_SCENE: + { + Scene *sce= (Scene *)ale->data; + + if (x < 16) { + /* toggle expand */ + sce->flag ^= SCE_DS_COLLAPSED; + + notifierFlags |= ND_ANIMCHAN_EDIT; + } + else { + /* set selection status */ + if (selectmode == SELECT_INVERT) { + /* swap select */ + sce->flag ^= SCE_DS_SELECTED; + } + else { + sce->flag |= SCE_DS_SELECTED; + } + + notifierFlags |= ND_ANIMCHAN_SELECT; + } + } + break; + case ANIMTYPE_OBJECT: + { + bDopeSheet *ads= (bDopeSheet *)ac->data; + Scene *sce= (Scene *)ads->source; + Base *base= (Base *)ale->data; + Object *ob= base->object; + + if (x < 16) { + /* toggle expand */ + ob->nlaflag ^= OB_ADS_COLLAPSED; // XXX + notifierFlags |= ND_ANIMCHAN_EDIT; + } + else if (nlaedit_is_tweakmode_on(ac) == 0) { + /* set selection status */ + if (selectmode == SELECT_INVERT) { + /* swap select */ + base->flag ^= SELECT; + ob->flag= base->flag; + } + else { + Base *b; + + /* deleselect all */ + for (b= sce->base.first; b; b= b->next) { + b->flag &= ~SELECT; + b->object->flag= b->flag; + } + + /* select object now */ + base->flag |= SELECT; + ob->flag |= SELECT; + } + + /* xxx should be ED_base_object_activate(), but we need context pointer for that... */ + //set_active_base(base); + + /* notifiers - channel was selected */ + notifierFlags |= ND_ANIMCHAN_SELECT; + } + } + break; + case ANIMTYPE_FILLMATD: + { + Object *ob= (Object *)ale->data; + ob->nlaflag ^= OB_ADS_SHOWMATS; // XXX + notifierFlags |= ND_ANIMCHAN_EDIT; + } + break; + + case ANIMTYPE_DSMAT: + { + Material *ma= (Material *)ale->data; + ma->flag ^= MA_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + break; + case ANIMTYPE_DSLAM: + { + Lamp *la= (Lamp *)ale->data; + la->flag ^= LA_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + break; + case ANIMTYPE_DSCAM: + { + Camera *ca= (Camera *)ale->data; + ca->flag ^= CAM_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + break; + case ANIMTYPE_DSCUR: + { + Curve *cu= (Curve *)ale->data; + cu->flag ^= CU_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + break; + case ANIMTYPE_DSSKEY: + { + Key *key= (Key *)ale->data; + key->flag ^= KEYBLOCK_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + break; + case ANIMTYPE_DSWOR: + { + World *wo= (World *)ale->data; + wo->flag ^= WO_DS_EXPAND; + notifierFlags |= ND_ANIMCHAN_EDIT; + } + break; + + case ANIMTYPE_NLATRACK: + { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + short offset; + + /* offset for start of channel (on LHS of channel-list) */ + if (ale->id) { + /* special exception for materials */ + if (GS(ale->id->name) == ID_MA) + offset= 21 + NLACHANNEL_BUTTON_WIDTH; + else + offset= 14; + } + else + offset= 0; + + if (x >= (NLACHANNEL_NAMEWIDTH-NLACHANNEL_BUTTON_WIDTH)) { + /* toggle protection (only if there's a toggle there) */ + nlt->flag ^= NLATRACK_PROTECTED; + + /* notifier flags - channel was edited */ + notifierFlags |= ND_ANIMCHAN_EDIT; + } + else if (x >= (NLACHANNEL_NAMEWIDTH-2*NLACHANNEL_BUTTON_WIDTH)) { + /* toggle mute */ + nlt->flag ^= NLATRACK_MUTED; + + /* notifier flags - channel was edited */ + notifierFlags |= ND_ANIMCHAN_EDIT; + } + else if (x <= ((NLACHANNEL_BUTTON_WIDTH*2)+offset)) { + /* toggle 'solo' */ + BKE_nlatrack_solo_toggle(adt, nlt); + + /* notifier flags - channel was edited */ + notifierFlags |= ND_ANIMCHAN_EDIT; + } + else if (nlaedit_is_tweakmode_on(ac) == 0) { + /* set selection */ + if (selectmode == SELECT_INVERT) { + /* inverse selection status of this F-Curve only */ + nlt->flag ^= NLATRACK_SELECTED; + } + else { + /* select F-Curve by itself */ + ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + nlt->flag |= NLATRACK_SELECTED; + } + + /* if NLA-Track is selected now, make NLA-Track the 'active' one in the visible list */ + if (nlt->flag & NLATRACK_SELECTED) + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK); + + /* notifier flags - channel was selected */ + notifierFlags |= ND_ANIMCHAN_SELECT; + } + } + break; + case ANIMTYPE_NLAACTION: + { + AnimData *adt= BKE_animdata_from_id(ale->owner); /* this won't crash, right? */ + + if (x >= (NLACHANNEL_NAMEWIDTH-NLACHANNEL_BUTTON_WIDTH)) { + if (nlaedit_is_tweakmode_on(ac) == 0) { + /* 'push-down' action - only usable when not in TweakMode */ + // TODO: make this use the operator instead of calling the function directly + // however, calling the operator requires that we supply the args, and that works with proper buttons only + BKE_nla_action_pushdown(adt); + } + else { + /* when in tweakmode, this button becomes the toggle for mapped editing */ + adt->flag ^= ADT_NLA_EDIT_NOMAP; + } + + /* changes to NLA-Action occurred */ + notifierFlags |= ND_NLA_ACTCHANGE; + } + } + break; + + default: + printf("Error: Invalid channel type in mouse_nla_channels() \n"); + } + + /* free channels */ + BLI_freelistN(&anim_data); + + /* return the notifier-flags set */ + return notifierFlags; +} + +/* ------------------- */ + +/* handle clicking */ +static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + bAnimContext ac; + Scene *scene; + ARegion *ar; + View2D *v2d; + int mval[2], channel_index; + int notifierFlags = 0; + short selectmode; + float x, y; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get useful pointers from animation context data */ + scene= ac.scene; + ar= ac.ar; + v2d= &ar->v2d; + + /* get mouse coordinates (in region coordinates) */ + mval[0]= (event->x - ar->winrct.xmin); + mval[1]= (event->y - ar->winrct.ymin); + + /* select mode is either replace (deselect all, then add) or add/extend */ + if (RNA_boolean_get(op->ptr, "extend")) + selectmode= SELECT_INVERT; + else + selectmode= SELECT_REPLACE; + + /* figure out which channel user clicked in + * Note: although channels technically start at y= NLACHANNEL_FIRST, we need to adjust by half a channel's height + * so that the tops of channels get caught ok. Since NLACHANNEL_FIRST is really NLACHANNEL_HEIGHT, we simply use + * NLACHANNEL_HEIGHT_HALF. + */ + UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); + UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP, 0, (float)NLACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index); + + /* handle mouse-click in the relevant channel then */ + notifierFlags= mouse_nla_channels(&ac, x, channel_index, selectmode); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|notifierFlags, NULL); + + return OPERATOR_FINISHED; +} + +void NLA_OT_channels_click (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Mouse Click on Channels"; + ot->idname= "NLA_OT_channels_click"; + + /* api callbacks */ + ot->invoke= nlachannels_mouseclick_invoke; + ot->poll= ED_operator_nla_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* id-props */ + RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY +} + +/* *********************************************** */ +/* Special Operators */ + +/* ******************** Add Tracks Operator ***************************** */ +/* Add NLA Tracks to the same AnimData block as a selected track, or above the selected tracks */ + +static int nlaedit_add_tracks_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + AnimData *lastAdt = NULL; + short above_sel= RNA_boolean_get(op->ptr, "above_selected"); + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the AnimData blocks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_SEL); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* add tracks... */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + + /* check if just adding a new track above this one, + * or whether we're adding a new one to the top of the stack that this one belongs to + */ + if (above_sel) { + /* just add a new one above this one */ + add_nlatrack(adt, nlt); + } + else if ((lastAdt == NULL) || (adt != lastAdt)) { + /* add one track to the top of the owning AnimData's stack, then don't add anymore to this stack */ + add_nlatrack(adt, NULL); + lastAdt= adt; + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_add_tracks (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Track(s)"; + ot->idname= "NLA_OT_add_tracks"; + ot->description= "Add NLA-Tracks above/after the selected tracks."; + + /* api callbacks */ + ot->exec= nlaedit_add_tracks_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "above_selected", 0, "Above Selected", "Add a new NLA Track above every existing selected one."); +} + +/* ******************** Delete Tracks Operator ***************************** */ +/* Delete selected NLA Tracks */ + +static int nlaedit_delete_tracks_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the AnimData blocks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_SEL); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* delete tracks */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + + /* call delete on this track - deletes all strips too */ + free_nlatrack(&adt->nla_tracks, nlt); + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_delete_tracks (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Delete Tracks"; + ot->idname= "NLA_OT_delete_tracks"; + ot->description= "Delete selected NLA-Tracks and the strips they contain."; + + /* api callbacks */ + ot->exec= nlaedit_delete_tracks_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* *********************************************** */ diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c new file mode 100644 index 00000000000..8d56670a149 --- /dev/null +++ b/source/blender/editors/space_nla/nla_draw.c @@ -0,0 +1,1028 @@ +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * + * Contributor(s): Joshua Leung (major recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include +#include + +#include "DNA_listBase.h" +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_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_windowmanager_types.h" +#include "DNA_world_types.h" +#include "DNA_vec_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_fcurve.h" +#include "BKE_nla.h" +#include "BKE_context.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_draw.h" +#include "ED_space_api.h" +#include "ED_screen.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "UI_interface_icons.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "ED_markers.h" + +#include "nla_intern.h" // own include + +/* XXX */ +extern void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad); +extern void gl_round_box_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown); + +/* *********************************************** */ +/* Strips */ + +/* Action-Line ---------------------- */ + +/* get colors for drawing Action-Line + * NOTE: color returned includes fine-tuned alpha! + */ +static void nla_action_get_color (AnimData *adt, bAction *act, float color[4]) +{ + // TODO: if tweaking some action, use the same color as for the tweaked track (quick hack done for now) + if (adt && (adt->flag & ADT_NLA_EDIT_ON)) { + // greenish color (same as tweaking strip) - hardcoded for now + color[0]= 0.30f; + color[1]= 0.95f; + color[2]= 0.10f; + color[3]= 0.30f; + } + else { + if (act) { + // reddish color - hardcoded for now + color[0]= 0.8f; + color[1]= 0.2f; + color[2]= 0.0f; + color[3]= 0.4f; + } + else { + // greyish-red color - hardcoded for now + color[0]= 0.6f; + color[1]= 0.5f; + color[2]= 0.5f; + color[3]= 0.3f; + } + } +} + +/* draw the keyframes in the specified Action */ +static void nla_action_draw_keyframes (AnimData *adt, bAction *act, View2D *v2d, float y, float ymin, float ymax) +{ + ListBase keys = {NULL, NULL}; + ActKeyColumn *ak; + float xscale, f1, f2; + float color[4]; + + /* get a list of the keyframes with NLA-scaling applied */ + action_to_keylist(adt, act, &keys, NULL); + + if ELEM(NULL, act, keys.first) + return; + + /* draw a darkened region behind the strips + * - get and reset the background color, this time without the alpha to stand out better + */ + nla_action_get_color(adt, act, color); + glColor3fv(color); + /* - draw a rect from the first to the last frame (no extra overlaps for now) + * that is slightly stumpier than the track background (hardcoded 2-units here) + */ + f1= ((ActKeyColumn *)keys.first)->cfra; + f2= ((ActKeyColumn *)keys.last)->cfra; + + glRectf(f1, ymin+2, f2, ymax-2); + + + /* get View2D scaling factor */ + UI_view2d_getscale(v2d, &xscale, NULL); + + /* for now, color is hardcoded to be black */ + glColor3f(0.0f, 0.0f, 0.0f); + + /* just draw each keyframe as a simple dot (regardless of the selection status) + * - size is 3.0f which is smaller than the editable keyframes, so that there is a distinction + */ + for (ak= keys.first; ak; ak= ak->next) + draw_keyframe_shape(ak->cfra, y, xscale, 3.0f, 0, KEYFRAME_SHAPE_FRAME); + + /* free icons */ + BLI_freelistN(&keys); +} + +/* Strips (Proper) ---------------------- */ + +/* get colors for drawing NLA-Strips */ +static void nla_strip_get_color_inside (AnimData *adt, NlaStrip *strip, float color[3]) +{ + if (strip->type == NLASTRIP_TYPE_TRANSITION) { + /* Transition Clip */ + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* selected - use a bright blue color */ + // FIXME: hardcoded temp-hack colors + color[0]= 0.18f; + color[1]= 0.46f; + color[2]= 0.86f; + } + else { + /* normal, unselected strip - use (hardly noticable) blue tinge */ + // FIXME: hardcoded temp-hack colors + color[0]= 0.11f; + color[1]= 0.15f; + color[2]= 0.19f; + } + } + else if (strip->type == NLASTRIP_TYPE_META) { + /* Meta Clip */ + // TODO: should temporary metas get different colours too? + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* selected - use a bold purple color */ + // FIXME: hardcoded temp-hack colors + color[0]= 0.41f; + color[1]= 0.13f; + color[2]= 0.59f; + } + else { + /* normal, unselected strip - use (hardly noticable) dark purple tinge */ + // FIXME: hardcoded temp-hack colors + color[0]= 0.20f; + color[1]= 0.15f; + color[2]= 0.26f; + } + } + else { + /* Action Clip (default/normal type of strip) */ + if ((strip->flag & NLASTRIP_FLAG_ACTIVE) && (adt && (adt->flag & ADT_NLA_EDIT_ON))) { + /* active strip should be drawn green when it is acting as the tweaking strip. + * however, this case should be skipped for when not in EditMode... + */ + // FIXME: hardcoded temp-hack colors + color[0]= 0.3f; + color[1]= 0.95f; + color[2]= 0.1f; + } + else if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) { + /* alert user that this strip is also used by the tweaking track (this is set when going into + * 'editmode' for that strip), since the edits made here may not be what the user anticipated + */ + // FIXME: hardcoded temp-hack colors + color[0]= 0.85f; + color[1]= 0.0f; + color[2]= 0.0f; + } + else if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* selected strip - use theme color for selected */ + UI_GetThemeColor3fv(TH_STRIP_SELECT, color); + } + else { + /* normal, unselected strip - use standard strip theme color */ + UI_GetThemeColor3fv(TH_STRIP, color); + } + } +} + +/* helper call for drawing influence/time control curves for a given NLA-strip */ +static void nla_draw_strip_curves (NlaStrip *strip, View2D *v2d, float yminc, float ymaxc) +{ + const float yheight = ymaxc - yminc; + + /* drawing color is simply a light-grey */ + // TODO: is this color suitable? + // XXX nasty hacked color for now... which looks quite bad too... + glColor3f(0.7f, 0.7f, 0.7f); + + /* draw with AA'd line, 2 units thick (it's either 1 or 2 px) */ + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glLineWidth(2.0f); + + /* influence -------------------------- */ + if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { + FCurve *fcu= list_find_fcurve(&strip->fcurves, "influence", 0); + float cfra; + + /* plot the curve (over the strip's main region) */ + glBegin(GL_LINE_STRIP); + /* sample at 1 frame intervals, and draw + * - min y-val is yminc, max is y-maxc, so clamp in those regions + */ + for (cfra= strip->start; cfra <= strip->end; cfra += 1.0f) { + float y= evaluate_fcurve(fcu, cfra); // assume this to be in 0-1 range + glVertex2f(cfra, ((y*yheight)+yminc)); + } + glEnd(); // GL_LINE_STRIP + } + else { + /* use blend in/out values only if both aren't zero */ + if ((IS_EQ(strip->blendin, 0.0f) && IS_EQ(strip->blendout, 0.0f))==0) { + glBegin(GL_LINE_STRIP); + /* start of strip - if no blendin, start straight at 1, otherwise from 0 to 1 over blendin frames */ + if (IS_EQ(strip->blendin, 0.0f) == 0) { + glVertex2f(strip->start, yminc); + glVertex2f(strip->start + strip->blendin, ymaxc); + } + else + glVertex2f(strip->start, ymaxc); + + /* end of strip */ + if (IS_EQ(strip->blendout, 0.0f) == 0) { + glVertex2f(strip->end - strip->blendout, ymaxc); + glVertex2f(strip->end, yminc); + } + else + glVertex2f(strip->end, ymaxc); + glEnd(); // GL_LINE_STRIP + } + } + + /* time -------------------------- */ + // XXX do we want to draw this curve? in a different colour too? + + /* turn off AA'd lines */ + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + glLineWidth(1.0f); +} + +/* main call for drawing a single NLA-strip */ +static void nla_draw_strip (AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc) +{ + float color[3]; + + /* get color of strip */ + nla_strip_get_color_inside(adt, strip, color); + + /* draw extrapolation info first (as backdrop) */ + if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) { + /* enable transparency... */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + switch (strip->extendmode) { + /* since this does both sides, only do the 'before' side, and leave the rest to the next case */ + case NLASTRIP_EXTEND_HOLD: + /* only need to draw here if there's no strip before since + * it only applies in such a situation + */ + if (strip->prev == NULL) { + /* set the drawing color to the color of the strip, but with very faint alpha */ + glColor4f(color[0], color[1], color[2], 0.15f); + + /* draw the rect to the edge of the screen */ + glBegin(GL_QUADS); + glVertex2f(v2d->cur.xmin, yminc); + glVertex2f(v2d->cur.xmin, ymaxc); + glVertex2f(strip->start, ymaxc); + glVertex2f(strip->start, yminc); + glEnd(); + } + /* no break needed... */ + + /* this only draws after the strip */ + case NLASTRIP_EXTEND_HOLD_FORWARD: + /* only need to try and draw if the next strip doesn't occur immediately after */ + if ((strip->next == NULL) || (IS_EQ(strip->next->start, strip->end)==0)) { + /* set the drawing color to the color of the strip, but this time less faint */ + glColor4f(color[0], color[1], color[2], 0.3f); + + /* draw the rect to the next strip or the edge of the screen */ + glBegin(GL_QUADS); + glVertex2f(strip->end, yminc); + glVertex2f(strip->end, ymaxc); + + if (strip->next) { + glVertex2f(strip->next->start, ymaxc); + glVertex2f(strip->next->start, yminc); + } + else { + glVertex2f(v2d->cur.xmax, ymaxc); + glVertex2f(v2d->cur.xmax, yminc); + } + glEnd(); + } + break; + } + + glDisable(GL_BLEND); + } + + /* draw 'inside' of strip itself */ + glColor3fv(color); + uiSetRoundBox(15); /* all corners rounded */ + gl_round_box_shade(GL_POLYGON, strip->start, yminc, strip->end, ymaxc, 0.0, 0.5, 0.1); + + + /* draw strip's control 'curves' */ + nla_draw_strip_curves(strip, v2d, yminc, ymaxc); + + /* draw strip outline + * - color used here is to indicate active vs non-active + */ + if (strip->flag & NLASTRIP_FLAG_ACTIVE) { + /* strip should appear 'sunken', so draw a light border around it */ + glColor3f(0.9f, 1.0f, 0.9f); // FIXME: hardcoded temp-hack colors + } + else { + /* strip should appear to stand out, so draw a dark border around it */ + glColor3f(0.0f, 0.0f, 0.0f); + } + + /* - line style: dotted for muted */ + if (strip->flag & NLASTRIP_FLAG_MUTED) + setlinestyle(4); + + /* draw outline */ + gl_round_box_shade(GL_LINE_LOOP, strip->start, yminc, strip->end, ymaxc, 0.0, 0.0, 0.1); + + /* if action-clip strip, draw lines delimiting repeats too (in the same color as outline) */ + if ((strip->type == NLASTRIP_TYPE_CLIP) && IS_EQ(strip->repeat, 1.0f)==0) { + float repeatLen = (strip->actend - strip->actstart) * strip->scale; + int i; + + /* only draw lines for whole-numbered repeats, starting from the first full-repeat + * up to the last full repeat (but not if it lies on the end of the strip) + */ + for (i = 1; i < strip->repeat; i++) { + float repeatPos = strip->start + (repeatLen * i); + + /* don't draw if line would end up on or after the end of the strip */ + if (repeatPos < strip->end) + fdrawline(repeatPos, yminc, repeatPos, ymaxc); + } + } + /* or if meta-strip, draw lines delimiting extents of sub-strips (in same color as outline, if more than 1 exists) */ + else if ((strip->type == NLASTRIP_TYPE_META) && (strip->strips.first != strip->strips.last)) { + NlaStrip *cs; + float y= (ymaxc-yminc)/2.0f + yminc; + + /* only draw first-level of child-strips, but don't draw any lines on the endpoints */ + for (cs= strip->strips.first; cs; cs= cs->next) { + /* draw start-line if not same as end of previous (and only if not the first strip) + * - on upper half of strip + */ + if ((cs->prev) && IS_EQ(cs->prev->end, cs->start)==0) + fdrawline(cs->start, y, cs->start, ymaxc); + + /* draw end-line if not the last strip + * - on lower half of strip + */ + if (cs->next) + fdrawline(cs->end, yminc, cs->end, y); + } + } + + /* reset linestyle */ + setlinestyle(0); +} + +/* add the relevant text to the cache of text-strings to draw in pixelspace */ +static void nla_draw_strip_text (NlaTrack *nlt, NlaStrip *strip, int index, View2D *v2d, float yminc, float ymaxc) +{ + char str[256], dir[3]; + rctf rect; + + /* 'dir' - direction that strip is played in */ + if (strip->flag & NLASTRIP_FLAG_REVERSE) + sprintf(dir, "<-"); + else + sprintf(dir, "->"); + + /* just print the name and the range */ + if (strip->flag & NLASTRIP_FLAG_TEMP_META) + sprintf(str, "Temp-Meta | %.2f %s %.2f", strip->start, dir, strip->end); + else + sprintf(str, "%s | %.2f %s %.2f", strip->name, strip->start, dir, strip->end); + + /* set text colour - if colours (see above) are light, draw black text, otherwise draw white */ + if (strip->flag & (NLASTRIP_FLAG_ACTIVE|NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_TWEAKUSER)) + glColor3f(0.0f, 0.0f, 0.0f); + else + glColor3f(1.0f, 1.0f, 1.0f); + + /* set bounding-box for text + * - padding of 2 'units' on either side + */ + // TODO: make this centered? + rect.xmin= strip->start + 0.5f; + rect.ymin= yminc; + rect.xmax= strip->end - 0.5f; + rect.ymax= ymaxc; + + /* add this string to the cache of texts to draw*/ + UI_view2d_text_cache_rectf(v2d, &rect, str); +} + +/* ---------------------- */ + +void draw_nla_main_data (bAnimContext *ac, SpaceNla *snla, ARegion *ar) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + View2D *v2d= &ar->v2d; + float y= 0.0f; + int items, height; + + /* build list of channels to draw */ + filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS); + items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* Update max-extent of channels here (taking into account scrollers): + * - this is done to allow the channel list to be scrollable, but must be done here + * to avoid regenerating the list again and/or also because channels list is drawn first + * - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for + * start of list offset, and the second is as a correction for the scrollers. + */ + height= ((items*NLACHANNEL_STEP) + (NLACHANNEL_HEIGHT*2)); + /* don't use totrect set, as the width stays the same + * (NOTE: this is ok here, the configuration is pretty straightforward) + */ + v2d->tot.ymin= (float)(-height); + /* need to do a view-sync here, so that the strips area doesn't jump around */ + UI_view2d_sync(NULL, ac->sa, v2d, V2D_VIEWSYNC_AREA_VERTICAL); + + /* loop through channels, and set up drawing depending on their type */ + y= (float)(-NLACHANNEL_HEIGHT); + + for (ale= anim_data.first; ale; ale= ale->next) { + const float yminc= (float)(y - NLACHANNEL_HEIGHT_HALF); + const float ymaxc= (float)(y + NLACHANNEL_HEIGHT_HALF); + + /* check if visible */ + if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || + IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) + { + /* data to draw depends on the type of channel */ + switch (ale->type) { + case ANIMTYPE_NLATRACK: + { + AnimData *adt= BKE_animdata_from_id(ale->id); + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + int index; + + /* draw each strip in the track (if visible) */ + for (strip=nlt->strips.first, index=1; strip; strip=strip->next, index++) { + if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) { + /* draw the visualisation of the strip */ + nla_draw_strip(adt, nlt, strip, v2d, yminc, ymaxc); + + /* add the text for this strip to the cache */ + nla_draw_strip_text(nlt, strip, index, v2d, yminc, ymaxc); + } + } + } + break; + + case ANIMTYPE_NLAACTION: + { + AnimData *adt= BKE_animdata_from_id(ale->id); + float color[4]; + + /* just draw a semi-shaded rect spanning the width of the viewable area if there's data, + * and a second darker rect within which we draw keyframe indicator dots if there's data + */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + /* get colors for drawing */ + nla_action_get_color(adt, ale->data, color); + glColor4fv(color); + + /* draw slightly shifted up for greater separation from standard channels, + * but also slightly shorter for some more contrast when viewing the strips + */ + glRectf(v2d->cur.xmin, yminc+NLACHANNEL_SKIP, v2d->cur.xmax, ymaxc-NLACHANNEL_SKIP); + + /* draw keyframes in the action */ + nla_action_draw_keyframes(adt, ale->data, v2d, y, yminc+NLACHANNEL_SKIP, ymaxc-NLACHANNEL_SKIP); + + /* draw 'embossed' lines above and below the strip for effect */ + /* white base-lines */ + glLineWidth(2.0f); + glColor4f(1.0f, 1.0f, 1.0f, 0.3); + fdrawline(v2d->cur.xmin, yminc+NLACHANNEL_SKIP, v2d->cur.xmax, yminc+NLACHANNEL_SKIP); + fdrawline(v2d->cur.xmin, ymaxc-NLACHANNEL_SKIP, v2d->cur.xmax, ymaxc-NLACHANNEL_SKIP); + + /* black top-lines */ + glLineWidth(1.0f); + glColor3f(0.0f, 0.0f, 0.0f); + fdrawline(v2d->cur.xmin, yminc+NLACHANNEL_SKIP, v2d->cur.xmax, yminc+NLACHANNEL_SKIP); + fdrawline(v2d->cur.xmin, ymaxc-NLACHANNEL_SKIP, v2d->cur.xmax, ymaxc-NLACHANNEL_SKIP); + + glDisable(GL_BLEND); + } + break; + } + } + + /* adjust y-position for next one */ + y -= NLACHANNEL_STEP; + } + + /* free tempolary channels */ + BLI_freelistN(&anim_data); +} + +/* *********************************************** */ +/* Channel List */ + +void draw_nla_channel_list (bAnimContext *ac, SpaceNla *snla, ARegion *ar) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + View2D *v2d= &ar->v2d; + float x= 0.0f, y= 0.0f; + int items, height; + + /* build list of channels to draw */ + filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS); + items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* Update max-extent of channels here (taking into account scrollers): + * - this is done to allow the channel list to be scrollable, but must be done here + * to avoid regenerating the list again and/or also because channels list is drawn first + * - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for + * start of list offset, and the second is as a correction for the scrollers. + */ + height= ((items*NLACHANNEL_STEP) + (NLACHANNEL_HEIGHT*2)); + /* don't use totrect set, as the width stays the same + * (NOTE: this is ok here, the configuration is pretty straightforward) + */ + v2d->tot.ymin= (float)(-height); + + /* loop through channels, and set up drawing depending on their type */ + y= (float)(-NLACHANNEL_HEIGHT); + + for (ale= anim_data.first; ale; ale= ale->next) { + const float yminc= (float)(y - NLACHANNEL_HEIGHT_HALF); + const float ymaxc= (float)(y + NLACHANNEL_HEIGHT_HALF); + const float ydatac= (float)(y - 7); + + /* check if visible */ + if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || + IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) + { + short indent= 0, offset= 0, sel= 0, group= 0; + int expand= -1, protect = -1, special= -1, mute = -1; + char name[128]; + + /* determine what needs to be drawn */ + switch (ale->type) { + case ANIMTYPE_SCENE: /* scene */ + { + Scene *sce= (Scene *)ale->data; + + group= 4; + indent= 0; + + special= ICON_SCENE_DATA; + + /* only show expand if there are any channels */ + if (EXPANDED_SCEC(sce)) + expand= ICON_TRIA_DOWN; + else + expand= ICON_TRIA_RIGHT; + + sel = SEL_SCEC(sce); + strcpy(name, sce->id.name+2); + } + break; + case ANIMTYPE_OBJECT: /* object */ + { + Base *base= (Base *)ale->data; + Object *ob= base->object; + + group= 4; + indent= 0; + + /* icon depends on object-type */ + if (ob->type == OB_ARMATURE) + special= ICON_ARMATURE_DATA; + else + special= ICON_OBJECT_DATA; + + /* only show expand if there are any channels */ + if (EXPANDED_OBJC(ob)) + expand= ICON_TRIA_DOWN; + else + expand= ICON_TRIA_RIGHT; + + sel = SEL_OBJC(base); + strcpy(name, ob->id.name+2); + } + break; + case ANIMTYPE_FILLMATD: /* object materials (dopesheet) expand widget */ + { + Object *ob = (Object *)ale->data; + + group = 4; + indent = 1; + special = ICON_MATERIAL_DATA; + + if (FILTER_MAT_OBJC(ob)) + expand = ICON_TRIA_DOWN; + else + expand = ICON_TRIA_RIGHT; + + strcpy(name, "Materials"); + } + break; + + + case ANIMTYPE_DSMAT: /* single material (dopesheet) expand widget */ + { + Material *ma = (Material *)ale->data; + + group = 0; + indent = 0; + special = ICON_MATERIAL_DATA; + offset = 21; + + if (FILTER_MAT_OBJD(ma)) + expand = ICON_TRIA_DOWN; + else + expand = ICON_TRIA_RIGHT; + + strcpy(name, ma->id.name+2); + } + break; + case ANIMTYPE_DSLAM: /* lamp (dopesheet) expand widget */ + { + Lamp *la = (Lamp *)ale->data; + + group = 4; + indent = 1; + special = ICON_LAMP_DATA; + + if (FILTER_LAM_OBJD(la)) + expand = ICON_TRIA_DOWN; + else + expand = ICON_TRIA_RIGHT; + + strcpy(name, la->id.name+2); + } + break; + case ANIMTYPE_DSCAM: /* camera (dopesheet) expand widget */ + { + Camera *ca = (Camera *)ale->data; + + group = 4; + indent = 1; + special = ICON_CAMERA_DATA; + + if (FILTER_CAM_OBJD(ca)) + expand = ICON_TRIA_DOWN; + else + expand = ICON_TRIA_RIGHT; + + strcpy(name, ca->id.name+2); + } + break; + case ANIMTYPE_DSCUR: /* curve (dopesheet) expand widget */ + { + Curve *cu = (Curve *)ale->data; + + group = 4; + indent = 1; + special = ICON_CURVE_DATA; + + if (FILTER_CUR_OBJD(cu)) + expand = ICON_TRIA_DOWN; + else + expand = ICON_TRIA_RIGHT; + + strcpy(name, cu->id.name+2); + } + break; + case ANIMTYPE_DSSKEY: /* shapekeys (dopesheet) expand widget */ + { + Key *key= (Key *)ale->data; + + group = 4; + indent = 1; + special = ICON_SHAPEKEY_DATA; // XXX + + if (FILTER_SKE_OBJD(key)) + expand = ICON_TRIA_DOWN; + else + expand = ICON_TRIA_RIGHT; + + //sel = SEL_OBJC(base); + strcpy(name, "Shape Keys"); + } + break; + case ANIMTYPE_DSWOR: /* world (dopesheet) expand widget */ + { + World *wo= (World *)ale->data; + + group = 4; + indent = 1; + special = ICON_WORLD_DATA; + + if (FILTER_WOR_SCED(wo)) + expand = ICON_TRIA_DOWN; + else + expand = ICON_TRIA_RIGHT; + + strcpy(name, wo->id.name+2); + } + break; + + case ANIMTYPE_NLATRACK: /* NLA Track */ + { + NlaTrack *nlt= (NlaTrack *)ale->data; + + indent= 0; + + if (ale->id) { + /* special exception for materials */ + if (GS(ale->id->name) == ID_MA) { + offset= 21; + indent= 1; + } + else + offset= 14; + } + else + offset= 0; + + /* FIXME: 'solo' as the 'special' button? + * - need special icons for these + */ + if (nlt->flag & NLATRACK_SOLO) + special= ICON_LAYER_ACTIVE; + else + special= ICON_LAYER_USED; + + /* if this track is active and we're tweaking it, don't draw these toggles */ + // TODO: need a special macro for this... + if ( ((nlt->flag & NLATRACK_ACTIVE) && (nlt->flag & NLATRACK_DISABLED)) == 0 ) + { + if (nlt->flag & NLATRACK_MUTED) + mute = ICON_MUTE_IPO_ON; + else + mute = ICON_MUTE_IPO_OFF; + + if (EDITABLE_NLT(nlt)) + protect = ICON_UNLOCKED; + else + protect = ICON_LOCKED; + } + + sel = SEL_NLT(nlt); + strcpy(name, nlt->name); + } + break; + case ANIMTYPE_NLAACTION: /* NLA Action-Line */ + { + bAction *act= (bAction *)ale->data; + + group = 5; + + if (ale->id) { + /* special exception for materials */ + if (GS(ale->id->name) == ID_MA) { + offset= 21; + indent= 1; + } + else + offset= 14; + } + else + offset= 0; + + special = ICON_ACTION; + + if (act) + sprintf(name, "ActAction: <%s>", act->id.name+2); + else + sprintf(name, ""); + } + break; + } + + /* now, start drawing based on this information */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + /* draw backing strip behind channel name */ + if (group == 4) { + /* only used in dopesheet... */ + if (ELEM(ale->type, ANIMTYPE_SCENE, ANIMTYPE_OBJECT)) { + /* object channel - darker */ + UI_ThemeColor(TH_DOPESHEET_CHANNELOB); + uiSetRoundBox((expand == ICON_TRIA_DOWN)? (8):(1|8)); + gl_round_box(GL_POLYGON, x+offset, yminc, (float)NLACHANNEL_NAMEWIDTH, ymaxc, 10); + } + else { + /* sub-object folders - lighter */ + UI_ThemeColor(TH_DOPESHEET_CHANNELSUBOB); + + offset += 7 * indent; + glBegin(GL_QUADS); + glVertex2f(x+offset, yminc); + glVertex2f(x+offset, ymaxc); + glVertex2f((float)ACHANNEL_NAMEWIDTH, ymaxc); + glVertex2f((float)ACHANNEL_NAMEWIDTH, yminc); + glEnd(); + + /* clear group value, otherwise we cause errors... */ + group = 0; + } + } + else if (group == 5) { + /* Action Line */ + AnimData *adt= BKE_animdata_from_id(ale->id); + + // TODO: if tweaking some action, use the same color as for the tweaked track (quick hack done for now) + if (adt && (adt->flag & ADT_NLA_EDIT_ON)) { + // greenish color (same as tweaking strip) - hardcoded for now + glColor3f(0.3f, 0.95f, 0.1f); + } + else { + if (ale->data) + glColor3f(0.8f, 0.2f, 0.0f); // reddish color - hardcoded for now + else + glColor3f(0.6f, 0.5f, 0.5f); // greyish-red color - hardcoded for now + } + + offset += 7 * indent; + + /* only on top two corners, to show that this channel sits on top of the preceeding ones */ + uiSetRoundBox((1|2)); + + /* draw slightly shifted up vertically to look like it has more separtion from other channels, + * but we then need to slightly shorten it so that it doesn't look like it overlaps + */ + gl_round_box(GL_POLYGON, x+offset, yminc+NLACHANNEL_SKIP, (float)NLACHANNEL_NAMEWIDTH, ymaxc+NLACHANNEL_SKIP-1, 8); + + /* clear group value, otherwise we cause errors... */ + group = 0; + } + else { + /* for normal channels + * - use 3 shades of color group/standard color for 3 indention level + */ + UI_ThemeColorShade(TH_HEADER, ((indent==0)?20: (indent==1)?-20: -40)); + + indent += group; + offset += 7 * indent; + glBegin(GL_QUADS); + glVertex2f(x+offset, yminc); + glVertex2f(x+offset, ymaxc); + glVertex2f((float)NLACHANNEL_NAMEWIDTH, ymaxc); + glVertex2f((float)NLACHANNEL_NAMEWIDTH, yminc); + glEnd(); + } + + /* draw expand/collapse triangle */ + if (expand > 0) { + UI_icon_draw(x+offset, ydatac, expand); + offset += 17; + } + + /* draw special icon indicating certain data-types */ + if (special > -1) { + /* for normal channels */ + UI_icon_draw(x+offset, ydatac, special); + offset += 17; + } + glDisable(GL_BLEND); + + /* draw name */ + if (sel) + UI_ThemeColor(TH_TEXT_HI); + else + UI_ThemeColor(TH_TEXT); + offset += 3; + UI_DrawString(x+offset, y-4, name); + + /* reset offset - for RHS of panel */ + offset = 0; + + /* set blending again, as text drawing may clear it */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + /* draw protect 'lock' */ + if (protect > -1) { + offset = 16; + UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, protect); + } + + /* draw mute 'eye' */ + if (mute > -1) { + offset += 16; + UI_icon_draw((float)(NLACHANNEL_NAMEWIDTH-offset), ydatac, mute); + } + + /* draw NLA-action line 'status-icons' - only when there's an action */ + if ((ale->type == ANIMTYPE_NLAACTION) && (ale->data)) { + AnimData *adt= BKE_animdata_from_id(ale->id); + + offset += 16; + + /* now draw some indicator icons */ + if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) { + /* toggle for tweaking with mapping/no-mapping (i.e. 'in place editing' toggle) */ + // for now, use pin icon to symbolise this + if (adt->flag & ADT_NLA_EDIT_NOMAP) + UI_icon_draw((float)(NLACHANNEL_NAMEWIDTH-offset), ydatac, ICON_PINNED); + else + UI_icon_draw((float)(NLACHANNEL_NAMEWIDTH-offset), ydatac, ICON_UNPINNED); + + fdrawline((float)(NLACHANNEL_NAMEWIDTH-offset), yminc, + (float)(NLACHANNEL_NAMEWIDTH-offset), ymaxc); + offset += 16;; + + /* 'tweaking action' indicator - not a button */ + UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, ICON_EDIT); + } + else { + /* XXX firstly draw a little rect to help identify that it's different from the toggles */ + glBegin(GL_LINE_LOOP); + glVertex2f((float)NLACHANNEL_NAMEWIDTH-offset-1, y-7); + glVertex2f((float)NLACHANNEL_NAMEWIDTH-offset-1, y+9); + glVertex2f((float)NLACHANNEL_NAMEWIDTH-1, y+9); + glVertex2f((float)NLACHANNEL_NAMEWIDTH-1, y-7); + glEnd(); // GL_LINES + + /* 'push down' icon for normal active-actions */ + UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, ICON_FREEZE); + } + } + + glDisable(GL_BLEND); + } + + /* adjust y-position for next one */ + y -= NLACHANNEL_STEP; + } + + /* free tempolary channels */ + BLI_freelistN(&anim_data); +} + +/* *********************************************** */ diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c new file mode 100644 index 00000000000..9ddd8daab0d --- /dev/null +++ b/source/blender/editors/space_nla/nla_edit.c @@ -0,0 +1,1484 @@ +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * + * Contributor(s): Joshua Leung (major recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_nla_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_fcurve.h" +#include "BKE_nla.h" +#include "BKE_context.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_edit.h" +#include "ED_markers.h" +#include "ED_space_api.h" +#include "ED_screen.h" +#include "ED_transform.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "nla_intern.h" // own include +#include "nla_private.h" // FIXME... maybe this shouldn't be included? + +/* *********************************************** */ +/* 'Special' Editing */ + +/* ******************** Tweak-Mode Operators ***************************** */ +/* 'Tweak mode' allows the action referenced by the active NLA-strip to be edited + * as if it were the normal Active-Action of its AnimData block. + */ + +static int nlaedit_enable_tweakmode_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + int ok=0; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the AnimData blocks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_ANIMDATA); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* if no blocks, popup error? */ + if (anim_data.first == NULL) { + BKE_report(op->reports, RPT_ERROR, "No AnimData blocks to enter tweakmode for"); + return OPERATOR_CANCELLED; + } + + /* for each AnimData block with NLA-data, try setting it in tweak-mode */ + for (ale= anim_data.first; ale; ale= ale->next) { + AnimData *adt= ale->data; + + /* try entering tweakmode if valid */ + ok += BKE_nla_tweakmode_enter(adt); + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* if we managed to enter tweakmode on at least one AnimData block, + * set the flag for this in the active scene and send notifiers + */ + if (ac.scene && ok) { + /* set editing flag */ + ac.scene->flag |= SCE_NLA_EDIT_ON; + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL); + } + else { + BKE_report(op->reports, RPT_ERROR, "No active strip(s) to enter tweakmode on."); + return OPERATOR_CANCELLED; + } + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_tweakmode_enter (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Enter Tweak Mode"; + ot->idname= "NLA_OT_tweakmode_enter"; + ot->description= "Enter tweaking mode for the action referenced by the active strip."; + + /* api callbacks */ + ot->exec= nlaedit_enable_tweakmode_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ------------- */ + +static int nlaedit_disable_tweakmode_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the AnimData blocks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_ANIMDATA); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* if no blocks, popup error? */ + if (anim_data.first == NULL) { + BKE_report(op->reports, RPT_ERROR, "No AnimData blocks to enter tweakmode for"); + return OPERATOR_CANCELLED; + } + + /* for each AnimData block with NLA-data, try exitting tweak-mode */ + for (ale= anim_data.first; ale; ale= ale->next) { + AnimData *adt= ale->data; + + /* try entering tweakmode if valid */ + BKE_nla_tweakmode_exit(adt); + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* if we managed to enter tweakmode on at least one AnimData block, + * set the flag for this in the active scene and send notifiers + */ + if (ac.scene) { + /* clear editing flag */ + ac.scene->flag &= ~SCE_NLA_EDIT_ON; + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL); + } + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_tweakmode_exit (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Exit Tweak Mode"; + ot->idname= "NLA_OT_tweakmode_exit"; + ot->description= "Exit tweaking mode for the action referenced by the active strip."; + + /* api callbacks */ + ot->exec= nlaedit_disable_tweakmode_exec; + ot->poll= nlaop_poll_tweakmode_on; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* *********************************************** */ +/* NLA Editing Operations (Constructive/Destructive) */ + +/* ******************** Add Action-Clip Operator ***************************** */ +/* Add a new Action-Clip strip to the active track (or the active block if no space in the track) */ + +/* pop up menu allowing user to choose the action to use */ +static int nlaedit_add_actionclip_invoke (bContext *C, wmOperator *op, wmEvent *evt) +{ + Main *m= CTX_data_main(C); + bAction *act; + uiPopupMenu *pup; + uiLayout *layout; + + pup= uiPupMenuBegin(C, "Add Action Clip", 0); + layout= uiPupMenuLayout(pup); + + /* loop through Actions in Main database, adding as items in the menu */ + for (act= m->action.first; act; act= act->id.next) + uiItemStringO(layout, act->id.name+2, 0, "NLA_OT_add_actionclip", "action", act->id.name); + uiItemS(layout); + + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; +} + +/* add the specified action as new strip */ +static int nlaedit_add_actionclip_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + Scene *scene; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter, items; + + bAction *act = NULL; + char actname[22]; + float cfra; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + scene= ac.scene; + cfra= (float)CFRA; + + /* get action to use */ + RNA_string_get(op->ptr, "action", actname); + act= (bAction *)find_id("AC", actname+2); + + if (act == NULL) { + BKE_report(op->reports, RPT_ERROR, "No valid Action to add."); + //printf("Add strip - actname = '%s' \n", actname); + return OPERATOR_CANCELLED; + } + + /* get a list of the editable tracks being shown in the NLA + * - this is limited to active ones for now, but could be expanded to + */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + items= ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + if (items == 0) { + BKE_report(op->reports, RPT_ERROR, "No active track(s) to add strip to."); + return OPERATOR_CANCELLED; + } + + /* for every active track, try to add strip to free space in track or to the top of the stack if no space */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + NlaStrip *strip= NULL; + + /* create a new strip, and offset it to start on the current frame */ + strip= add_nlastrip(act); + + strip->end += (cfra - strip->start); + strip->start = cfra; + + /* firstly try adding strip to our current track, but if that fails, add to a new track */ + if (BKE_nlatrack_add_strip(nlt, strip) == 0) { + /* trying to add to the current failed (no space), + * so add a new track to the stack, and add to that... + */ + nlt= add_nlatrack(adt, NULL); + BKE_nlatrack_add_strip(nlt, strip); + } + + /* auto-name it */ + BKE_nlastrip_validate_name(adt, strip); + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_add_actionclip (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Action Strip"; + ot->idname= "NLA_OT_add_actionclip"; + ot->description= "Add an Action-Clip strip (i.e. an NLA Strip referencing an Action) to the active track."; + + /* api callbacks */ + ot->invoke= nlaedit_add_actionclip_invoke; + ot->exec= nlaedit_add_actionclip_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* props */ + // TODO: this would be nicer as an ID-pointer... + RNA_def_string(ot->srna, "action", "", 21, "Action", "Name of Action to add as a new Action-Clip Strip."); +} + +/* ******************** Add Transition Operator ***************************** */ +/* Add a new transition strip between selected strips */ + +static int nlaedit_add_transition_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + int done = 0; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* for each track, find pairs of strips to add transitions to */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + NlaStrip *s1, *s2; + + /* get initial pair of strips */ + if ELEM(nlt->strips.first, NULL, nlt->strips.last) + continue; + s1= nlt->strips.first; + s2= s1->next; + + /* loop over strips */ + for (; s1 && s2; s1=s2, s2=s2->next) { + NlaStrip *strip; + + /* check if both are selected */ + if ELEM(0, (s1->flag & NLASTRIP_FLAG_SELECT), (s2->flag & NLASTRIP_FLAG_SELECT)) + continue; + /* check if there's space between the two */ + if (IS_EQ(s1->end, s2->start)) + continue; + /* make neither one is a transition + * - although this is impossible to create with the standard tools, + * the user may have altered the settings + */ + if (ELEM(NLASTRIP_TYPE_TRANSITION, s1->type, s2->type)) + continue; + + /* allocate new strip */ + strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip"); + BLI_insertlinkafter(&nlt->strips, s1, strip); + + /* set the type */ + strip->type= NLASTRIP_TYPE_TRANSITION; + + /* generic settings + * - selected flag to highlight this to the user + * - auto-blends to ensure that blend in/out values are automatically + * determined by overlaps of strips + */ + strip->flag = NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_AUTO_BLENDS; + + /* range is simply defined as the endpoints of the adjacent strips */ + strip->start = s1->end; + strip->end = s2->start; + + /* scale and repeat aren't of any use, but shouldn't ever be 0 */ + strip->scale= 1.0f; + strip->repeat = 1.0f; + + /* auto-name it */ + BKE_nlastrip_validate_name(adt, strip); + + /* make note of this */ + done++; + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* was anything added? */ + if (done) { + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "Needs at least a pair of adjacent selected strips with a gap between them."); + return OPERATOR_CANCELLED; + } +} + +void NLA_OT_add_transition (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Transition"; + ot->idname= "NLA_OT_add_transition"; + ot->description= "Add a transition strip between two adjacent selected strips."; + + /* api callbacks */ + ot->exec= nlaedit_add_transition_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Add Meta-Strip Operator ***************************** */ +/* Add new meta-strips incorporating the selected strips */ + +/* add the specified action as new strip */ +static int nlaedit_add_meta_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* for each track, find pairs of strips to add transitions to */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + NlaStrip *strip; + + /* create meta-strips from the continuous chains of selected strips */ + BKE_nlastrips_make_metas(&nlt->strips, 0); + + /* name the metas */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + /* auto-name this strip if selected (that means it is a meta) */ + if (strip->flag & NLASTRIP_FLAG_SELECT) + BKE_nlastrip_validate_name(adt, strip); + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_add_meta (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Meta-Strips"; + ot->idname= "NLA_OT_add_meta"; + ot->description= "Add new meta-strips incorporating the selected strips."; + + /* api callbacks */ + ot->exec= nlaedit_add_meta_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Remove Meta-Strip Operator ***************************** */ +/* Separate out the strips held by the selected meta-strips */ + +static int nlaedit_remove_meta_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* for each track, find pairs of strips to add transitions to */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + + /* clear all selected meta-strips, regardless of whether they are temporary or not */ + BKE_nlastrips_clear_metas(&nlt->strips, 1, 0); + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_remove_meta (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Remove Meta-Strips"; + ot->idname= "NLA_OT_remove_meta"; + ot->description= "Separate out the strips held by the selected meta-strips."; + + /* api callbacks */ + ot->exec= nlaedit_remove_meta_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Duplicate Strips Operator ************************** */ +/* Duplicates the selected NLA-Strips, putting them on new tracks above the one + * the originals were housed in. + */ + +static int nlaedit_duplicate_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + short done = 0; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* duplicate strips in tracks starting from the last one so that we're + * less likely to duplicate strips we just duplicated... + */ + for (ale= anim_data.last; ale; ale= ale->prev) { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + NlaStrip *strip, *nstrip, *next; + NlaTrack *track; + + for (strip= nlt->strips.first; strip; strip= next) { + next= strip->next; + + /* if selected, split the strip at its midpoint */ + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* make a copy (assume that this is possible) */ + nstrip= copy_nlastrip(strip); + + /* in case there's no space in the track above, or we haven't got a reference to it yet, try adding */ + if (BKE_nlatrack_add_strip(nlt->next, nstrip) == 0) { + /* need to add a new track above the one above the current one + * - if the current one is the last one, nlt->next will be NULL, which defaults to adding + * at the top of the stack anyway... + */ + track= add_nlatrack(adt, nlt->next); + BKE_nlatrack_add_strip(track, nstrip); + } + + /* deselect the original and the active flag */ + strip->flag &= ~(NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_ACTIVE); + + /* auto-name it */ + BKE_nlastrip_validate_name(adt, strip); + + done++; + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + if (done) { + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; + } + else + return OPERATOR_CANCELLED; +} + +static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + nlaedit_duplicate_exec(C, op); + + RNA_int_set(op->ptr, "mode", TFM_TIME_TRANSLATE); // XXX + WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr); + + return OPERATOR_FINISHED; +} + +void NLA_OT_duplicate (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Duplicate Strips"; + ot->idname= "NLA_OT_duplicate"; + ot->description= "Duplicate selected NLA-Strips, adding the new strips in new tracks above the originals."; + + /* api callbacks */ + ot->invoke= nlaedit_duplicate_invoke; + ot->exec= nlaedit_duplicate_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* to give to transform */ + RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX); +} + +/* ******************** Delete Strips Operator ***************************** */ +/* Deletes the selected NLA-Strips */ + +static int nlaedit_delete_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* for each NLA-Track, delete all selected strips */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip, *nstrip; + + for (strip= nlt->strips.first; strip; strip= nstrip) { + nstrip= strip->next; + + /* if selected, delete */ + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* if a strip either side of this was a transition, delete those too */ + if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) + free_nlastrip(&nlt->strips, strip->prev); + if ((nstrip) && (nstrip->type == NLASTRIP_TYPE_TRANSITION)) { + nstrip= nstrip->next; + free_nlastrip(&nlt->strips, strip->next); + } + + /* finally, delete this strip */ + free_nlastrip(&nlt->strips, strip); + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_delete (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Delete Strips"; + ot->idname= "NLA_OT_delete"; + ot->description= "Delete selected strips."; + + /* api callbacks */ + ot->exec= nlaedit_delete_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Split Strips Operator ***************************** */ +/* Splits the selected NLA-Strips into two strips at the midpoint of the strip */ +// TODO's? +// - multiple splits +// - variable-length splits? + +/* split a given Action-Clip strip */ +static void nlaedit_split_strip_actclip (AnimData *adt, NlaTrack *nlt, NlaStrip *strip) +{ + NlaStrip *nstrip; + float midframe, midaframe, len; + + /* calculate the frames to do the splitting at */ + /* strip extents */ + len= strip->end - strip->start; + if (IS_EQ(len, 0.0f)) + return; + else + midframe= strip->start + (len / 2.0f); + + /* action range */ + len= strip->actend - strip->actstart; + if (IS_EQ(len, 0.0f)) + midaframe= strip->actend; + else + midaframe= strip->actstart + (len / 2.0f); + + /* make a copy (assume that this is possible) and append + * it immediately after the current strip + */ + nstrip= copy_nlastrip(strip); + BLI_insertlinkafter(&nlt->strips, strip, nstrip); + + /* set the endpoint of the first strip and the start of the new strip + * to the midframe values calculated above + */ + strip->end= midframe; + nstrip->start= midframe; + + strip->actend= midaframe; + nstrip->actstart= midaframe; + + /* clear the active flag from the copy */ + nstrip->flag &= ~NLASTRIP_FLAG_ACTIVE; + + /* auto-name the new strip */ + BKE_nlastrip_validate_name(adt, nstrip); +} + +/* split a given Meta strip */ +static void nlaedit_split_strip_meta (AnimData *adt, NlaTrack *nlt, NlaStrip *strip) +{ + /* simply ungroup it for now... */ + BKE_nlastrips_clear_metastrip(&nlt->strips, strip); +} + +/* ----- */ + +static int nlaedit_split_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* for each NLA-Track, split all selected strips into two strips */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + AnimData *adt= BKE_animdata_from_id(ale->id); + NlaStrip *strip, *next; + + for (strip= nlt->strips.first; strip; strip= next) { + next= strip->next; + + /* if selected, split the strip at its midpoint */ + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* splitting method depends on the type of strip */ + switch (strip->type) { + case NLASTRIP_TYPE_CLIP: /* action-clip */ + nlaedit_split_strip_actclip(adt, nlt, strip); + break; + + case NLASTRIP_TYPE_META: /* meta-strips need special handling */ + nlaedit_split_strip_meta(adt, nlt, strip); + break; + + default: /* for things like Transitions, do not split! */ + break; + } + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_split (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Split Strips"; + ot->idname= "NLA_OT_split"; + ot->description= "Split selected strips at their midpoints."; + + /* api callbacks */ + ot->exec= nlaedit_split_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* *********************************************** */ +/* NLA Editing Operations (Modifying) */ + +/* ******************** Toggle Muting Operator ************************** */ +/* Toggles whether strips are muted or not */ + +static int nlaedit_toggle_mute_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* go over all selected strips */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* for every selected strip, toggle muting */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* just flip the mute flag for now */ + // TODO: have a pre-pass to check if mute all or unmute all? + strip->flag ^= NLASTRIP_FLAG_MUTED; + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_mute_toggle (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Toggle Muting"; + ot->idname= "NLA_OT_mute_toggle"; + ot->description= "Mute or un-muted selected strips."; + + /* api callbacks */ + ot->exec= nlaedit_toggle_mute_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Move Strips Up Operator ************************** */ +/* Tries to move the selected strips into the track above if possible. */ + +static int nlaedit_move_up_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* since we're potentially moving strips from lower tracks to higher tracks, we should + * loop over the tracks in reverse order to avoid moving earlier strips up multiple tracks + */ + for (ale= anim_data.last; ale; ale= ale->prev) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaTrack *nltn= nlt->next; + NlaStrip *strip, *stripn; + + /* if this track has no tracks after it, skip for now... */ + if (nltn == NULL) + continue; + + /* for every selected strip, try to move */ + for (strip= nlt->strips.first; strip; strip= stripn) { + stripn= strip->next; + + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* check if the track above has room for this strip */ + if (BKE_nlatrack_has_space(nltn, strip->start, strip->end)) { + /* remove from its current track, and add to the one above (it 'should' work, so no need to worry) */ + BLI_remlink(&nlt->strips, strip); + BKE_nlatrack_add_strip(nltn, strip); + } + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_move_up (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Move Strips Up"; + ot->idname= "NLA_OT_move_up"; + ot->description= "Move selected strips up a track if there's room."; + + /* api callbacks */ + ot->exec= nlaedit_move_up_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Move Strips Down Operator ************************** */ +/* Tries to move the selected strips into the track above if possible. */ + +static int nlaedit_move_down_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* loop through the tracks in normal order, since we're pushing strips down, + * strips won't get operated on twice + */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaTrack *nltp= nlt->prev; + NlaStrip *strip, *stripn; + + /* if this track has no tracks before it, skip for now... */ + if (nltp == NULL) + continue; + + /* for every selected strip, try to move */ + for (strip= nlt->strips.first; strip; strip= stripn) { + stripn= strip->next; + + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* check if the track below has room for this strip */ + if (BKE_nlatrack_has_space(nltp, strip->start, strip->end)) { + /* remove from its current track, and add to the one above (it 'should' work, so no need to worry) */ + BLI_remlink(&nlt->strips, strip); + BKE_nlatrack_add_strip(nltp, strip); + } + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_move_down (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Move Strips Down"; + ot->idname= "NLA_OT_move_down"; + ot->description= "Move selected strips down a track if there's room."; + + /* api callbacks */ + ot->exec= nlaedit_move_down_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Apply Scale Operator ***************************** */ +/* Reset the scaling of the selected strips to 1.0f */ + +/* apply scaling to keyframe */ +static short bezt_apply_nlamapping (BeztEditData *bed, BezTriple *bezt) +{ + /* NLA-strip which has this scaling is stored in bed->data */ + NlaStrip *strip= (NlaStrip *)bed->data; + + /* adjust all the times */ + bezt->vec[0][0]= nlastrip_get_frame(strip, bezt->vec[0][0], NLATIME_CONVERT_MAP); + bezt->vec[1][0]= nlastrip_get_frame(strip, bezt->vec[1][0], NLATIME_CONVERT_MAP); + bezt->vec[2][0]= nlastrip_get_frame(strip, bezt->vec[2][0], NLATIME_CONVERT_MAP); + + /* nothing to return or else we exit */ + return 0; +} + +static int nlaedit_apply_scale_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + BeztEditData bed; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* init the editing data */ + memset(&bed, 0, sizeof(BeztEditData)); + + /* for each NLA-Track, apply scale of all selected strips */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + for (strip= nlt->strips.first; strip; strip= strip->next) { + /* strip must be selected, and must be action-clip only (transitions don't have scale) */ + if ((strip->flag & NLASTRIP_FLAG_SELECT) && (strip->type == NLASTRIP_TYPE_CLIP)) { + /* if the referenced action is used by other strips, make this strip use its own copy */ + if (strip->act == NULL) + continue; + if (strip->act->id.us > 1) { + /* make a copy of the Action to work on */ + bAction *act= copy_action(strip->act); + + /* set this as the new referenced action, decrementing the users of the old one */ + strip->act->id.us--; + strip->act= act; + } + + /* setup iterator, and iterate over all the keyframes in the action, applying this scaling */ + bed.data= strip; + ANIM_animchanneldata_keys_bezier_loop(&bed, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, calchandles_fcurve, 0); + + /* clear scale of strip now that it has been applied, + * and recalculate the extents of the action now that it has been scaled + * but leave everything else alone + */ + strip->scale= 1.0f; + calc_action_range(strip->act, &strip->actstart, &strip->actend, 1); + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_apply_scale (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Apply Scale"; + ot->idname= "NLA_OT_apply_scale"; + ot->description= "Apply scaling of selected strips to their referenced Actions."; + + /* api callbacks */ + ot->exec= nlaedit_apply_scale_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Clear Scale Operator ***************************** */ +/* Reset the scaling of the selected strips to 1.0f */ + +static int nlaedit_clear_scale_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* for each NLA-Track, reset scale of all selected strips */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + for (strip= nlt->strips.first; strip; strip= strip->next) { + /* strip must be selected, and must be action-clip only (transitions don't have scale) */ + if ((strip->flag & NLASTRIP_FLAG_SELECT) && (strip->type == NLASTRIP_TYPE_CLIP)) { + PointerRNA strip_ptr; + + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); + RNA_float_set(&strip_ptr, "scale", 1.0f); + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_clear_scale (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Clear Scale"; + ot->idname= "NLA_OT_clear_scale"; + ot->description= "Reset scaling of selected strips."; + + /* api callbacks */ + ot->exec= nlaedit_clear_scale_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************** Snap Strips Operator ************************** */ +/* Moves the start-point of the selected strips to the specified places */ + +/* defines for snap keyframes tool */ +EnumPropertyItem prop_nlaedit_snap_types[] = { + {NLAEDIT_SNAP_CFRA, "CFRA", 0, "Current frame", ""}, + {NLAEDIT_SNAP_NEAREST_FRAME, "NEAREST_FRAME", 0, "Nearest Frame", ""}, // XXX as single entry? + {NLAEDIT_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, "Nearest Second", ""}, // XXX as single entry? + {NLAEDIT_SNAP_NEAREST_MARKER, "NEAREST_MARKER", 0, "Nearest Marker", ""}, + {0, NULL, 0, NULL, NULL} +}; + +static int nlaedit_snap_exec (bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + Scene *scene; + int mode = RNA_enum_get(op->ptr, "type"); + float secf; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* get some necessary vars */ + scene= ac.scene; + secf= (float)FPS; + + /* since we may add tracks, perform this in reverse order */ + for (ale= anim_data.last; ale; ale= ale->prev) { + ListBase tmp_strips = {NULL, NULL}; + AnimData *adt= BKE_animdata_from_id(ale->id); + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip, *stripn; + NlaTrack *track; + + /* create meta-strips from the continuous chains of selected strips */ + BKE_nlastrips_make_metas(&nlt->strips, 1); + + /* apply the snapping to all the temp meta-strips, then put them in a separate list to be added + * back to the original only if they still fit + */ + for (strip= nlt->strips.first; strip; strip= stripn) { + stripn= strip->next; + + if (strip->flag & NLASTRIP_FLAG_TEMP_META) { + float start, end; + + /* get the existing end-points */ + start= strip->start; + end= strip->end; + + /* calculate new start position based on snapping mode */ + switch (mode) { + case NLAEDIT_SNAP_CFRA: /* to current frame */ + strip->start= (float)CFRA; + break; + case NLAEDIT_SNAP_NEAREST_FRAME: /* to nearest frame */ + strip->start= (float)(floor(start+0.5)); + break; + case NLAEDIT_SNAP_NEAREST_SECOND: /* to nearest second */ + strip->start= ((float)floor(start/secf + 0.5f) * secf); + break; + case NLAEDIT_SNAP_NEAREST_MARKER: /* to nearest marker */ + strip->start= (float)ED_markers_find_nearest_marker_time(ac.markers, start); + break; + default: /* just in case... no snapping */ + strip->start= start; + break; + } + + /* get new endpoint based on start-point (and old length) */ + strip->end= strip->start + (end - start); + + /* apply transforms to meta-strip to its children */ + BKE_nlameta_flush_transforms(strip); + + /* remove strip from track, and add to the temp buffer */ + BLI_remlink(&nlt->strips, strip); + BLI_addtail(&tmp_strips, strip); + } + } + + /* try adding each meta-strip back to the track one at a time, to make sure they'll fit */ + for (strip= tmp_strips.first; strip; strip= stripn) { + stripn= strip->next; + + /* remove from temp-strips list */ + BLI_remlink(&tmp_strips, strip); + + /* in case there's no space in the current track, try adding */ + if (BKE_nlatrack_add_strip(nlt, strip) == 0) { + /* need to add a new track above the current one */ + track= add_nlatrack(adt, nlt); + BKE_nlatrack_add_strip(track, strip); + + /* clear temp meta-strips on this new track, as we may not be able to get back to it */ + BKE_nlastrips_clear_metas(&track->strips, 0, 1); + } + } + + /* remove the meta-strips now that we're done */ + BKE_nlastrips_clear_metas(&nlt->strips, 0, 1); + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_snap (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Snap Strips"; + ot->idname= "NLA_OT_snap"; + ot->description= "Move start of strips to specified time."; + + /* api callbacks */ + ot->invoke= WM_menu_invoke; + ot->exec= nlaedit_snap_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", prop_nlaedit_snap_types, 0, "Type", ""); +} + +/* *********************************************** */ +/* NLA Modifiers */ + +/* ******************** Add F-Modifier Operator *********************** */ + +/* present a special customised popup menu for this, with some filtering */ +static int nla_fmodifier_add_invoke (bContext *C, wmOperator *op, wmEvent *event) +{ + uiPopupMenu *pup; + uiLayout *layout; + int i; + + pup= uiPupMenuBegin(C, "Add F-Modifier", 0); + layout= uiPupMenuLayout(pup); + + /* start from 1 to skip the 'Invalid' modifier type */ + for (i = 1; i < FMODIFIER_NUM_TYPES; i++) { + FModifierTypeInfo *fmi= get_fmodifier_typeinfo(i); + + /* check if modifier is valid for this context */ + if (fmi == NULL) + continue; + if (i == FMODIFIER_TYPE_CYCLES) /* we already have repeat... */ + continue; + + /* add entry to add this type of modifier */ + uiItemEnumO(layout, fmi->name, 0, "NLA_OT_fmodifier_add", "type", i); + } + uiItemS(layout); + + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; +} + +static int nla_fmodifier_add_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + FModifier *fcm; + int type= RNA_enum_get(op->ptr, "type"); + short onlyActive = RNA_boolean_get(op->ptr, "only_active"); + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get a list of the editable tracks being shown in the NLA */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* for each NLA-Track, add the specified modifier to all selected strips */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + int i = 1; + + for (strip= nlt->strips.first; strip; strip=strip->next, i++) { + /* only add F-Modifier if on active strip? */ + if ((onlyActive) && (strip->flag & NLASTRIP_FLAG_ACTIVE)==0) + continue; + + /* add F-Modifier of specified type to selected, and make it the active one */ + fcm= add_fmodifier(&strip->modifiers, type); + + if (fcm) + set_active_fmodifier(&strip->modifiers, fcm); + else { + char errormsg[128]; + sprintf(errormsg, "Modifier couldn't be added to (%s : %d). See console for details.", nlt->name, i); + + BKE_report(op->reports, RPT_ERROR, errormsg); + } + } + } + + /* free temp data */ + BLI_freelistN(&anim_data); + + /* set notifier that things have changed */ + // FIXME: this doesn't really do it justice... + WM_event_add_notifier(C, NC_ANIMATION, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +void NLA_OT_fmodifier_add (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add F-Modifier"; + ot->idname= "NLA_OT_fmodifier_add"; + + /* api callbacks */ + ot->invoke= nla_fmodifier_add_invoke; + ot->exec= nla_fmodifier_add_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* id-props */ + RNA_def_enum(ot->srna, "type", fmodifier_type_items, 0, "Type", ""); + RNA_def_boolean(ot->srna, "only_active", 0, "Only Active", "Only add F-Modifier of the specified type to the active strip."); +} + +/* *********************************************** */ diff --git a/source/blender/editors/space_nla/nla_header.c b/source/blender/editors/space_nla/nla_header.c index 0f6b77da6f5..8e4c71b9f80 100644 --- a/source/blender/editors/space_nla/nla_header.c +++ b/source/blender/editors/space_nla/nla_header.c @@ -29,6 +29,10 @@ #include #include +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_nla_types.h" +#include "DNA_object_types.h" #include "DNA_space_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -37,19 +41,29 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" +#include "BKE_animsys.h" +#include "BKE_nla.h" #include "BKE_context.h" +#include "BKE_report.h" #include "BKE_screen.h" - -#include "ED_screen.h" #include "ED_types.h" #include "ED_util.h" -#include "WM_api.h" -#include "WM_types.h" +#include "ED_anim_api.h" +#include "ED_markers.h" +#include "ED_space_api.h" +#include "ED_screen.h" +#include "ED_transform.h" #include "BIF_gl.h" -#include "BIF_glutil.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" #include "UI_interface.h" #include "UI_resources.h" @@ -57,49 +71,146 @@ #include "nla_intern.h" +/* button events */ +enum { + B_REDR = 0, +} eActHeader_ButEvents; /* ************************ header area region *********************** */ -static void do_viewmenu(bContext *C, void *arg, int event) + +static void nla_viewmenu(bContext *C, uiLayout *layout, void *arg_unused) { + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa= CTX_wm_area(C); + Scene *scene= CTX_data_scene(C); + SpaceNla *snla= (SpaceNla*)CTX_wm_space_data(C); + PointerRNA spaceptr; + /* retrieve state */ + RNA_pointer_create(&sc->id, &RNA_SpaceNLA, snla, &spaceptr); + + /* create menu */ + uiItemO(layout, NULL, ICON_MENU_PANEL, "NLA_OT_properties"); + + uiItemS(layout); + + uiItemR(layout, NULL, 0, &spaceptr, "show_cframe_indicator", 0, 0, 0); + + if (snla->flag & SNLA_DRAWTIME) + uiItemO(layout, "Show Frames", 0, "ANIM_OT_time_toggle"); + else + uiItemO(layout, "Show Seconds", 0, "ANIM_OT_time_toggle"); + + uiItemS(layout); + + if (scene->flag & SCE_NLA_EDIT_ON) + uiItemO(layout, NULL, 0, "NLA_OT_tweakmode_exit"); + else + uiItemO(layout, NULL, 0, "NLA_OT_tweakmode_enter"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "ANIM_OT_previewrange_set"); + uiItemO(layout, NULL, 0, "ANIM_OT_previewrange_clear"); + + uiItemS(layout); + + //uiItemO(layout, NULL, 0, "NLA_OT_view_all"); + + if (sa->full) + uiItemO(layout, NULL, 0, "SCREEN_OT_screen_full_area"); // "Tile Window", Ctrl UpArrow + else + uiItemO(layout, NULL, 0, "SCREEN_OT_screen_full_area"); // "Maximize Window", Ctr DownArrow } -static uiBlock *dummy_viewmenu(bContext *C, ARegion *ar, void *arg_unused) +static void nla_selectmenu(bContext *C, uiLayout *layout, void *arg_unused) { - ScrArea *curarea= CTX_wm_area(C); - uiBlock *block; - short yco= 0, menuwidth=120; + uiItemO(layout, NULL, 0, "NLA_OT_select_all_toggle"); + uiItemBooleanO(layout, "Invert All", 0, "NLA_OT_select_all_toggle", "invert", 1); - block= uiBeginBlock(C, ar, "dummy_viewmenu", UI_EMBOSSP); - uiBlockSetButmFunc(block, do_viewmenu, NULL); + uiItemS(layout); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Nothing yet", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, 3, ""); - - if(curarea->headertype==HEADERTOP) { - uiBlockSetDirection(block, UI_DOWN); - } - else { - uiBlockSetDirection(block, UI_TOP); - uiBlockFlipOrder(block); - } - - uiTextBoundsBlock(block, 50); - uiEndBlock(C, block); - - return block; + uiItemO(layout, NULL, 0, "NLA_OT_select_border"); + uiItemBooleanO(layout, "Border Axis Range", 0, "NLA_OT_select_border", "axis_range", 1); } +static void nla_edit_transformmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + // XXX these operators may change for NLA... + uiItemEnumO(layout, "Grab/Move", 0, "TFM_OT_transform", "mode", TFM_TRANSLATION); + uiItemEnumO(layout, "Extend", 0, "TFM_OT_transform", "mode", TFM_TIME_EXTEND); + uiItemEnumO(layout, "Scale", 0, "TFM_OT_transform", "mode", TFM_TIME_SCALE); +} + +static void nla_edit_snapmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemEnumO(layout, NULL, 0, "NLA_OT_snap", "type", NLAEDIT_SNAP_CFRA); + uiItemEnumO(layout, NULL, 0, "NLA_OT_snap", "type", NLAEDIT_SNAP_NEAREST_FRAME); + uiItemEnumO(layout, NULL, 0, "NLA_OT_snap", "type", NLAEDIT_SNAP_NEAREST_SECOND); + uiItemEnumO(layout, NULL, 0, "NLA_OT_snap", "type", NLAEDIT_SNAP_NEAREST_MARKER); +} + +static void nla_editmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemMenuF(layout, "Transform", 0, nla_edit_transformmenu); + uiItemMenuF(layout, "Snap", 0, nla_edit_snapmenu); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "NLA_OT_duplicate"); + uiItemO(layout, NULL, 0, "NLA_OT_split"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "NLA_OT_delete"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "NLA_OT_mute_toggle"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "NLA_OT_apply_scale"); + uiItemO(layout, NULL, 0, "NLA_OT_clear_scale"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "NLA_OT_move_up"); + uiItemO(layout, NULL, 0, "NLA_OT_move_down"); +} + +static void nla_addmenu(bContext *C, uiLayout *layout, void *arg_unused) +{ + uiItemO(layout, NULL, 0, "NLA_OT_add_actionclip"); + uiItemO(layout, NULL, 0, "NLA_OT_add_transition"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "NLA_OT_add_meta"); + uiItemO(layout, NULL, 0, "NLA_OT_remove_meta"); + + uiItemS(layout); + + uiItemO(layout, NULL, 0, "NLA_OT_add_tracks"); + uiItemBooleanO(layout, "Add Tracks Above Selected", 0, "NLA_OT_add_tracks", "above_selected", 1); +} + +/* ------------------ */ + static void do_nla_buttons(bContext *C, void *arg, int event) { - switch(event) { + switch (event) { + case B_REDR: + ED_area_tag_redraw(CTX_wm_area(C)); + break; } } void nla_header_buttons(const bContext *C, ARegion *ar) { + SpaceNla *snla= (SpaceNla *)CTX_wm_space_data(C); ScrArea *sa= CTX_wm_area(C); uiBlock *block; int xco, yco= 3; @@ -109,17 +220,68 @@ void nla_header_buttons(const bContext *C, ARegion *ar) xco= ED_area_header_standardbuttons(C, block, yco); - if((sa->flag & HEADER_NO_PULLDOWN)==0) { + if ((sa->flag & HEADER_NO_PULLDOWN)==0) { int xmax; xmax= GetButStringLength("View"); - uiDefPulldownBut(block, dummy_viewmenu, CTX_wm_area(C), - "View", xco, yco-2, xmax-3, 24, ""); - xco+=XIC+xmax; + uiDefMenuBut(block, nla_viewmenu, NULL, "View", xco, yco, xmax-3, 20, ""); + xco+= xmax; + + xmax= GetButStringLength("Select"); + uiDefMenuBut(block, nla_selectmenu, NULL, "Select", xco, yco, xmax-3, 20, ""); + xco+= xmax; + + xmax= GetButStringLength("Edit"); + uiDefMenuBut(block, nla_editmenu, NULL, "Edit", xco, yco, xmax-3, 20, ""); + xco+= xmax; + + xmax= GetButStringLength("Add"); + uiDefMenuBut(block, nla_addmenu, NULL, "Add", xco, yco, xmax-3, 20, ""); + xco+= xmax; } uiBlockSetEmboss(block, UI_EMBOSS); - + + /* filtering buttons */ + if (snla->ads) { + uiBlockBeginAlign(block); + uiDefIconButBitI(block, TOG, ADS_FILTER_ONLYSEL, B_REDR, ICON_RESTRICT_SELECT_OFF, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Only display selected Objects"); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NLA_NOACT, B_REDR, ICON_ACTION, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Include AnimData blocks with no NLA Data"); + uiBlockEndAlign(block); + xco += 5; + + uiBlockBeginAlign(block); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NOSCE, B_REDR, ICON_SCENE_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Scene Animation"); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NOWOR, B_REDR, ICON_WORLD_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display World Animation"); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NOSHAPEKEYS, B_REDR, ICON_SHAPEKEY_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display ShapeKeys"); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NOMAT, B_REDR, ICON_MATERIAL_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Materials"); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NOLAM, B_REDR, ICON_LAMP_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Lamps"); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NOCAM, B_REDR, ICON_CAMERA_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Cameras"); + uiDefIconButBitI(block, TOGN, ADS_FILTER_NOCUR, B_REDR, ICON_CURVE_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Curves"); + uiBlockEndAlign(block); + xco += 15; + } + else { + // XXX this case shouldn't happen at all... for now, just pad out same amount of space + xco += 7*XIC + 15; + } + xco += (XIC + 8); + + /* auto-snap selector */ + if (snla->flag & SNLA_DRAWTIME) { + uiDefButS(block, MENU, B_REDR, + "Auto-Snap Keyframes %t|No Time-Snap %x0|Nearest Second %x2|Nearest Marker %x3", + xco,yco,90,YIC, &snla->autosnap, 0, 1, 0, 0, + "Auto-snapping mode for times when transforming"); + } + else { + uiDefButS(block, MENU, B_REDR, + "Auto-Snap Keyframes %t|No Time-Snap %x0|Nearest Frame %x2|Nearest Marker %x3", + xco,yco,90,YIC, &snla->autosnap, 0, 1, 0, 0, + "Auto-snapping mode for times when transforming"); + } + xco += 98; + /* always as last */ UI_view2d_totRect_set(&ar->v2d, xco+XIC+80, ar->v2d.tot.ymax-ar->v2d.tot.ymin); diff --git a/source/blender/editors/space_nla/nla_intern.h b/source/blender/editors/space_nla/nla_intern.h index c544bd9a408..7cc09707ba7 100644 --- a/source/blender/editors/space_nla/nla_intern.h +++ b/source/blender/editors/space_nla/nla_intern.h @@ -17,11 +17,11 @@ * 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. + * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung. * All rights reserved. * * - * Contributor(s): Blender Foundation + * Contributor(s): Blender Foundation, Joshua Leung * * ***** END GPL LICENSE BLOCK ***** */ @@ -30,10 +30,107 @@ /* internal exports only */ +/* **************************************** */ +/* Macros, etc. only used by NLA */ +/* **************************************** */ +/* space_nla.c / nla_buttons.c */ + +ARegion *nla_has_buttons_region(ScrArea *sa); + +void nla_buttons_register(ARegionType *art); +void NLA_OT_properties(wmOperatorType *ot); + +/* **************************************** */ +/* nla_draw.c */ + +void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *ar); +void draw_nla_channel_list(bAnimContext *ac, SpaceNla *snla, ARegion *ar); + +/* **************************************** */ /* nla_header.c */ + void nla_header_buttons(const bContext *C, ARegion *ar); +/* **************************************** */ +/* nla_select.c */ + +/* defines for left-right select tool */ +enum { + NLAEDIT_LRSEL_TEST = -1, + NLAEDIT_LRSEL_NONE, + NLAEDIT_LRSEL_LEFT, + NLAEDIT_LRSEL_RIGHT, +} eNlaEdit_LeftRightSelect_Mode; + +/* --- */ + +void NLA_OT_select_all_toggle(wmOperatorType *ot); +void NLA_OT_select_border(wmOperatorType *ot); +void NLA_OT_click_select(wmOperatorType *ot); + +/* **************************************** */ +/* nla_edit.c */ + +/* defines for snap strips + */ +enum { + NLAEDIT_SNAP_CFRA = 1, + NLAEDIT_SNAP_NEAREST_FRAME, + NLAEDIT_SNAP_NEAREST_SECOND, + NLAEDIT_SNAP_NEAREST_MARKER, +} eNlaEdit_Snap_Mode; + +/* --- */ + +void NLA_OT_tweakmode_enter(wmOperatorType *ot); +void NLA_OT_tweakmode_exit(wmOperatorType *ot); + +/* --- */ + +void NLA_OT_add_actionclip(wmOperatorType *ot); +void NLA_OT_add_transition(wmOperatorType *ot); + +void NLA_OT_add_meta(wmOperatorType *ot); +void NLA_OT_remove_meta(wmOperatorType *ot); + +void NLA_OT_duplicate(wmOperatorType *ot); +void NLA_OT_delete(wmOperatorType *ot); +void NLA_OT_split(wmOperatorType *ot); + +void NLA_OT_mute_toggle(wmOperatorType *ot); + +void NLA_OT_move_up(wmOperatorType *ot); +void NLA_OT_move_down(wmOperatorType *ot); + +void NLA_OT_apply_scale(wmOperatorType *ot); +void NLA_OT_clear_scale(wmOperatorType *ot); + +void NLA_OT_snap(wmOperatorType *ot); + +void NLA_OT_fmodifier_add(wmOperatorType *ot); + + +/* **************************************** */ +/* nla_channels.c */ + +void NLA_OT_channels_click(wmOperatorType *ot); + +void NLA_OT_add_tracks(wmOperatorType *ot); +void NLA_OT_delete_tracks(wmOperatorType *ot); + +/* **************************************** */ +/* nla_ops.c */ + +int nlaop_poll_tweakmode_off(bContext *C); +int nlaop_poll_tweakmode_on (bContext *C); + +short nlaedit_is_tweakmode_on(bAnimContext *ac); + +/* --- */ + +void nla_operatortypes(void); +void nla_keymap(wmWindowManager *wm); #endif /* ED_NLA_INTERN_H */ diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c new file mode 100644 index 00000000000..ad5f5174690 --- /dev/null +++ b/source/blender/editors/space_nla/nla_ops.c @@ -0,0 +1,307 @@ +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * + * Contributor(s): Joshua Leung (major recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_nla_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_nla.h" +#include "BKE_context.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "ED_anim_api.h" +#include "ED_space_api.h" +#include "ED_screen.h" +#include "ED_transform.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "nla_intern.h" // own include + +/* ************************** poll callbacks for operators **********************************/ + +/* tweakmode is NOT enabled */ +int nlaop_poll_tweakmode_off (bContext *C) +{ + Scene *scene; + + /* for now, we check 2 things: + * 1) active editor must be NLA + * 2) tweakmode is currently set as a 'per-scene' flag + * so that it will affect entire NLA data-sets, + * but not all AnimData blocks will be in tweakmode for + * various reasons + */ + if (ED_operator_nla_active(C) == 0) + return 0; + + scene= CTX_data_scene(C); + if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON)) + return 0; + + return 1; +} + +/* tweakmode IS enabled */ +int nlaop_poll_tweakmode_on (bContext *C) +{ + Scene *scene; + + /* for now, we check 2 things: + * 1) active editor must be NLA + * 2) tweakmode is currently set as a 'per-scene' flag + * so that it will affect entire NLA data-sets, + * but not all AnimData blocks will be in tweakmode for + * various reasons + */ + if (ED_operator_nla_active(C) == 0) + return 0; + + scene= CTX_data_scene(C); + if ((scene == NULL) || !(scene->flag & SCE_NLA_EDIT_ON)) + return 0; + + return 1; +} + +/* is tweakmode enabled - for use in NLA operator code */ +short nlaedit_is_tweakmode_on (bAnimContext *ac) +{ + if (ac && ac->scene) + return (ac->scene->flag & SCE_NLA_EDIT_ON); + return 0; +} + +/* ************************** registration - operator types **********************************/ + +void nla_operatortypes(void) +{ + /* view */ + WM_operatortype_append(NLA_OT_properties); + + /* channels */ + WM_operatortype_append(NLA_OT_channels_click); + + WM_operatortype_append(NLA_OT_add_tracks); + WM_operatortype_append(NLA_OT_delete_tracks); + + /* select */ + WM_operatortype_append(NLA_OT_click_select); + WM_operatortype_append(NLA_OT_select_border); + WM_operatortype_append(NLA_OT_select_all_toggle); + + /* edit */ + WM_operatortype_append(NLA_OT_tweakmode_enter); + WM_operatortype_append(NLA_OT_tweakmode_exit); + + WM_operatortype_append(NLA_OT_add_actionclip); + WM_operatortype_append(NLA_OT_add_transition); + + WM_operatortype_append(NLA_OT_add_meta); + WM_operatortype_append(NLA_OT_remove_meta); + + WM_operatortype_append(NLA_OT_duplicate); + WM_operatortype_append(NLA_OT_delete); + WM_operatortype_append(NLA_OT_split); + + WM_operatortype_append(NLA_OT_mute_toggle); + + WM_operatortype_append(NLA_OT_move_up); + WM_operatortype_append(NLA_OT_move_down); + + WM_operatortype_append(NLA_OT_apply_scale); + WM_operatortype_append(NLA_OT_clear_scale); + + WM_operatortype_append(NLA_OT_snap); + + WM_operatortype_append(NLA_OT_fmodifier_add); +} + +/* ************************** registration - keymaps **********************************/ + +static void nla_keymap_channels (wmWindowManager *wm, ListBase *keymap) +{ + /* NLA-specific (different to standard channels keymap) -------------------------- */ + /* selection */ + /* click-select */ + // XXX for now, only leftmouse.... + WM_keymap_add_item(keymap, "NLA_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "NLA_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1); + + /* channel operations */ + /* add tracks */ + WM_keymap_add_item(keymap, "NLA_OT_add_tracks", AKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "NLA_OT_add_tracks", AKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "above_selected", 1); + + /* delete tracks */ + WM_keymap_add_item(keymap, "NLA_OT_delete_tracks", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "NLA_OT_delete_tracks", DELKEY, KM_PRESS, 0, 0); + + /* General Animation Channels keymap (see anim_channels.c) ----------------------- */ + /* selection */ + /* borderselect - not in tweakmode */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0); + + /* deselect all - not in tweakmode */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1); + + /* settings */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); + WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0); + + /* settings - specialised hotkeys */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0); + + /* expand/collapse */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0); + + RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1); + RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1); +} + +static void nla_keymap_main (wmWindowManager *wm, ListBase *keymap) +{ + wmKeymapItem *kmi; + + /* selection */ + /* click select */ + WM_keymap_add_item(keymap, "NLA_OT_click_select", SELECTMOUSE, KM_PRESS, 0, 0); + kmi= WM_keymap_add_item(keymap, "NLA_OT_click_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", 1); + kmi= WM_keymap_add_item(keymap, "NLA_OT_click_select", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "left_right", NLAEDIT_LRSEL_TEST); + + /* deselect all */ + WM_keymap_add_item(keymap, "NLA_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "NLA_OT_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1); + + /* borderselect */ + WM_keymap_add_item(keymap, "NLA_OT_select_border", BKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "NLA_OT_select_border", BKEY, KM_PRESS, KM_ALT, 0)->ptr, "axis_range", 1); + + + /* editing */ + /* tweakmode + * - enter and exit are separate operators with the same hotkey... + * This works as they use different poll()'s + */ + WM_keymap_add_item(keymap, "NLA_OT_tweakmode_enter", TABKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "NLA_OT_tweakmode_exit", TABKEY, KM_PRESS, 0, 0); + + /* add strips */ + WM_keymap_add_item(keymap, "NLA_OT_add_actionclip", AKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "NLA_OT_add_transition", TKEY, KM_PRESS, KM_SHIFT, 0); + + /* meta-strips */ + WM_keymap_add_item(keymap, "NLA_OT_add_meta", GKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "NLA_OT_remove_meta", GKEY, KM_PRESS, KM_ALT, 0); + + /* duplicate */ + WM_keymap_add_item(keymap, "NLA_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); + + /* delete */ + WM_keymap_add_item(keymap, "NLA_OT_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "NLA_OT_delete", DELKEY, KM_PRESS, 0, 0); + + /* split */ + WM_keymap_add_item(keymap, "NLA_OT_split", YKEY, KM_PRESS, 0, 0); + + /* toggles */ + WM_keymap_add_item(keymap, "NLA_OT_mute_toggle", HKEY, KM_PRESS, 0, 0); + + /* move up */ + WM_keymap_add_item(keymap, "NLA_OT_move_up", PAGEUPKEY, KM_PRESS, 0, 0); + /* move down */ + WM_keymap_add_item(keymap, "NLA_OT_move_down", PAGEDOWNKEY, KM_PRESS, 0, 0); + + /* apply scale */ + WM_keymap_add_item(keymap, "NLA_OT_apply_scale", AKEY, KM_PRESS, KM_CTRL, 0); + /* clear scale */ + WM_keymap_add_item(keymap, "NLA_OT_clear_scale", SKEY, KM_PRESS, KM_ALT, 0); + + /* snap */ + WM_keymap_add_item(keymap, "NLA_OT_snap", SKEY, KM_PRESS, KM_SHIFT, 0); + + /* add f-modifier */ + WM_keymap_add_item(keymap, "NLA_OT_fmodifier_add", MKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0); + + /* transform system */ + transform_keymap_for_space(wm, keymap, SPACE_NLA); +} + +/* --------------- */ + +void nla_keymap(wmWindowManager *wm) +{ + ListBase *keymap; + + /* keymap for all regions */ + keymap= WM_keymap_listbase(wm, "NLA Generic", SPACE_NLA, 0); + WM_keymap_add_item(keymap, "NLA_OT_properties", NKEY, KM_PRESS, 0, 0); + + /* channels */ + /* Channels are not directly handled by the NLA Editor module, but are inherited from the Animation module. + * Most of the relevant operations, keymaps, drawing, etc. can therefore all be found in that module instead, as there + * are many similarities with the other Animation Editors. + * + * However, those operations which involve clicking on channels and/or the placement of them in the view are implemented here instead + */ + keymap= WM_keymap_listbase(wm, "NLA Channels", SPACE_NLA, 0); + nla_keymap_channels(wm, keymap); + + /* data */ + keymap= WM_keymap_listbase(wm, "NLA Data", SPACE_NLA, 0); + nla_keymap_main(wm, keymap); +} + diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c new file mode 100644 index 00000000000..dd9ef2621c5 --- /dev/null +++ b/source/blender/editors/space_nla/nla_select.c @@ -0,0 +1,614 @@ +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * + * Contributor(s): Joshua Leung (major recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_nla_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_nla.h" +#include "BKE_context.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_edit.h" +#include "ED_markers.h" +#include "ED_space_api.h" +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "nla_intern.h" // own include + +/* ******************** Utilities ***************************************** */ + +/* Convert SELECT_* flags to ACHANNEL_SETFLAG_* flags */ +static short selmodes_to_flagmodes (short sel) +{ + /* convert selection modes to selection modes */ + switch (sel) { + case SELECT_SUBTRACT: + return ACHANNEL_SETFLAG_CLEAR; + break; + + case SELECT_INVERT: + return ACHANNEL_SETFLAG_TOGGLE; + break; + + case SELECT_ADD: + default: + return ACHANNEL_SETFLAG_ADD; + break; + } +} + + +/* ******************** Deselect All Operator ***************************** */ +/* This operator works in one of three ways: + * 1) (de)select all (AKEY) - test if select all or deselect all + * 2) invert all (CTRL-IKEY) - invert selection of all keyframes + * 3) (de)select all - no testing is done; only for use internal tools as normal function... + */ + +enum { + DESELECT_STRIPS_NOTEST = 0, + DESELECT_STRIPS_TEST, + DESELECT_STRIPS_CLEARACTIVE, +} eDeselectNlaStrips; + +/* Deselects strips in the NLA Editor + * - This is called by the deselect all operator, as well as other ones! + * + * - test: check if select or deselect all (1) or clear all active (2) + * - sel: how to select keyframes + * 0 = deselect + * 1 = select + * 2 = invert + */ +static void deselect_nla_strips (bAnimContext *ac, short test, short sel) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + short smode; + + /* determine type-based settings */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS); + + /* filter data */ + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* See if we should be selecting or deselecting */ + if (test == DESELECT_STRIPS_TEST) { + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* if any strip is selected, break out, since we should now be deselecting */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->flag & NLASTRIP_FLAG_SELECT) { + sel= SELECT_SUBTRACT; + break; + } + } + + if (sel == SELECT_SUBTRACT) + break; + } + } + + /* convert selection modes to selection modes */ + smode= selmodes_to_flagmodes(sel); + + /* Now set the flags */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* apply same selection to all strips */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + /* set selection */ + if (test != DESELECT_STRIPS_CLEARACTIVE) + ACHANNEL_SET_FLAG(strip, smode, NLASTRIP_FLAG_SELECT); + + /* clear active flag */ + // TODO: for clear active, do we want to limit this to only doing this on a certain set of tracks though? + strip->flag &= ~NLASTRIP_FLAG_ACTIVE; + } + } + + /* Cleanup */ + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +static int nlaedit_deselectall_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* 'standard' behaviour - check if selected, then apply relevant selection */ + if (RNA_boolean_get(op->ptr, "invert")) + deselect_nla_strips(&ac, DESELECT_STRIPS_NOTEST, SELECT_INVERT); + else + deselect_nla_strips(&ac, DESELECT_STRIPS_TEST, SELECT_ADD); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_SELECT, NULL); + + return OPERATOR_FINISHED; +} + +void NLA_OT_select_all_toggle (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select All"; + ot->idname= "NLA_OT_select_all_toggle"; + + /* api callbacks */ + ot->exec= nlaedit_deselectall_exec; + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* props */ + RNA_def_boolean(ot->srna, "invert", 0, "Invert", ""); +} + +/* ******************** Border Select Operator **************************** */ +/* This operator currently works in one of three ways: + * -> BKEY - 1) all strips within region are selected (ACTKEYS_BORDERSEL_ALLSTRIPS) + * -> ALT-BKEY - depending on which axis of the region was larger... + * -> 2) x-axis, so select all frames within frame range (ACTKEYS_BORDERSEL_FRAMERANGE) + * -> 3) y-axis, so select all frames within channels that region included (ACTKEYS_BORDERSEL_CHANNELS) + */ + +/* defines for borderselect mode */ +enum { + NLA_BORDERSEL_ALLSTRIPS = 0, + NLA_BORDERSEL_FRAMERANGE, + NLA_BORDERSEL_CHANNELS, +} eActKeys_BorderSelect_Mode; + + +static void borderselect_nla_strips (bAnimContext *ac, rcti rect, short mode, short selectmode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + View2D *v2d= &ac->ar->v2d; + rctf rectf; + float ymin=(float)(-NLACHANNEL_HEIGHT), ymax=0; + + /* convert border-region to view coordinates */ + UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin+2, &rectf.xmin, &rectf.ymin); + UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax-2, &rectf.xmax, &rectf.ymax); + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* convert selection modes to selection modes */ + selectmode= selmodes_to_flagmodes(selectmode); + + /* loop over data, doing border select */ + for (ale= anim_data.first; ale; ale= ale->next) { + ymin= ymax - NLACHANNEL_STEP; + + /* perform vertical suitability check (if applicable) */ + if ( (mode == NLA_BORDERSEL_FRAMERANGE) || + !((ymax < rectf.ymin) || (ymin > rectf.ymax)) ) + { + /* loop over data selecting (only if NLA-Track) */ + if (ale->type == ANIMTYPE_NLATRACK) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* only select strips if they fall within the required ranges (if applicable) */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if ( (mode == NLA_BORDERSEL_CHANNELS) || + BKE_nlastrip_within_bounds(strip, rectf.xmin, rectf.xmax) ) + { + /* set selection */ + ACHANNEL_SET_FLAG(strip, selectmode, NLASTRIP_FLAG_SELECT); + + /* clear active flag */ + strip->flag &= ~NLASTRIP_FLAG_ACTIVE; + } + } + } + } + + /* set minimum extent to be the maximum of the next channel */ + ymax= ymin; + } + + /* cleanup */ + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +static int nlaedit_borderselect_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + rcti rect; + short mode=0, selectmode=0; + int event; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get settings from operator */ + rect.xmin= RNA_int_get(op->ptr, "xmin"); + rect.ymin= RNA_int_get(op->ptr, "ymin"); + rect.xmax= RNA_int_get(op->ptr, "xmax"); + rect.ymax= RNA_int_get(op->ptr, "ymax"); + + event= RNA_int_get(op->ptr, "event_type"); + if (event == LEFTMOUSE) // FIXME... hardcoded + selectmode = SELECT_ADD; + else + selectmode = SELECT_SUBTRACT; + + /* selection 'mode' depends on whether borderselect region only matters on one axis */ + if (RNA_boolean_get(op->ptr, "axis_range")) { + /* mode depends on which axis of the range is larger to determine which axis to use + * - checking this in region-space is fine, as it's fundamentally still going to be a different rect size + * - the frame-range select option is favoured over the channel one (x over y), as frame-range one is often + * used for tweaking timing when "blocking", while channels is not that useful... + */ + if ((rect.xmax - rect.xmin) >= (rect.ymax - rect.ymin)) + mode= NLA_BORDERSEL_FRAMERANGE; + else + mode= NLA_BORDERSEL_CHANNELS; + } + else + mode= NLA_BORDERSEL_ALLSTRIPS; + + /* apply borderselect action */ + borderselect_nla_strips(&ac, rect, mode, selectmode); + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_SELECT, NULL); + + return OPERATOR_FINISHED; +} + +void NLA_OT_select_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Border Select"; + ot->idname= "NLA_OT_select_border"; + + /* api callbacks */ + ot->invoke= WM_border_select_invoke; + ot->exec= nlaedit_borderselect_exec; + ot->modal= WM_border_select_modal; + + ot->poll= nlaop_poll_tweakmode_off; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* rna */ + RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX); + + RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", ""); +} + +/* ******************** Mouse-Click Select Operator *********************** */ +/* This operator works in one of 2 ways: + * 1) Select the strip directly under the mouse + * 2) Select all the strips to one side of the mouse + */ + +/* defines for left-right select tool */ +static EnumPropertyItem prop_nlaedit_leftright_select_types[] = { + {NLAEDIT_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""}, + {NLAEDIT_LRSEL_NONE, "OFF", 0, "Don't select", ""}, + {NLAEDIT_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""}, + {NLAEDIT_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""}, + {0, NULL, 0, NULL, NULL} +}; + +/* sensitivity factor for frame-selections */ +#define FRAME_CLICK_THRESH 0.1f + + +/* ------------------- */ + +/* option 1) select strip directly under mouse */ +static void mouse_nla_strips (bContext *C, bAnimContext *ac, int mval[2], short select_mode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale = NULL; + int filter; + + View2D *v2d= &ac->ar->v2d; + Scene *scene= ac->scene; + NlaStrip *strip = NULL; + int channel_index; + float xmin, xmax, dummy; + float x, y; + + + /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */ + UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); + UI_view2d_listview_view_to_cell(v2d, 0, NLACHANNEL_STEP, 0, (float)NLACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index); + + /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click + * (that is the size of keyframe icons, so user should be expecting similar tolerances) + */ + UI_view2d_region_to_view(v2d, mval[0]-7, mval[1], &xmin, &dummy); + UI_view2d_region_to_view(v2d, mval[0]+7, mval[1], &xmax, &dummy); + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* try to get channel */ + ale= BLI_findlink(&anim_data, channel_index); + if (ale == NULL) { + /* channel not found */ + printf("Error: animation channel (index = %d) not found in mouse_nla_strips() \n", channel_index); + BLI_freelistN(&anim_data); + return; + } + else { + /* found some channel - we only really should do somethign when its an Nla-Track */ + if (ale->type == ANIMTYPE_NLATRACK) { + NlaTrack *nlt= (NlaTrack *)ale->data; + + /* loop over NLA-strips in this track, trying to find one which occurs in the necessary bounds */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (BKE_nlastrip_within_bounds(strip, xmin, xmax)) + break; + } + } + + /* remove active channel from list of channels for separate treatment (since it's needed later on) */ + BLI_remlink(&anim_data, ale); + + /* free list of channels, since it's not used anymore */ + BLI_freelistN(&anim_data); + } + + /* if currently in tweakmode, exit tweakmode before changing selection states + * now that we've found our target... + */ + if (scene->flag & SCE_NLA_EDIT_ON) + WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL); + + /* for replacing selection, firstly need to clear existing selection */ + if (select_mode == SELECT_REPLACE) { + /* reset selection mode for next steps */ + select_mode = SELECT_ADD; + + /* deselect all strips */ + deselect_nla_strips(ac, 0, SELECT_SUBTRACT); + + /* deselect all other channels first */ + ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + + /* Highlight NLA-Track */ + if (ale->type == ANIMTYPE_NLATRACK) { + NlaTrack *nlt= (NlaTrack *)ale->data; + + nlt->flag |= NLATRACK_SELECTED; + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK); + } + } + + /* only select strip if we clicked on a valid channel and hit something */ + if (ale) { + /* select the strip accordingly (if a matching one was found) */ + if (strip) { + select_mode= selmodes_to_flagmodes(select_mode); + ACHANNEL_SET_FLAG(strip, select_mode, NLASTRIP_FLAG_SELECT); + + /* if we selected it, we can make it active too + * - we always need to clear the active strip flag though... + */ + deselect_nla_strips(ac, DESELECT_STRIPS_CLEARACTIVE, 0); + if (strip->flag & NLASTRIP_FLAG_SELECT) + strip->flag |= NLASTRIP_FLAG_ACTIVE; + } + + /* free this channel */ + MEM_freeN(ale); + } +} + +/* Option 2) Selects all the strips on either side of the current frame (depends on which side the mouse is on) */ +static void nlaedit_mselect_leftright (bContext *C, bAnimContext *ac, short leftright, short select_mode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + Scene *scene= ac->scene; + float xmin, xmax; + + /* if currently in tweakmode, exit tweakmode first */ + if (scene->flag & SCE_NLA_EDIT_ON) + WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL); + + /* if select mode is replace, deselect all keyframes (and channels) first */ + if (select_mode==SELECT_REPLACE) { + select_mode= SELECT_ADD; + + /* deselect all other channels and keyframes */ + ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + deselect_nla_strips(ac, 0, SELECT_SUBTRACT); + } + + /* get range, and get the right flag-setting mode */ + if (leftright == NLAEDIT_LRSEL_LEFT) { + xmin = MINAFRAMEF; + xmax = (float)(CFRA + FRAME_CLICK_THRESH); + } + else { + xmin = (float)(CFRA - FRAME_CLICK_THRESH); + xmax = MAXFRAMEF; + } + + select_mode= selmodes_to_flagmodes(select_mode); + + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* select strips on the side where most data occurs */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* check each strip to see if it is appropriate */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (BKE_nlastrip_within_bounds(strip, xmin, xmax)) { + ACHANNEL_SET_FLAG(strip, select_mode, NLASTRIP_FLAG_SELECT); + } + } + } + + /* Cleanup */ + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +/* handle clicking */ +static int nlaedit_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + bAnimContext ac; + Scene *scene; + ARegion *ar; + View2D *v2d; + short selectmode; + int mval[2]; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get useful pointers from animation context data */ + scene= ac.scene; + ar= ac.ar; + v2d= &ar->v2d; + + /* get mouse coordinates (in region coordinates) */ + mval[0]= (event->x - ar->winrct.xmin); + mval[1]= (event->y - ar->winrct.ymin); + + /* select mode is either replace (deselect all, then add) or add/extend */ + if (RNA_boolean_get(op->ptr, "extend")) + selectmode= SELECT_INVERT; + else + selectmode= SELECT_REPLACE; + + /* figure out action to take */ + if (RNA_enum_get(op->ptr, "left_right")) { + /* select all keys on same side of current frame as mouse */ + float x; + + UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, NULL); + if (x < CFRA) + RNA_int_set(op->ptr, "left_right", NLAEDIT_LRSEL_LEFT); + else + RNA_int_set(op->ptr, "left_right", NLAEDIT_LRSEL_RIGHT); + + nlaedit_mselect_leftright(C, &ac, RNA_enum_get(op->ptr, "left_right"), selectmode); + } + else { + /* select strips based upon mouse position */ + mouse_nla_strips(C, &ac, mval, selectmode); + } + + /* set notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_SELECT, NULL); + + /* for tweak grab to work */ + return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH; +} + +void NLA_OT_click_select (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Mouse Select"; + ot->idname= "NLA_OT_click_select"; + + /* api callbacks - absolutely no exec() this yet... */ + ot->invoke= nlaedit_clickselect_invoke; + ot->poll= ED_operator_nla_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* id-props */ + // XXX should we make this into separate operators? + RNA_def_enum(ot->srna, "left_right", prop_nlaedit_leftright_select_types, 0, "Left Right", ""); // CTRLKEY + RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY +} + +/* *********************************************** */ diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 6e1a97dea34..923208f3b35 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -29,6 +29,7 @@ #include #include +#include "DNA_anim_types.h" #include "DNA_nla_types.h" #include "DNA_object_types.h" #include "DNA_space_types.h" @@ -41,10 +42,16 @@ #include "BLI_arithb.h" #include "BLI_rand.h" +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_nla.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_screen.h" +#include "BKE_utildefines.h" +#include "ED_anim_api.h" +#include "ED_markers.h" #include "ED_space_api.h" #include "ED_screen.h" @@ -57,20 +64,58 @@ #include "UI_resources.h" #include "UI_view2d.h" -#include "ED_markers.h" - #include "nla_intern.h" // own include +/* ******************** manage regions ********************* */ + +ARegion *nla_has_buttons_region(ScrArea *sa) +{ + ARegion *ar, *arnew; + + for (ar= sa->regionbase.first; ar; ar= ar->next) { + if (ar->regiontype==RGN_TYPE_UI) + return ar; + } + + /* add subdiv level; after main */ + for (ar= sa->regionbase.first; ar; ar= ar->next) { + if (ar->regiontype==RGN_TYPE_WINDOW) + break; + } + + /* is error! */ + if (ar==NULL) return NULL; + + arnew= MEM_callocN(sizeof(ARegion), "buttons for nla"); + + BLI_insertlinkafter(&sa->regionbase, ar, arnew); + arnew->regiontype= RGN_TYPE_UI; + arnew->alignment= RGN_ALIGN_RIGHT; + + arnew->flag = RGN_FLAG_HIDDEN; + + return arnew; +} + + + /* ******************** default callbacks for nla space ***************** */ static SpaceLink *nla_new(const bContext *C) { + Scene *scene= CTX_data_scene(C); ARegion *ar; SpaceNla *snla; snla= MEM_callocN(sizeof(SpaceNla), "initnla"); snla->spacetype= SPACE_NLA; + /* allocate DopeSheet data for NLA Editor */ + snla->ads= MEM_callocN(sizeof(bDopeSheet), "NlaEdit DopeSheet"); + + /* set auto-snapping settings */ + snla->autosnap = SACTSNAP_FRAME; + /* header */ ar= MEM_callocN(sizeof(ARegion), "header for nla"); @@ -78,13 +123,23 @@ static SpaceLink *nla_new(const bContext *C) ar->regiontype= RGN_TYPE_HEADER; ar->alignment= RGN_ALIGN_BOTTOM; - /* channel list region XXX */ - ar= MEM_callocN(sizeof(ARegion), "area region from do_versions"); + /* channel list region */ + ar= MEM_callocN(sizeof(ARegion), "channel list for nla"); BLI_addtail(&snla->regionbase, ar); ar->regiontype= RGN_TYPE_CHANNELS; ar->alignment= RGN_ALIGN_LEFT; - ar->v2d.scroll = (V2D_SCROLL_RIGHT|V2D_SCROLL_BOTTOM); + /* only need to set these settings since this will use the 'stack' configuration */ + ar->v2d.scroll = V2D_SCROLL_BOTTOM; + ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL; + + /* ui buttons */ + ar= MEM_callocN(sizeof(ARegion), "buttons area for nla"); + + BLI_addtail(&snla->regionbase, ar); + ar->regiontype= RGN_TYPE_UI; + ar->alignment= RGN_ALIGN_RIGHT; + ar->flag = RGN_FLAG_HIDDEN; /* main area */ ar= MEM_callocN(sizeof(ARegion), "main area for nla"); @@ -92,29 +147,26 @@ static SpaceLink *nla_new(const bContext *C) BLI_addtail(&snla->regionbase, ar); ar->regiontype= RGN_TYPE_WINDOW; - ar->v2d.tot.xmin= 1.0f; - ar->v2d.tot.ymin= 0.0f; - ar->v2d.tot.xmax= 1000.0f; - ar->v2d.tot.ymax= 1000.0f; + ar->v2d.tot.xmin= (float)(SFRA-10); + ar->v2d.tot.ymin= -500.0f; + ar->v2d.tot.xmax= (float)(EFRA+10); + ar->v2d.tot.ymax= 0.0f; - ar->v2d.cur.xmin= -5.0f; - ar->v2d.cur.ymin= 0.0f; - ar->v2d.cur.xmax= 65.0f; - ar->v2d.cur.ymax= 1000.0f; + ar->v2d.cur = ar->v2d.tot; ar->v2d.min[0]= 0.0f; - ar->v2d.min[1]= 0.0f; + ar->v2d.min[1]= 0.0f; ar->v2d.max[0]= MAXFRAMEF; - ar->v2d.max[1]= 1000.0f; - - ar->v2d.minzoom= 0.1f; - ar->v2d.maxzoom= 50.0f; - - ar->v2d.scroll |= (V2D_SCROLL_BOTTOM|V2D_SCROLL_SCALE_HORIZONTAL); + ar->v2d.max[1]= 10000.0f; + + ar->v2d.minzoom= 0.01f; + ar->v2d.maxzoom= 50; + ar->v2d.scroll = (V2D_SCROLL_BOTTOM|V2D_SCROLL_SCALE_HORIZONTAL); ar->v2d.scroll |= (V2D_SCROLL_RIGHT); ar->v2d.keepzoom= V2D_LOCKZOOM_Y; - + ar->v2d.align= V2D_ALIGN_NO_POS_Y; + ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL; return (SpaceLink *)snla; } @@ -122,15 +174,25 @@ static SpaceLink *nla_new(const bContext *C) /* not spacelink itself */ static void nla_free(SpaceLink *sl) { -// SpaceNla *snla= (SpaceNla*) sl; + SpaceNla *snla= (SpaceNla*) sl; + if (snla->ads) { + BLI_freelistN(&snla->ads->chanbase); + MEM_freeN(snla->ads); + } } /* spacetype; init callback */ static void nla_init(struct wmWindowManager *wm, ScrArea *sa) { + SpaceNla *snla= (SpaceNla *)sa->spacedata.first; + + /* init dopesheet data if non-existant (i.e. for old files) */ + if (snla->ads == NULL) + snla->ads= MEM_callocN(sizeof(bDopeSheet), "NlaEdit DopeSheet"); + ED_area_tag_refresh(sa); } static SpaceLink *nla_duplicate(SpaceLink *sl) @@ -138,15 +200,33 @@ static SpaceLink *nla_duplicate(SpaceLink *sl) SpaceNla *snlan= MEM_dupallocN(sl); /* clear or remove stuff from old */ + snlan->ads= MEM_dupallocN(snlan->ads); return (SpaceLink *)snlan; } +/* add handlers, stuff you only do once or on area/region changes */ +static void nla_channel_area_init(wmWindowManager *wm, ARegion *ar) +{ + ListBase *keymap; + + UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy); + + /* own keymap */ + // TODO: cannot use generic copy, need special NLA version + keymap= WM_keymap_listbase(wm, "NLA Channels", SPACE_NLA, 0); /* XXX weak? */ + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + keymap= WM_keymap_listbase(wm, "NLA Generic", SPACE_NLA, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); +} + +/* draw entirely, view changes should be handled here */ static void nla_channel_area_draw(const bContext *C, ARegion *ar) { - /* draw entirely, view changes should be handled here */ - // SpaceNla *snla= (SpaceNla*)CTX_wm_space_data(C); - // View2D *v2d= &ar->v2d; + SpaceNla *snla= (SpaceNla*)CTX_wm_space_data(C); + bAnimContext ac; + View2D *v2d= &ar->v2d; + View2DScrollers *scrollers; float col[3]; /* clear and setup matrix */ @@ -154,15 +234,20 @@ static void nla_channel_area_draw(const bContext *C, ARegion *ar) glClearColor(col[0], col[1], col[2], 0.0); glClear(GL_COLOR_BUFFER_BIT); - // UI_view2d_view_ortho(C, v2d); - - /* data... */ + UI_view2d_view_ortho(C, v2d); + /* data */ + if (ANIM_animdata_get_context(C, &ac)) { + draw_nla_channel_list(&ac, snla, ar); + } /* reset view matrix */ - //UI_view2d_view_restore(C); + UI_view2d_view_restore(C); - /* scrollers? */ + /* scrollers */ + scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY); + UI_view2d_scrollers_draw(C, v2d, scrollers); + UI_view2d_scrollers_free(scrollers); } @@ -174,16 +259,22 @@ static void nla_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, "NLA", SPACE_NLA, 0); /* XXX weak? */ + keymap= WM_keymap_listbase(wm, "NLA Data", SPACE_NLA, 0); /* XXX weak? */ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + keymap= WM_keymap_listbase(wm, "NLA Generic", SPACE_NLA, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); } static void nla_main_area_draw(const bContext *C, ARegion *ar) { /* draw entirely, view changes should be handled here */ - // SpaceNla *snla= (SpaceNla*)CTX_wm_space_data(C); + SpaceNla *snla= (SpaceNla*)CTX_wm_space_data(C); + bAnimContext ac; View2D *v2d= &ar->v2d; + View2DGrid *grid; + View2DScrollers *scrollers; float col[3]; + short unit=0, flag=0; /* clear and setup matrix */ UI_GetThemeColor3fv(TH_BACK, col); @@ -191,25 +282,46 @@ static void nla_main_area_draw(const bContext *C, ARegion *ar) glClear(GL_COLOR_BUFFER_BIT); UI_view2d_view_ortho(C, v2d); - - /* data... */ + /* time grid */ + unit= (snla->flag & SNLA_DRAWTIME)? V2D_UNIT_SECONDS : V2D_UNIT_FRAMES; + grid= UI_view2d_grid_calc(C, v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY, ar->winx, ar->winy); + UI_view2d_grid_draw(C, v2d, grid, V2D_GRIDLINES_ALL); + UI_view2d_grid_free(grid); + + /* data */ + if (ANIM_animdata_get_context(C, &ac)) { + /* strips and backdrops */ + draw_nla_main_data(&ac, snla, ar); + + /* text draw cached, in pixelspace now */ + UI_view2d_text_cache_draw(ar); + } + + UI_view2d_view_ortho(C, v2d); + + /* current frame */ + if (snla->flag & SNLA_DRAWTIME) flag |= DRAWCFRA_UNIT_SECONDS; + if ((snla->flag & SNLA_NODRAWCFRANUM)==0) flag |= DRAWCFRA_SHOW_NUMBOX; + ANIM_draw_cfra(C, v2d, flag); + + /* markers */ + UI_view2d_view_orthoSpecial(C, v2d, 1); + draw_markers_time(C, 0); + + /* preview range */ + UI_view2d_view_ortho(C, v2d); + ANIM_draw_previewrange(C, v2d); /* reset view matrix */ UI_view2d_view_restore(C); - /* scrollers? */ + /* scrollers */ + scrollers= UI_view2d_scrollers_calc(C, v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY); + UI_view2d_scrollers_draw(C, v2d, scrollers); + UI_view2d_scrollers_free(scrollers); } -void nla_operatortypes(void) -{ - -} - -void nla_keymap(struct wmWindowManager *wm) -{ - -} /* add handlers, stuff you only do once or on area/region changes */ static void nla_header_area_init(wmWindowManager *wm, ARegion *ar) @@ -239,9 +351,144 @@ static void nla_header_area_draw(const bContext *C, ARegion *ar) UI_view2d_view_restore(C); } +/* add handlers, stuff you only do once or on area/region changes */ +static void nla_buttons_area_init(wmWindowManager *wm, ARegion *ar) +{ + ListBase *keymap; + + ED_region_panels_init(wm, ar); + + keymap= WM_keymap_listbase(wm, "NLA Generic", SPACE_NLA, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); +} + +static void nla_buttons_area_draw(const bContext *C, ARegion *ar) +{ + ED_region_panels(C, ar, 1, NULL); +} + +static void nla_region_listener(ARegion *ar, wmNotifier *wmn) +{ + /* context changes */ + switch(wmn->category) { + case NC_ANIMATION: + ED_region_tag_redraw(ar); + break; + case NC_SCENE: + switch(wmn->data) { + case ND_OB_ACTIVE: + case ND_FRAME: + case ND_MARKERS: + ED_region_tag_redraw(ar); + break; + } + break; + case NC_OBJECT: + switch(wmn->data) { + case ND_BONE_ACTIVE: + case ND_BONE_SELECT: + case ND_KEYS: + ED_region_tag_redraw(ar); + break; + } + break; + default: + if(wmn->data==ND_KEYS) + ED_region_tag_redraw(ar); + break; + } +} + + static void nla_main_area_listener(ARegion *ar, wmNotifier *wmn) { /* context changes */ + switch(wmn->category) { + case NC_ANIMATION: + ED_region_tag_redraw(ar); + break; + case NC_SCENE: + switch(wmn->data) { + case ND_OB_ACTIVE: + case ND_FRAME: + case ND_MARKERS: + ED_region_tag_redraw(ar); + break; + } + break; + case NC_OBJECT: + switch(wmn->data) { + case ND_BONE_ACTIVE: + case ND_BONE_SELECT: + case ND_KEYS: + case ND_TRANSFORM: + ED_region_tag_redraw(ar); + break; + } + break; + default: + if(wmn->data==ND_KEYS) + ED_region_tag_redraw(ar); + } +} + +static void nla_channel_area_listener(ARegion *ar, wmNotifier *wmn) +{ + /* context changes */ + switch(wmn->category) { + case NC_ANIMATION: + ED_region_tag_redraw(ar); + break; + case NC_SCENE: + switch(wmn->data) { + case ND_OB_ACTIVE: + ED_region_tag_redraw(ar); + break; + } + break; + case NC_OBJECT: + switch(wmn->data) { + case ND_BONE_ACTIVE: + case ND_BONE_SELECT: + case ND_KEYS: + ED_region_tag_redraw(ar); + break; + } + break; + default: + if(wmn->data==ND_KEYS) + ED_region_tag_redraw(ar); + } +} + +/* editor level listener */ +static void nla_listener(ScrArea *sa, wmNotifier *wmn) +{ + /* context changes */ + switch (wmn->category) { + case NC_ANIMATION: + // TODO: filter specific types of changes? + ED_area_tag_refresh(sa); + break; + case NC_SCENE: + /*switch (wmn->data) { + case ND_OB_ACTIVE: + case ND_OB_SELECT: + ED_area_tag_refresh(sa); + break; + }*/ + ED_area_tag_refresh(sa); + break; + case NC_OBJECT: + /*switch (wmn->data) { + case ND_BONE_SELECT: + case ND_BONE_ACTIVE: + ED_area_tag_refresh(sa); + break; + }*/ + ED_area_tag_refresh(sa); + break; + } } /* only called once, from space/spacetypes.c */ @@ -257,6 +504,7 @@ void ED_spacetype_nla(void) st->init= nla_init; st->duplicate= nla_duplicate; st->operatortypes= nla_operatortypes; + st->listener= nla_listener; st->keymap= nla_keymap; /* regions: main window */ @@ -265,7 +513,7 @@ void ED_spacetype_nla(void) art->init= nla_main_area_init; art->draw= nla_main_area_draw; art->listener= nla_main_area_listener; - art->keymapflag= ED_KEYMAP_VIEW2D|ED_KEYMAP_FRAMES; + art->keymapflag= ED_KEYMAP_VIEW2D/*|ED_KEYMAP_MARKERS*/|ED_KEYMAP_ANIMATION|ED_KEYMAP_FRAMES; BLI_addhead(&st->regiontypes, art); @@ -286,11 +534,25 @@ void ED_spacetype_nla(void) art->minsizex= 200; art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D; - //art->init= nla_channel_area_init; + art->init= nla_channel_area_init; art->draw= nla_channel_area_draw; + art->listener= nla_channel_area_listener; BLI_addhead(&st->regiontypes, art); + /* regions: UI buttons */ + art= MEM_callocN(sizeof(ARegionType), "spacetype nla region"); + art->regionid = RGN_TYPE_UI; + art->minsizex= 200; + art->keymapflag= ED_KEYMAP_UI; + art->listener= nla_region_listener; + art->init= nla_buttons_area_init; + art->draw= nla_buttons_area_draw; + + BLI_addhead(&st->regiontypes, art); + + nla_buttons_register(art); + BKE_spacetype_register(st); } diff --git a/source/blender/editors/space_outliner/outliner.c b/source/blender/editors/space_outliner/outliner.c index 8017b8437ff..6563a7dc7df 100644 --- a/source/blender/editors/space_outliner/outliner.c +++ b/source/blender/editors/space_outliner/outliner.c @@ -949,7 +949,6 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i /* NLA Data */ if (adt->nla_tracks.first) { -#if 0 TreeElement *tenla= outliner_add_element(soops, &te->subtree, adt, te, TSE_NLA, 0); NlaTrack *nlt; int a= 0; @@ -957,17 +956,18 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i tenla->name= "NLA Tracks"; for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { - TreeElement *tenlt= outliner_add_element(soops, &te->subtree, nlt, te, TSE_NLA_TRACK, a); - bActionStrip *strip; + TreeElement *tenlt= outliner_add_element(soops, &tenla->subtree, nlt, tenla, TSE_NLA_TRACK, a); + NlaStrip *strip; TreeElement *ten; int b= 0; - for (strip=nlt->strips.first; strip; strip=strip->next, a++) { - ten= outliner_add_element(soops, &tenla->subtree, strip->act, tenla, TSE_NLA_ACTION, a); + tenlt->name= nlt->name; + + for (strip=nlt->strips.first; strip; strip=strip->next, b++) { + ten= outliner_add_element(soops, &tenlt->subtree, strip->act, tenlt, TSE_NLA_ACTION, b); if(ten) ten->directdata= strip; } } -#endif } } else if(type==TSE_SEQUENCE) { @@ -3518,6 +3518,8 @@ static void tselem_draw_icon(float x, float y, TreeStoreElem *tselem, TreeElemen UI_icon_draw(x, y, ICON_ANIM_DATA); break; // xxx case TSE_NLA: UI_icon_draw(x, y, ICON_NLA); break; + case TSE_NLA_TRACK: + UI_icon_draw(x, y, ICON_NLA); break; // XXX case TSE_NLA_ACTION: UI_icon_draw(x, y, ICON_ACTION); break; case TSE_DEFGROUP_BASE: diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 48c904121a5..204298f2b70 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -94,6 +94,7 @@ typedef struct TreeElement { #define TSE_RNA_STRUCT 30 #define TSE_RNA_PROPERTY 31 #define TSE_RNA_ARRAY_ELEM 32 +#define TSE_NLA_TRACK 33 /* outliner search flags */ #define OL_FIND 0 diff --git a/source/blender/editors/space_time/time_header.c b/source/blender/editors/space_time/time_header.c index 29f31671670..9ffce53e572 100644 --- a/source/blender/editors/space_time/time_header.c +++ b/source/blender/editors/space_time/time_header.c @@ -373,6 +373,7 @@ static uiBlock *time_framemenu(bContext *C, ARegion *ar, void *arg_unused) #define B_REDRAWALL 750 #define B_TL_REW 751 #define B_TL_PLAY 752 +#define B_TL_RPLAY 760 #define B_TL_FF 753 #define B_TL_PREVKEY 754 #define B_TL_NEXTKEY 755 @@ -415,6 +416,18 @@ void do_time_buttons(bContext *C, void *arg, int event) sad->ar= time_top_left_3dwindow(screen); } + break; + case B_TL_RPLAY: + ED_screen_animation_timer(C, stime->redraws, -1); + + /* update region if TIME_REGION was set, to leftmost 3d window */ + if(screen->animtimer && (stime->redraws & TIME_REGION)) { + wmTimer *wt= screen->animtimer; + ScreenAnimData *sad= wt->customdata; + + sad->ar= time_top_left_3dwindow(screen); + } + break; case B_TL_STOP: ED_screen_animation_timer(C, 0, 0); @@ -536,9 +549,12 @@ void time_header_buttons(const bContext *C, ARegion *ar) xco += (short)(4.5 * XIC + 16); + /* MINAFRAMEF not MINFRAMEF, since MINAFRAMEF allows to set current frame negative + * to facilitate easier keyframing in some situations + */ uiDefButI(block, NUM, B_NEWFRAME, "", xco,yco, (int)3.5*XIC,YIC, - &(scene->r.cfra), MINFRAMEF, MAXFRAMEF, 0, 0, + &(scene->r.cfra), MINAFRAMEF, MAXFRAMEF, 0, 0, "Displays Current Frame of animation"); xco += (short)(3.5 * XIC + 16); @@ -550,14 +566,27 @@ void time_header_buttons(const bContext *C, ARegion *ar) xco, yco, XIC, YIC, 0, 0, 0, 0, 0, "Skip to previous keyframe (Ctrl PageDown)"); xco+= XIC+4; - if(CTX_wm_screen(C)->animtimer) + if(CTX_wm_screen(C)->animtimer) { + /* pause button is drawn centered between the two other buttons for now (saves drawing 2 buttons, or having position changes) */ + xco+= XIC/2 + 2; + uiDefIconBut(block, BUT, B_TL_STOP, ICON_PAUSE, xco, yco, XIC, YIC, 0, 0, 0, 0, 0, "Stop Playing Timeline"); - else + + xco+= XIC/2 + 2; + } + else { + // FIXME: the icon for this is crap + uiDefIconBut(block, BUT, B_TL_RPLAY, ICON_REW/*ICON_PLAY*/, + xco, yco, XIC, YIC, 0, 0, 0, 0, 0, "Play Timeline in Reverse"); + + xco+= XIC+4; + uiDefIconBut(block, BUT, B_TL_PLAY, ICON_PLAY, xco, yco, XIC, YIC, 0, 0, 0, 0, 0, "Play Timeline "); - + } xco+= XIC+4; + uiDefIconBut(block, BUT, B_TL_NEXTKEY, ICON_NEXT_KEYFRAME, xco, yco, XIC, YIC, 0, 0, 0, 0, 0, "Skip to next keyframe (Ctrl PageUp)"); xco+= XIC+4; diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index e0ec878ea4a..ec3b716e616 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -37,6 +37,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_constraint_types.h" @@ -52,6 +53,7 @@ #include "BLI_blenlib.h" #include "BLI_arithb.h" +#include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_constraint.h" @@ -61,6 +63,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_utildefines.h" @@ -2019,10 +2022,9 @@ static void draw_ebones(View3D *v3d, RegionView3D *rv3d, Object *ob, int dt) */ static void draw_pose_paths(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob) { + AnimData *adt= BKE_animdata_from_id(&ob->id); bArmature *arm= ob->data; bPoseChannel *pchan; - // bAction *act; // XXX old animsys - watch it! - // bActionChannel *achan; ActKeyColumn *ak; ListBase keys; float *fp, *fp_start; @@ -2168,14 +2170,11 @@ static void draw_pose_paths(Scene *scene, View3D *v3d, RegionView3D *rv3d, Objec /* build list of all keyframes in active action for pchan */ keys.first = keys.last = NULL; - #if 0 // XXX old animation system - act= ob->action; - if (act) { - achan= get_action_channel(act, pchan->name); - if (achan) - ipo_to_keylist(achan->ipo, &keys, NULL, NULL); + if (adt) { + bActionGroup *agrp= action_groups_find_named(adt->action, pchan->name); + if (agrp) + agroup_to_keylist(adt, agrp, &keys, NULL); } - #endif // XXX old animation system /* Draw slightly-larger yellow dots at each keyframe */ UI_ThemeColor(TH_VERTEX_SELECT); @@ -2254,6 +2253,7 @@ static void ghost_poses_tag_unselected(Object *ob, short unset) static void draw_ghost_poses_range(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base) { Object *ob= base->object; + AnimData *adt= BKE_animdata_from_id(&ob->id); bArmature *arm= ob->data; bPose *posen, *poseo; float start, end, stepsize, range, colfac; @@ -2290,7 +2290,7 @@ static void draw_ghost_poses_range(Scene *scene, View3D *v3d, RegionView3D *rv3d colfac = (end - (float)CFRA) / range; UI_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0*sqrt(colfac))); - //do_all_pose_actions(scene, ob); // XXX old animation system + BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL); where_is_pose(scene, ob); draw_pose_channels(scene, v3d, rv3d, base, OB_WIRE); } @@ -2315,22 +2315,22 @@ static void draw_ghost_poses_range(Scene *scene, View3D *v3d, RegionView3D *rv3d static void draw_ghost_poses_keys(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base) { Object *ob= base->object; - bAction *act= ob->action; // XXX old animsys stuff... watch it! + AnimData *adt= BKE_animdata_from_id(&ob->id); + bAction *act= (adt) ? adt->action : NULL; bArmature *arm= ob->data; bPose *posen, *poseo; ListBase keys= {NULL, NULL}; - ActKeysInc aki = {0, 0, 0}; ActKeyColumn *ak, *akn; float start, end, range, colfac, i; int cfrao, flago; - aki.start= start = (float)arm->ghostsf; - aki.end= end = (float)arm->ghostef; + start = (float)arm->ghostsf; + end = (float)arm->ghostef; if (end <= start) return; /* get keyframes - then clip to only within range */ - action_to_keylist(act, &keys, NULL, &aki); + action_to_keylist(adt, act, &keys, NULL); range= 0; for (ak= keys.first; ak; ak= akn) { akn= ak->next; @@ -2366,7 +2366,7 @@ static void draw_ghost_poses_keys(Scene *scene, View3D *v3d, RegionView3D *rv3d, CFRA= (int)ak->cfra; - //do_all_pose_actions(scene, ob); // XXX old animation system + BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL); where_is_pose(scene, ob); draw_pose_channels(scene, v3d, rv3d, base, OB_WIRE); } @@ -2391,38 +2391,27 @@ static void draw_ghost_poses_keys(Scene *scene, View3D *v3d, RegionView3D *rv3d, static void draw_ghost_poses(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base) { Object *ob= base->object; + AnimData *adt= BKE_animdata_from_id(&ob->id); bArmature *arm= ob->data; bPose *posen, *poseo; - //bActionStrip *strip; float cur, start, end, stepsize, range, colfac, actframe, ctime; - int cfrao, maptime, flago; + int cfrao, flago; /* pre conditions, get an action with sufficient frames */ - //if (ob->action==NULL) - // return; + if ELEM(NULL, adt, adt->action) + return; - calc_action_range(ob->action, &start, &end, 0); + calc_action_range(adt->action, &start, &end, 0); if (start == end) return; stepsize= (float)(arm->ghostsize); range= (float)(arm->ghostep)*stepsize + 0.5f; /* plus half to make the for loop end correct */ -#if 0 // XXX old animation system - /* we only map time for armature when an active strip exists */ - for (strip=ob->nlastrips.first; strip; strip=strip->next) - if (strip->flag & ACTSTRIP_ACTIVE) - break; -#endif // XXX old animsys - - //maptime= (strip!=NULL); - maptime= 0; - /* store values */ ob->flag &= ~OB_POSEMODE; cfrao= CFRA; - if (maptime) actframe= get_action_frame(ob, (float)CFRA); - else actframe= (float)CFRA; + actframe= BKE_nla_tweakedit_remap(adt, (float)CFRA, 0); flago= arm->flag; arm->flag &= ~(ARM_DRAWNAMES|ARM_DRAWAXES); @@ -2444,11 +2433,10 @@ static void draw_ghost_poses(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base /* only within action range */ if (actframe+ctime >= start && actframe+ctime <= end) { - if (maptime) CFRA= (int)get_action_frame_inv(ob, actframe+ctime); - else CFRA= (int)floor(actframe+ctime); + CFRA= (int)BKE_nla_tweakedit_remap(adt, actframe+ctime, NLATIME_CONVERT_MAP); if (CFRA != cfrao) { - //do_all_pose_actions(scene, ob); // xxx old animation system crap + BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL); where_is_pose(scene, ob); draw_pose_channels(scene, v3d, rv3d, base, OB_WIRE); } @@ -2460,11 +2448,10 @@ static void draw_ghost_poses(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base /* only within action range */ if ((actframe-ctime >= start) && (actframe-ctime <= end)) { - if (maptime) CFRA= (int)get_action_frame_inv(ob, actframe-ctime); - else CFRA= (int)floor(actframe-ctime); + CFRA= (int)BKE_nla_tweakedit_remap(adt, actframe-ctime, NLATIME_CONVERT_MAP); if (CFRA != cfrao) { - //do_all_pose_actions(scene, ob); // XXX old animation system crap... + BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL); where_is_pose(scene, ob); draw_pose_channels(scene, v3d, rv3d, base, OB_WIRE); } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index c93926c908e..e52d2886439 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -409,6 +409,15 @@ static void view3d_main_area_listener(ARegion *ar, wmNotifier *wmn) { /* context changes */ switch(wmn->category) { + case NC_ANIMATION: + switch(wmn->data) { + case ND_KEYFRAME_EDIT: + case ND_KEYFRAME_PROP: + case ND_NLA_ACTCHANGE: + case ND_ANIMCHAN_SELECT: + ED_region_tag_redraw(ar); + break; + } case NC_SCENE: switch(wmn->data) { case ND_TRANSFORM: diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 9a03a602ac2..e6d3ec54d44 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -117,8 +117,7 @@ #define TEST_EDITMESH if(obedit==0) return; \ if( (v3d->lay & obedit->lay)==0 ) return; -/* XXX port over */ -static void handle_view3d_lock(void) {} +/* XXX port over */ static void countall(void) {} extern void borderselect(); static int retopo_mesh_paint_check() {return 0;} @@ -201,6 +200,25 @@ static RegionView3D *wm_region_view3d(const bContext *C) return NULL; } +// XXX quickly ported across +static void handle_view3d_lock(bContext *C) +{ + Scene *scene= CTX_data_scene(C); + ScrArea *sa= CTX_wm_area(C); + View3D *v3d= (View3D *)CTX_wm_space_data(C); + + if (v3d != NULL && sa != NULL) { + if(v3d->localview==0 && v3d->scenelock && sa->spacetype==SPACE_VIEW3D) { + + /* copy to scene */ + scene->lay= v3d->lay; + scene->camera= v3d->camera; + + //copy_view3d_lock(REDRAW); + } + } +} + /* XXX; all this context stuff... should become operator */ void do_layer_buttons(bContext *C, short event) { @@ -230,7 +248,7 @@ void do_layer_buttons(bContext *C, short event) v3d->lay= (1<<20)-1; } - if(v3d->scenelock) handle_view3d_lock(); + if(v3d->scenelock) handle_view3d_lock(C); /* new layers might need unflushed events events */ DAG_scene_update_flags(scene, v3d->lay); /* tags all that moves and flushes */ @@ -266,7 +284,7 @@ static int layers_exec(bContext *C, wmOperator *op) else v3d->lay = (1<scenelock) handle_view3d_lock(); + if(v3d->scenelock) handle_view3d_lock(C); /* new layers might need unflushed events events */ DAG_scene_update_flags(scene, v3d->lay); /* tags all that moves and flushes */ @@ -4303,7 +4321,7 @@ static void do_view3d_header_buttons(bContext *C, void *arg, int event) v3d->layact= v3d->lay; } - if(v3d->scenelock) handle_view3d_lock(); + if(v3d->scenelock) handle_view3d_lock(C); ED_area_tag_redraw(sa); countall(); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 10cfcbdfa26..016400e0fa9 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -45,6 +45,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_action_types.h" /* for some special action-editor settings */ #include "DNA_constraint_types.h" @@ -76,9 +77,9 @@ //#include "BIF_editmesh.h" //#include "BIF_editsima.h" //#include "BIF_editparticle.h" -//#include "BIF_editaction.h" -#include "BKE_action.h" /* get_action_frame */ +#include "BKE_action.h" +#include "BKE_nla.h" //#include "BKE_bad_level_calls.h"/* popmenu and error */ #include "BKE_bmesh.h" #include "BKE_context.h" @@ -89,7 +90,6 @@ #include "BKE_utildefines.h" #include "BKE_context.h" -//#include "BSE_editaction_types.h" //#include "BSE_view.h" #include "ED_image.h" @@ -162,7 +162,7 @@ void convertViewVec(TransInfo *t, float *vec, short dx, short dy) vec[1]= aspy*(v2d->cur.ymax-v2d->cur.ymin)*(dy)/divy; vec[2]= 0.0f; } - else if(t->spacetype==SPACE_IPO) { + else if(ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) { View2D *v2d = t->view; float divx, divy; @@ -212,7 +212,7 @@ void projectIntView(TransInfo *t, float *vec, int *adr) UI_view2d_to_region_no_clip(t->view, v[0], v[1], adr, adr+1); } - else if(t->spacetype==SPACE_IPO) { + else if(ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) { int out[2] = {0, 0}; UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], out, out+1); @@ -241,7 +241,7 @@ void projectFloatView(TransInfo *t, float *vec, float *adr) adr[0]= a[0]; adr[1]= a[1]; } - else if(t->spacetype==SPACE_IPO) { + else if(ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) { int a[2]; projectIntView(t, vec, a); @@ -300,24 +300,15 @@ static void viewRedrawForce(bContext *C, TransInfo *t) WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); } else if (t->spacetype == SPACE_ACTION) { - SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first; - - // TRANSFORM_FIX_ME - if (saction->lock) { - // whole window... - } - else - ED_area_tag_redraw(t->sa); + //SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first; + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); } else if (t->spacetype == SPACE_IPO) { - SpaceIpo *sipo= (SpaceIpo *)t->sa->spacedata.first; - - // TRANSFORM_FIX_ME - if (sipo->lock) { - // whole window... - } - else - ED_area_tag_redraw(t->sa); + //SpaceIpo *sipo= (SpaceIpo *)t->sa->spacedata.first; + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + } + else if (t->spacetype == SPACE_NLA) { + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); } else if(t->spacetype == SPACE_NODE) { @@ -1323,10 +1314,10 @@ int initTransform(bContext *C, TransInfo *t, wmOperator *op, wmEvent *event, int case TFM_TIME_EXTEND: /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION - * (for Graph Editor only since it uses 'standard' transforms to get 2D movement) + * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement) * depending on which editor this was called from */ - if (t->spacetype == SPACE_IPO) + if ELEM(t->spacetype, SPACE_IPO, SPACE_NLA) initTranslation(t); else initTimeTranslate(t); @@ -4355,7 +4346,7 @@ static short getAnimEdit_DrawTime(TransInfo *t) /* This function is used by Animation Editor specific transform functions to do * the Snap Keyframe to Nearest Frame/Marker */ -static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, Object *ob, short autosnap) +static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, AnimData *adt, short autosnap) { /* snap key to nearest frame? */ if (autosnap == SACTSNAP_FRAME) { @@ -4365,8 +4356,8 @@ static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, Object *ob, short double val; /* convert frame to nla-action time (if needed) */ - if (ob) - val= get_action_frame_inv(ob, *(td->val)); + if (adt) + val= BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); else val= *(td->val); @@ -4377,8 +4368,8 @@ static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, Object *ob, short val= (float)( floor(val+0.5f) ); /* convert frame out of nla-action time */ - if (ob) - *(td->val)= get_action_frame(ob, val); + if (adt) + *(td->val)= BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); else *(td->val)= val; } @@ -4387,8 +4378,8 @@ static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, Object *ob, short float val; /* convert frame to nla-action time (if needed) */ - if (ob) - val= get_action_frame_inv(ob, *(td->val)); + if (adt) + val= BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); else val= *(td->val); @@ -4397,8 +4388,8 @@ static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, Object *ob, short val= (float)ED_markers_find_nearest_marker_time(&t->scene->markers, val); /* convert frame out of nla-action time */ - if (ob) - *(td->val)= get_action_frame(ob, val); + if (adt) + *(td->val)= BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); else *(td->val)= val; } @@ -4471,13 +4462,14 @@ static void applyTimeTranslate(TransInfo *t, float sval) /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ for (i = 0 ; i < t->total; i++, td++) { - /* it is assumed that td->ob is a pointer to the object, + /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from + * (this is only valid when not in NLA) */ - Object *ob= td->ob; + AnimData *adt= (t->spacetype != SPACE_NLA) ? td->extra : NULL; - /* check if any need to apply nla-scaling */ - if (ob) { + /* check if any need to apply nla-mapping */ + if (adt) { deltax = t->values[0]; if (autosnap == SACTSNAP_STEP) { @@ -4487,9 +4479,9 @@ static void applyTimeTranslate(TransInfo *t, float sval) deltax= (float)( floor(deltax + 0.5f) ); } - val = get_action_frame_inv(ob, td->ival); + val = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP); val += deltax; - *(td->val) = get_action_frame(ob, val); + *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); } else { deltax = val = t->values[0]; @@ -4505,7 +4497,7 @@ static void applyTimeTranslate(TransInfo *t, float sval) } /* apply nearest snapping */ - doAnimEdit_SnapFrame(t, td, ob, autosnap); + doAnimEdit_SnapFrame(t, td, adt, autosnap); } } @@ -4605,15 +4597,16 @@ static void applyTimeSlide(TransInfo *t, float sval) /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ for (i = 0 ; i < t->total; i++, td++) { - /* it is assumed that td->ob is a pointer to the object, + /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from + * (this is only valid when not in NLA) */ - Object *ob= td->ob; + AnimData *adt= (t->spacetype != SPACE_NLA) ? td->extra : NULL; float cval = t->values[0]; - /* apply scaling to necessary values */ - if (ob) - cval= get_action_frame(ob, cval); + /* apply NLA-mapping to necessary values */ + if (adt) + cval= BKE_nla_tweakedit_remap(adt, cval, NLATIME_CONVERT_UNMAP); /* only apply to data if in range */ if ((sval > minx) && (sval < maxx)) { @@ -4708,10 +4701,11 @@ static void applyTimeScale(TransInfo *t) { for (i = 0 ; i < t->total; i++, td++) { - /* it is assumed that td->ob is a pointer to the object, + /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from + * (this is only valid when not in NLA) */ - Object *ob= td->ob; + AnimData *adt= (t->spacetype != SPACE_NLA) ? td->extra : NULL; float startx= CFRA; float fac= t->values[0]; @@ -4722,9 +4716,9 @@ static void applyTimeScale(TransInfo *t) { fac= (float)( floor(fac + 0.5f) ); } - /* check if any need to apply nla-scaling */ - if (ob) - startx= get_action_frame(ob, startx); + /* check if any need to apply nla-mapping */ + if (adt) + startx= BKE_nla_tweakedit_remap(adt, startx, NLATIME_CONVERT_UNMAP); /* now, calculate the new value */ *(td->val) = td->ival - startx; @@ -4732,7 +4726,7 @@ static void applyTimeScale(TransInfo *t) { *(td->val) += startx; /* apply nearest snapping */ - doAnimEdit_SnapFrame(t, td, ob, autosnap); + doAnimEdit_SnapFrame(t, td, adt, autosnap); } } @@ -4746,18 +4740,6 @@ int TimeScale(TransInfo *t, short mval[2]) sval= t->imval[0]; cval= mval[0]; - // XXX ewww... we need a better factor! -#if 0 // TRANSFORM_FIX_ME - switch (t->spacetype) { - case SPACE_ACTION: - width= ACTWIDTH; - break; - case SPACE_NLA: - width= NLAWIDTH; - break; - } -#endif - /* calculate scaling factor */ startx= sval-(width/2+(t->ar->winx)/2); deltax= cval-(width/2+(t->ar->winx)/2); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 98719e714ba..74cf1df1fa5 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -161,6 +161,21 @@ typedef struct TransDataSeq { } TransDataSeq; +/* for NLA transform (stored in td->extra pointer) */ +typedef struct TransDataNla { + struct NlaTrack *oldTrack; /* Original NLA-Track that the strip belongs to */ + struct NlaTrack *nlt; /* Current NLA-Track that the strip belongs to */ + + struct NlaStrip *strip; /* NLA-strip this data represents */ + + /* dummy values for transform to write in - must have 3 elements... */ + float h1[3]; /* start handle */ + float h2[3]; /* end handle */ + + int trackIndex; /* index of track that strip is currently in */ + int handle; /* handle-index: 0 for dummy entry, -1 for start, 1 for end, 2 for both ends */ +} TransDataNla; + typedef struct TransData { float dist; /* Distance needed to affect element (for Proportionnal Editing) */ float rdist; /* Distance to the nearest element (for Proportionnal Editing) */ diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 6d2337bbc8c..47f9159d87f 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -90,6 +90,7 @@ #include "BKE_mball.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_sequence.h" @@ -100,16 +101,13 @@ #include "BKE_context.h" #include "BKE_report.h" -//#include "BIF_editaction.h" //#include "BIF_editview.h" //#include "BIF_editlattice.h" //#include "BIF_editconstraint.h" //#include "BIF_editmesh.h" -//#include "BIF_editnla.h" //#include "BIF_editsima.h" //#include "BIF_editparticle.h" #include "BIF_gl.h" -//#include "BIF_keyframing.h" //#include "BIF_poseobject.h" //#include "BIF_meshtools.h" //#include "BIF_mywindow.h" @@ -125,6 +123,7 @@ #include "ED_keyframing.h" #include "ED_keyframes_edit.h" #include "ED_object.h" +#include "ED_markers.h" #include "ED_mesh.h" #include "ED_retopo.h" #include "ED_types.h" @@ -133,13 +132,7 @@ #include "UI_view2d.h" -//#include "BSE_drawipo.h" //#include "BSE_edit.h" -//#include "BSE_editipo.h" -//#include "BSE_editipo_types.h" -//#include "BSE_editaction_types.h" - -//#include "BDR_drawaction.h" // list of keyframes in action //#include "BDR_editobject.h" // reset_slowparents() //#include "BDR_gpencil.h" @@ -2559,7 +2552,216 @@ int clipUVTransform(TransInfo *t, float *vec, int resize) return (clipx || clipy); } -/* ********************* ACTION/NLA EDITOR ****************** */ +/* ********************* ANIMATION EDITORS (GENERAL) ************************* */ + +/* This function tests if a point is on the "mouse" side of the cursor/frame-marking */ +static short FrameOnMouseSide(char side, float frame, float cframe) +{ + /* both sides, so it doesn't matter */ + if (side == 'B') return 1; + + /* only on the named side */ + if (side == 'R') + return (frame >= cframe) ? 1 : 0; + else + return (frame <= cframe) ? 1 : 0; +} + +/* ********************* NLA EDITOR ************************* */ + +static void createTransNlaData(bContext *C, TransInfo *t) +{ + Scene *scene= CTX_data_scene(C); + TransData *td = NULL; + TransDataNla *tdn = NULL; + + bAnimContext ac; + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + int count=0; + char side; + + /* determine what type of data we are operating on */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return; + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* which side of the current frame should be allowed */ + if (t->mode == TFM_TIME_EXTEND) { + /* only side on which mouse is gets transformed */ + float xmouse, ymouse; + + UI_view2d_region_to_view(&ac.ar->v2d, t->imval[0], t->imval[1], &xmouse, &ymouse); + side = (xmouse > CFRA) ? 'R' : 'L'; // XXX use t->frame_side + } + else { + /* normal transform - both sides of current frame are considered */ + side = 'B'; + } + + /* loop 1: count how many strips are selected (consider each strip as 2 points) */ + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* make some meta-strips for chains of selected strips */ + BKE_nlastrips_make_metas(&nlt->strips, 1); + + /* only consider selected strips */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + // TODO: we can make strips have handles later on... + /* transition strips can't get directly transformed */ + if (strip->type != NLASTRIP_TYPE_TRANSITION) { + if (strip->flag & NLASTRIP_FLAG_SELECT) { + if (FrameOnMouseSide(side, strip->start, (float)CFRA)) count++; + if (FrameOnMouseSide(side, strip->end, (float)CFRA)) count++; + } + } + } + } + + /* stop if trying to build list if nothing selected */ + if (count == 0) { + /* cleanup temp list */ + BLI_freelistN(&anim_data); + return; + } + + /* allocate memory for data */ + t->total= count; + + t->data= MEM_callocN(t->total*sizeof(TransData), "TransData(NLA Editor)"); + td= t->data; + t->customData= MEM_callocN(t->total*sizeof(TransDataNla), "TransDataNla (NLA Editor)"); + tdn= t->customData; + + /* loop 2: build transdata array */ + for (ale= anim_data.first; ale; ale= ale->next) { + /* only if a real NLA-track */ + if (ale->type == ANIMTYPE_NLATRACK) { + NlaTrack *nlt= (NlaTrack *)ale->data; + NlaStrip *strip; + + /* only consider selected strips */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + // TODO: we can make strips have handles later on... + /* transition strips can't get directly transformed */ + if (strip->type != NLASTRIP_TYPE_TRANSITION) { + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* our transform data is constructed as follows: + * - only the handles on the right side of the current-frame get included + * - td structs are transform-elements operated on by the transform system + * and represent a single handle. The storage/pointer used (val or loc) depends on + * whether we're scaling or transforming. Ultimately though, the handles + * the td writes to will simply be a dummy in tdn + * - for each strip being transformed, a single tdn struct is used, so in some + * cases, there will need to be 1 of these tdn elements in the array skipped... + */ + float center[3], yval; + + /* firstly, init tdn settings */ + tdn->oldTrack= tdn->nlt= nlt; + tdn->strip= strip; + tdn->trackIndex= BLI_findindex(&nlt->strips, strip); + + yval= (float)(tdn->trackIndex * NLACHANNEL_STEP); + + tdn->h1[0]= strip->start; + tdn->h1[1]= yval; + tdn->h2[0]= strip->end; + tdn->h2[1]= yval; + + center[0]= (float)CFRA; + center[1]= yval; + center[2]= 0.0f; + + /* set td's based on which handles are applicable */ + if (FrameOnMouseSide(side, strip->start, (float)CFRA)) + { + /* just set tdn to assume that it only has one handle for now */ + tdn->handle= -1; + + /* now, link the transform data up to this data */ + if (t->mode == TFM_TRANSLATION) { + td->loc= tdn->h1; + VECCOPY(td->iloc, tdn->h1); + + /* store all the other gunk that is required by transform */ + VECCOPY(td->center, center); + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext= NULL; td->tdi= NULL; td->val= NULL; + + td->flag |= TD_SELECTED; + td->dist= 0.0f; + + Mat3One(td->mtx); + Mat3One(td->smtx); + } + else { + td->val= &tdn->h1[0]; + td->ival= tdn->h1[0]; + } + + td->extra= tdn; + td++; + } + if (FrameOnMouseSide(side, strip->end, (float)CFRA)) + { + /* if tdn is already holding the start handle, then we're doing both, otherwise, only end */ + tdn->handle= (tdn->handle) ? 2 : 1; + + /* now, link the transform data up to this data */ + if (t->mode == TFM_TRANSLATION) { + td->loc= tdn->h2; + VECCOPY(td->iloc, tdn->h2); + + /* store all the other gunk that is required by transform */ + VECCOPY(td->center, center); + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext= NULL; td->tdi= NULL; td->val= NULL; + + td->flag |= TD_SELECTED; + td->dist= 0.0f; + + Mat3One(td->mtx); + Mat3One(td->smtx); + } + else { + td->val= &tdn->h2[0]; + td->ival= tdn->h2[0]; + } + + td->extra= tdn; + td++; + } + + /* if both handles were used, skip the next tdn (i.e. leave it blank) since the counting code is dumb... + * otherwise, just advance to the next one... + */ + if (tdn->handle == 2) + tdn += 2; + else + tdn++; + } + } + } + } + } + + /* cleanup temp list */ + BLI_freelistN(&anim_data); +} + +/* ********************* ACTION EDITOR ****************** */ /* Called by special_aftertrans_update to make sure selected gp-frames replace * any other gp-frames which may reside on that frame (that are not selected). @@ -2730,12 +2932,12 @@ static void posttrans_action_clean (bAnimContext *ac, bAction *act) * - all keyframes are converted in/out of global time */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(ac, ale); + AnimData *adt= ANIM_nla_mapping_get(ac, ale); - if (nob) { - //ANIM_nla_mapping_apply_ipocurve(nob, ale->key_data, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); posttrans_fcurve_clean(ale->key_data); - //ANIM_nla_mapping_apply_ipocurve(nob, ale->key_data, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } else posttrans_fcurve_clean(ale->key_data); @@ -2747,19 +2949,6 @@ static void posttrans_action_clean (bAnimContext *ac, bAction *act) /* ----------------------------- */ -/* This function tests if a point is on the "mouse" side of the cursor/frame-marking */ -static short FrameOnMouseSide(char side, float frame, float cframe) -{ - /* both sides, so it doesn't matter */ - if (side == 'B') return 1; - - /* only on the named side */ - if (side == 'R') - return (frame >= cframe) ? 1 : 0; - else - return (frame <= cframe) ? 1 : 0; -} - /* fully select selected beztriples, but only include if it's on the right side of cfra */ static int count_fcurve_keys(FCurve *fcu, char side, float cfra) { @@ -2808,17 +2997,16 @@ static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra) #endif /* This function assigns the information to transdata */ -static void TimeToTransData(TransData *td, float *time, Object *ob) +static void TimeToTransData(TransData *td, float *time, AnimData *adt) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = time; td->ival = *(time); - /* store the Object where this keyframe exists as a keyframe of the - * active action as td->ob. Usually, this member is only used for constraints - * drawing + /* store the AnimData where this keyframe exists as a keyframe of the + * active action as td->extra. */ - td->ob= ob; + td->extra= adt; } /* This function advances the address to which td points to, so it must return @@ -2828,7 +3016,7 @@ static void TimeToTransData(TransData *td, float *time, Object *ob) * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data * on the named side are used. */ -static TransData *FCurveToTransData(TransData *td, FCurve *fcu, Object *ob, char side, float cfra) +static TransData *FCurveToTransData(TransData *td, FCurve *fcu, AnimData *adt, char side, float cfra) { BezTriple *bezt; int i; @@ -2842,13 +3030,13 @@ static TransData *FCurveToTransData(TransData *td, FCurve *fcu, Object *ob, char /* only add if on the right 'side' of the current frame */ if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { /* each control point needs to be added separetely */ - TimeToTransData(td, bezt->vec[0], ob); + TimeToTransData(td, bezt->vec[0], adt); td++; - TimeToTransData(td, bezt->vec[1], ob); + TimeToTransData(td, bezt->vec[1], adt); td++; - TimeToTransData(td, bezt->vec[2], ob); + TimeToTransData(td, bezt->vec[2], adt); td++; } } @@ -2958,13 +3146,13 @@ static void createTransActionData(bContext *C, TransInfo *t) /* loop 1: fully select ipo-keys and count how many BezTriples are selected */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(&ac, ale); + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); /* convert current-frame to action-time (slightly less accurate, espcially under * higher scaling ratios, but is faster than converting all points) */ - if (nob) - cfra = get_action_frame(nob, (float)CFRA); + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); else cfra = (float)CFRA; @@ -3011,18 +3199,18 @@ static void createTransActionData(bContext *C, TransInfo *t) // tfd += i; //} //else { - Object *nob= ANIM_nla_mapping_get(&ac, ale); + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); FCurve *fcu= (FCurve *)ale->key_data; /* convert current-frame to action-time (slightly less accurate, espcially under * higher scaling ratios, but is faster than converting all points) */ - if (nob) - cfra = get_action_frame(nob, (float)CFRA); + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); else cfra = (float)CFRA; - td= FCurveToTransData(td, fcu, nob, side, cfra); + td= FCurveToTransData(td, fcu, adt, side, cfra); //} } @@ -3051,28 +3239,26 @@ static void createTransActionData(bContext *C, TransInfo *t) /* ********************* GRAPH EDITOR ************************* */ - - /* Helper function for createTransGraphEditData, which is reponsible for associating * source data with transform data */ -static void bezt_to_transdata (TransData *td, TransData2D *td2d, Object *nob, float *loc, float *cent, short selected, short ishandle, short intvals) +static void bezt_to_transdata (TransData *td, TransData2D *td2d, AnimData *adt, float *loc, float *cent, short selected, short ishandle, short intvals) { /* New location from td gets dumped onto the old-location of td2d, which then * gets copied to the actual data at td2d->loc2d (bezt->vec[n]) * - * Due to NLA scaling, we apply NLA scaling to some of the verts here, - * and then that scaling will be undone after transform is done. + * Due to NLA mapping, we apply NLA mapping to some of the verts here, + * and then that mapping will be undone after transform is done. */ - if (nob) { - td2d->loc[0] = get_action_frame_inv(nob, loc[0]); + if (adt) { + td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_UNMAP); td2d->loc[1] = loc[1]; td2d->loc[2] = 0.0f; td2d->loc2d = loc; td->loc = td2d->loc; - td->center[0] = get_action_frame_inv(nob, cent[0]); + td->center[0] = BKE_nla_tweakedit_remap(adt, cent[0], NLATIME_CONVERT_UNMAP); td->center[1] = cent[1]; td->center[2] = 0.0f; @@ -3093,6 +3279,9 @@ static void bezt_to_transdata (TransData *td, TransData2D *td2d, Object *nob, fl td->axismtx[2][2] = 1.0f; td->ext= NULL; td->tdi= NULL; td->val= NULL; + + /* store AnimData info in td->extra, for applying mapping when flushing */ + td->extra= adt; if (selected) { td->flag |= TD_SELECTED; @@ -3153,14 +3342,14 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) /* loop 1: count how many BezTriples (specifically their verts) are selected (or should be edited) */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= NULL; //ANIM_nla_mapping_get(&ac, ale); // XXX we don't handle NLA mapping for now here... + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); FCurve *fcu= (FCurve *)ale->key_data; /* convert current-frame to action-time (slightly less accurate, espcially under * higher scaling ratios, but is faster than converting all points) */ - if (nob) - cfra = get_action_frame(nob, (float)CFRA); + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); else cfra = (float)CFRA; @@ -3210,13 +3399,19 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) td2d= t->data2d; /* loop 2: build transdata arrays */ - cfra = (float)CFRA; - for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= NULL; //ANIM_nla_mapping_get(&ac, ale); // XXX we don't handle NLA mapping here yet + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); FCurve *fcu= (FCurve *)ale->key_data; short intvals= (fcu->flag & FCURVE_INT_VALUES); + /* convert current-frame to action-time (slightly less accurate, espcially under + * higher scaling ratios, but is faster than converting all points) + */ + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra = (float)CFRA; + /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */ bezt= fcu->bezt; prevbezt= NULL; @@ -3230,7 +3425,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) if ( (!prevbezt && (bezt->ipo==BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo==BEZT_IPO_BEZ)) ) { if (bezt->f1 & SELECT) { hdata = initTransDataCurveHandes(td, bezt); - bezt_to_transdata(td++, td2d++, nob, bezt->vec[0], bezt->vec[1], 1, 1, intvals); + bezt_to_transdata(td++, td2d++, adt, bezt->vec[0], bezt->vec[1], 1, 1, intvals); } else h1= 0; @@ -3239,7 +3434,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) if (bezt->f3 & SELECT) { if (hdata==NULL) hdata = initTransDataCurveHandes(td, bezt); - bezt_to_transdata(td++, td2d++, nob, bezt->vec[2], bezt->vec[1], 1, 1, intvals); + bezt_to_transdata(td++, td2d++, adt, bezt->vec[2], bezt->vec[1], 1, 1, intvals); } else h2= 0; @@ -3255,7 +3450,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) hdata = initTransDataCurveHandes(td, bezt); } - bezt_to_transdata(td++, td2d++, nob, bezt->vec[1], bezt->vec[1], 1, 0, intvals); + bezt_to_transdata(td++, td2d++, adt, bezt->vec[1], bezt->vec[1], 1, 0, intvals); } /* special hack (must be done after initTransDataCurveHandes(), as that stores handle settings to restore...): @@ -3475,30 +3670,37 @@ void flushTransGraphData(TransInfo *t) SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first; TransData *td; TransData2D *td2d; + Scene *scene= t->scene; + double secf= FPS; int a; /* flush to 2d vector from internally used 3d vector */ for (a=0, td= t->data, td2d=t->data2d; atotal; a++, td++, td2d++) { + AnimData *adt= (AnimData *)td->extra; /* pointers to relevant AnimData blocks are stored in the td->extra pointers */ + /* handle snapping for time values * - we should still be in NLA-mapping timespace * - only apply to keyframes (but never to handles) */ if ((td->flag & TD_NOTIMESNAP)==0) { switch (sipo->autosnap) { - case SACTSNAP_FRAME: /* snap to nearest frame */ - td2d->loc[0]= (float)( floor(td2d->loc[0]+0.5f) ); + case SACTSNAP_FRAME: /* snap to nearest frame (or second if drawing seconds) */ + if (sipo->flag & SIPO_DRAWTIME) + td2d->loc[0]= (float)( floor((td2d->loc[0]/secf) + 0.5f) * secf ); + else + td2d->loc[0]= (float)( floor(td2d->loc[0]+0.5f) ); break; case SACTSNAP_MARKER: /* snap to nearest marker */ - //td2d->loc[0]= (float)find_nearest_marker_time(td2d->loc[0]); + td2d->loc[0]= (float)ED_markers_find_nearest_marker_time(&t->scene->markers, td2d->loc[0]); break; } } /* we need to unapply the nla-scaling from the time in some situations */ - //if (NLA_IPO_SCALED) - // td2d->loc2d[0]= get_action_frame(OBACT, td2d->loc[0]); - //else + if (adt) + td2d->loc2d[0]= BKE_nla_tweakedit_remap(adt, td2d->loc[0], NLATIME_CONVERT_UNMAP); + else td2d->loc2d[0]= td2d->loc[0]; /* if int-values only, truncate to integers */ @@ -3509,7 +3711,6 @@ void flushTransGraphData(TransInfo *t) } } - /* **************** IpoKey stuff, for Object TransData ********** */ /* while transforming */ @@ -4477,16 +4678,16 @@ void special_aftertrans_update(TransInfo *t) /* these should all be ipo-blocks */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(&ac, ale); + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); FCurve *fcu= (FCurve *)ale->key_data; if ( (saction->flag & SACTION_NOTRANSKEYCULL)==0 && ((cancelled == 0) || (duplicate)) ) { - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, fcu, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 1); posttrans_fcurve_clean(fcu); - ANIM_nla_mapping_apply_fcurve(nob, fcu, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 1); } else posttrans_fcurve_clean(fcu); @@ -4588,16 +4789,16 @@ void special_aftertrans_update(TransInfo *t) /* these should all be ipo-blocks */ for (ale= anim_data.first; ale; ale= ale->next) { - Object *nob= ANIM_nla_mapping_get(&ac, ale); + AnimData *adt= ANIM_nla_mapping_get(&ac, ale); FCurve *fcu= (FCurve *)ale->key_data; if ( (sipo->flag & SIPO_NOTRANSKEYCULL)==0 && ((cancelled == 0) || (duplicate)) ) { - if (nob) { - ANIM_nla_mapping_apply_fcurve(nob, fcu, 0, 1); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 1); posttrans_fcurve_clean(fcu); - ANIM_nla_mapping_apply_fcurve(nob, fcu, 1, 1); + ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 1); } else posttrans_fcurve_clean(fcu); @@ -4611,6 +4812,51 @@ void special_aftertrans_update(TransInfo *t) /* make sure all F-Curves are set correctly */ ANIM_editkeyframes_refresh(&ac); } + else if (t->spacetype == SPACE_NLA) { + Scene *scene; + bAnimContext ac; + + /* initialise relevant anim-context 'context' data from TransInfo data */ + /* NOTE: sync this with the code in ANIM_animdata_get_context() */ + memset(&ac, 0, sizeof(bAnimContext)); + + scene= ac.scene= t->scene; + ob= ac.obact= OBACT; + ac.sa= t->sa; + ac.ar= t->ar; + ac.spacetype= (t->sa)? t->sa->spacetype : 0; + ac.regiontype= (t->ar)? t->ar->regiontype : 0; + + if (ANIM_animdata_context_getdata(&ac) == 0) + return; + + if (ac.datatype) + { + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + short filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NLATRACKS); + + /* get channels to work on */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + for (ale= anim_data.first; ale; ale= ale->next) { + NlaTrack *nlt= (NlaTrack *)ale->data; + + /* make sure strips are in order again */ + BKE_nlatrack_sort_strips(nlt); + + /* remove the temp metas */ + BKE_nlastrips_clear_metas(&nlt->strips, 0, 1); + } + + /* free temp memory */ + BLI_freelistN(&anim_data); + } + + // XXX check on the calls below... we need some of these sanity checks + //synchronize_action_strips(); + //ANIM_editkeyframes_refresh(&ac); + } else if (t->obedit) { // TRANSFORM_FIX_ME // if (t->mode==TFM_BONESIZE || t->mode==TFM_BONE_ENVELOPE) @@ -4695,24 +4941,6 @@ void special_aftertrans_update(TransInfo *t) } } -#if 0 // TRANSFORM_FIX_ME - else if (t->spacetype == SPACE_NLA) { - recalc_all_ipos(); // bad - synchronize_action_strips(); - - /* cleanup */ - for (base=t->scene->base.first; base; base=base->next) - base->flag &= ~(BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA); - - /* after transform, remove duplicate keyframes on a frame that resulted from transform */ - if ( (G.snla->flag & SNLA_NOTRANSKEYCULL)==0 && - ((cancelled == 0) || (duplicate)) ) - { - posttrans_nla_clean(t); - } - } -#endif - clear_trans_object_base_flags(t); #if 0 // TRANSFORM_FIX_ME @@ -4945,8 +5173,7 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_NLA) { t->flag |= T_POINTS|T_2D_EDIT; - // TRANSFORM_FIX_ME - //createTransNlaData(C, t); + createTransNlaData(C, t); } else if (t->spacetype == SPACE_SEQ) { t->flag |= T_POINTS|T_2D_EDIT; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 5bb1a2e1329..3415c266cf1 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -59,9 +59,7 @@ //#include "BIF_screen.h" //#include "BIF_mywindow.h" #include "BIF_gl.h" -//#include "BIF_editaction.h" //#include "BIF_editmesh.h" -//#include "BIF_editnla.h" //#include "BIF_editsima.h" //#include "BIF_editparticle.h" //#include "BIF_meshtools.h" @@ -81,6 +79,7 @@ #include "BKE_key.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_nla.h" #include "BKE_object.h" #include "BKE_utildefines.h" #include "BKE_context.h" @@ -89,6 +88,7 @@ #include "ED_armature.h" #include "ED_image.h" #include "ED_keyframing.h" +#include "ED_markers.h" #include "ED_mesh.h" #include "ED_retopo.h" #include "ED_space_api.h" @@ -102,6 +102,8 @@ #include "BLI_editVert.h" #include "BLI_rand.h" +#include "RNA_access.h" + #include "WM_types.h" #include "UI_resources.h" @@ -268,92 +270,6 @@ void recalcData(TransInfo *t) Scene *scene = t->scene; Base *base; -#if 0 // TRANSFORM_FIX_ME - if (t->spacetype == SPACE_ACTION) { - Object *ob= OBACT; - void *data; - short context; - - /* determine what type of data we are operating on */ - data = get_action_context(&context); - if (data == NULL) return; - - /* always flush data if gpencil context */ - if (context == ACTCONT_GPENCIL) { - flushTransGPactionData(t); - } - - if (G.saction->lock) { - if (context == ACTCONT_ACTION) { - if(ob) { - ob->ctime= -1234567.0f; - if(ob->pose || ob_get_key(ob)) - DAG_object_flush_update(G.scene, ob, OB_RECALC); - else - DAG_object_flush_update(G.scene, ob, OB_RECALC_OB); - } - } - else if (context == ACTCONT_SHAPEKEY) { - DAG_object_flush_update(G.scene, OBACT, OB_RECALC_OB|OB_RECALC_DATA); - } - } - } - else if (t->spacetype == SPACE_NLA) { - if (G.snla->lock) { - for (base=G.scene->base.first; base; base=base->next) { - if (base->flag & BA_HAS_RECALC_OB) - base->object->recalc |= OB_RECALC_OB; - if (base->flag & BA_HAS_RECALC_DATA) - base->object->recalc |= OB_RECALC_DATA; - - if (base->object->recalc) - base->object->ctime= -1234567.0f; // eveil! - - /* recalculate scale of selected nla-strips */ - if (base->object->nlastrips.first) { - Object *bob= base->object; - bActionStrip *strip; - - for (strip= bob->nlastrips.first; strip; strip= strip->next) { - if (strip->flag & ACTSTRIP_SELECT) { - float actlen= strip->actend - strip->actstart; - float len= strip->end - strip->start; - - strip->scale= len / (actlen * strip->repeat); - } - } - } - } - - DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); - } - else { - for (base=G.scene->base.first; base; base=base->next) { - /* recalculate scale of selected nla-strips */ - if (base->object && base->object->nlastrips.first) { - Object *bob= base->object; - bActionStrip *strip; - - for (strip= bob->nlastrips.first; strip; strip= strip->next) { - if (strip->flag & ACTSTRIP_SELECT) { - float actlen= strip->actend - strip->actstart; - float len= strip->end - strip->start; - - /* prevent 'negative' scaling */ - if (len < 0) { - SWAP(float, strip->start, strip->end); - len= fabs(len); - } - - /* calculate new scale */ - strip->scale= len / (actlen * strip->repeat); - } - } - } - } - } - } -#endif if (t->obedit) { } else if(G.f & G_PARTICLEEDIT) { @@ -420,6 +336,179 @@ void recalcData(TransInfo *t) } } + else if (t->spacetype == SPACE_NLA) { + TransDataNla *tdn= (TransDataNla *)t->customData; + SpaceNla *snla= (SpaceNla *)t->sa->spacedata.first; + Scene *scene= t->scene; + double secf= FPS; + int i; + + /* for each strip we've got, perform some additional validation of the values that got set before + * using RNA to set the value (which does some special operations when setting these values to make + * sure that everything works ok) + */ + for (i = 0; i < t->total; i++, tdn++) { + NlaStrip *strip= tdn->strip; + PointerRNA strip_ptr; + short pExceeded, nExceeded, iter; + int delta_y1, delta_y2; + + /* if this tdn has no handles, that means it is just a dummy that should be skipped */ + if (tdn->handle == 0) + continue; + + /* if cancelling transform, just write the values without validating, then move on */ + if (t->state == TRANS_CANCEL) { + /* clear the values by directly overwriting the originals, but also need to restore + * endpoints of neighboring transition-strips + */ + + /* start */ + strip->start= tdn->h1[0]; + + if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) + strip->prev->end= tdn->h1[0]; + + /* end */ + strip->end= tdn->h2[0]; + + if ((strip->next) && (strip->next->type == NLASTRIP_TYPE_TRANSITION)) + strip->next->start= tdn->h2[0]; + + /* flush transforms to child strips (since this should be a meta) */ + BKE_nlameta_flush_transforms(strip); + + /* restore to original track (if needed) */ + if (tdn->oldTrack != tdn->nlt) { + /* just append to end of list for now, since strips get sorted in special_aftertrans_update() */ + BLI_remlink(&tdn->nlt->strips, strip); + BLI_addtail(&tdn->oldTrack->strips, strip); + } + + continue; + } + + /* firstly, check if the proposed transform locations would overlap with any neighbouring strips + * (barring transitions) which are absolute barriers since they are not being moved + * + * this is done as a iterative procedure (done 5 times max for now) + */ + for (iter=0; iter < 5; iter++) { + pExceeded= ((strip->prev) && (strip->prev->type != NLASTRIP_TYPE_TRANSITION) && (tdn->h1[0] < strip->prev->end)); + nExceeded= ((strip->next) && (strip->next->type != NLASTRIP_TYPE_TRANSITION) && (tdn->h2[0] > strip->next->start)); + + if ((pExceeded && nExceeded) || (iter == 4) ) { + /* both endpoints exceeded (or iteration ping-pong'd meaning that we need a compromise) + * - simply crop strip to fit within the bounds of the strips bounding it + * - if there were no neighbours, clear the transforms (make it default to the strip's current values) + */ + if (strip->prev && strip->next) { + tdn->h1[0]= strip->prev->end; + tdn->h2[0]= strip->next->start; + } + else { + tdn->h1[0]= strip->start; + tdn->h2[0]= strip->end; + } + } + else if (nExceeded) { + /* move backwards */ + float offset= tdn->h2[0] - strip->next->start; + + tdn->h1[0] -= offset; + tdn->h2[0] -= offset; + } + else if (pExceeded) { + /* more forwards */ + float offset= strip->prev->end - tdn->h1[0]; + + tdn->h1[0] += offset; + tdn->h2[0] += offset; + } + else /* all is fine and well */ + break; + } + + /* handle auto-snapping */ + switch (snla->autosnap) { + case SACTSNAP_FRAME: /* snap to nearest frame/time */ + if (snla->flag & SNLA_DRAWTIME) { + tdn->h1[0]= (float)( floor((tdn->h1[0]/secf) + 0.5f) * secf ); + tdn->h2[0]= (float)( floor((tdn->h2[0]/secf) + 0.5f) * secf ); + } + else { + tdn->h1[0]= (float)( floor(tdn->h1[0]+0.5f) ); + tdn->h2[0]= (float)( floor(tdn->h2[0]+0.5f) ); + } + break; + + case SACTSNAP_MARKER: /* snap to nearest marker */ + tdn->h1[0]= (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h1[0]); + tdn->h2[0]= (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h2[0]); + break; + } + + /* use RNA to write the values... */ + // TODO: do we need to write in 2 passes to make sure that no truncation goes on? + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); + + RNA_float_set(&strip_ptr, "start_frame", tdn->h1[0]); + RNA_float_set(&strip_ptr, "end_frame", tdn->h2[0]); + + /* flush transforms to child strips (since this should be a meta) */ + BKE_nlameta_flush_transforms(strip); + + + /* now, check if we need to try and move track + * - we need to calculate both, as only one may have been altered by transform if only 1 handle moved + */ + delta_y1= ((int)tdn->h1[1] / NLACHANNEL_STEP - tdn->trackIndex); + delta_y2= ((int)tdn->h2[1] / NLACHANNEL_STEP - tdn->trackIndex); + + if (delta_y1 || delta_y2) { + NlaTrack *track; + int delta = (delta_y2) ? delta_y2 : delta_y1; + int n; + + /* move in the requested direction, checking at each layer if there's space for strip to pass through, + * stopping on the last track available or that we're able to fit in + */ + if (delta > 0) { + for (track=tdn->nlt->next, n=0; (track) && (n < delta); track=track->next, n++) { + /* check if space in this track for the strip */ + if (BKE_nlatrack_has_space(track, strip->start, strip->end)) { + /* move strip to this track */ + BLI_remlink(&tdn->nlt->strips, strip); + BKE_nlatrack_add_strip(track, strip); + + tdn->nlt= track; + tdn->trackIndex += (n + 1); /* + 1, since n==0 would mean that we didn't change track */ + } + else /* can't move any further */ + break; + } + } + else { + /* make delta 'positive' before using it, since we now know to go backwards */ + delta= -delta; + + for (track=tdn->nlt->prev, n=0; (track) && (n < delta); track=track->prev, n++) { + /* check if space in this track for the strip */ + if (BKE_nlatrack_has_space(track, strip->start, strip->end)) { + /* move strip to this track */ + BLI_remlink(&tdn->nlt->strips, strip); + BKE_nlatrack_add_strip(track, strip); + + tdn->nlt= track; + tdn->trackIndex -= (n - 1); /* - 1, since n==0 would mean that we didn't change track */ + } + else /* can't move any further */ + break; + } + } + } + } + } else if (t->obedit) { if ELEM(t->obedit->type, OB_CURVE, OB_SURF) { Curve *cu= t->obedit->data; @@ -867,7 +956,7 @@ void postTrans (TransInfo *t) if(sima->flag & SI_LIVE_UNWRAP) ED_uvedit_live_unwrap_end(t->state == TRANS_CANCEL); } - else if(t->spacetype==SPACE_ACTION) { + else if(ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA)) { if (t->customData) MEM_freeN(t->customData); } diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index dcd34011eef..ee4f1b90451 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -634,6 +634,19 @@ void transform_keymap_for_space(struct wmWindowManager *wm, struct ListBase *key km = WM_keymap_add_item(keymap, "TFM_OT_resize", SKEY, KM_PRESS, 0, 0); break; + case SPACE_NLA: + km= WM_keymap_add_item(keymap, "TFM_OT_transform", GKEY, KM_PRESS, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TRANSLATION); + + km= WM_keymap_add_item(keymap, "TFM_OT_transform", EVT_TWEAK_S, KM_ANY, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TRANSLATION); + + km= WM_keymap_add_item(keymap, "TFM_OT_transform", EKEY, KM_PRESS, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TIME_EXTEND); + + km= WM_keymap_add_item(keymap, "TFM_OT_transform", SKEY, KM_PRESS, 0, 0); + RNA_int_set(km->ptr, "mode", TFM_TIME_SCALE); + break; case SPACE_NODE: km= WM_keymap_add_item(keymap, "TFM_OT_translation", GKEY, KM_PRESS, 0, 0); diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 7e54045b5e4..4eddebc5b67 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -225,10 +225,18 @@ typedef struct bActionGroup { /* Action Group flags */ typedef enum eActionGroup_Flag { + /* group is selected */ AGRP_SELECTED = (1<<0), + /* group is 'active' / last selected one */ AGRP_ACTIVE = (1<<1), + /* keyframes/channels belonging to it cannot be edited */ AGRP_PROTECTED = (1<<2), + /* for UI, sub-channels are shown */ AGRP_EXPANDED = (1<<3), + /* sub-channels are not evaluated */ + AGRP_MUTED = (1<<4), + /* sub-channels are not visible in Graph Editor */ + AGRP_NOTVISIBLE = (1<<5), AGRP_TEMP = (1<<30), AGRP_MOVED = (1<<31) @@ -269,6 +277,7 @@ typedef enum eAction_Flags { /* flags for evaluation/editing */ ACT_MUTED = (1<<9), ACT_PROTECTED = (1<<10), + ACT_DISABLED = (1<<11), } eAction_Flags; @@ -288,8 +297,10 @@ typedef struct bDopeSheet { /* DopeSheet filter-flag */ typedef enum DOPESHEET_FILTERFLAG { /* general filtering */ - ADS_FILTER_ONLYSEL = (1<<0), - ADS_FILTER_ONLYDRIVERS = (1<<1), + ADS_FILTER_ONLYSEL = (1<<0), /* only include channels relating to selected data */ + + ADS_FILTER_ONLYDRIVERS = (1<<1), /* for 'Drivers' editor - only include Driver data from AnimData */ + ADS_FILTER_ONLYNLA = (1<<2), /* for 'NLA' editor - only include NLA data from AnimData */ /* datatype-based filtering */ ADS_FILTER_NOSHAPEKEYS = (1<<6), @@ -300,9 +311,11 @@ typedef enum DOPESHEET_FILTERFLAG { ADS_FILTER_NOWOR = (1<<14), ADS_FILTER_NOSCE = (1<<15), + /* NLA-specific filters */ + ADS_FILTER_NLA_NOACT = (1<<20), /* if the AnimData block has no NLA data, don't include to just show Action-line */ + /* combination filters (some only used at runtime) */ ADS_FILTER_NOOBDATA = (ADS_FILTER_NOCAM|ADS_FILTER_NOMAT|ADS_FILTER_NOLAM|ADS_FILTER_NOCUR), - ADS_FILTER_NLADUMMY = (ADS_FILTER_NOSHAPEKEYS|ADS_FILTER_NOOBDATA), } DOPESHEET_FILTERFLAG; /* DopeSheet general flags */ diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index bf6b9bed5a1..fed0c490014 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -1,5 +1,28 @@ -/* Testing code for new animation system in 2.5 - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #ifndef DNA_ANIM_TYPES_H @@ -43,9 +66,10 @@ typedef struct FModifier { enum { FMODIFIER_TYPE_NULL = 0, FMODIFIER_TYPE_GENERATOR, + FMODIFIER_TYPE_FN_GENERATOR, FMODIFIER_TYPE_ENVELOPE, FMODIFIER_TYPE_CYCLES, - FMODIFIER_TYPE_NOISE, /* unimplemented - generate variations using some basic noise generator... */ + FMODIFIER_TYPE_NOISE, FMODIFIER_TYPE_FILTER, /* unimplemented - for applying: fft, high/low pass filters, etc. */ FMODIFIER_TYPE_PYTHON, FMODIFIER_TYPE_LIMITS, @@ -68,39 +92,55 @@ enum { /* --- */ -/* generator modifier data */ +/* Generator modifier data */ typedef struct FMod_Generator { - /* generator based on PyExpression */ - char expression[256]; /* python expression to use as generator */ - /* general generator information */ float *coefficients; /* coefficients array */ unsigned int arraysize; /* size of the coefficients array */ - short poly_order; /* order of polynomial generated (i.e. 1 for linear, 2 for quadratic) */ - short func_type; /* builtin math function eFMod_Generator_Functions */ - - int pad; + int poly_order; /* order of polynomial generated (i.e. 1 for linear, 2 for quadratic) */ + int mode; /* which 'generator' to use eFMod_Generator_Modes */ /* settings */ - short flag; /* settings */ - short mode; /* which 'generator' to use eFMod_Generator_Modes */ + int flag; /* settings */ } FMod_Generator; /* generator modes */ enum { FCM_GENERATOR_POLYNOMIAL = 0, FCM_GENERATOR_POLYNOMIAL_FACTORISED, - FCM_GENERATOR_FUNCTION, - FCM_GENERATOR_EXPRESSION, } eFMod_Generator_Modes; -/* generator flags */ + +/* generator flags + * - shared by Generator and Function Generator + */ enum { /* generator works in conjunction with other modifiers (i.e. doesn't replace those before it) */ FCM_GENERATOR_ADDITIVE = (1<<0), } eFMod_Generator_Flags; + +/* 'Built-In Function' Generator modifier data + * + * This uses the general equation for equations: + * y = amplitude * fn(phase_multiplier*x + phase_offset) + y_offset + * + * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients, + * x is the evaluation 'time', and 'y' is the resultant value + */ +typedef struct FMod_FunctionGenerator { + /* coefficients for general equation (as above) */ + float amplitude; + float phase_multiplier; + float phase_offset; + float value_offset; + + /* flags */ + int type; /* eFMod_Generator_Functions */ + int flag; /* eFMod_Generator_flags */ +} FMod_FunctionGenerator; + /* 'function' generator types */ enum { FCM_GENERATOR_FN_SIN = 0, @@ -108,6 +148,7 @@ enum { FCM_GENERATOR_FN_TAN, FCM_GENERATOR_FN_SQRT, FCM_GENERATOR_FN_LN, + FCM_GENERATOR_FN_SINC, } eFMod_Generator_Functions; @@ -386,85 +427,104 @@ typedef struct AnimMapper { /* ************************************************ */ /* NLA - Non-Linear Animation */ -// TODO: the concepts here still need to be refined to solve any unresolved items - -/* NLA Modifiers ---------------------------------- */ - -/* These differ from F-Curve modifiers, as although F-Curve modifiers also operate on a - * per-channel basis too (in general), they are part of the animation data itself, which - * means that their effects are inherited by all of their users. In order to counteract this, - * the modifiers here should be used to provide variation to pre-created motions only. - */ /* NLA Strips ------------------------------------- */ /* NLA Strip (strip) * * A NLA Strip is a container for the reuse of Action data, defining parameters - * to control the remapping of the Action data to some destination. Actions being - * referenced by NLA-Strips SHOULD-NOT be editable, unless they were created in such - * a way that results in very little mapping distortion (i.e. for layered animation only, - * opposed to prebuilt 'blocks' which are quickly dumped into the NLA for crappymatic machima-type - * stuff) + * to control the remapping of the Action data to some destination. */ typedef struct NlaStrip { struct NlaStrip *next, *prev; - bAction *act; /* Action that is referenced by this strip */ + ListBase strips; /* 'Child' strips (used for 'meta' strips) */ + bAction *act; /* Action that is referenced by this strip (strip is 'user' of the action) */ AnimMapper *remap; /* Remapping info this strip (for tweaking correspondance of action with context) */ - ListBase modifiers; /* NLA Modifiers */ + ListBase fcurves; /* F-Curves for controlling this strip's influence and timing */ // TODO: move out? + ListBase modifiers; /* F-Curve modifiers to be applied to the entire strip's referenced F-Curves */ + + char name[64]; /* User-Visible Identifier for Strip */ - ListBase fcurves; /* F-Curves for controlling this strip's influence and timing */ float influence; /* Influence of strip */ - float act_time; /* Current 'time' within action being used */ + float strip_time; /* Current 'time' within action being used (automatically evaluated, but can be overridden) */ float start, end; /* extents of the strip */ float actstart, actend; /* range of the action to use */ - float repeat; /* The number of times to repeat the action range (only when no F-Curves) */ - float scale; /* The amount the action range is scaled by (only when no F-Curves) */ + float repeat; /* The number of times to repeat the action range (only when no F-Curves) */ + float scale; /* The amount the action range is scaled by (only when no F-Curves) */ float blendin, blendout; /* strip blending length (only used when there are no F-Curves) */ - int blendmode; /* strip blending mode */ + short blendmode; /* strip blending mode (layer-based mixing) */ + short extendmode; /* strip extrapolation mode (time-based mixing) */ - int flag; /* settings */ - - // umm... old unused cruft? - int stride_axis; /* axis for stridebone stuff - 0=x, 1=y, 2=z */ - int pad; - - float actoffs; /* Offset within action, for cycles and striding (only set for ACT_USESTRIDE) */ - float stridelen; /* The stridelength (considered when flag & ACT_USESTRIDE) */ - - char stridechannel[32]; /* Instead of stridelen, it uses an action channel */ - char offs_bone[32]; /* if repeat, use this bone/channel for defining offset */ + short flag; /* settings */ + short type; /* type of NLA strip */ } NlaStrip; /* NLA Strip Blending Mode */ enum { - NLASTRIPMODE_BLEND = 0, - NLASTRIPMODE_ADD, - NLASTRIPMODE_SUBTRACT, -} eActStrip_Mode; + NLASTRIP_MODE_REPLACE = 0, + NLASTRIP_MODE_ADD, + NLASTRIP_MODE_SUBTRACT, + NLASTRIP_MODE_MULTIPLY, +} eNlaStrip_Blend_Mode; + +/* NLA Strip Extrpolation Mode */ +enum { + /* extend before first frame if no previous strips in track, and always hold+extend last frame */ + NLASTRIP_EXTEND_HOLD = 0, + /* only hold+extend last frame */ + NLASTRIP_EXTEND_HOLD_FORWARD, + /* don't contribute at all */ + NLASTRIP_EXTEND_NOTHING, +} eNlaStrip_Extrapolate_Mode; /* NLA Strip Settings */ -// TODO: check on which of these are still useful... enum { - NLASTRIP_SELECT = (1<<0), - NLASTRIP_USESTRIDE = (1<<1), - NLASTRIP_BLENDTONEXT = (1<<2), /* Not implemented. Is not used anywhere */ - NLASTRIP_HOLDLASTFRAME = (1<<3), - NLASTRIP_ACTIVE = (1<<4), - NLASTRIP_LOCK_ACTION = (1<<5), - NLASTRIP_MUTE = (1<<6), - NLASTRIP_REVERSE = (1<<7), /* This has yet to be implemented. To indicate that a strip should be played backwards */ - NLASTRIP_CYCLIC_USEX = (1<<8), - NLASTRIP_CYCLIC_USEY = (1<<9), - NLASTRIP_CYCLIC_USEZ = (1<<10), - NLASTRIP_AUTO_BLENDS = (1<<11), - NLASTRIP_TWEAK = (1<<12), /* This strip is a tweaking strip (only set if owner track is a tweak track) */ -} eActionStrip_Flag; + /* UI selection flags */ + /* NLA strip is the active one in the track (also indicates if strip is being tweaked) */ + NLASTRIP_FLAG_ACTIVE = (1<<0), + /* NLA strip is selected for editing */ + NLASTRIP_FLAG_SELECT = (1<<1), +// NLASTRIP_FLAG_SELECT_L = (1<<2), // left handle selected +// NLASTRIP_FLAG_SELECT_R = (1<<3), // right handle selected + /* NLA strip uses the same action that the action being tweaked uses (not set for the twaking one though) */ + NLASTRIP_FLAG_TWEAKUSER = (1<<4), + + /* controls driven by local F-Curves */ + /* strip influence is controlled by local F-Curve */ + NLASTRIP_FLAG_USR_INFLUENCE = (1<<5), + NLASTRIP_FLAG_USR_TIME = (1<<6), + + /* playback flags (may be overriden by F-Curves) */ + /* NLA strip blendin/out values are set automatically based on overlaps */ + NLASTRIP_FLAG_AUTO_BLENDS = (1<<10), + /* NLA strip is played back in reverse order */ + NLASTRIP_FLAG_REVERSE = (1<<11), + /* NLA strip is muted (i.e. doesn't contribute in any way) */ + // TODO: this overlaps a lot with the functionality in track + NLASTRIP_FLAG_MUTED = (1<<12), + /* NLA strip length is synced to the length of the referenced action */ + NLASTRIP_FLAG_SYNC_LENGTH = (1<<13), + + /* temporary editing flags */ + /* NLA-Strip is really just a temporary meta used to facilitate easier transform code */ + NLASTRIP_FLAG_TEMP_META = (1<<14), + NLASTRIP_FLAG_EDIT_TOUCHED = (1<<15), +} eNlaStrip_Flag; + +/* NLA Strip Type */ +enum { + /* 'clip' - references an Action */ + NLASTRIP_TYPE_CLIP = 0, + /* 'transition' - blends between the adjacent strips */ + NLASTRIP_TYPE_TRANSITION, + /* 'meta' - a strip which acts as a container for a few others */ + NLASTRIP_TYPE_META, +} eNlaStrip_Type; /* NLA Tracks ------------------------------------- */ @@ -483,14 +543,12 @@ typedef struct NlaTrack { int flag; /* settings for this track */ int index; /* index of the track in the stack (NOTE: not really useful, but we need a pad var anyways!) */ - char info[64]; /* short user-description of this track */ + char name[64]; /* short user-description of this track */ } NlaTrack; /* settings for track */ enum { - /* track is the one that settings can be modified on (doesn't indicate - * that it's for 'tweaking' though) - */ + /* track is the one that settings can be modified on, also indicates if track is being 'tweaked' */ NLATRACK_ACTIVE = (1<<0), /* track is selected in UI for relevant editing operations */ NLATRACK_SELECTED = (1<<1), @@ -500,10 +558,9 @@ enum { NLATRACK_SOLO = (1<<3), /* track's settings (and strips) cannot be edited (to guard against unwanted changes) */ NLATRACK_PROTECTED = (1<<4), - /* strip is the 'last' one that should be evaluated, as the active action - * is being used to tweak the animation of the strips up to here - */ - NLATRACK_TWEAK = (1<<5), + + /* track is not allowed to execute, usually as result of tweaking being enabled (internal flag) */ + NLATRACK_DISABLED = (1<<10), } eNlaTrack_Flag; @@ -646,11 +703,15 @@ typedef struct AnimOverride { * blocks may override local settings. * * This datablock should be placed immediately after the ID block where it is used, so that - * the code which retrieves this data can do so in an easier manner. See blenkernel/internal/anim_sys.c for details. + * the code which retrieves this data can do so in an easier manner. See blenkernel/intern/anim_sys.c for details. */ typedef struct AnimData { /* active action - acts as the 'tweaking track' for the NLA */ - bAction *action; + bAction *action; + /* temp-storage for the 'real' active action (i.e. the one used before the tweaking-action + * took over to be edited in the Animation Editors) + */ + bAction *tmpact; /* remapping-info for active action - should only be used if needed * (for 'foreign' actions that aren't working correctly) */ @@ -658,6 +719,8 @@ typedef struct AnimData { /* nla-tracks */ ListBase nla_tracks; + /* active NLA-strip (only set/used during tweaking, so no need to worry about dangling pointers) */ + NlaStrip *actstrip; /* 'drivers' for this ID-block's settings - FCurves, but are completely * separate from those for animation data @@ -676,11 +739,20 @@ enum { ADT_NLA_SOLO_TRACK = (1<<0), /* don't use NLA */ ADT_NLA_EVAL_OFF = (1<<1), - /* don't execute drivers */ - ADT_DRIVERS_DISABLED = (1<<2), + /* NLA is being 'tweaked' (i.e. in EditMode) */ + ADT_NLA_EDIT_ON = (1<<2), + /* active Action for 'tweaking' does not have mapping applied for editing */ + ADT_NLA_EDIT_NOMAP = (1<<3), + /* NLA-Strip F-Curves are expanded in UI */ + ADT_NLA_SKEYS_COLLAPSED = (1<<4), /* drivers expanded in UI */ ADT_DRIVERS_COLLAPSED = (1<<10), + /* don't execute drivers */ + ADT_DRIVERS_DISABLED = (1<<11), + + /* F-Curves from this AnimData block are not visible in the Graph Editor */ + ADT_CURVES_NOT_VISIBLE = (1<<16), } eAnimData_Flag; /* Animation Data recalculation settings (to be set by depsgraph) */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index f8ea5f95d65..b0f089d670f 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -148,7 +148,7 @@ typedef struct Curve { ListBase *editnurb; /* edited data, not in file, use pointer so we can check for it */ struct Object *bevobj, *taperobj, *textoncurve; - struct Ipo *ipo; + struct Ipo *ipo; // XXX depreceated... old animation system Path *path; struct Key *key; struct Material **mat; @@ -193,7 +193,8 @@ typedef struct Curve { int sepchar; - int totbox, actbox, pad; + float ctime; /* current evaltime - for use by Objects parented to curves */ + int totbox, actbox; struct TextBox *tb; int selstart, selend; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index e9b246a323d..c71aa81147d 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -783,6 +783,10 @@ typedef struct Scene { #define MINFRAME 1 #define MINFRAMEF 1.0f +/* (minimum frame number for current-frame) */ +#define MINAFRAME -300000 +#define MINAFRAMEF -300000.0f + /* depricate this! */ #define TESTBASE(v3d, base) ( ((base)->flag & SELECT) && ((base)->lay & v3d->lay) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0) ) #define TESTBASELIB(v3d, base) ( ((base)->flag & SELECT) && ((base)->lay & v3d->lay) && ((base)->object->id.lib==0) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0)) @@ -851,6 +855,7 @@ typedef struct Scene { /* sce->flag */ #define SCE_DS_SELECTED (1<<0) #define SCE_DS_COLLAPSED (1<<1) +#define SCE_NLA_EDIT_ON (1<<2) /* return flag next_object function */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 182e5481999..dc0ec51fa4f 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -96,10 +96,6 @@ typedef struct SpaceIpo { short blockhandler[8]; View2D v2d; /* depricated, copied to region */ - // 'IPO keys' - vertical lines for editing multiple keyframes at once - use Dopesheet instead for this? - //ListBase ipokey; // XXX it's not clear how these will come back yet - //short showkey; // XXX this doesn't need to be restored until ipokeys come back - struct bDopeSheet *ads; /* settings for filtering animation data (NOTE: we use a pointer due to code-linking issues) */ ListBase ghostCurves; /* sampled snapshots of F-Curves used as in-session guides */ @@ -107,7 +103,7 @@ typedef struct SpaceIpo { short mode; /* mode for the Graph editor (eGraphEdit_Mode) */ short flag; /* settings for Graph editor */ short autosnap; /* time-transform autosnapping settings for Graph editor (eAnimEdit_AutoSnap in DNA_action_types.h) */ - char pin, lock; + char pin, lock; // XXX old, unused vars that are probably going to be depreceated soon... } SpaceIpo; typedef struct SpaceButs { @@ -276,10 +272,11 @@ typedef struct SpaceNla { short blockhandler[8]; - short menunr, lock; short autosnap; /* this uses the same settings as autosnap for Action Editor */ short flag; + int pad; + struct bDopeSheet *ads; View2D v2d; /* depricated, copied to region */ } SpaceNla; @@ -751,8 +748,11 @@ enum { #define IMS_INFILESLI 4 /* nla->flag */ + // depreceated #define SNLA_ALLKEYED (1<<0) + // depreceated #define SNLA_ACTIVELAYERS (1<<1) + #define SNLA_DRAWTIME (1<<2) #define SNLA_NOTRANSKEYCULL (1<<3) #define SNLA_NODRAWCFRANUM (1<<4) diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 93fbc8b5f17..3631d83310a 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -174,12 +174,14 @@ extern StructRNA RNA_ExplodeModifier; extern StructRNA RNA_ExpressionController; extern StructRNA RNA_Event; extern StructRNA RNA_FCurve; +extern StructRNA RNA_FCurveSample; extern StructRNA RNA_FileSelectParams; extern StructRNA RNA_FModifier; extern StructRNA RNA_FModifierCycles; extern StructRNA RNA_FModifierEnvelope; +extern StructRNA RNA_FModifierEnvelopeControlPoint; +extern StructRNA RNA_FModifierFunctionGenerator; extern StructRNA RNA_FModifierGenerator; -extern StructRNA RNA_FModifierGenerator_Function; extern StructRNA RNA_FModifierGenerator_PolyExpanded; extern StructRNA RNA_FModifierLimits; extern StructRNA RNA_FModifierNoise; @@ -274,6 +276,8 @@ extern StructRNA RNA_MultiresModifier; extern StructRNA RNA_MusgraveTexture; extern StructRNA RNA_NandController; extern StructRNA RNA_NearSensor; +extern StructRNA RNA_NlaTrack; +extern StructRNA RNA_NlaStrip; extern StructRNA RNA_Node; extern StructRNA RNA_NodeTree; extern StructRNA RNA_NoiseTexture; @@ -366,7 +370,10 @@ extern StructRNA RNA_SoundSequence; extern StructRNA RNA_Space; extern StructRNA RNA_Space3DView; extern StructRNA RNA_SpaceButtonsWindow; +extern StructRNA RNA_SpaceDopeSheetEditor; +extern StructRNA RNA_SpaceGraphEditor; extern StructRNA RNA_SpaceImageEditor; +extern StructRNA RNA_SpaceNLA; extern StructRNA RNA_SpaceOutliner; extern StructRNA RNA_SpaceSequenceEditor; extern StructRNA RNA_SpaceTextEditor; diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 5230c260dbd..61a8f8a29e9 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1921,6 +1921,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh}, {"rna_meta.c", NULL, RNA_def_meta}, {"rna_modifier.c", NULL, RNA_def_modifier}, + {"rna_nla.c", NULL, RNA_def_nla}, {"rna_nodetree.c", NULL, RNA_def_nodetree}, {"rna_object.c", "rna_object_api.c", RNA_def_object}, {"rna_object_force.c", NULL, RNA_def_object_force}, diff --git a/source/blender/makesrna/intern/rna_animation.c b/source/blender/makesrna/intern/rna_animation.c index 2ed47effec1..3469d716853 100644 --- a/source/blender/makesrna/intern/rna_animation.c +++ b/source/blender/makesrna/intern/rna_animation.c @@ -184,7 +184,7 @@ void rna_def_animdata(BlenderRNA *brna) /* NLA */ prop= RNA_def_property(srna, "nla_tracks", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "nla_tracks", NULL); - RNA_def_property_struct_type(prop, "UnknownType"); // XXX! + RNA_def_property_struct_type(prop, "NlaTrack"); RNA_def_property_ui_text(prop, "NLA Tracks", "NLA Tracks (i.e. Animation Layers)."); /* Action */ diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index cd3642f65a2..56c0ca1b491 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -728,13 +728,13 @@ static void rna_def_constraint_action(BlenderRNA *brna) prop= RNA_def_property(srna, "start_frame", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "start"); - RNA_def_property_range(prop, MINFRAME, MAXFRAME); + RNA_def_property_range(prop, MINAFRAME, MAXFRAME); RNA_def_property_ui_text(prop, "Start Frame", "First frame of the Action to use."); RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); prop= RNA_def_property(srna, "end_frame", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "end"); - RNA_def_property_range(prop, MINFRAME, MAXFRAME); + RNA_def_property_range(prop, MINAFRAME, MAXFRAME); RNA_def_property_ui_text(prop, "End Frame", "Last frame of the Action to use."); RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 41a47e279e9..91488aa2a49 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -31,6 +31,7 @@ #include "DNA_curve_types.h" #include "DNA_material_types.h" +#include "DNA_scene_types.h" EnumPropertyItem beztriple_handle_type_items[] = { {HD_FREE, "FREE", 0, "Free", ""}, @@ -558,6 +559,11 @@ static void rna_def_curve(BlenderRNA *brna) RNA_def_property_ui_range(prop, 1, 1024, 1, 0); RNA_def_property_ui_text(prop, "Render Resolution V", "Surface resolution in V direction used while rendering. Zero skips this property."); + + prop= RNA_def_property(srna, "eval_time", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ctime"); + RNA_def_property_ui_text(prop, "Evaluation Time", "Parametric position along the length of the curve that Objects 'following' it should be at."); + /* pointers */ prop= RNA_def_property(srna, "bevel_object", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "bevobj"); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 806219ec6bf..95a0482557f 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -37,6 +37,7 @@ EnumPropertyItem fmodifier_type_items[] = { {FMODIFIER_TYPE_NULL, "NULL", 0, "Invalid", ""}, {FMODIFIER_TYPE_GENERATOR, "GENERATOR", 0, "Generator", ""}, + {FMODIFIER_TYPE_FN_GENERATOR, "FNGENERATOR", 0, "Built-In Function", ""}, {FMODIFIER_TYPE_ENVELOPE, "ENVELOPE", 0, "Envelope", ""}, {FMODIFIER_TYPE_CYCLES, "CYCLES", 0, "Cycles", ""}, {FMODIFIER_TYPE_NOISE, "NOISE", 0, "Noise", ""}, @@ -47,62 +48,6 @@ EnumPropertyItem fmodifier_type_items[] = { #ifdef RNA_RUNTIME -float FModGenFunc_amplitude_get(PointerRNA *ptr) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - return (data->coefficients) ? (float)data->coefficients[0] : 1.0f; -} - -void FModGenFunc_amplitude_set(PointerRNA *ptr, float value) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - if (data->coefficients) data->coefficients[0]= value; -} - -float FModGenFunc_pre_multiplier_get(PointerRNA *ptr) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - return (data->coefficients) ? (float)data->coefficients[1] : 1.0f; -} - -void FModGenFunc_pre_multiplier_set(PointerRNA *ptr, float value) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - if (data->coefficients) data->coefficients[1]= value; -} - -float FModGenFunc_x_offset_get(PointerRNA *ptr) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - return (data->coefficients) ? (float)data->coefficients[2] : 0.0f; -} - -void FModGenFunc_x_offset_set(PointerRNA *ptr, float value) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - if (data->coefficients) data->coefficients[2]= value; -} - -float FModGenFunc_y_offset_get(PointerRNA *ptr) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - return (data->coefficients) ? (float)data->coefficients[3] : 0.0f; -} - -void FModGenFunc_y_offset_set(PointerRNA *ptr, float value) -{ - FModifier *fcm = (FModifier *)(ptr->data); - FMod_Generator *data= (FMod_Generator*)(fcm->data); - if (data->coefficients) data->coefficients[3]= value; -} - /* --------- */ StructRNA *rna_FModifierType_refine(struct PointerRNA *ptr) @@ -111,20 +56,9 @@ StructRNA *rna_FModifierType_refine(struct PointerRNA *ptr) switch (fcm->type) { case FMODIFIER_TYPE_GENERATOR: - { - FMod_Generator *gen= (FMod_Generator *)fcm->data; - - switch (gen->mode) { - case FCM_GENERATOR_POLYNOMIAL: - return &RNA_FModifierGenerator_PolyExpanded; - //case FCM_GENERATOR_POLYNOMIAL_FACTORISED: - case FCM_GENERATOR_FUNCTION: - return &RNA_FModifierGenerator_Function; - //case FCM_GENERATOR_EXPRESSION: - default: - return &RNA_FModifierGenerator; - } - } + return &RNA_FModifierGenerator; + case FMODIFIER_TYPE_FN_GENERATOR: + return &RNA_FModifierFunctionGenerator; case FMODIFIER_TYPE_ENVELOPE: return &RNA_FModifierEnvelope; case FMODIFIER_TYPE_CYCLES: @@ -215,21 +149,22 @@ static void rna_FCurve_RnaPath_set(PointerRNA *ptr, const char *value) #else -static void rna_def_fmodifier_generator_common(StructRNA *srna) + +static void rna_def_fmodifier_generator(BlenderRNA *brna) { + StructRNA *srna; PropertyRNA *prop; static EnumPropertyItem prop_mode_items[] = { {FCM_GENERATOR_POLYNOMIAL, "POLYNOMIAL", 0, "Expanded Polynomial", ""}, {FCM_GENERATOR_POLYNOMIAL_FACTORISED, "POLYNOMIAL_FACTORISED", 0, "Factorised Polynomial", ""}, - {FCM_GENERATOR_FUNCTION, "FUNCTION", 0, "Built-In Function", ""}, - {FCM_GENERATOR_EXPRESSION, "EXPRESSION", 0, "Expression", ""}, {0, NULL, 0, NULL, NULL}}; - - /* struct wrapping settings */ - RNA_def_struct_sdna_from(srna, "FMod_Generator", "data"); - /* settings */ + srna= RNA_def_struct(brna, "FModifierGenerator", "FModifier"); + RNA_def_struct_ui_text(srna, "Generator F-Curve Modifier", "Deterministically generates values for the modified F-Curve."); + RNA_def_struct_sdna_from(srna, "FMod_Generator", "data"); + + /* define common props */ prop= RNA_def_property(srna, "additive", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE); RNA_def_property_ui_text(prop, "Additive", "Values generated by this modifier are applied on top of the existing values instead of overwriting them."); @@ -238,42 +173,23 @@ static void rna_def_fmodifier_generator_common(StructRNA *srna) prop= RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_mode_items); RNA_def_property_ui_text(prop, "Mode", "Type of generator to use."); -} - -/* this is a temporary dummy generator-modifier wrapping (to be discarded) */ -static void rna_def_fmodifier_generator(BlenderRNA *brna) -{ - StructRNA *srna; - srna= RNA_def_struct(brna, "FModifierGenerator", "FModifier"); - RNA_def_struct_ui_text(srna, "Generator F-Curve Modifier", "Deterministically generates values for the modified F-Curve."); - - /* define common props */ - rna_def_fmodifier_generator_common(srna); -} - -static void rna_def_fmodifier_generator_polyexpanded(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna= RNA_def_struct(brna, "FModifierGenerator_PolyExpanded", "FModifier"); - RNA_def_struct_ui_text(srna, "Expanded Polynomial Generator", "Generates values for the modified F-Curve using expanded polynomial expresion."); - - /* define common props */ - rna_def_fmodifier_generator_common(srna); /* order of the polynomial */ // XXX this has a special validation func prop= RNA_def_property(srna, "poly_order", PROP_INT, PROP_NONE); - RNA_def_property_ui_text(prop, "Polynomial Order", "The highest power of 'x' for this polynomial. (i.e. the number of coefficients - 1)"); + RNA_def_property_ui_text(prop, "Polynomial Order", "The highest power of 'x' for this polynomial. (number of coefficients - 1)"); /* coefficients array */ - //prop= RNA_def_property(srna, "coefficients", PROP_FLOAT, PROP_NONE); - //RNA_def_property_ui_text(prop, "Coefficients", "Coefficients for 'x' (starting from lowest power)."); + // FIXME: this is quite difficult to try to wrap + //prop= RNA_def_property(srna, "coefficients", PROP_COLLECTION, PROP_NONE); + //RNA_def_property_collection_funcs(prop, "rna_FModifierGenerator_coefficients_begin", "rna_FModifierGenerator_coefficients_next", "rna_FModifierGenerator_coefficients_end", "rna_iterator_array_get", "rna_FModifierGenerator_coefficients_length", 0, 0, 0, 0); + //RNA_def_property_ui_text(prop, "Coefficients", "Coefficients for 'x' (starting from lowest power of x^0)."); } -static void rna_def_fmodifier_generator_function(BlenderRNA *brna) +/* --------- */ + +static void rna_def_fmodifier_function_generator(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; @@ -284,48 +200,95 @@ static void rna_def_fmodifier_generator_function(BlenderRNA *brna) {2, "TAN", 0, "Tangent", ""}, {3, "SQRT", 0, "Square Root", ""}, {4, "LN", 0, "Natural Logarithm", ""}, + {5, "SINC", 0, "Normalised Sine", "sin(x) / x"}, {0, NULL, 0, NULL, NULL}}; - - srna= RNA_def_struct(brna, "FModifierGenerator_Function", "FModifier"); - RNA_def_struct_ui_text(srna, "Built-In Function Generator", "Generates values for modified F-Curve using Built-In Function."); - - /* common settings */ - rna_def_fmodifier_generator_common(srna); - - /* type */ - prop= RNA_def_property(srna, "func_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, prop_type_items); - RNA_def_property_ui_text(prop, "Type", "Type of Built-In function to use as generator."); + srna= RNA_def_struct(brna, "FModifierFunctionGenerator", "FModifier"); + RNA_def_struct_ui_text(srna, "Built-In Function F-Modifier", "Generates values using a Built-In Function."); + RNA_def_struct_sdna_from(srna, "FMod_FunctionGenerator", "data"); /* coefficients */ prop= RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_funcs(prop, "FModGenFunc_amplitude_get", "FModGenFunc_amplitude_set", NULL); - RNA_def_property_ui_text(prop, "Amplitude", "Scale factor for y-values generated by the function."); + RNA_def_property_ui_text(prop, "Amplitude", "Scale factor determining the maximum/minimum values."); - prop= RNA_def_property(srna, "pre_multiplier", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_funcs(prop, "FModGenFunc_pre_multiplier_get", "FModGenFunc_pre_multiplier_set", NULL); - RNA_def_property_ui_text(prop, "PreMultiplier", "Scale factor for x-value inputs to function."); + prop= RNA_def_property(srna, "phase_multiplier", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Phase Multiplier", "Scale factor determining the 'speed' of the function."); - prop= RNA_def_property(srna, "x_offset", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_funcs(prop, "FModGenFunc_x_offset_get", "FModGenFunc_x_offset_set", NULL); - RNA_def_property_ui_text(prop, "X Offset", "Offset for x-value inputs to function."); + prop= RNA_def_property(srna, "phase_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Phase Offset", "Constant factor to offset time by for function."); - prop= RNA_def_property(srna, "y_offset", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_funcs(prop, "FModGenFunc_y_offset_get", "FModGenFunc_y_offset_set", NULL); - RNA_def_property_ui_text(prop, "Y Offset", "Offset for y-values generated by the function."); + prop= RNA_def_property(srna, "value_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Value Offset", "Constant factor to offset values by."); + + /* flags */ + prop= RNA_def_property(srna, "additive", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE); + RNA_def_property_ui_text(prop, "Additive", "Values generated by this modifier are applied on top of the existing values instead of overwriting them."); + + prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_ui_text(prop, "Type", "Type of built-in function to use."); } /* --------- */ +static void rna_def_fmodifier_envelope_ctrl(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "FModifierEnvelopeControlPoint", NULL); + RNA_def_struct_ui_text(srna, "Envelope Control Point", "Control point for envelope F-Modifier."); + RNA_def_struct_sdna(srna, "FCM_EnvelopeData"); + + /* min/max extents + * - for now, these are allowed to go past each other, so that we can have inverted action + * - technically, the range is limited by the settings in the envelope-modifier data, not here... + */ + prop= RNA_def_property(srna, "minimum", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "min"); + RNA_def_property_ui_text(prop, "Minimum Value", "Lower bound of envelope at this control-point."); + + prop= RNA_def_property(srna, "maximum", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "max"); + RNA_def_property_ui_text(prop, "Maximum Value", "Upper bound of envelope at this control-point."); + + /* Frame */ + prop= RNA_def_property(srna, "frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "time"); + RNA_def_property_ui_text(prop, "Frame", "Frame this control-point occurs on."); + + // TODO: + // - selection flags (not implemented in UI yet though) +} + static void rna_def_fmodifier_envelope(BlenderRNA *brna) { StructRNA *srna; - //PropertyRNA *prop; + PropertyRNA *prop; srna= RNA_def_struct(brna, "FModifierEnvelope", "FModifier"); - RNA_def_struct_ui_text(srna, "Envelope F-Curve Modifier", "Scales the values of the modified F-Curve."); + RNA_def_struct_ui_text(srna, "Envelope F-Modifier", "Scales the values of the modified F-Curve."); RNA_def_struct_sdna_from(srna, "FMod_Envelope", "data"); + + /* Collections */ + prop= RNA_def_property(srna, "control_points", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "data", "totvert"); + RNA_def_property_struct_type(prop, "FModifierEnvelopeControlPoint"); + RNA_def_property_ui_text(prop, "Control Points", "Control points defining the shape of the envelope."); + + /* Range Settings */ + prop= RNA_def_property(srna, "reference_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "midval"); + RNA_def_property_ui_text(prop, "Reference Value", "Value that envelope's influence is centered around / based on."); + + prop= RNA_def_property(srna, "default_minimum", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "min"); + RNA_def_property_ui_text(prop, "Default Minimum", "Lower distance from Reference Value for 1:1 default influence."); + + prop= RNA_def_property(srna, "default_maximum", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "max"); + RNA_def_property_ui_text(prop, "Default Maximum", "Upper distance from Reference Value for 1:1 default influence."); } /* --------- */ @@ -343,7 +306,7 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}}; srna= RNA_def_struct(brna, "FModifierCycles", "FModifier"); - RNA_def_struct_ui_text(srna, "Cycles F-Curve Modifier", "Repeats the values of the modified F-Curve."); + RNA_def_struct_ui_text(srna, "Cycles F-Modifier", "Repeats the values of the modified F-Curve."); RNA_def_struct_sdna_from(srna, "FMod_Cycles", "data"); /* before */ @@ -372,7 +335,7 @@ static void rna_def_fmodifier_python(BlenderRNA *brna) //PropertyRNA *prop; srna= RNA_def_struct(brna, "FModifierPython", "FModifier"); - RNA_def_struct_ui_text(srna, "Python F-Curve Modifier", "Performs user-defined operation on the modified F-Curve."); + RNA_def_struct_ui_text(srna, "Python F-Modifier", "Performs user-defined operation on the modified F-Curve."); RNA_def_struct_sdna_from(srna, "FMod_Python", "data"); } @@ -384,7 +347,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) PropertyRNA *prop; srna= RNA_def_struct(brna, "FModifierLimits", "FModifier"); - RNA_def_struct_ui_text(srna, "Limits F-Curve Modifier", "Limits the time/value ranges of the modified F-Curve."); + RNA_def_struct_ui_text(srna, "Limits F-Modifier", "Limits the time/value ranges of the modified F-Curve."); RNA_def_struct_sdna_from(srna, "FMod_Limits", "data"); prop= RNA_def_property(srna, "use_minimum_x", PROP_BOOLEAN, PROP_NONE); @@ -435,7 +398,7 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}}; srna= RNA_def_struct(brna, "FModifierNoise", "FModifier"); - RNA_def_struct_ui_text(srna, "Noise F-Curve Modifier", "Gives randomness to the modified F-Curve."); + RNA_def_struct_ui_text(srna, "Noise F-Modifier", "Gives randomness to the modified F-Curve."); RNA_def_struct_sdna_from(srna, "FMod_Noise", "data"); prop= RNA_def_property(srna, "modification", PROP_ENUM, PROP_NONE); @@ -471,7 +434,7 @@ void rna_def_fmodifier(BlenderRNA *brna) /* base struct definition */ srna= RNA_def_struct(brna, "FModifier", NULL); RNA_def_struct_refine_func(srna, "rna_FModifierType_refine"); - RNA_def_struct_ui_text(srna, "FCurve Modifier", "Modifier for values of F-Curve."); + RNA_def_struct_ui_text(srna, "F-Modifier", "Modifier for values of F-Curve."); #if 0 // XXX not used yet /* name */ @@ -569,6 +532,27 @@ void rna_def_channeldriver(BlenderRNA *brna) /* *********************** */ +static void rna_def_fpoint(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "FCurveSample", NULL); + RNA_def_struct_sdna(srna, "FPoint"); + RNA_def_struct_ui_text(srna, "F-Curve Sample", "Sample point for F-Curve."); + + /* Boolean values */ + prop= RNA_def_property(srna, "selected", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", 1); + RNA_def_property_ui_text(prop, "Selected", "Selection status"); + + /* Vector value */ + prop= RNA_def_property(srna, "point", PROP_FLOAT, PROP_VECTOR); + RNA_def_property_array(prop, 2); + RNA_def_property_float_sdna(prop, NULL, "vec"); + RNA_def_property_ui_text(prop, "Point", "Point coordinates"); +} + void rna_def_fcurve(BlenderRNA *brna) { StructRNA *srna; @@ -619,7 +603,7 @@ void rna_def_fcurve(BlenderRNA *brna) /* Collections */ prop= RNA_def_property(srna, "sampled_points", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fpt", "totvert"); - RNA_def_property_struct_type(prop, "CurvePoint"); // XXX FPoints not BPoints here! FPoints are much smaller! + RNA_def_property_struct_type(prop, "FCurveSample"); RNA_def_property_ui_text(prop, "Sampled Points", "Sampled animation data"); prop= RNA_def_property(srna, "keyframe_points", PROP_COLLECTION, PROP_NONE); @@ -637,6 +621,7 @@ void rna_def_fcurve(BlenderRNA *brna) void RNA_def_fcurve(BlenderRNA *brna) { rna_def_fcurve(brna); + rna_def_fpoint(brna); rna_def_drivertarget(brna); rna_def_channeldriver(brna); @@ -644,9 +629,9 @@ void RNA_def_fcurve(BlenderRNA *brna) rna_def_fmodifier(brna); rna_def_fmodifier_generator(brna); - rna_def_fmodifier_generator_polyexpanded(brna); - rna_def_fmodifier_generator_function(brna); + rna_def_fmodifier_function_generator(brna); rna_def_fmodifier_envelope(brna); + rna_def_fmodifier_envelope_ctrl(brna); rna_def_fmodifier_cycles(brna); rna_def_fmodifier_python(brna); rna_def_fmodifier_limits(brna); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 084d05db51e..6a5d5ccfdc3 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -139,6 +139,7 @@ void RNA_def_material(struct BlenderRNA *brna); void RNA_def_mesh(struct BlenderRNA *brna); void RNA_def_meta(struct BlenderRNA *brna); void RNA_def_modifier(struct BlenderRNA *brna); +void RNA_def_nla(struct BlenderRNA *brna); void RNA_def_nodetree(struct BlenderRNA *brna); void RNA_def_object(struct BlenderRNA *brna); void RNA_def_object_force(struct BlenderRNA *brna); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 30e9346a961..a789f9065e3 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -506,7 +506,7 @@ static void rna_def_modifier_build(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_MOD_BUILD); prop= RNA_def_property(srna, "start", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 1, MAXFRAMEF); + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); RNA_def_property_ui_text(prop, "Start", "Specify the start frame of the effect."); RNA_def_property_update(prop, NC_OBJECT|ND_MODIFIER, "rna_Modifier_update"); @@ -660,18 +660,18 @@ static void rna_def_modifier_wave(BlenderRNA *brna) prop= RNA_def_property(srna, "time_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "timeoffs"); - RNA_def_property_range(prop, -MAXFRAMEF, MAXFRAMEF); + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); RNA_def_property_ui_text(prop, "Time Offset", "Either the starting frame (for positive speed) or ending frame (for negative speed.)"); RNA_def_property_update(prop, NC_OBJECT|ND_MODIFIER, "rna_Modifier_update"); prop= RNA_def_property(srna, "lifetime", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, -MAXFRAMEF, MAXFRAMEF); + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); RNA_def_property_ui_text(prop, "Lifetime", ""); RNA_def_property_update(prop, NC_OBJECT|ND_MODIFIER, "rna_Modifier_update"); prop= RNA_def_property(srna, "damping_time", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "damp"); - RNA_def_property_range(prop, -MAXFRAMEF, MAXFRAMEF); + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); RNA_def_property_ui_text(prop, "Damping Time", ""); RNA_def_property_update(prop, NC_OBJECT|ND_MODIFIER, "rna_Modifier_update"); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c new file mode 100644 index 00000000000..bc636af6849 --- /dev/null +++ b/source/blender/makesrna/intern/rna_nla.c @@ -0,0 +1,460 @@ +/** + * $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. + * + * Contributor(s): Blender Foundation (2009), Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "rna_internal.h" + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#ifdef RNA_RUNTIME + +#include +#include + +/* needed for some of the validation stuff... */ +#include "BKE_animsys.h" +#include "BKE_nla.h" + +/* temp constant defined for these funcs only... */ +#define NLASTRIP_MIN_LEN_THRESH 0.1f + +void rna_NlaStrip_name_set(PointerRNA *ptr, const char *value) +{ + NlaStrip *data= (NlaStrip *)ptr->data; + + /* copy the name first */ + BLI_strncpy(data->name, value, sizeof(data->name)); + + /* validate if there's enough info to do so */ + if (ptr->id.data) { + AnimData *adt= BKE_animdata_from_id(ptr->id.data); + BKE_nlastrip_validate_name(adt, data); + } +} + + +static void rna_NlaStrip_start_frame_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + + /* clamp value to lie within valid limits + * - cannot start past the end of the strip + some flexibility threshold + * - cannot start before the previous strip (if present) ends + * -> but if it was a transition, we could go up to the start of the strip + some flexibility threshold + * as long as we re-adjust the transition afterwards + * - minimum frame is -MAXFRAME so that we don't get clipping on frame 0 + */ + if (data->prev) { + if (data->prev->type == NLASTRIP_TYPE_TRANSITION) { + CLAMP(value, data->prev->start+NLASTRIP_MIN_LEN_THRESH, data->end-NLASTRIP_MIN_LEN_THRESH); + + /* readjust the transition to stick to the endpoints of the action-clips */ + data->prev->end= value; + } + else { + CLAMP(value, data->prev->end, data->end-NLASTRIP_MIN_LEN_THRESH); + } + } + else { + CLAMP(value, MINAFRAME, data->end); + } + data->start= value; +} + +static void rna_NlaStrip_end_frame_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + + /* clamp value to lie within valid limits + * - must not have zero or negative length strip, so cannot start before the first frame + * + some minimum-strip-length threshold + * - cannot end later than the start of the next strip (if present) + * -> but if it was a transition, we could go up to the start of the end - some flexibility threshold + * as long as we re-adjust the transition afterwards + */ + if (data->next) { + if (data->next->type == NLASTRIP_TYPE_TRANSITION) { + CLAMP(value, data->start+NLASTRIP_MIN_LEN_THRESH, data->next->end-NLASTRIP_MIN_LEN_THRESH); + + /* readjust the transition to stick to the endpoints of the action-clips */ + data->next->start= value; + } + else { + CLAMP(value, data->start+NLASTRIP_MIN_LEN_THRESH, data->next->start); + } + } + else { + CLAMP(value, data->start+NLASTRIP_MIN_LEN_THRESH, MAXFRAME); + } + data->end= value; + + + /* calculate the lengths the strip and its action (if applicable) */ + if (data->type == NLASTRIP_TYPE_CLIP) { + float len, actlen; + + len= data->end - data->start; + actlen= data->actend - data->actstart; + if (IS_EQ(actlen, 0.0f)) actlen= 1.0f; + + /* now, adjust the 'scale' setting to reflect this (so that this change can be valid) */ + data->scale= len / ((actlen) * data->repeat); + } +} + +static void rna_NlaStrip_scale_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + float actlen, mapping; + + /* set scale value */ + CLAMP(value, 0.0001f, 1000.0f); /* NOTE: these need to be synced with the values in the property definition in rna_def_nlastrip() */ + data->scale= value; + + /* calculate existing factors */ + actlen= data->actend - data->actstart; + if (IS_EQ(actlen, 0.0f)) actlen= 1.0f; + mapping= data->scale * data->repeat; + + /* adjust endpoint of strip in response to this */ + if (IS_EQ(mapping, 0.0f) == 0) + data->end = (actlen * mapping) + data->start; + else + printf("NlaStrip Set Scale Error (in RNA): Scale = %0.4f, Repeat = %0.4f \n", data->scale, data->repeat); +} + +static void rna_NlaStrip_repeat_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + float actlen, mapping; + + /* set scale value */ + CLAMP(value, 0.01f, 1000.0f); /* NOTE: these need to be synced with the values in the property definition in rna_def_nlastrip() */ + data->repeat= value; + + /* calculate existing factors */ + actlen= data->actend - data->actstart; + if (IS_EQ(actlen, 0.0f)) actlen= 1.0f; + mapping= data->scale * data->repeat; + + /* adjust endpoint of strip in response to this */ + if (IS_EQ(mapping, 0.0f) == 0) + data->end = (actlen * mapping) + data->start; + else + printf("NlaStrip Set Repeat Error (in RNA): Scale = %0.4f, Repeat = %0.4f \n", data->scale, data->repeat); +} + +static void rna_NlaStrip_blend_in_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + float len; + + /* blend-in is limited to the length of the strip, and also cannot overlap with blendout */ + len= (data->end - data->start) - data->blendout; + CLAMP(value, 0, len); + + data->blendin= value; +} + +static void rna_NlaStrip_blend_out_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + float len; + + /* blend-out is limited to the length of the strip */ + len= (data->end - data->start); + CLAMP(value, 0, len); + + /* it also cannot overlap with blendin */ + if ((len - value) < data->blendin) + value= len - data->blendin; + + data->blendout= value; +} + +static void rna_NlaStrip_action_start_frame_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + CLAMP(value, MINAFRAME, data->actend); + data->actstart= value; +} + +static void rna_NlaStrip_action_end_frame_set(PointerRNA *ptr, float value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + CLAMP(value, data->actstart, MAXFRAME); + data->actend= value; +} + +static void rna_NlaStrip_animated_influence_set(PointerRNA *ptr, int value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + + if (value) { + /* set the flag, then make sure a curve for this exists */ + data->flag |= NLASTRIP_FLAG_USR_INFLUENCE; + BKE_nlastrip_validate_fcurves(data); + } + else + data->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE; +} + +static void rna_NlaStrip_animated_time_set(PointerRNA *ptr, int value) +{ + NlaStrip *data= (NlaStrip*)ptr->data; + + if (value) { + /* set the flag, then make sure a curve for this exists */ + data->flag |= NLASTRIP_FLAG_USR_TIME; + BKE_nlastrip_validate_fcurves(data); + } + else + data->flag &= ~NLASTRIP_FLAG_USR_TIME; +} + +#else + +void rna_def_nlastrip(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* enum defs */ + static EnumPropertyItem prop_type_items[] = { + {NLASTRIP_TYPE_CLIP, "CLIP", 0, "Action Clip", "NLA Strip references some Action."}, + {NLASTRIP_TYPE_TRANSITION, "TRANSITION", 0, "Transition", "NLA Strip 'transitions' between adjacent strips."}, + {NLASTRIP_TYPE_META, "META", 0, "Meta", "NLA Strip acts as a container for adjacent strips."}, + {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem prop_mode_blend_items[] = { + {NLASTRIP_MODE_REPLACE, "REPLACE", 0, "Replace", "Result strip replaces the accumulated results by amount specified by influence."}, + {NLASTRIP_MODE_ADD, "ADD", 0, "Add", "Weighted result of strip is added to the accumlated results."}, + {NLASTRIP_MODE_SUBTRACT, "SUBTRACT", 0, "Subtract", "Weighted result of strip is removed from the accumlated results."}, + {NLASTRIP_MODE_MULTIPLY, "MULITPLY", 0, "Multiply", "Weighted result of strip is multiplied with the accumlated results."}, + {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem prop_mode_extend_items[] = { + {NLASTRIP_EXTEND_NOTHING, "NOTHING", 0, "Nothing", "Strip has no influence past its extents."}, + {NLASTRIP_EXTEND_HOLD, "HOLD", 0, "Hold", "Hold the first frame if no previous strips in track, and always hold last frame."}, + {NLASTRIP_EXTEND_HOLD_FORWARD, "HOLD_FORWARD", 0, "Hold Forward", "Only hold last frame."}, + {0, NULL, 0, NULL, NULL}}; + + /* struct definition */ + srna= RNA_def_struct(brna, "NlaStrip", NULL); + RNA_def_struct_ui_text(srna, "NLA Strip", "A container referencing an existing Action."); + RNA_def_struct_ui_icon(srna, ICON_NLA); // XXX + + /* name property */ + prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", ""); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NlaStrip_name_set"); + RNA_def_struct_name_property(srna, prop); + + /* Enums */ + prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); // XXX for now, not editable, since this is dangerous + RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_ui_text(prop, "Type", "Type of NLA Strip."); + + prop= RNA_def_property(srna, "extrapolation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "extendmode"); + RNA_def_property_enum_items(prop, prop_mode_extend_items); + RNA_def_property_ui_text(prop, "Extrapolation", "Action to take for gaps past the strip extents."); + + prop= RNA_def_property(srna, "blending", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "blendmode"); + RNA_def_property_enum_items(prop, prop_mode_blend_items); + RNA_def_property_ui_text(prop, "Blending", "Method used for combining strip's result with accumulated result."); + + /* Strip extents */ + prop= RNA_def_property(srna, "start_frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "start"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_start_frame_set", NULL); + RNA_def_property_ui_text(prop, "Start Frame", ""); + + prop= RNA_def_property(srna, "end_frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "end"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_end_frame_set", NULL); + RNA_def_property_ui_text(prop, "End Frame", ""); + + /* Blending */ + prop= RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "blendin"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_blend_in_set", NULL); + RNA_def_property_ui_text(prop, "Blend In", "Number of frames at start of strip to fade in influence."); + + prop= RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "blendout"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_blend_out_set", NULL); + RNA_def_property_ui_text(prop, "Blend Out", ""); + + prop= RNA_def_property(srna, "auto_blending", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_AUTO_BLENDS); + RNA_def_property_ui_text(prop, "Auto Blend In/Out", "Number of frames for Blending In/Out is automatically determined from overlapping strips."); + + /* Action */ + prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "act"); + RNA_def_property_ui_text(prop, "Action", "Action referenced by this strip."); + + /* Action extents */ + prop= RNA_def_property(srna, "action_start_frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "actstart"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_action_start_frame_set", NULL); + RNA_def_property_ui_text(prop, "Action Start Frame", ""); + + prop= RNA_def_property(srna, "action_end_frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "actend"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_action_end_frame_set", NULL); + RNA_def_property_ui_text(prop, "Action End Frame", ""); + + /* Action Reuse */ + prop= RNA_def_property(srna, "repeat", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "repeat"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_repeat_set", NULL); + RNA_def_property_range(prop, 0.1f, 1000.0f); /* these limits have currently be chosen arbitarily, but could be extended (minimum should still be > 0 though) if needed... */ + RNA_def_property_ui_text(prop, "Repeat", "Number of times to repeat the action range."); + + prop= RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scale"); + RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_scale_set", NULL); + RNA_def_property_range(prop, 0.0001f, 1000.0f); /* these limits can be extended, but beyond this, we can get some crazy+annoying bugs due to numeric errors */ + RNA_def_property_ui_text(prop, "Scale", "Scaling factor for action."); + + /* Strip's F-Curves */ + prop= RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "FCurve"); + RNA_def_property_ui_text(prop, "F-Curves", "F-Curves for controlling the strip's influence and timing."); + + /* Strip's F-Modifiers */ + prop= RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "FModifier"); + RNA_def_property_ui_text(prop, "Modifiers", "Modifiers affecting all the F-Curves in the referenced Action."); + + /* Strip's Sub-Strips (for Meta-Strips) */ + prop= RNA_def_property(srna, "strips", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "NlaStrip"); + RNA_def_property_ui_text(prop, "NLA Strips", "NLA Strips that this strip acts as a container for (if it is of type Meta)."); + + /* Settings - Values necessary for evaluation */ + prop= RNA_def_property(srna, "influence", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Influence", "Amount the strip contributes to the current result."); + + prop= RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate."); + + // TODO: should the animated_influence/time settings be animatable themselves? + prop= RNA_def_property(srna, "animated_influence", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_INFLUENCE); + RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_influence_set"); + RNA_def_property_ui_text(prop, "Animated Influence", "Influence setting is controlled by an F-Curve rather than automatically determined."); + + prop= RNA_def_property(srna, "animated_time", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME); + RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_time_set"); + RNA_def_property_ui_text(prop, "Animated Strip Time", "Strip time is controlled by an F-Curve rather than automatically determined."); + + /* settings */ + prop= RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* can be made editable by hooking it up to the necessary NLA API methods */ + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_ACTIVE); + RNA_def_property_ui_text(prop, "Active", "NLA Strip is active."); + + prop= RNA_def_property(srna, "selected", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_SELECT); + RNA_def_property_ui_text(prop, "Selected", "NLA Strip is selected."); + + prop= RNA_def_property(srna, "muted", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_MUTED); + RNA_def_property_ui_text(prop, "Muted", "NLA Strip is not evaluated."); + + prop= RNA_def_property(srna, "reversed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_REVERSE); + RNA_def_property_ui_text(prop, "Reversed", "NLA Strip is played back in reverse order (only when timing is automatically determined)."); + + // TODO: + // - sync length +} + +void rna_def_nlatrack(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "NlaTrack", NULL); + RNA_def_struct_ui_text(srna, "NLA Track", "A animation layer containing Actions referenced as NLA strips."); + RNA_def_struct_ui_icon(srna, ICON_NLA); + + /* strips collection */ + prop= RNA_def_property(srna, "strips", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "NlaStrip"); + RNA_def_property_ui_text(prop, "NLA Strips", "NLA Strips on this NLA-track."); + + /* name property */ + prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", ""); + RNA_def_struct_name_property(srna, prop); + + /* settings */ + prop= RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* can be made editable by hooking it up to the necessary NLA API methods */ + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_ACTIVE); + RNA_def_property_ui_text(prop, "Active", "NLA Track is active."); + + prop= RNA_def_property(srna, "solo", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* can be made editable by hooking it up to the necessary NLA API methods */ + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_SOLO); + RNA_def_property_ui_text(prop, "Solo", "NLA Track is evaluated itself (i.e. active Action and all other NLA Tracks in the same AnimData block are disabled)."); + + prop= RNA_def_property(srna, "selected", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_SELECTED); + RNA_def_property_ui_text(prop, "Selected", "NLA Track is selected."); + + prop= RNA_def_property(srna, "muted", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_MUTED); + RNA_def_property_ui_text(prop, "Muted", "NLA Track is not evaluated."); + + prop= RNA_def_property(srna, "locked", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_PROTECTED); + RNA_def_property_ui_text(prop, "Locked", "NLA Track is locked."); +} + +/* --------- */ + +void RNA_def_nla(BlenderRNA *brna) +{ + rna_def_nlatrack(brna); + rna_def_nlastrip(brna); +} + + +#endif diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index f814980f367..092d15de96e 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1170,7 +1170,7 @@ static void rna_def_object(BlenderRNA *brna) prop= RNA_def_property(srna, "time_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "sf"); - RNA_def_property_range(prop, -MAXFRAMEF, MAXFRAMEF); + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); RNA_def_property_ui_text(prop, "Time Offset", "Animation offset in frames for IPO's and dupligroup instances."); RNA_def_property_update(prop, NC_OBJECT|ND_TRANSFORM, "rna_Object_update"); @@ -1264,29 +1264,6 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Pose Mode", "Object with armature data is in pose mode."); - // XXX this stuff should be moved to AnimData... -/* - prop= RNA_def_property(srna, "nla_disable_path", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "nlaflag", OB_DISABLE_PATH); - RNA_def_property_ui_text(prop, "NLA Disable Path", "Disable path temporally, for editing cycles."); - - prop= RNA_def_property(srna, "nla_collapsed", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "nlaflag", OB_NLA_COLLAPSED); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "NLA Collapsed", ""); - - prop= RNA_def_property(srna, "nla_override", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "nlaflag", OB_NLA_OVERRIDE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "NLA Override", ""); - - prop= RNA_def_property(srna, "nla_strips", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "nlastrips", NULL); - RNA_def_property_struct_type(prop, "UnknownType"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "NLA Strips", "NLA strips of the object."); -*/ - /* shape keys */ prop= RNA_def_property(srna, "shape_key_lock", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index c4f2c29409f..98ed12afd5a 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -226,8 +226,8 @@ static void rna_PartSettings_start_set(struct PointerRNA *ptr, float value) if(settings->type==PART_REACTOR && value < 1.0) value = 1.0; - else if (value < -30000.0f) //TODO: replace 30000 with MAXFRAMEF when available in 2.5 - value = -30000.0f; + else if (value < MINAFRAMEF) + value = MINAFRAMEF; settings->sta = value; } @@ -1090,19 +1090,19 @@ static void rna_def_particle_settings(BlenderRNA *brna) /* general values */ prop= RNA_def_property(srna, "start", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "sta");//optional if prop names are the same - RNA_def_property_range(prop, -30000.0f, 30000.0f); //TODO: replace 30000 with MAXFRAMEF when available in 2.5 + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); RNA_def_property_float_funcs(prop, NULL, "rna_PartSettings_start_set", NULL); RNA_def_property_ui_text(prop, "Start", "Frame # to start emitting particles."); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); prop= RNA_def_property(srna, "end", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, -30000.0f, 30000.0f); //TODO: replace 30000 with MAXFRAMEF when available in 2.5 + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); RNA_def_property_float_funcs(prop, NULL, "rna_PartSettings_end_set", NULL); RNA_def_property_ui_text(prop, "End", "Frame # to stop emitting particles."); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); prop= RNA_def_property(srna, "lifetime", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 1.0f, 30000.0f); + RNA_def_property_range(prop, 1.0f, MAXFRAMEF); RNA_def_property_ui_text(prop, "Lifetime", "Specify the life span of the particles"); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 3cd45bb6dd8..6e2a6e27928 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -975,7 +975,7 @@ void RNA_def_scene(BlenderRNA *brna) prop= RNA_def_property(srna, "current_frame", PROP_INT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATEABLE); RNA_def_property_int_sdna(prop, NULL, "r.cfra"); - RNA_def_property_range(prop, MINFRAME, MAXFRAME); + RNA_def_property_range(prop, MINAFRAME, MAXFRAME); RNA_def_property_ui_text(prop, "Current Frame", ""); RNA_def_property_update(prop, NC_SCENE|ND_FRAME, "rna_Scene_frame_update"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index b4422541718..9bf3f54e159 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -30,6 +30,7 @@ #include "rna_internal.h" +#include "DNA_action_types.h" #include "DNA_object_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" @@ -89,9 +90,8 @@ static StructRNA* rna_Space_refine(struct PointerRNA *ptr) switch(space->spacetype) { case SPACE_VIEW3D: return &RNA_Space3DView; - /*case SPACE_IPO: + case SPACE_IPO: return &RNA_SpaceGraphEditor; - */ case SPACE_OUTLINER: return &RNA_SpaceOutliner; case SPACE_BUTS: @@ -109,12 +109,12 @@ static StructRNA* rna_Space_refine(struct PointerRNA *ptr) //case SPACE_IMASEL: // return &RNA_SpaceImageBrowser; /*case SPACE_SOUND: - return &RNA_SpaceAudioWindow; + return &RNA_SpaceAudioWindow;*/ case SPACE_ACTION: return &RNA_SpaceDopeSheetEditor; case SPACE_NLA: - return &RNA_SpaceNLAEditor; - case SPACE_SCRIPT: + return &RNA_SpaceNLA; + /*case SPACE_SCRIPT: return &RNA_SpaceScriptsWindow; case SPACE_TIME: return &RNA_SpaceTimeline; @@ -872,6 +872,117 @@ static void rna_def_space_text(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Replace Text", "Text to replace selected text with using the replace tool."); } +static void rna_def_space_dopesheet(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {SACTCONT_DOPESHEET, "DOPESHEET", 0, "DopeSheet", ""}, + {SACTCONT_ACTION, "ACTION", 0, "Action Editor", ""}, + {SACTCONT_SHAPEKEY, "SHAPEKEY", 0, "ShapeKey Editor", ""}, // XXX to be depreceated? + {SACTCONT_GPENCIL, "GPENCIL", 0, "Grease Pencil", ""}, + {0, NULL, 0, NULL, NULL}}; + + + srna= RNA_def_struct(brna, "SpaceDopeSheetEditor", "Space"); + RNA_def_struct_sdna(srna, "SpaceAction"); + RNA_def_struct_ui_text(srna, "Space DopeSheet Editor", "DopeSheet space data."); + + /* mode */ + prop= RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Editing context being displayed."); + + /* display */ + prop= RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SACTION_DRAWTIME); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); // XXX for now, only set with operator + RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames."); + + prop= RNA_def_property(srna, "show_cframe_indicator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SACTION_NODRAWCFRANUM); + RNA_def_property_ui_text(prop, "Show Frame Number Indicator", "Show frame number beside the current frame indicator line."); + + prop= RNA_def_property(srna, "show_sliders", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SACTION_SLIDERS); + RNA_def_property_ui_text(prop, "Show Sliders", "Show sliders beside F-Curve channels."); + + /* editing */ + prop= RNA_def_property(srna, "automerge_keyframes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SACTION_NOTRANSKEYCULL); + RNA_def_property_ui_text(prop, "AutoMerge Keyframes", "Show handles of Bezier control points."); + + // TODO... autosnap, dopesheet? +} + +static void rna_def_space_graph(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {SIPO_MODE_ANIMATION, "FCURVES", 0, "F-Curves", ""}, + {SIPO_MODE_DRIVERS, "DRIVERS", 0, "Drivers", ""}, + {0, NULL, 0, NULL, NULL}}; + + + srna= RNA_def_struct(brna, "SpaceGraphEditor", "Space"); + RNA_def_struct_sdna(srna, "SpaceIpo"); + RNA_def_struct_ui_text(srna, "Space Graph Editor", "Graph Editor space data."); + + /* mode */ + prop= RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Editing context being displayed."); + + /* display */ + prop= RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_DRAWTIME); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); // XXX for now, only set with operator + RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames."); + + prop= RNA_def_property(srna, "show_cframe_indicator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SIPO_NODRAWCFRANUM); + RNA_def_property_ui_text(prop, "Show Frame Number Indicator", "Show frame number beside the current frame indicator line."); + + prop= RNA_def_property(srna, "show_handles", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_NOHANDLES); + RNA_def_property_ui_text(prop, "Show Handles", "Show handles of Bezier control points."); + + /* editing */ + prop= RNA_def_property(srna, "automerge_keyframes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SIPO_NOTRANSKEYCULL); + RNA_def_property_ui_text(prop, "AutoMerge Keyframes", "Show handles of Bezier control points."); + + // TODO... autosnap, dopesheet? +} + +static void rna_def_space_nla(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "SpaceNLA", "Space"); + RNA_def_struct_sdna(srna, "SpaceNla"); + RNA_def_struct_ui_text(srna, "Space Nla Editor", "NLA editor space data."); + + /* display */ + prop= RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SNLA_DRAWTIME); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); // XXX for now, only set with operator + RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames."); + + prop= RNA_def_property(srna, "show_cframe_indicator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SNLA_NODRAWCFRANUM); + RNA_def_property_ui_text(prop, "Show Frame Number Indicator", "Show frame number beside the current frame indicator line."); + + /* editing */ + // TODO... autosnap, dopesheet? +} + static void rna_def_fileselect_params(BlenderRNA *brna) { StructRNA *srna; @@ -1009,6 +1120,9 @@ void RNA_def_space(BlenderRNA *brna) rna_def_background_image(brna); rna_def_space_3dview(brna); rna_def_space_buttons(brna); + rna_def_space_dopesheet(brna); + rna_def_space_graph(brna); + rna_def_space_nla(brna); } #endif diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index e3a7a906fef..432140f1cf7 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -129,6 +129,7 @@ typedef struct wmNotifier { #define NC_TEXT (12<<24) #define NC_WORLD (13<<24) #define NC_FILE (14<<24) +#define NC_ANIMATION (15<<24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 @@ -188,6 +189,16 @@ typedef struct wmNotifier { #define ND_PARAMS (60<<16) #define ND_FILELIST (61<<16) + /* NC_ANIMATION Animato */ +#define ND_KEYFRAME_SELECT (70<<16) +#define ND_KEYFRAME_EDIT (71<<16) +#define ND_KEYFRAME_PROP (72<<16) +#define ND_ANIMCHAN_SELECT (73<<16) +#define ND_ANIMCHAN_EDIT (74<<16) +#define ND_NLA_SELECT (75<<16) +#define ND_NLA_EDIT (76<<16) +#define ND_NLA_ACTCHANGE (77<<16) + /* subtype, 256 entries too */ #define NOTE_SUBTYPE 0x0000FF00