Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_action.h3
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h8
-rw-r--r--source/blender/blenkernel/BKE_nla.h55
-rw-r--r--source/blender/blenkernel/BKE_utildefines.h1
-rw-r--r--source/blender/blenkernel/intern/action.c49
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c620
-rw-r--r--source/blender/blenkernel/intern/fcurve.c221
-rw-r--r--source/blender/blenkernel/intern/nla.c745
-rw-r--r--source/blender/blenkernel/intern/object.c8
-rw-r--r--source/blender/blenloader/intern/readfile.c156
-rw-r--r--source/blender/blenloader/intern/writefile.c130
-rw-r--r--source/blender/editors/animation/anim_channels.c68
-rw-r--r--source/blender/editors/animation/anim_filter.c691
-rw-r--r--source/blender/editors/animation/keyframes_draw.c2
-rw-r--r--source/blender/editors/animation/keyframes_edit.c24
-rw-r--r--source/blender/editors/include/ED_anim_api.h51
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/UI_view2d.h2
-rw-r--r--source/blender/editors/interface/resources.c19
-rw-r--r--source/blender/editors/interface/view2d.c23
-rw-r--r--source/blender/editors/screen/screen_ops.c6
-rw-r--r--source/blender/editors/space_action/action_select.c29
-rw-r--r--source/blender/editors/space_file/filesel.c3
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c5
-rw-r--r--source/blender/editors/space_graph/graph_select.c2
-rw-r--r--source/blender/editors/space_graph/space_graph.c2
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c399
-rw-r--r--source/blender/editors/space_nla/nla_channels.c424
-rw-r--r--source/blender/editors/space_nla/nla_draw.c793
-rw-r--r--source/blender/editors/space_nla/nla_edit.c699
-rw-r--r--source/blender/editors/space_nla/nla_header.c199
-rw-r--r--source/blender/editors/space_nla/nla_intern.h87
-rw-r--r--source/blender/editors/space_nla/nla_ops.c257
-rw-r--r--source/blender/editors/space_nla/nla_select.c600
-rw-r--r--source/blender/editors/space_nla/space_nla.c339
-rw-r--r--source/blender/editors/space_outliner/outliner.c14
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h1
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c1
-rw-r--r--source/blender/editors/transform/transform.c3
-rw-r--r--source/blender/editors/transform/transform.h7
-rw-r--r--source/blender/editors/transform/transform_conversions.c192
-rw-r--r--source/blender/editors/transform/transform_generics.c100
-rw-r--r--source/blender/editors/transform/transform_ops.c13
-rw-r--r--source/blender/makesdna/DNA_action_types.h11
-rw-r--r--source/blender/makesdna/DNA_anim_types.h140
-rw-r--r--source/blender/makesdna/DNA_scene_types.h1
-rw-r--r--source/blender/makesdna/DNA_space_types.h6
-rw-r--r--source/blender/makesrna/RNA_access.h3
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_animation.c2
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c1
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_nla.c349
-rw-r--r--source/blender/makesrna/intern/rna_object.c23
-rw-r--r--source/blender/makesrna/intern/rna_space.c28
55 files changed, 6670 insertions, 948 deletions
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index f0796697670..1fb200b94a8 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 */
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index 9b8a2990fe5..5c77e3c2ae4 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -108,11 +108,17 @@ 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 *fcurve_find_active_modifier(struct FCurve *fcu);
void fcurve_set_active_modifier(struct FCurve *fcu, struct FModifier *fcm);
+short fcurve_has_suitable_modifier(struct FCurve *fcu, 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 ******************** */
/* -------- Data Managemnt -------- */
diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h
index 230096d7ea7..5200ca6d4d7 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,48 @@
#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 */
+
+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_nla_action_pushdown(struct AnimData *adt);
+
+short BKE_nla_tweakmode_enter(struct AnimData *adt);
+void BKE_nla_tweakmode_exit(struct AnimData *adt);
-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 6584af085cd..0bed2c095e2 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)? ((b<a && a<c)? 1:0) : ((c<a && a<b)? 1:0))
+#define IN_RANGE_INCL(a, b, c) ((b < c)? ((b<=a && a<=c)? 1:0) : ((c<=a && a<=b)? 1:0))
/* this weirdo pops up in two places ... */
#if !defined(WIN32) && !defined(__BeOS)
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index f7e15cef4c4..f88e249d38c 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -788,50 +788,45 @@ static bActionStrip *get_active_strip(Object *ob)
return NULL;
}
-/* 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 */
+// TODO: change this adt
float get_action_frame(Object *ob, float cframe)
{
bActionStrip *strip= get_active_strip(ob);
- if(strip)
- return get_actionstrip_frame(strip, cframe, 0);
+ //if(strip)
+ // return get_actionstrip_frame(strip, cframe, 0);
return cframe;
}
/* inverted, strip time to current time */
+// TODO: change this to adt
float get_action_frame_inv(Object *ob, float cframe)
{
bActionStrip *strip= get_active_strip(ob);
- if(strip)
- return get_actionstrip_frame(strip, cframe, 1);
+ //if(strip)
+ // return get_actionstrip_frame(strip, cframe, 1);
return cframe;
}
-
+/* Check if the given action has any keyframes */
+short action_has_motion(const bAction *act)
+{
+ FCurve *fcu;
+
+ /* 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;
+ }
+ }
+
+ /* nothing found */
+ return 0;
+}
/* 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 30dcb383ef6..2efb4f2b2d3 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -5,6 +5,8 @@
#include <stdio.h>
#include <string.h>
#include <stddef.h>
+#include <float.h>
+#include <math.h>
#include "MEM_guardedalloc.h"
@@ -15,6 +17,7 @@
#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"
@@ -115,7 +118,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);
@@ -145,9 +154,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);
@@ -544,19 +554,24 @@ 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) */
+ 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;
-/* bNlaEvalStrip->strip_mode */
+/* NlaEvalStrip->strip_mode */
enum {
+ /* standard evaluation */
NES_TIME_BEFORE = -1,
NES_TIME_WITHIN,
NES_TIME_AFTER,
- NES_TIME_AFTER_BLEND
+
+ /* transition-strip evaluations */
+ NES_TIME_TRANSITION_START,
+ NES_TIME_TRANSITION_END,
} eNlaEvalStrip_StripMode;
@@ -565,8 +580,9 @@ enum {
typedef struct NlaEvalChannel {
struct NlaEvalChannel *next, *prev;
- char *path; /* ready-to-use path (i.e. remapped already) */
- int array_index; /* if applicable... */
+ 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;
@@ -574,117 +590,548 @@ typedef struct NlaEvalChannel {
/* ---------------------- */
-/* evaluate the F-Curves controlling settings for the NLA-strips (currently, not relinkable) */
-static void nlastrip_evaluate_fcurves (NlaStrip *strip, float ctime)
+/* 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 invert)
{
- //PointerRNA actstrip_ptr;
- //FCurve *fcu;
+ float length, 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 */
- /* create RNA-pointer needed to set values */
- //RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &actstrip_ptr);
+ /* length of referenced action */
+ actlength = strip->actend - strip->actstart;
+ if (IS_EQ(actlength, 0.0f)) actlength = 1.0f;
- /* execute these settings as per normal */
- //animsys_evaluate_fcurves(&actstrip_ptr, &strip->fcurves, NULL, ctime);
+ /* length of strip */
+ length= actlength * scale * repeat;
+ if (IS_EQ(length, 0.0f)) length= strip->end - strip->start;
+
+ /* reversed = play strip backwards */
+ if (strip->flag & NLASTRIP_FLAG_REVERSE) {
+ /* invert = convert action-strip time to global time */
+ if (invert)
+ return length*(strip->actend - cframe)/(repeat*actlength) + strip->start;
+ else
+ return strip->actend - repeat*actlength*(cframe - strip->start)/length;
+ }
+ else {
+ /* 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;
+ }
+}
+
+/* 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 invert)
+{
+ float length;
+
+ /* length of strip */
+ length= strip->end - strip->start;
+
+ /* reversed = play strip backwards */
+ if (strip->flag & NLASTRIP_FLAG_REVERSE) {
+ /* invert = convert within-strip-time to global time */
+ if (invert)
+ return strip->end - (length * cframe);
+ else
+ return (strip->end - cframe) / length;
+ }
+ else {
+ /* invert = convert within-strip-time to global time */
+ if (invert)
+ return (length * cframe) + strip->start;
+ else
+ return (cframe - strip->start) / length;
+ }
}
+/* non clipped mapping for strip-time <-> global time
+ * invert = convert action-strip time to global time
+ */
+static float nlastrip_get_frame (NlaStrip *strip, float cframe, short invert)
+{
+ switch (strip->type) {
+ case NLASTRIP_TYPE_TRANSITION: /* transition */
+ return nlastrip_get_frame_transition(strip, cframe, invert);
+
+ case NLASTRIP_TYPE_CLIP: /* action-clip (default) */
+ default:
+ return nlastrip_get_frame_actionclip(strip, cframe, invert);
+ }
+}
+
+/* calculate influence of strip based for given frame based on blendin/out values */
+static float nlastrip_get_influence (NlaStrip *strip, float cframe)
+{
+ /* sanity checks - normalise the blendin/out values? */
+ strip->blendin= (float)fabs(strip->blendin);
+ strip->blendout= (float)fabs(strip->blendout);
+
+ /* 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 */
+/* evaluate the evaluation time and influence for the strip, storing the results in the strip */
+void nlastrip_evaluate_controls (NlaStrip *strip, float ctime)
+{
+ /* 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, 0);
+ 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 track for evaluation purposes */
static void nlatrack_ctime_get_strip (ListBase *list, NlaTrack *nlt, short index, float ctime)
{
- NlaStrip *strip, *astrip=NULL, *bstrip=NULL;
+ 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;
+ /* 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 time occurred before current strip... */
+ if (ctime < strip->start) {
if (strip == nlt->strips.first) {
- astrip= strip;
+ /* 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 == nlt->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)
+ /* 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;
-
- /* 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);
- if ((astrip->influence <= 0.0f) && (bstrip->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;
}
- else {
- /* calculate/set the influence+act_time of this strip - don't consider if 0 influence */
- nlastrip_evaluate_fcurves(astrip, ctime);
+
+ /* 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) // XXX is it useful to invert the strip?
+ return;
- if (astrip->influence <= 0.0f)
- return;
+ /* 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;
+ 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;
+
+ /* 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);
}
/* ---------------------- */
+/* 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)
+{
+ 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_BLEND:
+ 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;
+ }
+}
+
+/* ---------------------- */
+
+/* evaluate action-clip strip */
+static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes)
+{
+ NlaStrip *strip= nes->strip;
+ FCurve *fcu;
+ float evaltime;
+
+ /* evaluate strip's modifiers which modify time to evaluate the base curves at */
+ evaltime= evaluate_time_fmodifiers(&strip->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;
+
+ /* 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(&strip->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);
+ }
+}
+
+/* evaluate transition strip */
+static void nlastrip_evaluate_transition (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes)
+{
+ ListBase tmp_channels = {NULL, NULL};
+ NlaEvalChannel *nec, *necn, *necd;
+ NlaEvalStrip tmp_nes;
+ NlaStrip *s1, *s2;
+
+ /* 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) */
+ /* first strip */
+ tmp_nes.strip_mode= NES_TIME_TRANSITION_START;
+ tmp_nes.strip= s1;
+ nlastrip_evaluate_actionclip(ptr, &tmp_channels, &tmp_nes);
+
+ /* second strip */
+ tmp_nes.strip_mode= NES_TIME_TRANSITION_END;
+ tmp_nes.strip= s2;
+ nlastrip_evaluate_actionclip(ptr, &tmp_channels, &tmp_nes);
+
+
+ /* optimise - abort if no channels */
+ if (tmp_channels.first == NULL)
+ return;
+
+
+ /* accumulate results in tmp_channels buffer to the accumulation buffer */
+ for (nec= tmp_channels.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_channels, nec);
+ BLI_addtail(channels, nec);
+ }
+ }
+
+ /* free temp-channels that haven't been assimilated into the buffer */
+ BLI_freelistN(&tmp_channels);
+}
+
/* 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)
+static void nlastrip_evaluate (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes)
{
- // 1. (in old code) was to extract 'IPO-channels' from actions
- // 2. blend between the 'accumulated' data, and the new data
+ /* actions to take depend on the type of strip */
+ switch (nes->strip->type) {
+ case NLASTRIP_TYPE_CLIP: /* action-clip */
+ nlastrip_evaluate_actionclip(ptr, channels, nes);
+ break;
+ case NLASTRIP_TYPE_TRANSITION: /* transition */
+ nlastrip_evaluate_transition(ptr, channels, nes);
+ break;
+ }
}
/* write the accumulated settings to */
-static void nladata_flush_channels (PointerRNA *ptr, ListBase *channels)
+static 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,8 +1150,21 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
NlaEvalStrip *nes;
/* 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++)
+ 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 */
nlatrack_ctime_get_strip(&estrips, nlt, track_index, ctime);
+ }
/* only continue if there are strips to evaluate */
if (estrips.first == NULL)
@@ -713,10 +1173,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, 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);
@@ -798,17 +1258,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 */
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index ad8115ba9aa..856930e5a44 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -1410,6 +1410,18 @@ static void fcm_generator_verify (FModifier *fcm)
}
}
+/* 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_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
{
FMod_Generator *data= (FMod_Generator *)fcm->data;
@@ -1490,6 +1502,9 @@ static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue,
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 */
@@ -1527,7 +1542,7 @@ static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue,
}
}
break;
-
+
default:
printf("Invalid Function-Generator for F-Modifier - %d \n", data->func_type);
}
@@ -2190,34 +2205,6 @@ void fcurve_free_modifiers (FCurve *fcu)
}
}
-/* 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)
{
@@ -2255,6 +2242,134 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *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 fcurve_has_suitable_modifier (FCurve *fcu, int mtype, short acttype)
+{
+ FModifier *fcm;
+
+ /* if there are no specific filtering criteria, just skip */
+ if ((mtype == 0) && (acttype == 0))
+ return (fcu && fcu->modifiers.first);
+
+ /* sanity checks */
+ if ELEM(NULL, fcu, fcu->modifiers.first)
+ return 0;
+
+ /* find the first mdifier fitting these criteria */
+ for (fcm= fcu->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->first)
+ 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= 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)
+ 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 */
+ fcurve_free_modifiers(fcu);
+
+ /* restore driver */
+ fcu->driver= driver;
+}
+
/* ***************************** F-Curve - Evaluation ********************************* */
/* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime")
@@ -2262,7 +2377,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 +2389,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 +2402,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 +2415,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)) ||
+ fcurve_has_suitable_modifier(fcu, 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/nla.c b/source/blender/blenkernel/intern/nla.c
index dc2bf26759f..0684d943754 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -17,171 +17,722 @@
* 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 <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.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"
#ifdef HAVE_CONFIG_H
#include <config.h>
#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 */
+
+/* Freeing ------------------------------------------- */
- if (!*src){
- *dst=NULL;
+/* Remove the given NLA strip from the NLA track it occupies, free the strip's data,
+ * and the strip itself.
+ */
+// TODO: with things like transitions, should these get freed too? Maybe better as a UI tool
+void free_nlastrip (ListBase *strips, NlaStrip *strip)
+{
+ FModifier *fcm, *fmn;
+
+ /* sanity checks */
+ if (strip == NULL)
return;
+
+ /* remove reference to action */
+ if (strip->act)
+ strip->act->id.us--;
+
+ /* free remapping info */
+ //if (strip->remap)
+ // BKE_animremap_free();
+
+ /* free own F-Curves */
+ free_fcurves(&strip->fcurves);
+
+ /* free F-Modifiers */
+ for (fcm= strip->modifiers.first; fcm; fcm= fmn) {
+ fmn= fcm->next;
+
+ BLI_remlink(&strip->modifiers, fcm);
+ fcurve_remove_modifier(NULL, fcm);
}
+
+ /* free the strip itself */
+ if (strips)
+ BLI_freelinkN(strips, strip);
+ else
+ MEM_freeN(strip);
+}
- *dst = MEM_dupallocN(sstrip);
+/* 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)
+{
+ 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);
+}
- dstrip = *dst;
- if (dstrip->act)
- dstrip->act->id.us++;
+/* 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;
+}
- if (dstrip->ipo)
- dstrip->ipo->id.us++;
+/* Copying ------------------------------------------- */
+
+/* Copy NLA strip */
+NlaStrip *copy_nlastrip (NlaStrip *strip)
+{
+ NlaStrip *strip_d;
+
+ /* sanity check */
+ if (strip == NULL)
+ return NULL;
+
+ /* make a copy */
+ strip_d= MEM_dupallocN(strip);
+ strip_d->next= strip_d->prev= NULL;
- if (dstrip->modifiers.first) {
- BLI_duplicatelist (&dstrip->modifiers, &sstrip->modifiers);
+ /* 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);
+ fcurve_copy_modifiers(&strip_d->modifiers, &strip->modifiers);
+
+ /* 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;
}
-void copy_nlastrips (ListBase *dst, ListBase *src)
+/* Copy all NLA data */
+void copy_nladata (ListBase *dst, ListBase *src)
{
- bActionStrip *strip;
+ 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);
+ }
+}
- dst->first=dst->last=NULL;
+/* Adding ------------------------------------------- */
- BLI_duplicatelist (dst, src);
+/* 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;
+}
- /* Update specific data */
- if (!dst->first)
- return;
+/* 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;
+}
- 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;
- }
+/* 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);
}
+
+ /* returns the strip added */
+ return strip;
}
-/* from editnla, for convert_action_to_strip -- no UI code so should be ok here.. */
-void find_stridechannel(Object *ob, bActionStrip *strip)
+/* *************************************************** */
+/* Basic Utilities */
+
+/* NLA-Tracks ---------------------------------------- */
+
+/* Find the active NLA-track for the given stack */
+NlaTrack *BKE_nlatrack_find_active (ListBase *tracks)
{
- 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);
- else
- strip->stridechannel[0]= 0;
+ 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;
}
-//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)
+/* 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)
{
- 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;
+ 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;
+}
- if(ob->nlastrips.first == NULL)
- ob->nlaflag |= OB_NLA_OVERRIDE;
+/* 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;
- 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 */
+ /* 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 last track to add the given strip */
+short BKE_nlatrack_has_space (NlaTrack *nlt, float start, float end)
+{
+ NlaStrip *strip;
+
+ /* 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);
+ }
+
+ /* loop over NLA strips checking for any overlaps with this area... */
+ for (strip= nlt->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;
+}
-/* not strip itself! */
-void free_actionstrip(bActionStrip* strip)
+/* 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)
{
- if (!strip)
+ ListBase tmp = {NULL, NULL};
+ NlaStrip *strip, *sstrip;
+
+ /* sanity checks */
+ if ELEM(NULL, nlt, nlt->strips.first)
return;
+
+ /* 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= nlt->strips.first; strip; strip= strip->next) {
+ short not_added = 1;
+
+ /* 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(&nlt->strips, strip);
+
+ for (sstrip= tmp.last; not_added && 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 */
+ nlt->strips.first= tmp.first;
+ nlt->strips.last= tmp.last;
+}
- if (strip->act){
- strip->act->id.us--;
- strip->act = NULL;
+/* 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)
+{
+ NlaStrip *ns;
+ short not_added = 1;
+
+ /* sanity checks */
+ if ELEM(NULL, nlt, strip)
+ return 0;
+
+ /* check if any space to add */
+ if (BKE_nlatrack_has_space(nlt, strip->start, strip->end)==0)
+ return 0;
+
+ /* find the right place to add the strip to the nominated track */
+ for (ns= nlt->strips.first; ns; ns= ns->next) {
+ /* if current strip occurs after the new strip, add it before */
+ if (ns->start > strip->end) {
+ BLI_insertlinkbefore(&nlt->strips, ns, strip);
+ not_added= 0;
+ break;
+ }
}
- if (strip->ipo){
- strip->ipo->id.us--;
- strip->ipo = NULL;
+ if (not_added) {
+ /* just add to the end of the list of the strips then... */
+ BLI_addtail(&nlt->strips, strip);
}
- if (strip->modifiers.first) {
- BLI_freelistN(&strip->modifiers);
+
+ /* added... */
+ return 1;
+}
+
+/* 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;
}
-void free_nlastrips (ListBase *nlalist)
+/* Does the given NLA-strip fall within the given bounds (times)? */
+short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max)
{
- bActionStrip *strip;
+ 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)) )
+ {
+ 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;
+}
+
+/* Tools ------------------------------------------- */
- if (!nlalist->first)
+/* 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
+ */
+ strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD;
+ }
+ }
+}
- /* Do any specific freeing */
- for (strip=nlalist->first; strip; strip=strip->next)
- {
- free_actionstrip (strip);
- };
- /* Free the whole list */
- BLI_freelistN(nlalist);
+/* 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)
+ */
+ // FIXME: hopefully the flag is correct!
+ 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; // XXX probably don't need to clear this...
+ }
+ }
+
+
+ /* 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)
+ */
+ adt->tmpact= adt->action;
+ adt->action= activeStrip->act;
+ 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)
+{
+ 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 NLA-tracks, clear the 'disabled' flag */
+ for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next)
+ nlt->flag &= ~NLATRACK_DISABLED;
+
+ /* 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
+ */
+ if (adt->action) adt->action->id.us--;
+ adt->action= adt->tmpact;
+ adt->tmpact= 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..c2916bc2231 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; a<obn->totcol; a++) id_us_plus((ID *)obn->mat[a]);
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 1502b475350..74226cabba6 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,44 @@ static void direct_link_action(FileData *fd, bAction *act)
}
}
+
+static void lib_link_nladata (FileData *fd, ID *id, ListBase *list)
+{
+ NlaTrack *nlt;
+ NlaStrip *strip;
+
+ /* we only acare about the NLA strips inside the tracks */
+ for (nlt= list->first; nlt; nlt= nlt->next) {
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ /* reassign the counted-reference to action */
+ strip->act = newlibadr_us(fd, id->lib, strip->act);
+ }
+ }
+}
+
+/* NOTE: this assumes that link_list has already been called on the list */
+static void direct_link_nladata(FileData *fd, ListBase *list)
+{
+ NlaTrack *nlt;
+ NlaStrip *strip;
+
+ for (nlt= list->first; nlt; nlt= nlt->next) {
+ /* relink list of strips */
+ link_list(fd, &nlt->strips);
+
+ /* relink strip data */
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ /* 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);
+ }
+ }
+}
+
/* ------- */
static void lib_link_keyingsets(FileData *fd, ID *id, ListBase *list)
@@ -1854,6 +1906,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 +1914,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 +1931,8 @@ 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);
}
/* ************ READ NODE TREE *************** */
@@ -4701,6 +4755,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;
@@ -9497,9 +9556,12 @@ static void expand_keyingsets(FileData *fd, Main *mainvar, ListBase *list)
static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
{
FCurve *fcd;
+ NlaTrack *nlt;
+ NlaStrip *strip;
/* 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) {
@@ -9509,6 +9571,12 @@ 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) {
+ for (strip= nlt->strips.first; strip; strip= strip->next)
+ expand_doit(fd, mainvar, strip->act);
+ }
}
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 f8112406e80..0c385911721 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -760,10 +760,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 */
@@ -794,50 +843,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);
}
}
@@ -888,6 +895,29 @@ static void write_keyingsets(WriteData *wd, ListBase *list)
}
}
+static void write_nladata(WriteData *wd, ListBase *nlabase)
+{
+ NlaTrack *nlt;
+ NlaStrip *strip;
+
+ /* write all the tracks */
+ for (nlt= nlabase->first; nlt; nlt= nlt->next) {
+ /* write the track first */
+ writestruct(wd, DATA, "NlaTrack", 1, nlt);
+
+ for (strip= nlt->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);
+
+ // TODO write the remaps
+ }
+ }
+}
+
static void write_animdata(WriteData *wd, AnimData *adt)
{
AnimOverride *aor;
@@ -899,14 +929,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)
@@ -1876,7 +1909,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 c52ade1bba8..d753f65ff50 100644
--- a/source/blender/editors/animation/anim_channels.c
+++ b/source/blender/editors/animation/anim_channels.c
@@ -87,43 +87,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,6 +122,13 @@ 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;
}
}
@@ -167,6 +147,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 +203,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 +253,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;
}
}
@@ -1218,6 +1216,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;
}
}
@@ -1474,7 +1480,7 @@ 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);
}
}
break;
@@ -1520,7 +1526,7 @@ 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);
}
}
break;
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index afad396607b..f9c1b1bb42f 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) {
@@ -610,6 +717,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 +936,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 +985,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 +1052,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 +1067,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 +1086,79 @@ 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? */
- 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);
+ }
+ },
+ { /* 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 (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++;
+ 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);
+ 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;
+ /* 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);
@@ -985,15 +1168,13 @@ 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);
}
}
- }
+ );
}
-
/* Materials? */
if ((ob->totcol) && !(ads->filterflag & ADS_FILTER_NOMAT))
@@ -1006,14 +1187,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 +1200,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 +1213,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 +1232,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 +1249,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? */
- 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);
+ }
+ },
+ { /* 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 (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 +1319,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 +1347,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 +1359,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 +1426,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 +1467,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 +1489,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 +1642,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/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index cfbd6d2bced..144cd68f6df 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -412,8 +412,6 @@ void scene_to_keylist(Scene *sce, ListBase *keys, ListBase *blocks, ActKeysInc *
/* 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;
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/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index efc0a0b9a57..e44c7ff5603 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -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,6 +140,7 @@ 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 */
@@ -156,7 +162,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 +210,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 */
@@ -245,7 +257,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 -------------- */
@@ -299,6 +311,8 @@ void ipo_rainbow(int cur, int tot, float *out);
/* ------------- NLA-Mapping ----------------------- */
/* anim_draw.c */
+// XXX these are soon to be depreceated?
+
/* Obtain the Object providing NLA-scaling for the given channel if applicable */
struct Object *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale);
@@ -308,10 +322,35 @@ void ANIM_nla_mapping_draw(struct gla2DDrawInfo *di, struct Object *ob, short re
/* 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);
-/* ------------- 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 -------- */
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index ef682c871bc..c104f2034e8 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/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index 4813b695795..50283113b62 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/resources.c b/source/blender/editors/interface/resources.c
index 0a65718b708..af68c5e6b25 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);
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index 4621be6eda0..aa1cc5790ef 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -207,6 +207,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:
{
@@ -245,14 +262,14 @@ 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= v2d->tot;
}
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_ops.c b/source/blender/editors/screen/screen_ops.c
index 2aa6758850e..14121ccdc4d 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -164,6 +164,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);
@@ -179,6 +180,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);
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index b82e44f3aa3..6bfdf77e2e7 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -174,7 +174,7 @@ static int actkeys_deselectall_exec(bContext *C, wmOperator *op)
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!
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
return OPERATOR_FINISHED;
}
@@ -766,6 +766,7 @@ 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 {
@@ -857,17 +858,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) {
@@ -880,7 +883,7 @@ 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) {
+ if (ale && found) {
/* apply selection to keyframes */
if (/*gpl*/0) {
/* grease pencil */
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index d57fc7f90bc..4c2def72de0 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -131,6 +131,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..5b00205b5d0 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -405,7 +405,7 @@ static void draw_modifier__generator(uiBlock *block, FCurve *fcu, FModifier *fcm
{
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";
+ char fn_type[]="Built-In Function%t|Sin%x0|Cos%x1|Tan%x2|Square Root%x3|Natural Log%x4|Normalised Sin%x5";
int cy= *yco - 30;
uiBut *but;
@@ -555,6 +555,9 @@ static void draw_modifier__generator(uiBlock *block, FCurve *fcu, FModifier *fcm
case FCM_GENERATOR_FN_SQRT: /* square root */
sprintf(func_name, "sqrt(");
break;
+ case FCM_GENERATOR_FN_SINC: /* normalised sine wave */
+ sprintf(func_name, "sinc(");
+ break;
default: /* unknown */
sprintf(func_name, "<fn?>(");
break;
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index 9aaef9fca8a..a222109b0fe 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -722,7 +722,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);
}
}
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 74002f64187..ef42b009bd4 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -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;
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..cb76f7fc735
--- /dev/null
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -0,0 +1,399 @@
+/**
+ * $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 <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
+
+#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;
+
+ /* 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 ------------------------------------- */
+ /* strip type */
+ row= uiLayoutRow(layout, 1);
+ 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);
+ 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);
+ 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;
+
+ // XXX FIXME: move this check into a poll callback
+ if (RNA_enum_get(&strip_ptr, "type") != NLASTRIP_TYPE_CLIP)
+ 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... 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);
+ 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, *row, *subcol;
+ 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);
+
+ // influence
+ // strip_time
+ // animated_influence
+ // animated_time
+}
+
+/* F-Modifiers for active NLA-Strip */
+static void nla_panel_modifiers(const bContext *C, Panel *pa)
+{
+ PointerRNA strip_ptr;
+ uiLayout *layout= pa->layout;
+ //uiLayout *column, *row, *subcol;
+ 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);
+
+ // TODO...
+}
+
+/* ******************* 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 NLAEDIT_OT_properties(wmOperatorType *ot)
+{
+ ot->name= "Properties";
+ ot->idname= "NLAEDIT_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..f928daa523b
--- /dev/null
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -0,0 +1,424 @@
+/**
+ * $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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#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 void mouse_nla_channels (bAnimContext *ac, float x, int channel_index, short selectmode)
+{
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ /* 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;
+ }
+
+ /* 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;
+ }
+ else {
+ /* set selection status */
+ if (selectmode == SELECT_INVERT) {
+ /* swap select */
+ sce->flag ^= SCE_DS_SELECTED;
+ }
+ else {
+ sce->flag |= SCE_DS_SELECTED;
+ }
+ }
+ }
+ 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
+ }
+ else {
+ /* 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);
+ }
+ }
+ break;
+ case ANIMTYPE_FILLMATD:
+ {
+ Object *ob= (Object *)ale->data;
+ ob->nlaflag ^= OB_ADS_SHOWMATS; // XXX
+ }
+ break;
+
+ case ANIMTYPE_DSMAT:
+ {
+ Material *ma= (Material *)ale->data;
+ ma->flag ^= MA_DS_EXPAND;
+ }
+ break;
+ case ANIMTYPE_DSLAM:
+ {
+ Lamp *la= (Lamp *)ale->data;
+ la->flag ^= LA_DS_EXPAND;
+ }
+ break;
+ case ANIMTYPE_DSCAM:
+ {
+ Camera *ca= (Camera *)ale->data;
+ ca->flag ^= CAM_DS_EXPAND;
+ }
+ break;
+ case ANIMTYPE_DSCUR:
+ {
+ Curve *cu= (Curve *)ale->data;
+ cu->flag ^= CU_DS_EXPAND;
+ }
+ break;
+ case ANIMTYPE_DSSKEY:
+ {
+ Key *key= (Key *)ale->data;
+ key->flag ^= KEYBLOCK_DS_EXPAND;
+ }
+ break;
+ case ANIMTYPE_DSWOR:
+ {
+ World *wo= (World *)ale->data;
+ wo->flag ^= WO_DS_EXPAND;
+ }
+ 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;
+ }
+ else if (x >= (NLACHANNEL_NAMEWIDTH-2*NLACHANNEL_BUTTON_WIDTH)) {
+ /* toggle mute */
+ nlt->flag ^= NLATRACK_MUTED;
+ }
+ else if (x <= ((NLACHANNEL_BUTTON_WIDTH*2)+offset)) {
+ /* toggle 'solo' */
+ BKE_nlatrack_solo_toggle(adt, nlt);
+ }
+ else {
+ /* 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);
+ }
+ }
+ break;
+ case ANIMTYPE_NLAACTION:
+ {
+ AnimData *adt= BKE_animdata_from_id(ale->owner); /* this won't crash, right? */
+
+ /* for now, only do something if user clicks on the 'push-down' button */
+ if (x >= (NLACHANNEL_NAMEWIDTH-NLACHANNEL_BUTTON_WIDTH)) {
+ /* activate push-down function */
+ // 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);
+ }
+ }
+ break;
+
+ default:
+ printf("Error: Invalid channel type in mouse_nla_channels() \n");
+ }
+
+ /* free channels */
+ BLI_freelistN(&anim_data);
+}
+
+/* ------------------- */
+
+/* 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;
+ 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 */
+ mouse_nla_channels(&ac, x, channel_index, selectmode);
+
+ /* set notifier tha things have changed */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+
+ 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= nlaop_poll_tweakmode_off; // xxx?
+
+ /* 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 */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, 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.");
+}
+
+/* *********************************************** */
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..9a9cbeeff21
--- /dev/null
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -0,0 +1,793 @@
+/**
+ * $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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <float.h>
+
+#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_nla.h"
+#include "BKE_context.h"
+#include "BKE_screen.h"
+#include "BKE_utildefines.h"
+
+#include "ED_anim_api.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 */
+
+static void nla_strip_get_color_inside (AnimData *adt, NlaStrip *strip, float color[3])
+{
+ 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);
+ }
+}
+
+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 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);
+
+ /* 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];
+ rctf rect;
+
+ /* for now, just init the string with fixed-formats */
+ switch (strip->type) {
+ case NLASTRIP_TYPE_TRANSITION: /* Transition */
+ sprintf(str, "%d | Transition | %.2f <-> %.2f", index, strip->start, strip->end);
+ break;
+
+ case NLASTRIP_TYPE_CLIP: /* Action-Clip (default) */
+ default:
+ if (strip->act)
+ sprintf(str, "%d | Act: %s | %.2f <-> %.2f", index, strip->act->id.name+2, strip->start, strip->end);
+ else
+ sprintf(str, "%d | Act: <NONE>", index); // xxx... need a better format?
+ break;
+ }
+
+ /* 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 + 2;
+ rect.ymin= yminc;
+ rect.xmax= strip->end - 2;
+ 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);
+
+ /* 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 backdrop? */
+ // TODO...
+
+ /* 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);
+
+ /* 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);
+
+ // 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
+ glColor4f(0.3f, 0.95f, 0.1f, 0.3f); // FIXME: only draw the actual range of the action darker?
+ }
+ else {
+ if (ale->data)
+ glColor4f(0.8f, 0.2f, 0.0f, 0.4f); // reddish color - hardcoded for now
+ else
+ glColor4f(0.6f, 0.5f, 0.5f, 0.3f); // greyish-red color - hardcoded for now
+ }
+
+ /* draw slightly shifted up for greater separation from standard channels,
+ * but also slightly shorter for some more contrast when viewing the strips
+ */
+ glBegin(GL_QUADS);
+ glVertex2f(v2d->cur.xmin, yminc+NLACHANNEL_SKIP);
+ glVertex2f(v2d->cur.xmin, ymaxc-NLACHANNEL_SKIP);
+ glVertex2f(v2d->cur.xmax, ymaxc-NLACHANNEL_SKIP);
+ glVertex2f(v2d->cur.xmax, yminc+NLACHANNEL_SKIP);
+ glEnd();
+
+ 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, "<No Action>");
+ }
+ 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)) {
+ /* 'tweaking action' - 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..8b7c6bb99c6
--- /dev/null
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -0,0 +1,699 @@
+/**
+ * $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 <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#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_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_markers.h"
+#include "ED_space_api.h"
+#include "ED_screen.h"
+
+#include "BIF_transform.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
+
+/* *********************************************** */
+/* '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 */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, NULL);
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "No active strip(s) to enter tweakmode on.");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLAEDIT_OT_tweakmode_enter (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Enter Tweak Mode";
+ ot->idname= "NLAEDIT_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 */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, NULL);
+ }
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLAEDIT_OT_tweakmode_exit (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Exit Tweak Mode";
+ ot->idname= "NLAEDIT_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 */
+
+/* ******************** 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, "NLAEDIT_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);
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLAEDIT_OT_add_actionclip (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Action Strip";
+ ot->idname= "NLAEDIT_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 */
+
+/* add the specified action as new strip */
+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;
+ 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;
+
+ /* 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;
+
+ /* make note of this */
+ done++;
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* was anything added? */
+ if (done) {
+ /* set notifier that things have changed */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Needs at least a pair of adjacent selected strips.");
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void NLAEDIT_OT_add_transition (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Transition";
+ ot->idname= "NLAEDIT_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;
+}
+
+/* ******************** 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 */
+ strip->flag &= ~NLASTRIP_FLAG_SELECT;
+
+ done++;
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ if (done) {
+ /* set notifier that things have changed */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, 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 NLAEDIT_OT_duplicate (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Duplicate Strips";
+ ot->idname= "NLAEDIT_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)
+ free_nlastrip(&nlt->strips, strip);
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLAEDIT_OT_delete (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Delete Strips";
+ ot->idname= "NLAEDIT_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?
+
+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, delete all selected strips */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip, *nstrip, *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) {
+ 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))
+ continue;
+ 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;
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+ WM_event_add_notifier(C, NC_SCENE, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLAEDIT_OT_split (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Split Strips";
+ ot->idname= "NLAEDIT_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;
+}
+
+/* *********************************************** */
diff --git a/source/blender/editors/space_nla/nla_header.c b/source/blender/editors/space_nla/nla_header.c
index 0f6b77da6f5..f8c6ba2131a 100644
--- a/source/blender/editors/space_nla/nla_header.c
+++ b/source/blender/editors/space_nla/nla_header.c
@@ -29,6 +29,10 @@
#include <string.h>
#include <stdio.h>
+#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,69 +41,143 @@
#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_space_api.h"
+#include "ED_screen.h"
#include "BIF_gl.h"
-#include "BIF_glutil.h"
+#include "BIF_transform.h"
+
+#include "RNA_access.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"
+#include "ED_markers.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, "NLAEDIT_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, "NLAEDIT_OT_tweakmode_exit");
+ else
+ uiItemO(layout, NULL, 0, "NLAEDIT_OT_tweakmode_enter");
+
+ 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, "NLAEDIT_OT_select_all_toggle");
+ uiItemBooleanO(layout, "Invert All", 0, "NLAEDIT_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, "");
+ uiItemO(layout, NULL, 0, "NLAEDIT_OT_select_border");
+ uiItemBooleanO(layout, "Border Axis Range", 0, "NLAEDIT_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_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 nla_editmenu(bContext *C, uiLayout *layout, void *arg_unused)
+{
+ uiItemMenuF(layout, "Transform", 0, nla_edit_transformmenu);
- 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, "NLAEDIT_OT_duplicate");
+ uiItemO(layout, NULL, 0, "NLAEDIT_OT_split");
- return block;
+ uiItemS(layout);
+
+ uiItemO(layout, NULL, 0, "NLAEDIT_OT_delete");
}
+static void nla_addmenu(bContext *C, uiLayout *layout, void *arg_unused)
+{
+ uiItemO(layout, NULL, 0, "NLAEDIT_OT_add_actionclip");
+ uiItemO(layout, NULL, 0, "NLAEDIT_OT_add_transition");
+
+ 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);
+ Scene *scene= CTX_data_scene(C);
ScrArea *sa= CTX_wm_area(C);
uiBlock *block;
int xco, yco= 3;
@@ -109,17 +187,76 @@ 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;
+
+ /* Tweakmode... */
+ // XXX these icons need to be changed
+ if (scene->flag & SCE_NLA_EDIT_ON)
+ uiDefIconTextButO(block, BUT, "NLAEDIT_OT_tweakmode_exit", WM_OP_INVOKE_REGION_WIN, ICON_NLA, "Exit TweakMode", xco,yco,130,YIC, "Restore the true active action. (TAB)");
+ else
+ uiDefIconTextButO(block, BUT, "NLAEDIT_OT_tweakmode_enter", WM_OP_INVOKE_REGION_WIN, ICON_EDIT, "Enter TweakMode", xco,yco,130,YIC, "Temporarily set the action referenced by the active strip as the active action so that it can be tweaked. (TAB)");
+ xco+= 150;
+
/* 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..1237542172f 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,93 @@
/* internal exports only */
+/* **************************************** */
+/* Macros, etc. only used by NLA */
+/* -------------- 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
+
+/* **************************************** */
+/* space_nla.c / nla_buttons.c */
+
+ARegion *nla_has_buttons_region(ScrArea *sa);
+
+void nla_buttons_register(ARegionType *art);
+void NLAEDIT_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 NLAEDIT_OT_select_all_toggle(wmOperatorType *ot);
+void NLAEDIT_OT_select_border(wmOperatorType *ot);
+void NLAEDIT_OT_click_select(wmOperatorType *ot);
+
+/* **************************************** */
+/* nla_edit.c */
+
+void NLAEDIT_OT_tweakmode_enter(wmOperatorType *ot);
+void NLAEDIT_OT_tweakmode_exit(wmOperatorType *ot);
+
+/* --- */
+
+void NLAEDIT_OT_add_actionclip(wmOperatorType *ot);
+void NLAEDIT_OT_add_transition(wmOperatorType *ot);
+
+void NLAEDIT_OT_duplicate(wmOperatorType *ot);
+void NLAEDIT_OT_delete(wmOperatorType *ot);
+void NLAEDIT_OT_split(wmOperatorType *ot);
+
+
+/* **************************************** */
+/* nla_channels.c */
+
+void NLA_OT_channels_click(wmOperatorType *ot);
+
+void NLA_OT_add_tracks(wmOperatorType *ot);
+
+/* **************************************** */
+/* nla_ops.c */
+
+int nlaop_poll_tweakmode_off(bContext *C);
+int nlaop_poll_tweakmode_on (bContext *C);
+
+/* --- */
+
+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..52b529661cb
--- /dev/null
+++ b/source/blender/editors/space_nla/nla_ops.c
@@ -0,0 +1,257 @@
+/**
+ * $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 <string.h>
+#include <stdio.h>
+
+#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 "BIF_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;
+}
+
+/* ************************** registration - operator types **********************************/
+
+void nla_operatortypes(void)
+{
+ /* view */
+ WM_operatortype_append(NLAEDIT_OT_properties);
+
+ /* channels */
+ WM_operatortype_append(NLA_OT_channels_click);
+
+ WM_operatortype_append(NLA_OT_add_tracks);
+
+ /* select */
+ WM_operatortype_append(NLAEDIT_OT_click_select);
+ WM_operatortype_append(NLAEDIT_OT_select_border);
+ WM_operatortype_append(NLAEDIT_OT_select_all_toggle);
+
+ /* edit */
+ WM_operatortype_append(NLAEDIT_OT_tweakmode_enter);
+ WM_operatortype_append(NLAEDIT_OT_tweakmode_exit);
+
+ WM_operatortype_append(NLAEDIT_OT_add_actionclip);
+ WM_operatortype_append(NLAEDIT_OT_add_transition);
+
+ WM_operatortype_append(NLAEDIT_OT_duplicate);
+ WM_operatortype_append(NLAEDIT_OT_delete);
+ WM_operatortype_append(NLAEDIT_OT_split);
+}
+
+/* ************************** 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);
+
+ /* General Animation Channels keymap (see anim_channels.c) ----------------------- */
+ /* selection */
+ /* borderselect */
+ WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
+
+ /* deselect all */
+ 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, "NLAEDIT_OT_click_select", SELECTMOUSE, KM_PRESS, 0, 0);
+ kmi= WM_keymap_add_item(keymap, "NLAEDIT_OT_click_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "extend", 1);
+ kmi= WM_keymap_add_item(keymap, "NLAEDIT_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, "NLAEDIT_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "NLAEDIT_OT_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
+
+ /* borderselect */
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_select_border", BKEY, KM_PRESS, 0, 0);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "NLAEDIT_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, "NLAEDIT_OT_tweakmode_enter", TABKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_tweakmode_exit", TABKEY, KM_PRESS, 0, 0);
+
+ /* add strips */
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_add_actionclip", AKEY, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_add_transition", TKEY, KM_PRESS, KM_SHIFT, 0);
+
+ /* duplicate */
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0);
+
+ /* delete */
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_delete", XKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_delete", DELKEY, KM_PRESS, 0, 0);
+
+ /* split */
+ WM_keymap_add_item(keymap, "NLAEDIT_OT_split", YKEY, KM_PRESS, 0, 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, "NLAEDIT_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..b850ec76f82
--- /dev/null
+++ b/source/blender/editors/space_nla/nla_select.c
@@ -0,0 +1,600 @@
+/**
+ * $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 <string.h>
+#include <stdio.h>
+
+#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 */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+
+ return OPERATOR_FINISHED;
+}
+
+void NLAEDIT_OT_select_all_toggle (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select All";
+ ot->idname= "NLAEDIT_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);
+
+ return OPERATOR_FINISHED;
+}
+
+void NLAEDIT_OT_select_border(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Border Select";
+ ot->idname= "NLAEDIT_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", "Check if Select Left or Right", ""},
+ {NLAEDIT_LRSEL_NONE, "OFF", "Don't select", ""},
+ {NLAEDIT_LRSEL_LEFT, "LEFT", "Before current frame", ""},
+ {NLAEDIT_LRSEL_RIGHT, "RIGHT", "After current frame", ""},
+ {0, NULL, 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 (bAnimContext *ac, int mval[2], short select_mode)
+{
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale = NULL;
+ int filter;
+
+ View2D *v2d= &ac->ar->v2d;
+ 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);
+ }
+
+ /* 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 (bAnimContext *ac, short leftright, short select_mode)
+{
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ Scene *scene= ac->scene;
+ float xmin, xmax;
+
+ /* 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 = -MAXFRAMEF;
+ 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_CURVESONLY);
+ 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(&ac, RNA_enum_get(op->ptr, "left_right"), selectmode);
+ }
+ else {
+ /* select strips based upon mouse position */
+ mouse_nla_strips(&ac, mval, selectmode);
+ }
+
+ /* set notifier that things have changed */
+ ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+
+ /* for tweak grab to work */
+ return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
+}
+
+void NLAEDIT_OT_click_select (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Mouse Select";
+ ot->idname= "NLAEDIT_OT_click_select";
+
+ /* api callbacks - absolutely no exec() this yet... */
+ ot->invoke= nlaedit_clickselect_invoke;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* 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..a7e9844726d 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -29,6 +29,7 @@
#include <string.h>
#include <stdio.h>
+#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);
+
+ /* 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);
- /* data... */
+ /* 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,131 @@ 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_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);
+
+ }
+}
+
+
static void nla_main_area_listener(ARegion *ar, wmNotifier *wmn)
{
/* context changes */
+ switch(wmn->category) {
+ 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_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_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 +491,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 +500,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,10 +521,24 @@ 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 16748af39d5..eba0aa84e85 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) {
@@ -3521,6 +3521,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_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 6e33b1dcaab..eaff77aabe9 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -393,7 +393,6 @@ static void view3d_main_area_listener(ARegion *ar, wmNotifier *wmn)
case ND_GEOM_DATA:
case ND_DRAW:
case ND_MODIFIER:
- case ND_CONSTRAINT:
case ND_KEYS:
case ND_PARTICLE:
ED_region_tag_redraw(ar);
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 674de81a9f5..fa93d2a143d 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -319,6 +319,9 @@ static void viewRedrawForce(bContext *C, TransInfo *t)
else
ED_area_tag_redraw(t->sa);
}
+ else if (t->spacetype == SPACE_NLA) {
+ ED_area_tag_redraw(t->sa); // XXX this should use a notifier instead!
+ }
else if(t->spacetype == SPACE_NODE)
{
//ED_area_tag_redraw(t->sa);
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index ee767fada58..c0a57a85033 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -161,6 +161,13 @@ typedef struct TransDataSeq {
} TransDataSeq;
+/* for NLA transform (stored in td->extra pointer) */
+typedef struct TransDataNla {
+ struct NlaStrip *strip; /* NLA-strip that handle belongs to */
+ float val; /* value for the handle that the transform tools write to */
+ int handle; /* handle-index, 0 for start, 1 for end */
+} TransDataNla;
+
typedef struct TransData {
float dist; /* Distance needed to affect element (for Proportionnal Editing) */
float rdist; /* Distance to the nearest element (for Proportionnal Editing) */
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 6c7aa1ee49d..fb7d9c57eaf 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -2559,7 +2559,137 @@ 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;
+
+ /* only consider selected strips */
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ // TODO: we can make strips have handles later on...
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ if (FrameOnMouseSide(side, strip->start, (float)CFRA)) count++;
+ if (FrameOnMouseSide(side, strip->end, (float)CFRA)) count++;
+ }
+ }
+ }
+
+ /* stop if trying to build list if nothing selected */
+ if (count == 0) {
+ /* cleanup temp list */
+ BLI_freelistN(&anim_data);
+ return;
+ }
+
+ /* allocate memory for data */
+ t->total= count;
+
+ t->data= MEM_callocN(t->total*sizeof(TransData), "TransData(NLA Editor)");
+ td= t->data;
+ t->customData= MEM_callocN(t->total*sizeof(TransDataNla), "TransDataNla (NLA Editor)");
+ tdn= t->customData;
+
+ /* loop 2: build transdata array */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ /* only if a real NLA-track */
+ if (ale->type == ANIMTYPE_NLATRACK) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip;
+
+ /* only consider selected strips */
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ // TODO: we can make strips have handles later on...
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ if (FrameOnMouseSide(side, strip->start, (float)CFRA))
+ {
+ /* init the 'extra' data for NLA strip handles first */
+ tdn->strip= strip;
+ tdn->val= strip->start;
+ tdn->handle= 0;
+
+ /* now, link the transform data up to this data */
+ td->val= &tdn->val;
+ td->ival= tdn->val;
+ td->extra= tdn;
+ td++;
+ tdn++;
+ }
+ if (FrameOnMouseSide(side, strip->end, (float)CFRA))
+ {
+ /* init the 'extra' data for NLA strip handles first */
+ tdn->strip= strip;
+ tdn->val= strip->end;
+ tdn->handle= 1;
+
+ /* now, link the transform data up to this data */
+ td->val= &tdn->val;
+ td->ival= tdn->val;
+ td->extra= tdn;
+ td++;
+ tdn++;
+ }
+ }
+ }
+ }
+ }
+
+ /* cleanup temp list */
+ BLI_freelistN(&anim_data);
+}
+
+/* ********************* ACTION EDITOR ****************** */
/* Called by special_aftertrans_update to make sure selected gp-frames replace
* any other gp-frames which may reside on that frame (that are not selected).
@@ -2744,19 +2874,6 @@ static void posttrans_action_clean (bAnimContext *ac, bAction *act)
/* ----------------------------- */
-/* This function tests if a point is on the "mouse" side of the cursor/frame-marking */
-static short FrameOnMouseSide(char side, float frame, float cframe)
-{
- /* both sides, so it doesn't matter */
- if (side == 'B') return 1;
-
- /* only on the named side */
- if (side == 'R')
- return (frame >= cframe) ? 1 : 0;
- else
- return (frame <= cframe) ? 1 : 0;
-}
-
/* fully select selected beztriples, but only include if it's on the right side of cfra */
static int count_fcurve_keys(FCurve *fcu, char side, float cfra)
{
@@ -3044,8 +3161,6 @@ static void createTransActionData(bContext *C, TransInfo *t)
/* ********************* GRAPH EDITOR ************************* */
-
-
/* Helper function for createTransGraphEditData, which is reponsible for associating
* source data with transform data
*/
@@ -3502,7 +3617,6 @@ void flushTransGraphData(TransInfo *t)
}
}
-
/* **************** IpoKey stuff, for Object TransData ********** */
/* while transforming */
@@ -4604,6 +4718,29 @@ void special_aftertrans_update(TransInfo *t)
/* make sure all F-Curves are set correctly */
ANIM_editkeyframes_refresh(&ac);
}
+ else if (t->spacetype == SPACE_NLA) {
+ SpaceNla *snla= (SpaceNla *)t->sa->spacedata.first;
+ Scene *scene;
+ bAnimContext ac;
+
+ /* initialise relevant anim-context 'context' data from TransInfo data */
+ /* NOTE: sync this with the code in ANIM_animdata_get_context() */
+ memset(&ac, 0, sizeof(bAnimContext));
+
+ scene= ac.scene= t->scene;
+ ob= ac.obact= OBACT;
+ ac.sa= t->sa;
+ ac.ar= t->ar;
+ ac.spacetype= (t->sa)? t->sa->spacetype : 0;
+ ac.regiontype= (t->ar)? t->ar->regiontype : 0;
+
+ if (ANIM_animdata_context_getdata(&ac) == 0)
+ return;
+
+ // XXX check on the calls below... we need some of these sanity checks
+ //synchronize_action_strips();
+ //ANIM_editkeyframes_refresh(&ac);
+ }
else if (t->obedit) {
// TRANSFORM_FIX_ME
// if (t->mode==TFM_BONESIZE || t->mode==TFM_BONE_ENVELOPE)
@@ -4682,24 +4819,6 @@ void special_aftertrans_update(TransInfo *t)
}
}
-#if 0 // TRANSFORM_FIX_ME
- else if (t->spacetype == SPACE_NLA) {
- recalc_all_ipos(); // bad
- synchronize_action_strips();
-
- /* cleanup */
- for (base=t->scene->base.first; base; base=base->next)
- base->flag &= ~(BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA);
-
- /* after transform, remove duplicate keyframes on a frame that resulted from transform */
- if ( (G.snla->flag & SNLA_NOTRANSKEYCULL)==0 &&
- ((cancelled == 0) || (duplicate)) )
- {
- posttrans_nla_clean(t);
- }
- }
-#endif
-
clear_trans_object_base_flags(t);
#if 0 // TRANSFORM_FIX_ME
@@ -4932,8 +5051,7 @@ void createTransData(bContext *C, TransInfo *t)
}
else if (t->spacetype == SPACE_NLA) {
t->flag |= T_POINTS|T_2D_EDIT;
- // TRANSFORM_FIX_ME
- //createTransNlaData(C, t);
+ createTransNlaData(C, t);
}
else if (t->spacetype == SPACE_SEQ) {
t->flag |= T_POINTS|T_2D_EDIT;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 171665c9282..4c9592fb27a 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -102,6 +102,8 @@
#include "BLI_editVert.h"
#include "BLI_rand.h"
+#include "RNA_access.h"
+
#include "WM_types.h"
#include "UI_resources.h"
@@ -299,61 +301,6 @@ void recalcData(TransInfo *t)
DAG_object_flush_update(G.scene, OBACT, OB_RECALC_OB|OB_RECALC_DATA);
}
}
- }
- else if (t->spacetype == SPACE_NLA) {
- if (G.snla->lock) {
- for (base=G.scene->base.first; base; base=base->next) {
- if (base->flag & BA_HAS_RECALC_OB)
- base->object->recalc |= OB_RECALC_OB;
- if (base->flag & BA_HAS_RECALC_DATA)
- base->object->recalc |= OB_RECALC_DATA;
-
- if (base->object->recalc)
- base->object->ctime= -1234567.0f; // eveil!
-
- /* recalculate scale of selected nla-strips */
- if (base->object->nlastrips.first) {
- Object *bob= base->object;
- bActionStrip *strip;
-
- for (strip= bob->nlastrips.first; strip; strip= strip->next) {
- if (strip->flag & ACTSTRIP_SELECT) {
- float actlen= strip->actend - strip->actstart;
- float len= strip->end - strip->start;
-
- strip->scale= len / (actlen * strip->repeat);
- }
- }
- }
- }
-
- DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0);
- }
- else {
- for (base=G.scene->base.first; base; base=base->next) {
- /* recalculate scale of selected nla-strips */
- if (base->object && base->object->nlastrips.first) {
- Object *bob= base->object;
- bActionStrip *strip;
-
- for (strip= bob->nlastrips.first; strip; strip= strip->next) {
- if (strip->flag & ACTSTRIP_SELECT) {
- float actlen= strip->actend - strip->actstart;
- float len= strip->end - strip->start;
-
- /* prevent 'negative' scaling */
- if (len < 0) {
- SWAP(float, strip->start, strip->end);
- len= fabs(len);
- }
-
- /* calculate new scale */
- strip->scale= len / (actlen * strip->repeat);
- }
- }
- }
- }
- }
}
#endif
if (t->obedit) {
@@ -422,6 +369,47 @@ void recalcData(TransInfo *t)
}
}
+ else if (t->spacetype == SPACE_NLA) {
+ TransData *td= t->data;
+ int i;
+
+ /* for each point we've captured, look at its 'extra' data, which is basically a wrapper around the strip
+ * it is for + some extra storage for the values that get set, and use RNA to set this value (performing validation
+ * work so that we don't need to repeat it here)
+ */
+ for (i = 0; i < t->total; i++, td++)
+ {
+ if (td->extra)
+ {
+ TransDataNla *tdn= td->extra;
+ NlaStrip *strip= tdn->strip;
+
+ /* if we're just cancelling (i.e. the user aborted the transform),
+ * just restore the data by directly overwriting the values with the original
+ * ones (i.e. don't go through RNA), as we get some artifacts...
+ */
+ if (t->state == TRANS_CANCEL) {
+ /* write the value set by the transform tools to the appropriate property using RNA */
+ if (tdn->handle)
+ strip->end= tdn->val;
+ else
+ strip->start= tdn->val;
+ }
+ else {
+ PointerRNA strip_ptr;
+
+ /* make RNA-pointer */
+ RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr);
+
+ /* write the value set by the transform tools to the appropriate property using RNA */
+ if (tdn->handle)
+ RNA_float_set(&strip_ptr, "end_frame", tdn->val);
+ else
+ RNA_float_set(&strip_ptr, "start_frame", tdn->val);
+ }
+ }
+ }
+ }
else if (t->obedit) {
if ELEM(t->obedit->type, OB_CURVE, OB_SURF) {
Curve *cu= t->obedit->data;
@@ -866,7 +854,7 @@ void postTrans (TransInfo *t)
if(sima->flag & SI_LIVE_UNWRAP)
ED_uvedit_live_unwrap_end(t->state == TRANS_CANCEL);
}
- else if(t->spacetype==SPACE_ACTION) {
+ else if(ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA)) {
if (t->customData)
MEM_freeN(t->customData);
}
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index e697b6dfa7d..de731ad7d95 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -607,6 +607,19 @@ void transform_keymap_for_space(struct wmWindowManager *wm, struct ListBase *key
km = WM_keymap_add_item(keymap, "TFM_OT_resize", SKEY, KM_PRESS, 0, 0);
break;
+ case SPACE_NLA:
+ km= WM_keymap_add_item(keymap, "TFM_OT_transform", GKEY, KM_PRESS, 0, 0);
+ RNA_int_set(km->ptr, "mode", TFM_TIME_TRANSLATE);
+
+ km= WM_keymap_add_item(keymap, "TFM_OT_transform", EVT_TWEAK_S, KM_ANY, 0, 0);
+ RNA_int_set(km->ptr, "mode", TFM_TIME_TRANSLATE);
+
+ km= WM_keymap_add_item(keymap, "TFM_OT_transform", EKEY, KM_PRESS, 0, 0);
+ RNA_int_set(km->ptr, "mode", TFM_TIME_EXTEND);
+
+ km= WM_keymap_add_item(keymap, "TFM_OT_transform", SKEY, KM_PRESS, 0, 0);
+ RNA_int_set(km->ptr, "mode", TFM_TIME_SCALE);
+ break;
case SPACE_NODE:
km= WM_keymap_add_item(keymap, "TFM_OT_translation", GKEY, KM_PRESS, 0, 0);
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 7e54045b5e4..a566f733978 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -269,6 +269,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 +289,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 +303,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..a784adaf35f 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -108,6 +108,7 @@ enum {
FCM_GENERATOR_FN_TAN,
FCM_GENERATOR_FN_SQRT,
FCM_GENERATOR_FN_LN,
+ FCM_GENERATOR_FN_SINC,
} eFMod_Generator_Functions;
@@ -386,85 +387,93 @@ 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 */
+ 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 */
+ 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 */
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 */
-
- int flag; /* settings */
+ short blendmode; /* strip blending mode (layer-based mixing) */
+ short extendmode; /* strip extrapolation mode (time-based mixing) */
- // 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_BLEND = 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),
+} eNlaStrip_Flag;
+
+/* NLA Strip Type */
+enum {
+ /* 'clip' - references an Action */
+ NLASTRIP_TYPE_CLIP = 0,
+ /* 'transition' - blends between the adjacent strips */
+ NLASTRIP_TYPE_TRANSITION,
+} eNlaStrip_Type;
/* NLA Tracks ------------------------------------- */
@@ -483,14 +492,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 +507,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 +652,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)
*/
@@ -676,11 +686,13 @@ 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),
/* drivers expanded in UI */
ADT_DRIVERS_COLLAPSED = (1<<10),
+ /* don't execute drivers */
+ ADT_DRIVERS_DISABLED = (1<<11),
} eAnimData_Flag;
/* Animation Data recalculation settings (to be set by depsgraph) */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 7391201776e..2219535bfd3 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -833,6 +833,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 416acd7467e..28b5f882288 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -246,10 +246,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;
@@ -711,8 +712,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 9b653bd924a..d4e952882ce 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -272,6 +272,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;
@@ -365,6 +367,7 @@ extern StructRNA RNA_Space;
extern StructRNA RNA_Space3DView;
extern StructRNA RNA_SpaceButtonsWindow;
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 75293d83346..eacf3a65c7d 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -1846,6 +1846,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", 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_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index 806219ec6bf..a1cd95c9d90 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -284,6 +284,7 @@ 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}};
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 362217e3123..a3d5c4fe388 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_nla.c b/source/blender/makesrna/intern/rna_nla.c
new file mode 100644
index 00000000000..4b5c14aab82
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_nla.c
@@ -0,0 +1,349 @@
+/**
+ * $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 <stdlib.h>
+
+#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 <stdio.h>
+#include <math.h>
+
+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
+ * - minimum frame is -MAXFRAME so that we don't get clipping on frame 0
+ */
+ if (data->prev) {
+ CLAMP(value, data->prev->end, data->end-0.1f);
+ }
+ else {
+ CLAMP(value, -MAXFRAME, data->end);
+ }
+ data->start= value;
+}
+
+static void rna_NlaStrip_end_frame_set(PointerRNA *ptr, float value)
+{
+ NlaStrip *data= (NlaStrip*)ptr->data;
+ float len, actlen;
+
+ /* clamp value to lie within valid limits
+ * - must not have zero or negative length strip, so cannot start before the first frame
+ * + some minimum-strip-length threshold
+ * - cannot end later than the start of the next strip (if present)
+ */
+ if (data->next) {
+ CLAMP(value, data->start+0.1f, data->next->start);
+ }
+ else {
+ CLAMP(value, data->start+0.1f, MAXFRAME);
+ }
+ data->end= value;
+
+ /* calculate the lengths the strip and its action (if applicable) */
+ 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_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, -MAXFRAME, 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;
+}
+
+#else
+
+void rna_def_nlastrip(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ /* enum defs */
+ static EnumPropertyItem prop_type_items[] = {
+ {NLASTRIP_TYPE_CLIP, "CLIP", "Action Clip", "NLA Strip references some Action."},
+ {NLASTRIP_TYPE_TRANSITION, "TRANSITION", "Transition", "NLA Strip 'transitions' between adjacent strips."},
+ {0, NULL, NULL, NULL}};
+ static EnumPropertyItem prop_mode_blend_items[] = {
+ {NLASTRIP_MODE_BLEND, "BLEND", "Blend", "Results of strip and accumulated results are combined in ratio governed by influence."},
+ {NLASTRIP_MODE_ADD, "ADD", "Add", "Weighted result of strip is added to the accumlated results."},
+ {NLASTRIP_MODE_SUBTRACT, "SUBTRACT", "Subtract", "Weighted result of strip is removed from the accumlated results."},
+ {NLASTRIP_MODE_MULTIPLY, "MULITPLY", "Multiply", "Weighted result of strip is multiplied with the accumlated results."},
+ {0, NULL, NULL, NULL}};
+ static EnumPropertyItem prop_mode_extend_items[] = {
+ {NLASTRIP_EXTEND_NOTHING, "NOTHING", "Nothing", "Strip has no influence past its extents."},
+ {NLASTRIP_EXTEND_HOLD, "HOLD", "Hold", "Hold the first frame if no previous strips in track, and always hold last frame."},
+ {NLASTRIP_EXTEND_HOLD_FORWARD, "HOLD_FORWARD", "Hold Forward", "Only hold last frame."},
+ {0, NULL, 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
+
+ /* Enums */
+ prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ 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_range(prop, 1.0f, 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 ");
+
+ 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.");
+
+ // TODO: strip's F-Curves?
+
+ /* 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.");
+
+ /* 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.");
+
+ prop= RNA_def_property(srna, "animated_influence", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE); // XXX for now, not editable
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_INFLUENCE);
+ 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_clear_flag(prop, PROP_EDITABLE); // XXX for now, not editable
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_INFLUENCE);
+ 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 ff9777d283e..a17cc1aca3d 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -1075,29 +1075,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_space.c b/source/blender/makesrna/intern/rna_space.c
index 41acc458cd9..78c49a493f4 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -111,10 +111,10 @@ static StructRNA* rna_Space_refine(struct PointerRNA *ptr)
/*case SPACE_SOUND:
return &RNA_SpaceAudioWindow;
case SPACE_ACTION:
- return &RNA_SpaceDopeSheetEditor;
+ 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;
@@ -868,6 +868,27 @@ 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_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.");
+
+}
+
void RNA_def_space(BlenderRNA *brna)
{
rna_def_space(brna);
@@ -878,6 +899,7 @@ void RNA_def_space(BlenderRNA *brna)
rna_def_background_image(brna);
rna_def_space_3dview(brna);
rna_def_space_buttons(brna);
+ rna_def_space_nla(brna);
}
#endif