2.5 - NLA SoC Branch Merged to 2.5!
authorJoshua Leung <aligorith@gmail.com>
Sat, 11 Jul 2009 05:41:21 +0000 (05:41 +0000)
committerJoshua Leung <aligorith@gmail.com>
Sat, 11 Jul 2009 05:41:21 +0000 (05:41 +0000)
(19863 to 21513)

Highlights of the new NLA System (and related Animation System changes):
* NLA Data is stored in AnimData alongside Action and Drivers. The NLA stack is evaluated before the Action, with the Action always overriding the results of the stack.
* NLA Data is arranged in 'Tracks', which act like PhotoShop layers. These can be muted, protected, and/or made to be played back by themselves
* Within each track, there can be multiple 'Strips'. There are 3 types of strip -> Action Clip (references some action), Transition (blends between the endpoints of two strips), and Meta (container for several strips that occur sequentially)
* FModifiers can be applied to strips, and strips can have animated influence/time controls. Playback for strips can also be backwards now!
* Playback can now go in forward and backwards directions.
* Animation Editors have been polished (unfinished features added, existing features cleaned up and made more consistent)

Notes for BuildSystem Maintainers:
* Only scons has been actively tested. Makefiles should work fine.
* MSVC ProjectFiles are broken due to the very way they work.
* CMake status unknown...

Other notes:
* Hopefully I haven't made any mistakes while doing the merge. More files than expected were showing some weird conflicts, so you may have some broken code...
* Not all old files (with NLA) data load exactly the same anymore. However, the bulk of the files out there should be ok (I hope)

96 files changed:
source/blender/blenkernel/BKE_action.h
source/blender/blenkernel/BKE_fcurve.h
source/blender/blenkernel/BKE_nla.h
source/blender/blenkernel/BKE_utildefines.h
source/blender/blenkernel/intern/action.c
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/fcurve.c
source/blender/blenkernel/intern/fmodifier.c [new file with mode: 0644]
source/blender/blenkernel/intern/ipo.c
source/blender/blenkernel/intern/nla.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/nla_private.h [new file with mode: 0644]
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/animation/anim_channels.c
source/blender/editors/animation/anim_deps.c
source/blender/editors/animation/anim_draw.c
source/blender/editors/animation/anim_filter.c
source/blender/editors/animation/anim_ops.c
source/blender/editors/animation/drivers.c
source/blender/editors/animation/fmodifier_ui.c [new file with mode: 0644]
source/blender/editors/animation/keyframes_draw.c
source/blender/editors/animation/keyframes_edit.c
source/blender/editors/animation/keyframes_general.c
source/blender/editors/animation/keyframing.c
source/blender/editors/animation/keyingsets.c
source/blender/editors/armature/poselib.c
source/blender/editors/include/ED_anim_api.h
source/blender/editors/include/ED_keyframes_draw.h
source/blender/editors/include/ED_keyframing.h
source/blender/editors/include/ED_screen.h
source/blender/editors/include/ED_screen_types.h
source/blender/editors/include/ED_transform.h
source/blender/editors/include/UI_view2d.h
source/blender/editors/interface/interface_anim.c
source/blender/editors/interface/resources.c
source/blender/editors/interface/view2d.c
source/blender/editors/screen/screen_edit.c
source/blender/editors/screen/screen_ops.c
source/blender/editors/space_action/action_draw.c
source/blender/editors/space_action/action_edit.c
source/blender/editors/space_action/action_header.c
source/blender/editors/space_action/action_intern.h
source/blender/editors/space_action/action_ops.c
source/blender/editors/space_action/action_select.c
source/blender/editors/space_action/space_action.c
source/blender/editors/space_buttons/buttons_header.c
source/blender/editors/space_file/filesel.c
source/blender/editors/space_graph/graph_buttons.c
source/blender/editors/space_graph/graph_draw.c
source/blender/editors/space_graph/graph_edit.c
source/blender/editors/space_graph/graph_header.c
source/blender/editors/space_graph/graph_intern.h
source/blender/editors/space_graph/graph_ops.c
source/blender/editors/space_graph/graph_select.c
source/blender/editors/space_graph/graph_utils.c [new file with mode: 0644]
source/blender/editors/space_graph/space_graph.c
source/blender/editors/space_nla/nla_buttons.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_channels.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_draw.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_edit.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_header.c
source/blender/editors/space_nla/nla_intern.h
source/blender/editors/space_nla/nla_ops.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_select.c [new file with mode: 0644]
source/blender/editors/space_nla/space_nla.c
source/blender/editors/space_outliner/outliner.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/editors/space_time/time_header.c
source/blender/editors/space_view3d/drawarmature.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_header.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform.h
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/transform/transform_ops.c
source/blender/makesdna/DNA_action_types.h
source/blender/makesdna/DNA_anim_types.h
source/blender/makesdna/DNA_curve_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_space_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_animation.c
source/blender/makesrna/intern/rna_constraint.c
source/blender/makesrna/intern/rna_curve.c
source/blender/makesrna/intern/rna_fcurve.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_modifier.c
source/blender/makesrna/intern/rna_nla.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_object.c
source/blender/makesrna/intern/rna_particle.c
source/blender/makesrna/intern/rna_scene.c
source/blender/makesrna/intern/rna_space.c
source/blender/windowmanager/WM_types.h

index f0796697670a86ed8ce7f584f4aeb9806b616285..0c9bba5e413c1c810a79599f18f16ce88b801421 100644 (file)
@@ -68,6 +68,9 @@ void make_local_action(struct bAction *act);
 /* Some kind of bounding box operation on the action */
 void calc_action_range(const struct bAction *act, float *start, float *end, int incl_hidden);
 
+/* Does action have any motion data at all? */
+short action_has_motion(const struct bAction *act);
+
 /* Action Groups API ----------------- */
 
 /* Make the given Action Group the active one */
@@ -145,12 +148,6 @@ void copy_pose_result(struct bPose *to, struct bPose *from);
 /* clear all transforms */
 void rest_pose(struct bPose *pose);
 
-/* map global time (frame nr) to strip converted time, doesn't clip */
-float get_action_frame(struct Object *ob, float cframe);
-/* map strip time to global time (frame nr)  */
-float get_action_frame_inv(struct Object *ob, float cframe);
-
-
 /* functions used by the game engine */
 void game_copy_pose(struct bPose **dst, struct bPose *src);
 void game_free_pose(struct bPose *pose);
index 9b8a2990fe57dfbf5cb807bf2447dea175fae772..cda64c6b2416a7470d36032c408310e5dbad1e13 100644 (file)
@@ -1,12 +1,33 @@
-/* Testing code for new animation system in 2.5 
- * Copyright 2009, Joshua Leung
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ *
+ * Contributor(s): Joshua Leung (full recode)
+ *
+ * ***** END GPL LICENSE BLOCK *****
  */
 
 #ifndef BKE_FCURVE_H
 #define BKE_FCURVE_H
 
-//struct ListBase;
-
 struct FCurve;
 struct FModifier;
 struct ChannelDriver;
@@ -54,8 +75,8 @@ typedef struct FModifierTypeInfo {
        short size;                             /* size in bytes of the struct */
        short acttype;                  /* eFMI_Action_Types */
        short requires;                 /* eFMI_Requirement_Flags */
-       char name[32];                  /* name of modifier in interface */
-       char structName[32];    /* name of struct for SDNA */
+       char name[64];                  /* name of modifier in interface */
+       char structName[64];    /* name of struct for SDNA */
        
        /* data management function pointers - special handling */
                /* free any data that is allocated separately (optional) */
@@ -104,14 +125,20 @@ FModifierTypeInfo *get_fmodifier_typeinfo(int type);
 
 /* ---------------------- */
 
-struct FModifier *fcurve_add_modifier(struct FCurve *fcu, int type);
-void fcurve_copy_modifiers(ListBase *dst, ListBase *src);
-void fcurve_remove_modifier(struct FCurve *fcu, struct FModifier *fcm);
-void fcurve_free_modifiers(struct FCurve *fcu);
-void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end);
+struct FModifier *add_fmodifier(ListBase *modifiers, int type);
+void copy_fmodifiers(ListBase *dst, ListBase *src);
+void remove_fmodifier(ListBase *modifiers, struct FModifier *fcm);
+void free_fmodifiers(ListBase *modifiers);
+
+struct FModifier *find_active_fmodifier(ListBase *modifiers);
+void set_active_fmodifier(ListBase *modifiers, struct FModifier *fcm);
+
+short list_has_suitable_fmodifier(ListBase *modifiers, int mtype, short acttype);
 
-struct FModifier *fcurve_find_active_modifier(struct FCurve *fcu);
-void fcurve_set_active_modifier(struct FCurve *fcu, struct FModifier *fcm);
+float evaluate_time_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float cvalue, float evaltime);
+void evaluate_value_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float *cvalue, float evaltime);
+
+void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end);
 
 /* ************** F-Curves API ******************** */
 
@@ -126,9 +153,6 @@ void copy_fcurves(ListBase *dst, ListBase *src);
 /* find matching F-Curve in the given list of F-Curves */
 struct FCurve *list_find_fcurve(ListBase *list, const char rna_path[], const int array_index);
 
-/* test if there is a keyframe at cfra */
-short on_keyframe_fcurve(struct FCurve *fcu, float cfra);
-
 /* get the time extents for F-Curve */
 void calc_fcurve_range(struct FCurve *fcu, float *min, float *max);
 
index 230096d7ea745c2d01e4c1fe53b6108fc7d81782..bb5a2782663179b5d1287d0b54b045614c26fb2a 100644 (file)
  * 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 *****
  */
 #ifndef BKE_NLA_H
 #define BKE_NLA_H
 
-struct bActionStrip;
-struct ListBase;
-struct Object;
+struct AnimData;
+struct NlaStrip;
+struct NlaTrack;
+struct bAction;
+
+/* ----------------------------- */
+/* Data Management */
+
+void free_nlastrip(ListBase *strips, struct NlaStrip *strip);
+void free_nlatrack(ListBase *tracks, struct NlaTrack *nlt);
+void free_nladata(ListBase *tracks);
+
+struct NlaStrip *copy_nlastrip(struct NlaStrip *strip);
+struct NlaTrack *copy_nlatrack(struct NlaTrack *nlt);
+void copy_nladata(ListBase *dst, ListBase *src);
+
+struct NlaTrack *add_nlatrack(struct AnimData *adt, struct NlaTrack *prev);
+struct NlaStrip *add_nlastrip(struct bAction *act);
+struct NlaStrip *add_nlastrip_to_stack(struct AnimData *adt, struct bAction *act);
+
+/* ----------------------------- */
+/* API */
+
+short BKE_nlastrips_has_space(ListBase *strips, float start, float end);
+void BKE_nlastrips_sort_strips(ListBase *strips);
+
+short BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip);
+
+
+void BKE_nlastrips_make_metas(ListBase *strips, short temp);
+void BKE_nlastrips_clear_metas(ListBase *strips, short onlySel, short onlyTemp);
+void BKE_nlastrips_clear_metastrip(ListBase *strips, struct NlaStrip *strip);
+short BKE_nlameta_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip);
+void BKE_nlameta_flush_transforms(struct NlaStrip *mstrip);
+
+/* ............ */
+
+struct NlaTrack *BKE_nlatrack_find_active(ListBase *tracks);
+void BKE_nlatrack_set_active(ListBase *tracks, struct NlaTrack *nlt);
+
+void BKE_nlatrack_solo_toggle(struct AnimData *adt, struct NlaTrack *nlt);
+
+short BKE_nlatrack_has_space(struct NlaTrack *nlt, float start, float end);
+void BKE_nlatrack_sort_strips(struct NlaTrack *nlt);
+
+short BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip);
+
+/* ............ */
+
+struct NlaStrip *BKE_nlastrip_find_active(struct NlaTrack *nlt);
+
+short BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max);
+
+void BKE_nlastrip_validate_name(struct AnimData *adt, struct NlaStrip *strip);
+
+/* ............ */
+
+short BKE_nlatrack_has_animated_strips(struct NlaTrack *nlt);
+short BKE_nlatracks_have_animated_strips(ListBase *tracks);
+void BKE_nlastrip_validate_fcurves(struct NlaStrip *strip);
+
+/* ............ */
+
+void BKE_nla_action_pushdown(struct AnimData *adt);
+
+short BKE_nla_tweakmode_enter(struct AnimData *adt);
+void BKE_nla_tweakmode_exit(struct AnimData *adt);
+
+/* ----------------------------- */
+/* Time Mapping */
+
+/* time mapping conversion modes */
+enum {
+               /* convert from global time to strip time - for evaluation */
+       NLATIME_CONVERT_EVAL = 0,
+               /* convert from global time to strip time - for editing corrections */
+               // XXX old 0 invert
+       NLATIME_CONVERT_UNMAP,
+               /* convert from strip time to global time */
+               // xxx old 1 invert
+       NLATIME_CONVERT_MAP,
+} eNlaTime_ConvertModes;
+
+float BKE_nla_tweakedit_remap(struct AnimData *adt, float cframe, short mode);
 
-void free_actionstrip (struct bActionStrip* strip);
-void free_nlastrips (struct ListBase *nlalist);
-void copy_nlastrips (struct ListBase *dst, struct ListBase *src);
-void copy_actionstrip (struct bActionStrip **dst, struct bActionStrip **src);
-void find_stridechannel(struct Object *ob, struct bActionStrip *strip);
-struct bActionStrip *convert_action_to_strip (struct Object *ob);
 #endif
 
index 419f0f5beeba5f3b35e07a561ce0614d024fc9f8..f6c305b202de29c671c8b94bc0994a7f822b10e0 100644 (file)
 
 #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)
index f7e15cef4c4137ab158f2198c75f1f60dc3807ab..96896509f601d198ceb4907c445ddb04b9e79d8a 100644 (file)
@@ -769,70 +769,23 @@ void framechange_poses_clear_unkeyed(void)
 
 /* ************** time ****************** */
 
-static bActionStrip *get_active_strip(Object *ob)
+/* Check if the given action has any keyframes */
+short action_has_motion(const bAction *act)
 {
-#if 0  // XXX old animation system
-       bActionStrip *strip;
-       
-       if(ob->action==NULL)
-               return NULL;
-               
-       for (strip=ob->nlastrips.first; strip; strip=strip->next)
-               if(strip->flag & ACTSTRIP_ACTIVE)
-                       break;
-       
-       if(strip && strip->act==ob->action)
-               return strip;
-#endif // XXX old animation system
-               
-       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 */
-float get_action_frame(Object *ob, float cframe)
-{
-       bActionStrip *strip= get_active_strip(ob);
+       FCurve *fcu;
        
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 0);
-       return cframe;
-}
-
-/* inverted, strip time to current time */
-float get_action_frame_inv(Object *ob, float cframe)
-{
-       bActionStrip *strip= get_active_strip(ob);
+       /* return on the first F-Curve that has some keyframes/samples defined */
+       if (act) {
+               for (fcu= act->curves.first; fcu; fcu= fcu->next) {
+                       if (fcu->totvert)
+                               return 1;
+               }
+       }
        
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 1);
-       return cframe;
+       /* nothing found */
+       return 0;
 }
 
-
-
-
 /* Calculate the extents of given action */
 void calc_action_range(const bAction *act, float *start, float *end, int incl_hidden)
 {
index 441e17f3318a6aa1f1c0d3a5bdc8ff12d558986d..19337f9de5de726374d6fea2ca172fdf4b3e9a44 100644 (file)
@@ -1,10 +1,37 @@
-/* Testing code for new animation system in 2.5 
- * Copyright 2009, Joshua Leung
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Joshua Leung (full recode)
+ *
+ * ***** END GPL LICENSE BLOCK *****
  */
 
 #include <stdio.h>
 #include <string.h>
 #include <stddef.h>
+#include <float.h>
+#include <math.h>
 
 #include "MEM_guardedalloc.h"
 
 #include "BLI_arithb.h"
 #include "BLI_dynstr.h"
 
+#include "DNA_anim_types.h"
+
 #include "BKE_animsys.h"
 #include "BKE_action.h"
 #include "BKE_fcurve.h"
+#include "BKE_nla.h"
 #include "BKE_global.h"
 #include "BKE_main.h"
 #include "BKE_utildefines.h"
@@ -22,7 +52,7 @@
 #include "RNA_access.h"
 #include "RNA_types.h"
 
-#include "DNA_anim_types.h"
+#include "nla_private.h"
 
 /* ***************************************** */
 /* AnimData API */
@@ -116,7 +146,13 @@ void BKE_free_animdata (ID *id)
                        /* unlink action (don't free, as it's in its own list) */
                        if (adt->action)
                                adt->action->id.us--;
+                       /* same goes for the temporarily displaced action */
+                       if (adt->tmpact)
+                               adt->tmpact->id.us--;
                                
+                       /* free nla data */
+                       free_nladata(&adt->nla_tracks);
+                       
                        /* free drivers - stored as a list of F-Curves */
                        free_fcurves(&adt->drivers);
                        
@@ -146,9 +182,10 @@ AnimData *BKE_copy_animdata (AnimData *adt)
        // XXX review this... it might not be optimal behaviour yet...
        //id_us_plus((ID *)dadt->action);
        dadt->action= copy_action(adt->action);
+       dadt->tmpact= copy_action(adt->tmpact);
        
        /* duplicate NLA data */
-       // XXX todo...
+       copy_nladata(&dadt->nla_tracks, &adt->nla_tracks);
        
        /* duplicate drivers (F-Curves) */
        copy_fcurves(&dadt->drivers, &adt->drivers);
@@ -355,10 +392,10 @@ void BKE_keyingsets_free (ListBase *list)
 short animsys_remap_path (AnimMapper *remap, char *path, char **dst)
 {
        /* is there a valid remapping table to use? */
-       if (remap) {
+       //if (remap) {
                /* find a matching entry... to use to remap */
                // ...TODO...
-       }
+       //}
        
        /* nothing suitable found, so just set dst to look at path (i.e. no alloc/free needed) */
        *dst= path;
@@ -455,11 +492,14 @@ static void animsys_evaluate_fcurves (PointerRNA *ptr, ListBase *list, AnimMappe
        /* calculate then execute each curve */
        for (fcu= list->first; fcu; fcu= fcu->next) 
        {
-               /* check if this curve should be skipped */
-               if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) 
-               {
-                       calculate_fcurve(fcu, ctime);
-                       animsys_execute_fcurve(ptr, remap, fcu); 
+               /* check if this F-Curve doesn't belong to a muted group */
+               if ((fcu->grp == NULL) || (fcu->grp->flag & AGRP_MUTED)==0) {
+                       /* check if this curve should be skipped */
+                       if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) 
+                       {
+                               calculate_fcurve(fcu, ctime);
+                               animsys_execute_fcurve(ptr, remap, fcu); 
+                       }
                }
        }
 }
@@ -481,7 +521,6 @@ static void animsys_evaluate_drivers (PointerRNA *ptr, AnimData *adt, float ctim
                short ok= 0;
                
                /* check if this driver's curve should be skipped */
-               // FIXME: maybe we shouldn't check for muted, though that would make things more confusing, as there's already too many ways to disable?
                if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) 
                {
                        /* check if driver itself is tagged for recalculation */
@@ -514,6 +553,10 @@ void animsys_evaluate_action_group (PointerRNA *ptr, bAction *act, bActionGroup
        if ELEM(NULL, act, agrp) return;
        if ((remap) && (remap->target != act)) remap= NULL;
        
+       /* if group is muted, don't evaluated any of the F-Curve */
+       if (agrp->flag & AGRP_MUTED)
+               return;
+       
        /* calculate then execute each curve */
        for (fcu= agrp->channels.first; (fcu) && (fcu->grp == agrp); fcu= fcu->next) 
        {
@@ -540,152 +583,590 @@ void animsys_evaluate_action (PointerRNA *ptr, bAction *act, AnimMapper *remap,
 /* ***************************************** */
 /* NLA System - Evaluation */
 
-/* used for list of strips to accumulate at current time */
-typedef struct NlaEvalStrip {
-       struct NlaEvalStrip *next, *prev;
-       
-       NlaTrack *track;                        /* track that this strip belongs to */
-       NlaStrip *strip;                /* strip that's being used */
-       NlaStrip *sblend;               /* strip that's being blended towards (if applicable) */
-       
-       short track_index;                      /* the index of the track within the list */
-       short strip_mode;                       /* which end of the strip are we looking at */
-} NlaEvalStrip;
-
-/* bNlaEvalStrip->strip_mode */
-enum {
-       NES_TIME_BEFORE = -1,
-       NES_TIME_WITHIN,
-       NES_TIME_AFTER,
-       NES_TIME_AFTER_BLEND
-} eNlaEvalStrip_StripMode;
+/* 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;
+       }
+}
 
+/* evaluate the evaluation time and influence for the strip, storing the results in the strip */
+static 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, NLATIME_CONVERT_EVAL);
+       if ((strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) == 0)
+               strip->influence= nlastrip_get_influence(strip, ctime);
+       
+       /* now strip's evaluate F-Curves for these settings (if applicable) */
+       if (strip->fcurves.first) {
+               PointerRNA strip_ptr;
+               
+               /* create RNA-pointer needed to set values */
+               RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr);
+               
+               /* execute these settings as per normal */
+               animsys_evaluate_fcurves(&strip_ptr, &strip->fcurves, NULL, ctime);
+       }
+}
 
-/* temp channel for accumulating data from NLA (avoids needing to clear all values first) */
-// TODO: maybe this will be used as the 'cache' stuff needed for editable values too?
-typedef struct NlaEvalChannel {
-       struct NlaEvalChannel *next, *prev;
+/* gets the strip active at the current time for a list of strips for evaluation purposes */
+NlaEvalStrip *nlastrips_ctime_get_strip (ListBase *list, ListBase *strips, short index, float ctime)
+{
+       NlaStrip *strip, *estrip=NULL;
+       NlaEvalStrip *nes;
+       short side= 0;
+       
+       /* loop over strips, checking if they fall within the range */
+       for (strip= strips->first; strip; strip= strip->next) {
+               /* check if current time occurs within this strip  */
+               if (IN_RANGE_INCL(ctime, strip->start, strip->end)) {
+                       /* this strip is active, so try to use it */
+                       estrip= strip;
+                       side= NES_TIME_WITHIN;
+                       break;
+               }
+               
+               /* if time occurred before current strip... */
+               if (ctime < strip->start) {
+                       if (strip == strips->first) {
+                               /* before first strip - only try to use it if it extends backwards in time too */
+                               if (strip->extendmode == NLASTRIP_EXTEND_HOLD)
+                                       estrip= strip;
+                                       
+                               /* side is 'before' regardless of whether there's a useful strip */
+                               side= NES_TIME_BEFORE;
+                       }
+                       else {
+                               /* 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 (strip->extendmode != NLASTRIP_EXTEND_NOTHING)
+                                       estrip= strip;
+                               side= NES_TIME_AFTER;
+                       }
+                       break;
+               }
+               
+               /* if time occurred after current strip... */
+               if (ctime > strip->end) {
+                       /* only if this is the last strip should we do anything, and only if that is being held */
+                       if (strip == strips->last) {
+                               if (strip->extendmode != NLASTRIP_EXTEND_NOTHING)
+                                       estrip= strip;
+                                       
+                               side= NES_TIME_AFTER;
+                               break;
+                       }
+                       
+                       /* otherwise, skip... as the 'before' case will catch it more elegantly! */
+               }
+       }
        
-       char *path;                             /* ready-to-use path (i.e. remapped already) */
-       int array_index;                /* if applicable... */
+       /* check if a valid strip was found
+        *      - must not be muted (i.e. will have contribution
+        */
+       if ((estrip == NULL) || (estrip->flag & NLASTRIP_FLAG_MUTED)) 
+               return NULL;
+               
+       /* if 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;
+       }
        
-       float value;                    /* value of this channel */
-} NlaEvalChannel;
-
+       /* evaluate strip's evaluation controls  
+        *      - skip if no influence (i.e. same effect as muting the strip)
+        *      - negative influence is not supported yet... how would that be defined?
+        */
+       // TODO: this sounds a bit hacky having a few isolated F-Curves stuck on some data it operates on...
+       nlastrip_evaluate_controls(estrip, ctime);
+       if (estrip->influence <= 0.0f)
+               return NULL;
+               
+       /* check if strip has valid data to evaluate,
+        * and/or perform any additional type-specific actions
+        */
+       switch (estrip->type) {
+               case NLASTRIP_TYPE_CLIP: 
+                       /* clip must have some action to evaluate */
+                       if (estrip->act == NULL)
+                               return NULL;
+                       break;
+               case NLASTRIP_TYPE_TRANSITION:
+                       /* there must be strips to transition from and to (i.e. prev and next required) */
+                       if (ELEM(NULL, estrip->prev, estrip->next))
+                               return NULL;
+                               
+                       /* evaluate controls for the relevant extents of the bordering strips... */
+                       nlastrip_evaluate_controls(estrip->prev, estrip->start);
+                       nlastrip_evaluate_controls(estrip->next, estrip->end);
+                       break;
+       }
+       
+       /* add to list of strips we need to evaluate */
+       nes= MEM_callocN(sizeof(NlaEvalStrip), "NlaEvalStrip");
+       
+       nes->strip= estrip;
+       nes->strip_mode= side;
+       nes->track_index= index;
+       nes->strip_time= estrip->strip_time;
+       
+       if (list)
+               BLI_addtail(list, nes);
+       
+       return nes;
+}
 
 /* ---------------------- */
 
-/* evaluate the F-Curves controlling settings for the NLA-strips (currently, not relinkable) */
-static void nlastrip_evaluate_fcurves (NlaStrip *strip, float ctime)
+/* find an NlaEvalChannel that matches the given criteria 
+ *     - ptr and prop are the RNA data to find a match for
+ */
+static NlaEvalChannel *nlaevalchan_find_match (ListBase *channels, PointerRNA *ptr, PropertyRNA *prop, int array_index)
 {
-       //PointerRNA actstrip_ptr;
-       //FCurve *fcu;
+       NlaEvalChannel *nec;
        
-       /* create RNA-pointer needed to set values */
-       //RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &actstrip_ptr);
+       /* sanity check */
+       if (channels == NULL)
+               return NULL;
        
-       /* execute these settings as per normal */
-       //animsys_evaluate_fcurves(&actstrip_ptr, &strip->fcurves, NULL, ctime);
+       /* 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;
+}
 
-/* gets the strip active at the current time for a track */
-static void nlatrack_ctime_get_strip (ListBase *list, NlaTrack *nlt, short index, float ctime)
+/* 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, *astrip=NULL, *bstrip=NULL;
-       NlaEvalStrip *nes;
-       short side= 0;
+       NlaStrip *strip= nes->strip;
+       short blendmode= strip->blendmode;
+       float inf= strip->influence;
+       
+       /* if channel is new, just store value regardless of blending factors, etc. */
+       if (newChan) {
+               nec->value= value;
+               return;
+       }
+               
+       /* if this is being performed as part of transition evaluation, incorporate
+        * an additional weighting factor for the influence
+        */
+       if (nes->strip_mode == NES_TIME_TRANSITION_END) 
+               inf *= nes->strip_time;
+       
+       /* premultiply the value by the weighting factor */
+       if (IS_EQ(inf, 0)) return;
+       value *= inf;
+       
+       /* perform blending */
+       switch (blendmode) {
+               case NLASTRIP_MODE_ADD:
+                       /* simply add the scaled value on to the stack */
+                       nec->value += value;
+                       break;
+                       
+               case NLASTRIP_MODE_SUBTRACT:
+                       /* simply subtract the scaled value from the stack */
+                       nec->value -= value;
+                       break;
+                       
+               case NLASTRIP_MODE_MULTIPLY:
+                       /* multiply the scaled value with the stack */
+                       nec->value *= value;
+                       break;
+               
+               case NLASTRIP_MODE_REPLACE:
+               default: // TODO: do we really want to blend by default? it seems more uses might prefer add...
+                       /* do linear interpolation 
+                        *      - the influence of the accumulated data (elsewhere, that is called dstweight) 
+                        *        is 1 - influence, since the strip's influence is srcweight
+                        */
+                       nec->value= nec->value * (1.0f - inf)   +   value;
+                       break;
+       }
+}
+
+/* accumulate the results of a temporary buffer with the results of the full-buffer */
+static void nlaevalchan_buffers_accumulate (ListBase *channels, ListBase *tmp_buffer, NlaEvalStrip *nes)
+{
+       NlaEvalChannel *nec, *necn, *necd;
        
-       /* skip if track is muted */
-       if (nlt->flag & NLATRACK_MUTED) 
+       /* optimise - abort if no channels */
+       if (tmp_buffer->first == NULL)
                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!
+       /* accumulate results in tmp_channels buffer to the accumulation buffer */
+       for (nec= tmp_buffer->first; nec; nec= necn) {
+               /* get pointer to next channel in case we remove the current channel from the temp-buffer */
+               necn= nec->next;
+               
+               /* try to find an existing matching channel for this setting in the accumulation buffer */
+               necd= nlaevalchan_find_match(channels, &nec->ptr, nec->prop, nec->index);
+               
+               /* if there was a matching channel already in the buffer, accumulate to it,
+                * otherwise, add the current channel to the buffer for efficiency
                 */
-               if (IN_RANGE(ctime, strip->start, strip->end)) {
-                       astrip= strip;
-                       side= NES_TIME_WITHIN;
-                       break;
+               if (necd)
+                       nlaevalchan_accumulate(necd, nes, 0, nec->value);
+               else {
+                       BLI_remlink(tmp_buffer, nec);
+                       BLI_addtail(channels, nec);
                }
-               else if (ctime < strip->start) {
-                       if (strip == nlt->strips.first) {
-                               astrip= strip;
-                               side= NES_TIME_BEFORE;
-                               break;
-                       }
-                       else {
-                               astrip= strip->prev;
-                               
-                               if (astrip->flag & NLASTRIP_HOLDLASTFRAME) {
-                                       side= NES_TIME_AFTER;
-                                       break;
-                               }
-                               else {
-                                       bstrip= strip;
-                                       side= NES_TIME_AFTER_BLEND;
-                                       break;
-                               }
-                       }
+       }
+       
+       /* free temp-channels that haven't been assimilated into the buffer */
+       BLI_freelistN(tmp_buffer);
+}
+
+/* ---------------------- */
+/* F-Modifier stack joining/separation utilities - should we generalise these for BLI_listbase.h interface? */
+
+/* Temporarily join two lists of modifiers together, storing the result in a third list */
+static void nlaeval_fmodifiers_join_stacks (ListBase *result, ListBase *list1, ListBase *list2)
+{
+       FModifier *fcm1, *fcm2;
+       
+       /* if list1 is invalid...  */
+       if ELEM(NULL, list1, list1->first) {
+               if (list2 && list2->first) {
+                       result->first= list2->first;
+                       result->last= list2->last;
                }
        }
+       /* if list 2 is invalid... */
+       else if ELEM(NULL, list2, list2->first) {
+               result->first= list1->first;
+               result->last= list1->last;
+       }
+       else {
+               /* list1 should be added first, and list2 second, with the endpoints of these being the endpoints for result 
+                *      - the original lists must be left unchanged though, as we need that fact for restoring
+                */
+               result->first= list1->first;
+               result->last= list2->last;
+               
+               fcm1= list1->last;
+               fcm2= list2->first;
+               
+               fcm1->next= fcm2;
+               fcm2->prev= fcm1;
+       }
+}
+
+/* Split two temporary lists of modifiers */
+static void nlaeval_fmodifiers_split_stacks (ListBase *list1, ListBase *list2)
+{
+       FModifier *fcm1, *fcm2;
        
-       /* check if strip has been found (and whether it has data worth considering) */
-       if (ELEM(NULL, astrip, astrip->act)) 
+       /* if list1/2 is invalid... just skip */
+       if ELEM(NULL, list1, list2)
                return;
-       if (astrip->flag & NLASTRIP_MUTE) 
+       if ELEM(NULL, list1->first, list2->first)
                return;
+               
+       /* get endpoints */
+       fcm1= list1->last;
+       fcm2= list2->first;
+       
+       /* clear their links */
+       fcm1->next= NULL;
+       fcm2->prev= NULL;
+}
+
+/* ---------------------- */
+
+/* evaluate action-clip strip */
+static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes)
+{
+       ListBase tmp_modifiers = {NULL, NULL};
+       NlaStrip *strip= nes->strip;
+       FCurve *fcu;
+       float evaltime;
+       
+       /* join this strip's modifiers to the parent's modifiers (own modifiers first) */
+       nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers);
+       
+       /* evaluate strip's modifiers which modify time to evaluate the base curves at */
+       evaltime= evaluate_time_fmodifiers(&tmp_modifiers, NULL, 0.0f, strip->strip_time);
        
-       /* 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);
+       /* 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;
                
-               if ((astrip->influence <= 0.0f) && (bstrip->influence <= 0.0f))
-                       return;
+               /* check if this curve should be skipped */
+               if (fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) 
+                       continue;
+               if ((fcu->grp) && (fcu->grp->flag & AGRP_MUTED))
+                       continue;
+                       
+               /* evaluate the F-Curve's value for the time given in the strip 
+                * NOTE: we use the modified time here, since strip's F-Curve Modifiers are applied on top of this 
+                */
+               value= evaluate_fcurve(fcu, evaltime);
+               
+               /* apply strip's F-Curve Modifiers on this value 
+                * NOTE: we apply the strip's original evaluation time not the modified one (as per standard F-Curve eval)
+                */
+               evaluate_value_fmodifiers(&tmp_modifiers, fcu, &value, strip->strip_time);
+               
+               
+               /* get an NLA evaluation channel to work with, and accumulate the evaluated value with the value(s)
+                * stored in this channel if it has been used already
+                */
+               nec= nlaevalchan_verify(ptr, channels, nes, fcu, &newChan);
+               if (nec)
+                       nlaevalchan_accumulate(nec, nes, newChan, value);
+       }
+       
+       /* unlink this strip's modifiers from the parent's modifiers again */
+       nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers);
+}
+
+/* evaluate transition strip */
+static void nlastrip_evaluate_transition (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes)
+{
+       ListBase tmp_channels = {NULL, NULL};
+       ListBase tmp_modifiers = {NULL, NULL};
+       NlaEvalStrip tmp_nes;
+       NlaStrip *s1, *s2;
+       
+       /* join this strip's modifiers to the parent's modifiers (own modifiers first) */
+       nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &nes->strip->modifiers, modifiers);
+       
+       /* get the two strips to operate on 
+        *      - we use the endpoints of the strips directly flanking our strip
+        *        using these as the endpoints of the transition (destination and source)
+        *      - these should have already been determined to be valid...
+        *      - if this strip is being played in reverse, we need to swap these endpoints
+        *        otherwise they will be interpolated wrong
+        */
+       if (nes->strip->flag & NLASTRIP_FLAG_REVERSE) {
+               s1= nes->strip->next;
+               s2= nes->strip->prev;
        }
        else {
-               /* calculate/set the influence+act_time of this strip - don't consider if 0 influence */
-               nlastrip_evaluate_fcurves(astrip, ctime);
-               
-               if (astrip->influence <= 0.0f) 
-                       return;
+               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;
        
-       /* allocate new eval-strip for this strip + add to stack */
-       nes= MEM_callocN(sizeof(NlaEvalStrip), "NlaEvalStrip");
+       /* evaluate these strips into a temp-buffer (tmp_channels) */
+       // FIXME: modifier evalation here needs some work...
+               /* first strip */
+       tmp_nes.strip_mode= NES_TIME_TRANSITION_START;
+       tmp_nes.strip= s1;
+       nlastrip_evaluate_actionclip(ptr, &tmp_channels, &tmp_modifiers, &tmp_nes);
+       
+               /* second strip */
+       tmp_nes.strip_mode= NES_TIME_TRANSITION_END;
+       tmp_nes.strip= s2;
+       nlastrip_evaluate_actionclip(ptr, &tmp_channels, &tmp_modifiers, &tmp_nes);
        
-       nes->track= nlt;
-       nes->strip= astrip;
-       nes->sblend= bstrip;
-       nes->track_index= index;
-       nes->strip_mode= side;
        
-       BLI_addtail(list, nes);
+       /* assumulate temp-buffer and full-buffer, using the 'real' strip */
+       nlaevalchan_buffers_accumulate(channels, &tmp_channels, nes);
+       
+       /* unlink this strip's modifiers from the parent's modifiers again */
+       nlaeval_fmodifiers_split_stacks(&nes->strip->modifiers, modifiers);
 }
 
-/* ---------------------- */
+/* evaluate meta-strip */
+static void nlastrip_evaluate_meta (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes)
+{
+       ListBase tmp_channels = {NULL, NULL};
+       ListBase tmp_modifiers = {NULL, NULL};
+       NlaStrip *strip= nes->strip;
+       NlaEvalStrip *tmp_nes;
+       float evaltime;
+       
+       /* meta-strip was calculated normally to have some time to be evaluated at
+        * and here we 'look inside' the meta strip, treating it as a decorated window to
+        * it's child strips, which get evaluated as if they were some tracks on a strip 
+        * (but with some extra modifiers to apply).
+        *
+        * NOTE: keep this in sync with animsys_evaluate_nla()
+        */
+        
+       /* join this strip's modifiers to the parent's modifiers (own modifiers first) */
+       nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); 
+       
+       /* find the child-strip to evaluate */
+       evaltime= (nes->strip_time * (strip->end - strip->start)) + strip->start;
+       tmp_nes= nlastrips_ctime_get_strip(NULL, &strip->strips, -1, evaltime);
+       if (tmp_nes == NULL)
+               return;
+               
+       /* evaluate child-strip into tmp_channels buffer before accumulating 
+        * in the accumulation buffer
+        */
+       nlastrip_evaluate(ptr, &tmp_channels, &tmp_modifiers, tmp_nes);
+       
+       /* assumulate temp-buffer and full-buffer, using the 'real' strip */
+       nlaevalchan_buffers_accumulate(channels, &tmp_channels, nes);
+       
+       /* free temp eval-strip */
+       MEM_freeN(tmp_nes);
+       
+       /* unlink this strip's modifiers from the parent's modifiers again */
+       nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers);
+}
 
 /* evaluates the given evaluation strip */
-// 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)
+void nlastrip_evaluate (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, 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, modifiers, nes);
+                       break;
+               case NLASTRIP_TYPE_TRANSITION: /* transition */
+                       nlastrip_evaluate_transition(ptr, channels, modifiers, nes);
+                       break;
+               case NLASTRIP_TYPE_META: /* meta */
+                       nlastrip_evaluate_meta(ptr, channels, modifiers, nes);
+                       break;
+       }
 }
 
 /* write the accumulated settings to */
-static void nladata_flush_channels (PointerRNA *ptr, ListBase *channels)
+void nladata_flush_channels (ListBase *channels)
 {
+       NlaEvalChannel *nec;
+       
+       /* sanity checks */
+       if (channels == NULL)
+               return;
        
+       /* for each channel with accumulated values, write its value on the property it affects */
+       for (nec= channels->first; nec; nec= nec->next) {
+               PointerRNA *ptr= &nec->ptr;
+               PropertyRNA *prop= nec->prop;
+               int array_index= nec->index;
+               float value= nec->value;
+               
+               /* write values - see animsys_write_rna_setting() to sync the code */
+               switch (RNA_property_type(prop)) 
+               {
+                       case PROP_BOOLEAN:
+                               if (RNA_property_array_length(prop))
+                                       RNA_property_boolean_set_index(ptr, prop, array_index, (int)value);
+                               else
+                                       RNA_property_boolean_set(ptr, prop, (int)value);
+                               break;
+                       case PROP_INT:
+                               if (RNA_property_array_length(prop))
+                                       RNA_property_int_set_index(ptr, prop, array_index, (int)value);
+                               else
+                                       RNA_property_int_set(ptr, prop, (int)value);
+                               break;
+                       case PROP_FLOAT:
+                               if (RNA_property_array_length(prop))
+                                       RNA_property_float_set_index(ptr, prop, array_index, value);
+                               else
+                                       RNA_property_float_set(ptr, prop, value);
+                               break;
+                       case PROP_ENUM:
+                               RNA_property_enum_set(ptr, prop, (int)value);
+                               break;
+                       default:
+                               // can't do anything with other types of property....
+                               break;
+               }
+       }
 }
 
 /* ---------------------- */
@@ -703,9 +1184,26 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
        ListBase echannels= {NULL, NULL};
        NlaEvalStrip *nes;
        
+       // TODO: need to zero out all channels used, otherwise we have problems with threadsafety
+       // and also when the user jumps between different times instead of moving sequentially...
+       
        /* 1. get the stack of strips to evaluate at current time (influence calculated here) */
-       for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) 
-               nlatrack_ctime_get_strip(&estrips, nlt, track_index, ctime);
+       for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) { 
+               /* if tweaking is on and this strip is the tweaking track, stop on this one */
+               if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED))
+                       break;
+                       
+               /* skip if we're only considering a track tagged 'solo' */
+               if ((adt->flag & ADT_NLA_SOLO_TRACK) && (nlt->flag & NLATRACK_SOLO)==0)
+                       continue;
+               /* skip if track is muted */
+               if (nlt->flag & NLATRACK_MUTED) 
+                       continue;
+                       
+               /* otherwise, get strip to evaluate for this channel */
+               nes= nlastrips_ctime_get_strip(&estrips, &nlt->strips, track_index, ctime);
+               if (nes) nes->track= nlt;
+       }
        
        /* only continue if there are strips to evaluate */
        if (estrips.first == NULL)
@@ -714,10 +1212,10 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
        
        /* 2. for each strip, evaluate then accumulate on top of existing channels, but don't set values yet */
        for (nes= estrips.first; nes; nes= nes->next) 
-               nlastrip_ctime_evaluate(&echannels, nes, ctime);
+               nlastrip_evaluate(ptr, &echannels, NULL, nes);
        
        /* 3. flush effects of accumulating channels in NLA to the actual data they affect */
-       nladata_flush_channels(ptr, &echannels);
+       nladata_flush_channels(&echannels);
        
        /* 4. free temporary evaluation data */
        BLI_freelistN(&estrips);
@@ -799,17 +1297,23 @@ void BKE_animsys_evaluate_animdata (ID *id, AnimData *adt, float ctime, short re
         *      - NLA before Active Action, as Active Action behaves as 'tweaking track'
         *        that overrides 'rough' work in NLA
         */
+       // TODO: need to double check that this all works correctly
        if ((recalc & ADT_RECALC_ANIM) || (adt->recalc & ADT_RECALC_ANIM))
        {
                /* evaluate NLA data */
                if ((adt->nla_tracks.first) && !(adt->flag & ADT_NLA_EVAL_OFF))
                {
+                       /* evaluate NLA-stack */
                        animsys_evaluate_nla(&id_ptr, adt, ctime);
+                       
+                       /* evaluate 'active' Action (may be tweaking track) on top of results of NLA-evaluation 
+                        *      - only do this if we're not exclusively evaluating the 'solo' NLA-track
+                        */
+                       if ((adt->action) && !(adt->flag & ADT_NLA_SOLO_TRACK))
+                               animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime);
                }
-               
-               /* evaluate Action data */
-               // FIXME: what if the solo track was not tweaking one, then nla-solo should be checked too?
-               if (adt->action) 
+               /* evaluate Active Action only */
+               else if (adt->action)
                        animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime);
                
                /* reset tag */
@@ -876,10 +1380,22 @@ void BKE_animsys_evaluate_all_animation (Main *main, float ctime)
        EVAL_ANIM_IDS(main->camera.first, ADT_RECALC_ANIM);
        
        /* shapekeys */
+               // TODO: we probably need the same hack as for curves (ctime-hack)
        EVAL_ANIM_IDS(main->key.first, ADT_RECALC_ANIM);
        
        /* curves */
-       // TODO...
+               /* we need to perform a special hack here to ensure that the ctime 
+                * value of the curve gets set in case there's no animation for that
+                *      - it needs to be set before animation is evaluated just so that 
+                *        animation can successfully override...
+                */
+       for (id= main->curve.first; id; id= id->next) {
+               AnimData *adt= BKE_animdata_from_id(id);
+               Curve *cu= (Curve *)id;
+               
+               cu->ctime= ctime;
+               BKE_animsys_evaluate_animdata(id, adt, ctime, ADT_RECALC_ANIM);
+       }
        
        /* meshes */
        // TODO...
index ad8115ba9aaf487c62b6ce884df2558944bdfaeb..ebd94b94f8c777771a37f628f707ca78443cca28 100644 (file)
@@ -1,5 +1,30 @@
-/* Testing code for new animation system in 2.5 
- * Copyright 2009, Joshua Leung
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Joshua Leung (full recode)
+ *
+ * ***** END GPL LICENSE BLOCK *****
  */
  
 
@@ -31,7 +56,7 @@
 #include "RNA_types.h"
 
 #ifndef DISABLE_PYTHON
-#include "BPY_extern.h" /* for BPY_pydriver_eval() */
+#include "BPY_extern.h" 
 #endif
 
 #define SMALL -1.0e-10
@@ -59,7 +84,7 @@ void free_fcurve (FCurve *fcu)
        
        /* free extra data - i.e. modifiers, and driver */
        fcurve_free_driver(fcu);
-       fcurve_free_modifiers(fcu);
+       free_fmodifiers(&fcu->modifiers);
        
        /* free f-curve itself */
        MEM_freeN(fcu);
@@ -115,7 +140,7 @@ FCurve *copy_fcurve (FCurve *fcu)
        fcu_d->driver= fcurve_copy_driver(fcu_d->driver);
        
        /* copy modifiers */
-       fcurve_copy_modifiers(&fcu_d->modifiers, &fcu->modifiers);
+       copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers);
        
        /* return new data */
        return fcu_d;
@@ -175,20 +200,6 @@ FCurve *list_find_fcurve (ListBase *list, const char rna_path[], const int array
        return NULL;
 }
 
-short on_keyframe_fcurve(FCurve *fcu, float cfra)
-{
-       BezTriple *bezt;
-       unsigned i;
-
-       bezt= fcu->bezt;
-       for (i=0; i<fcu->totvert; i++, bezt++) {
-               if (IS_EQ(bezt->vec[1][0], cfra))
-                       return 1;
-       }
-       
-       return 0;
-}
-
 /* Calculate the extents of F-Curve's data */
 void calc_fcurve_bounds (FCurve *fcu, float *xmin, float *xmax, float *ymin, float *ymax)
 {
@@ -1245,1016 +1256,6 @@ static float fcurve_eval_samples (FCurve *fcu, FPoint *fpts, float evaltime)
        return cvalue;
 }
 
-/* ******************************** F-Curve Modifiers ********************************* */
-
-/* Template --------------------------- */
-
-/* Each modifier defines a set of functions, which will be called at the appropriate
- * times. In addition to this, each modifier should have a type-info struct, where
- * its functions are attached for use. 
- */
-/* Template for type-info data:
- *     - make a copy of this when creating new modifiers, and just change the functions
- *       pointed to as necessary
- *     - although the naming of functions doesn't matter, it would help for code
- *       readability, to follow the same naming convention as is presented here
- *     - any functions that a constraint doesn't need to define, don't define
- *       for such cases, just use NULL 
- *     - these should be defined after all the functions have been defined, so that
- *       forward-definitions/prototypes don't need to be used!
- *     - keep this copy #if-def'd so that future constraints can get based off this
- */
-#if 0
-static FModifierTypeInfo FMI_MODNAME = {
-       FMODIFIER_TYPE_MODNAME, /* type */
-       sizeof(FMod_ModName), /* size */
-       FMI_TYPE_SOME_ACTION, /* action type */
-       FMI_REQUIRES_SOME_REQUIREMENT, /* requirements */
-       "Modifier Name", /* name */
-       "FMod_ModName", /* struct name */
-       fcm_modname_free, /* free data */
-       fcm_modname_relink, /* relink data */
-       fcm_modname_copy, /* copy data */
-       fcm_modname_new_data, /* new data */
-       fcm_modname_verify, /* verify */
-       fcm_modname_time, /* evaluate time */
-       fcm_modname_evaluate /* evaluate */
-};
-#endif
-
-/* Generator F-Curve Modifier --------------------------- */
-
-/* Generators available:
- *     1) simple polynomial generator:
- *             - Exanded form - (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n])  
- *             - Factorised form - (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1]))
- *     2) simple builin 'functions':
- *             of the form (y = C[0] * fn( C[1]*x + C[2] ) + C[3])
- *        where fn() can be any one of:
- *             sin, cos, tan, ln, sqrt
- *     3) expression...
- */
-
-static void fcm_generator_free (FModifier *fcm)
-{
-       FMod_Generator *data= (FMod_Generator *)fcm->data;
-       
-       /* free polynomial coefficients array */
-       if (data->coefficients)
-               MEM_freeN(data->coefficients);
-}
-
-static void fcm_generator_copy (FModifier *fcm, FModifier *src)
-{
-       FMod_Generator *gen= (FMod_Generator *)fcm->data;
-       FMod_Generator *ogen= (FMod_Generator *)src->data;
-       
-       /* copy coefficients array? */
-       if (ogen->coefficients)
-               gen->coefficients= MEM_dupallocN(ogen->coefficients);
-}
-
-static void fcm_generator_new_data (void *mdata)
-{
-       FMod_Generator *data= (FMod_Generator *)mdata;
-       float *cp;
-       
-       /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */
-       data->poly_order= 1;
-       data->arraysize= 2;
-       cp= data->coefficients= MEM_callocN(sizeof(float)*2, "FMod_Generator_Coefs");
-       cp[0] = 0; // y-offset 
-       cp[1] = 1; // gradient
-}
-
-static void fcm_generator_verify (FModifier *fcm)
-{
-       FMod_Generator *data= (FMod_Generator *)fcm->data;
-       
-       /* requirements depend on mode */
-       switch (data->mode) {
-               case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
-               {
-                       /* arraysize needs to be order+1, so resize if not */
-                       if (data->arraysize != (data->poly_order+1)) {
-                               float *nc;
-                               
-                               /* make new coefficients array, and copy over as much data as can fit */
-                               nc= MEM_callocN(sizeof(float)*(data->poly_order+1), "FMod_Generator_Coefs");
-                               
-                               if (data->coefficients) {
-                                       if (data->arraysize > (data->poly_order+1))
-                                               memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order+1));
-                                       else
-                                               memcpy(nc, data->coefficients, sizeof(float)*data->arraysize);
-                                               
-                                       /* free the old data */
-                                       MEM_freeN(data->coefficients);
-                               }       
-                               
-                               /* set the new data */
-                               data->coefficients= nc;
-                               data->arraysize= data->poly_order+1;
-                       }
-               }
-                       break;
-               
-               case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */
-               {
-                       /* arraysize needs to be 2*order, so resize if not */
-                       if (data->arraysize != (data->poly_order * 2)) {
-                               float *nc;
-                               
-                               /* make new coefficients array, and copy over as much data as can fit */
-                               nc= MEM_callocN(sizeof(float)*(data->poly_order*2), "FMod_Generator_Coefs");
-                               
-                               if (data->coefficients) {
-                                       if (data->arraysize > (data->poly_order * 2))
-                                               memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order * 2));
-                                       else
-                                               memcpy(nc, data->coefficients, sizeof(float)*data->arraysize);
-                                               
-                                       /* free the old data */
-                                       MEM_freeN(data->coefficients);
-                               }       
-                               
-                               /* set the new data */
-                               data->coefficients= nc;
-                               data->arraysize= data->poly_order * 2;
-                       }
-               }
-                       break;
-                       
-               case FCM_GENERATOR_FUNCTION: /* builtin function */
-               {
-                       /* arraysize needs to be 4*/
-                       if (data->arraysize != 4) {
-                               float *nc;
-                               
-                               /* free the old data */
-                               if (data->coefficients)
-                                       MEM_freeN(data->coefficients);
-                               
-                               /* make new coefficients array, and init using default values */
-                               nc= data->coefficients= MEM_callocN(sizeof(float)*4, "FMod_Generator_Coefs");
-                               data->arraysize= 4;
-                               
-                               nc[0]= 1.0f;
-                               nc[1]= 1.0f;
-                               nc[2]= 0.0f;
-                               nc[3]= 0.0f;
-                       }
-               }
-                       break;  
-       }
-}
-
-static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
-{
-       FMod_Generator *data= (FMod_Generator *)fcm->data;
-       
-       /* behaviour depends on mode 
-        * NOTE: the data in its default state is fine too
-        */
-       switch (data->mode) {
-               case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
-               {
-                       /* we overwrite cvalue with the sum of the polynomial */
-                       float *powers = MEM_callocN(sizeof(float)*data->arraysize, "Poly Powers");
-                       float value= 0.0f;
-                       unsigned int i;
-                       
-                       /* for each x^n, precalculate value based on previous one first... this should be 
-                        * faster that calling pow() for each entry
-                        */
-                       for (i=0; i < data->arraysize; i++) {
-                               /* first entry is x^0 = 1, otherwise, calculate based on previous */
-                               if (i)
-                                       powers[i]= powers[i-1] * evaltime;
-                               else
-                                       powers[0]= 1;
-                       }
-                       
-                       /* for each coefficient, add to value, which we'll write to *cvalue in one go */
-                       for (i=0; i < data->arraysize; i++)
-                               value += data->coefficients[i] * powers[i];
-                       
-                       /* only if something changed, write *cvalue in one go */
-                       if (data->poly_order) {
-                               if (data->flag & FCM_GENERATOR_ADDITIVE)
-                                       *cvalue += value;
-                               else
-                                       *cvalue= value;
-                       }
-                               
-                       /* cleanup */
-                       if (powers) 
-                               MEM_freeN(powers);
-               }
-                       break;
-                       
-               case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */
-               {
-                       float value= 1.0f, *cp=NULL;
-                       unsigned int i;
-                       
-                       /* for each coefficient pair, solve for that bracket before accumulating in value by multiplying */
-                       for (cp=data->coefficients, i=0; (cp) && (i < data->poly_order); cp+=2, i++) 
-                               value *= (cp[0]*evaltime + cp[1]);
-                               
-                       /* only if something changed, write *cvalue in one go */
-                       if (data->poly_order) {
-                               if (data->flag & FCM_GENERATOR_ADDITIVE)
-                                       *cvalue += value;
-                               else
-                                       *cvalue= value;
-                       }
-               }
-                       break;
-                       
-               case FCM_GENERATOR_FUNCTION: /* builtin function */
-               {
-                       double arg= data->coefficients[1]*evaltime + data->coefficients[2];
-                       double (*fn)(double v) = NULL;
-                       
-                       /* get function pointer to the func to use:
-                        * WARNING: must perform special argument validation hereto guard against crashes  
-                        */
-                       switch (data->func_type)
-                       {
-                               /* simple ones */                       
-                               case FCM_GENERATOR_FN_SIN: /* sine wave */
-                                       fn= sin;
-                                       break;
-                               case FCM_GENERATOR_FN_COS: /* cosine wave */
-                                       fn= cos;
-                                       break;
-                                       
-                               /* validation required */
-                               case FCM_GENERATOR_FN_TAN: /* tangent wave */
-                               {
-                                       /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */
-                                       if IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0) {
-                                               if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
-                                                       *cvalue = 0.0f; /* no value possible here */
-                                       }
-                                       else
-                                               fn= tan;
-                               }
-                                       break;
-                               case FCM_GENERATOR_FN_LN: /* natural log */
-                               {
-                                       /* check that value is greater than 1? */
-                                       if (arg > 1.0f) {
-                                               fn= log;
-                                       }
-                                       else {
-                                               if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
-                                                       *cvalue = 0.0f; /* no value possible here */
-                                       }
-                               }
-                                       break;
-                               case FCM_GENERATOR_FN_SQRT: /* square root */
-                               {
-                                       /* no negative numbers */
-                                       if (arg > 0.0f) {
-                                               fn= sqrt;
-                                       }
-                                       else {
-                                               if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
-                                                       *cvalue = 0.0f; /* no value possible here */
-                                       }
-                               }
-                                       break;
-                                       
-                               default:
-                                       printf("Invalid Function-Generator for F-Modifier - %d \n", data->func_type);
-                       }
-                       
-                       /* execute function callback to set value if appropriate */
-                       if (fn) {
-                               float value= (float)(data->coefficients[0]*fn(arg) + data->coefficients[3]);
-                               
-                               if (data->flag & FCM_GENERATOR_ADDITIVE)
-                                       *cvalue += value;
-                               else
-                                       *cvalue= value;
-                       }
-               }
-                       break;
-
-#ifndef DISABLE_PYTHON
-               case FCM_GENERATOR_EXPRESSION: /* py-expression */
-                       // TODO...
-                       break;
-#endif /* DISABLE_PYTHON */
-       }
-}
-
-static FModifierTypeInfo FMI_GENERATOR = {
-       FMODIFIER_TYPE_GENERATOR, /* type */
-       sizeof(FMod_Generator), /* size */
-       FMI_TYPE_GENERATE_CURVE, /* action type */
-       FMI_REQUIRES_NOTHING, /* requirements */
-       "Generator", /* name */
-       "FMod_Generator", /* struct name */
-       fcm_generator_free, /* free data */
-       fcm_generator_copy, /* copy data */
-       fcm_generator_new_data, /* new data */
-       fcm_generator_verify, /* verify */
-       NULL, /* evaluate time */
-       fcm_generator_evaluate /* evaluate */
-};
-
-/* Envelope F-Curve Modifier --------------------------- */
-
-static void fcm_envelope_free (FModifier *fcm)
-{
-       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
-       
-       /* free envelope data array */
-       if (env->data)
-               MEM_freeN(env->data);
-}
-
-static void fcm_envelope_copy (FModifier *fcm, FModifier *src)
-{
-       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
-       FMod_Envelope *oenv= (FMod_Envelope *)src->data;
-       
-       /* copy envelope data array */
-       if (oenv->data)
-               env->data= MEM_dupallocN(oenv->data);
-}
-
-static void fcm_envelope_new_data (void *mdata)
-{
-       FMod_Envelope *env= (FMod_Envelope *)mdata;
-       
-       /* set default min/max ranges */
-       env->min= -1.0f;
-       env->max= 1.0f;
-}
-
-static void fcm_envelope_verify (FModifier *fcm)
-{
-       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
-       
-       /* if the are points, perform bubble-sort on them, as user may have changed the order */
-       if (env->data) {
-               // XXX todo...
-       }
-}
-
-static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
-{
-       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
-       FCM_EnvelopeData *fed, *prevfed, *lastfed;
-       float min=0.0f, max=0.0f, fac=0.0f;
-       int a;
-       
-       /* get pointers */
-       if (env->data == NULL) return;
-       prevfed= env->data;
-       fed= prevfed + 1;
-       lastfed= prevfed + (env->totvert-1);
-       
-       /* get min/max values for envelope at evaluation time (relative to mid-value) */
-       if (prevfed->time >= evaltime) {
-               /* before or on first sample, so just extend value */
-               min= prevfed->min;
-               max= prevfed->max;
-       }
-       else if (lastfed->time <= evaltime) {
-               /* after or on last sample, so just extend value */
-               min= lastfed->min;
-               max= lastfed->max;
-       }
-       else {
-               /* evaltime occurs somewhere between segments */
-               // TODO: implement binary search for this to make it faster?
-               for (a=0; prevfed && fed && (a < env->totvert-1); a++, prevfed=fed, fed++) {  
-                       /* evaltime occurs within the interval defined by these two envelope points */
-                       if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) {
-                               float afac, bfac, diff;
-                               
-                               diff= fed->time - prevfed->time;
-                               afac= (evaltime - prevfed->time) / diff;
-                               bfac= (fed->time - evaltime) / diff;
-                               
-                               min= bfac*prevfed->min + afac*fed->min;
-                               max= bfac*prevfed->max + afac*fed->max;
-                               
-                               break;
-                       }
-               }
-       }
-       
-       /* adjust *cvalue 
-        *      - fac is the ratio of how the current y-value corresponds to the reference range
-        *      - thus, the new value is found by mapping the old range to the new!
-        */
-       fac= (*cvalue - (env->midval + env->min)) / (env->max - env->min);
-       *cvalue= min + fac*(max - min); 
-}
-
-static FModifierTypeInfo FMI_ENVELOPE = {
-       FMODIFIER_TYPE_ENVELOPE, /* type */
-       sizeof(FMod_Envelope), /* size */
-       FMI_TYPE_REPLACE_VALUES, /* action type */
-       0, /* requirements */
-       "Envelope", /* name */
-       "FMod_Envelope", /* struct name */
-       fcm_envelope_free, /* free data */
-       fcm_envelope_copy, /* copy data */
-       fcm_envelope_new_data, /* new data */
-       fcm_envelope_verify, /* verify */
-       NULL, /* evaluate time */
-       fcm_envelope_evaluate /* evaluate */
-};
-
-/* Cycles F-Curve Modifier  --------------------------- */
-
-/* This modifier changes evaltime to something that exists within the curve's frame-range, 
- * then re-evaluates modifier stack up to this point using the new time. This re-entrant behaviour
- * is very likely to be more time-consuming than the original approach... (which was tighly integrated into 
- * the calculation code...).
- *
- * NOTE: this needs to be at the start of the stack to be of use, as it needs to know the extents of the keyframes/sample-data
- * Possible TODO - store length of cycle information that can be initialised from the extents of the keyframes/sample-data, and adjusted
- *                             as appropriate
- */
-
-/* temp data used during evaluation */
-typedef struct tFCMED_Cycles {
-       float cycyofs;          /* y-offset to apply */
-} tFCMED_Cycles;
-static void fcm_cycles_new_data (void *mdata)
-{
-       FMod_Cycles *data= (FMod_Cycles *)mdata;
-       
-       /* turn on cycles by default */
-       data->before_mode= data->after_mode= FCM_EXTRAPOLATE_CYCLIC;
-}
-
-static float fcm_cycles_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
-{
-       FMod_Cycles *data= (FMod_Cycles *)fcm->data;
-       float prevkey[2], lastkey[2], cycyofs=0.0f;
-       short side=0, mode=0;
-       int cycles=0;
-       
-       /* check if modifier is first in stack, otherwise disable ourself... */
-       // FIXME...
-       if (fcm->prev) {
-               fcm->flag |= FMODIFIER_FLAG_DISABLED;
-               return evaltime;
-       }
-       
-       /* calculate new evaltime due to cyclic interpolation */
-       if (fcu && fcu->bezt) {
-               BezTriple *prevbezt= fcu->bezt;
-               BezTriple *lastbezt= prevbezt + fcu->totvert-1;
-               
-               prevkey[0]= prevbezt->vec[1][0];
-               prevkey[1]= prevbezt->vec[1][1];
-               
-               lastkey[0]= lastbezt->vec[1][0];
-               lastkey[1]= lastbezt->vec[1][1];
-       }
-       else if (fcu && fcu->fpt) {
-               FPoint *prevfpt= fcu->fpt;
-               FPoint *lastfpt= prevfpt + fcu->totvert-1;
-               
-               prevkey[0]= prevfpt->vec[0];
-               prevkey[1]= prevfpt->vec[1];
-               
-               lastkey[0]= lastfpt->vec[0];
-               lastkey[1]= lastfpt->vec[1];
-       }
-       else
-               return evaltime;
-               
-       /* check if modifier will do anything
-        *      1) if in data range, definitely don't do anything
-        *      2) if before first frame or after last frame, make sure some cycling is in use
-        */
-       if (evaltime < prevkey[0]) {
-               if (data->before_mode)  {
-                       side= -1;
-                       mode= data->before_mode;
-                       cycles= data->before_cycles;
-               }
-       }
-       else if (evaltime > lastkey[0]) {
-               if (data->after_mode) {
-                       side= 1;
-                       mode= data->after_mode;
-                       cycles= data->after_cycles;
-               }
-       }
-       if ELEM(0, side, mode)
-               return evaltime;
-               
-       /* find relative place within a cycle */
-       {
-               float cycdx=0, cycdy=0, ofs=0;
-               float cycle= 0;
-               
-               /* ofs is start frame of cycle */
-               ofs= prevkey[0];
-               
-               /* calculate period and amplitude (total height) of a cycle */
-               cycdx= lastkey[0] - prevkey[0];
-               cycdy= lastkey[1] - prevkey[1];
-               
-               /* check if cycle is infinitely small, to be point of being impossible to use */
-               if (cycdx == 0)
-                       return evaltime;
-                       
-               /* calculate the 'number' of the cycle */
-               cycle= ((float)side * (evaltime - ofs) / cycdx);
-               
-               /* check that cyclic is still enabled for the specified time */
-               if (cycles == 0) {
-                       /* catch this case so that we don't exit when we have cycles=0
-                        * as this indicates infinite cycles...
-                        */
-               }
-               else if (cycle > (cycles+1)) {
-                       /* we are too far away from range to evaluate
-                        * TODO: but we should still hold last value... 
-                        */
-                       return evaltime;
-               }
-               
-               /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */
-               if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
-                       cycyofs = (float)floor((evaltime - ofs) / cycdx);
-                       cycyofs *= cycdy;
-               }
-               
-               /* calculate where in the cycle we are (overwrite evaltime to reflect this) */
-               if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)(cycle) % 2)) {
-                       /* when 'mirror' option is used and cycle number is odd, this cycle is played in reverse 
-                        *      - for 'before' extrapolation, we need to flip in a different way, otherwise values past
-                        *        then end of the curve get referenced (result of fmod will be negative, and with different phase)
-                        */
-                       if (side < 0)
-                               evaltime= (float)(prevkey[0] - fmod(evaltime-ofs, cycdx));
-                       else
-                               evaltime= (float)(lastkey[0] - fmod(evaltime-ofs, cycdx));
-               }
-               else {
-                       /* the cycle is played normally... */
-                       evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs);
-               }
-               if (evaltime < ofs) evaltime += cycdx;
-       }
-       
-       /* store temp data if needed */
-       if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
-               tFCMED_Cycles *edata;
-               
-               /* for now, this is just a float, but we could get more stuff... */
-               fcm->edata= edata= MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles");
-               edata->cycyofs= cycyofs;
-       }
-       
-       /* return the new frame to evaluate */
-       return evaltime;
-}
-static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
-{
-       tFCMED_Cycles *edata= (tFCMED_Cycles *)fcm->edata;
-       
-       /* use temp data */
-       if (edata) {
-               /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */
-               *cvalue += edata->cycyofs;
-               
-               /* free temp data */
-               MEM_freeN(edata);
-               fcm->edata= NULL;
-       }
-}
-
-static FModifierTypeInfo FMI_CYCLES = {
-       FMODIFIER_TYPE_CYCLES, /* type */
-       sizeof(FMod_Cycles), /* size */
-       FMI_TYPE_EXTRAPOLATION, /* action type */
-       FMI_REQUIRES_ORIGINAL_DATA, /* requirements */
-       "Cycles", /* name */
-       "FMod_Cycles", /* struct name */
-       NULL, /* free data */
-       NULL, /* copy data */
-       fcm_cycles_new_data, /* new data */
-       NULL /*fcm_cycles_verify*/, /* verify */
-       fcm_cycles_time, /* evaluate time */
-       fcm_cycles_evaluate /* evaluate */
-};
-
-/* Noise F-Curve Modifier  --------------------------- */
-
-static void fcm_noise_new_data (void *mdata)
-{
-       FMod_Noise *data= (FMod_Noise *)mdata;
-       
-       /* defaults */
-       data->size= 1.0f;
-       data->strength= 1.0f;
-       data->phase= 1.0f;
-       data->depth = 0;
-       data->modification = FCM_NOISE_MODIF_REPLACE;
-}
-static void fcm_noise_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
-{
-       FMod_Noise *data= (FMod_Noise *)fcm->data;
-       float noise;
-       
-       noise = BLI_turbulence(data->size, evaltime, data->phase, 0.f, data->depth);
-       
-       switch (data->modification) {
-               case FCM_NOISE_MODIF_ADD:
-                       *cvalue= *cvalue + noise * data->strength;
-                       break;
-               case FCM_NOISE_MODIF_SUBTRACT:
-                       *cvalue= *cvalue - noise * data->strength;
-                       break;
-               case FCM_NOISE_MODIF_MULTIPLY:
-                       *cvalue= *cvalue * noise * data->strength;
-                       break;
-               case FCM_NOISE_MODIF_REPLACE:
-               default:
-                       *cvalue= *cvalue + (noise - 0.5f) * data->strength;
-                       break;
-       }
-}
-
-static FModifierTypeInfo FMI_NOISE = {
-       FMODIFIER_TYPE_NOISE, /* type */
-       sizeof(FMod_Noise), /* size */
-       FMI_TYPE_REPLACE_VALUES, /* action type */
-       0, /* requirements */
-       "Noise", /* name */
-       "FMod_Noise", /* struct name */
-       NULL, /* free data */
-       NULL, /* copy data */
-       fcm_noise_new_data, /* new data */
-       NULL /*fcm_noise_verify*/, /* verify */
-       NULL, /* evaluate time */
-       fcm_noise_evaluate /* evaluate */
-};
-
-/* Filter F-Curve Modifier --------------------------- */
-
-#if 0 // XXX not yet implemented 
-static FModifierTypeInfo FMI_FILTER = {
-       FMODIFIER_TYPE_FILTER, /* type */
-       sizeof(FMod_Filter), /* size */
-       FMI_TYPE_REPLACE_VALUES, /* action type */
-       0, /* requirements */
-       "Filter", /* name */
-       "FMod_Filter", /* struct name */
-       NULL, /* free data */
-       NULL, /* copy data */
-       NULL, /* new data */
-       NULL /*fcm_filter_verify*/, /* verify */
-       NULL, /* evlauate time */
-       fcm_filter_evaluate /* evaluate */
-};
-#endif // XXX not yet implemented
-
-
-/* Python F-Curve Modifier --------------------------- */
-
-static void fcm_python_free (FModifier *fcm)
-{
-       FMod_Python *data= (FMod_Python *)fcm->data;
-       
-       /* id-properties */
-       IDP_FreeProperty(data->prop);
-       MEM_freeN(data->prop);
-}
-
-static void fcm_python_new_data (void *mdata) 
-{
-       FMod_Python *data= (FMod_Python *)mdata;
-       
-       /* everything should be set correctly by calloc, except for the prop->type constant.*/
-       data->prop = MEM_callocN(sizeof(IDProperty), "PyFModifierProps");
-       data->prop->type = IDP_GROUP;
-}
-
-static void fcm_python_copy (FModifier *fcm, FModifier *src)
-{
-       FMod_Python *pymod = (FMod_Python *)fcm->data;
-       FMod_Python *opymod = (FMod_Python *)src->data;
-       
-       pymod->prop = IDP_CopyProperty(opymod->prop);
-}
-
-static void fcm_python_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
-{
-#ifndef DISABLE_PYTHON
-       //FMod_Python *data= (FMod_Python *)fcm->data;
-       
-       /* FIXME... need to implement this modifier...
-        *      It will need it execute a script using the custom properties 
-        */
-#endif /* DISABLE_PYTHON */
-}
-
-static FModifierTypeInfo FMI_PYTHON = {
-       FMODIFIER_TYPE_PYTHON, /* type */
-       sizeof(FMod_Python), /* size */
-       FMI_TYPE_GENERATE_CURVE, /* action type */
-       FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
-       "Python", /* name */
-       "FMod_Python", /* struct name */
-       fcm_python_free, /* free data */
-       fcm_python_copy, /* copy data */
-       fcm_python_new_data, /* new data */
-       NULL /*fcm_python_verify*/, /* verify */
-       NULL /*fcm_python_time*/, /* evaluate time */
-       fcm_python_evaluate /* evaluate */
-};
-
-
-/* Limits F-Curve Modifier --------------------------- */
-
-static float fcm_limits_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
-{
-       FMod_Limits *data= (FMod_Limits *)fcm->data;
-       
-       /* check for the time limits */
-       if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin))
-               return data->rect.xmin;
-       if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax))
-               return data->rect.xmax;
-               
-       /* modifier doesn't change time */
-       return evaltime;
-}
-
-static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
-{
-       FMod_Limits *data= (FMod_Limits *)fcm->data;
-       
-       /* value limits now */
-       if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin))
-               *cvalue= data->rect.ymin;
-       if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax))
-               *cvalue= data->rect.ymax;
-}
-
-static FModifierTypeInfo FMI_LIMITS = {
-       FMODIFIER_TYPE_LIMITS, /* type */
-       sizeof(FMod_Limits), /* size */
-       FMI_TYPE_GENERATE_CURVE, /* action type */  /* XXX... err... */   
-       FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
-       "Limits", /* name */
-       "FMod_Limits", /* struct name */
-       NULL, /* free data */
-       NULL, /* copy data */
-       NULL, /* new data */
-       NULL, /* verify */
-       fcm_limits_time, /* evaluate time */
-       fcm_limits_evaluate /* evaluate */
-};
-
-/* F-Curve Modifier API --------------------------- */
-/* All of the F-Curve Modifier api functions use FModifierTypeInfo structs to carry out
- * and operations that involve F-Curve modifier specific code.
- */
-
-/* These globals only ever get directly accessed in this file */
-static FModifierTypeInfo *fmodifiersTypeInfo[FMODIFIER_NUM_TYPES];
-static short FMI_INIT= 1; /* when non-zero, the list needs to be updated */
-
-/* This function only gets called when FMI_INIT is non-zero */
-static void fmods_init_typeinfo () 
-{
-       fmodifiersTypeInfo[0]=  NULL;                                   /* 'Null' F-Curve Modifier */
-       fmodifiersTypeInfo[1]=  &FMI_GENERATOR;                 /* Generator F-Curve Modifier */
-       fmodifiersTypeInfo[2]=  &FMI_ENVELOPE;                  /* Envelope F-Curve Modifier */
-       fmodifiersTypeInfo[3]=  &FMI_CYCLES;                    /* Cycles F-Curve Modifier */
-       fmodifiersTypeInfo[4]=  &FMI_NOISE;                             /* Apply-Noise F-Curve Modifier */
-       fmodifiersTypeInfo[5]=  NULL/*&FMI_FILTER*/;                    /* Filter F-Curve Modifier */  // XXX unimplemented
-       fmodifiersTypeInfo[6]=  &FMI_PYTHON;                    /* Custom Python F-Curve Modifier */
-       fmodifiersTypeInfo[7]=  &FMI_LIMITS;                    /* Limits F-Curve Modifier */
-}
-
-/* This function should be used for getting the appropriate type-info when only
- * a F-Curve modifier type is known
- */
-FModifierTypeInfo *get_fmodifier_typeinfo (int type)
-{
-       /* initialise the type-info list? */
-       if (FMI_INIT) {
-               fmods_init_typeinfo();
-               FMI_INIT = 0;
-       }
-       
-       /* only return for valid types */
-       if ( (type >= FMODIFIER_TYPE_NULL) && 
-                (type <= FMODIFIER_NUM_TYPES ) ) 
-       {
-               /* there shouldn't be any segfaults here... */
-               return fmodifiersTypeInfo[type];
-       }
-       else {
-               printf("No valid F-Curve Modifier type-info data available. Type = %i \n", type);
-       }
-       
-       return NULL;
-} 
-/* This function should always be used to get the appropriate type-info, as it
- * has checks which prevent segfaults in some weird cases.
- */
-FModifierTypeInfo *fmodifier_get_typeinfo (FModifier *fcm)
-{
-       /* only return typeinfo for valid modifiers */
-       if (fcm)
-               return get_fmodifier_typeinfo(fcm->type);
-       else
-               return NULL;
-}
-
-/* API --------------------------- */
-
-/* Add a new F-Curve Modifier to the given F-Curve of a certain type */
-FModifier *fcurve_add_modifier (FCurve *fcu, int type)
-{
-       FModifierTypeInfo *fmi= get_fmodifier_typeinfo(type);
-       FModifier *fcm;
-       
-       /* sanity checks */
-       if ELEM(NULL, fcu, fmi)
-               return NULL;
-       
-       /* special checks for whether modifier can be added */
-       if ((fcu->modifiers.first) && (type == FMODIFIER_TYPE_CYCLES)) {
-               /* cycles modifier must be first in stack, so for now, don't add if it can't be */
-               // TODO: perhaps there is some better way, but for now, 
-               printf("Error: Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be first in stack. \n");
-               return NULL;
-       }
-       
-       /* add modifier itself */
-       fcm= MEM_callocN(sizeof(FModifier), "F-Curve Modifier");
-       fcm->type = type;
-       fcm->flag = FMODIFIER_FLAG_EXPANDED;
-       BLI_addtail(&fcu->modifiers, fcm);
-       
-       /* add modifier's data */
-       fcm->data= MEM_callocN(fmi->size, fmi->structName);
-               
-       /* init custom settings if necessary */
-       if (fmi->new_data)      
-               fmi->new_data(fcm->data);
-               
-       /* return modifier for further editing */
-       return fcm;
-}
-
-/* Duplicate all of the F-Curve Modifiers in the Modifier stacks */
-void fcurve_copy_modifiers (ListBase *dst, ListBase *src)
-{
-       FModifier *fcm, *srcfcm;
-       
-       if ELEM(NULL, dst, src)
-               return;
-       
-       dst->first= dst->last= NULL;
-       BLI_duplicatelist(dst, src);
-       
-       for (fcm=dst->first, srcfcm=src->first; fcm && srcfcm; srcfcm=srcfcm->next, fcm=fcm->next) {
-               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-               
-               /* make a new copy of the F-Modifier's data */
-               fcm->data = MEM_dupallocN(fcm->data);
-               
-               /* only do specific constraints if required */
-               if (fmi && fmi->copy_data)
-                       fmi->copy_data(fcm, srcfcm);
-       }
-}
-
-/* Remove and free the given F-Curve Modifier from the given F-Curve's stack  */
-void fcurve_remove_modifier (FCurve *fcu, FModifier *fcm)
-{
-       FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-       
-       /* sanity check */
-       if (fcm == NULL)
-               return;
-       
-       /* free modifier's special data (stored inside fcm->data) */
-       if (fcm->data) {
-               if (fmi && fmi->free_data)
-                       fmi->free_data(fcm);
-                       
-               /* free modifier's data (fcm->data) */
-               MEM_freeN(fcm->data);
-       }
-       
-       /* remove modifier from stack */
-       if (fcu)
-               BLI_freelinkN(&fcu->modifiers, fcm);
-       else {
-               // XXX this case can probably be removed some day, as it shouldn't happen...
-               printf("fcurve_remove_modifier() - no fcurve \n");
-               MEM_freeN(fcm);
-       }
-}
-
-/* Remove all of a given F-Curve's modifiers */
-void fcurve_free_modifiers (FCurve *fcu)
-{
-       FModifier *fcm, *fmn;
-       
-       /* sanity check */
-       if (fcu == NULL)
-               return;
-       
-       /* free each modifier in order - modifier is unlinked from list and freed */
-       for (fcm= fcu->modifiers.first; fcm; fcm= fmn) {
-               fmn= fcm->next;
-               fcurve_remove_modifier(fcu, fcm);
-       }
-}
-
-/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
- * by start and end (inclusive).
- */
-void fcurve_bake_modifiers (FCurve *fcu, int start, int end)
-{
-       ChannelDriver *driver;
-       
-       /* sanity checks */
-       // TODO: make these tests report errors using reports not printf's
-       if ELEM(NULL, fcu, fcu->modifiers.first) {
-               printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
-               return;
-       }
-       
-       /* temporarily, disable driver while we sample, so that they don't influence the outcome */
-       driver= fcu->driver;
-       fcu->driver= NULL;
-       
-       /* bake the modifiers, by sampling the curve at each frame */
-       fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
-       
-       /* free the modifiers now */
-       fcurve_free_modifiers(fcu);
-       
-       /* restore driver */
-       fcu->driver= driver;
-}
-
-/* Find the active F-Curve Modifier */
-FModifier *fcurve_find_active_modifier (FCurve *fcu)
-{
-       FModifier *fcm;
-       
-       /* sanity checks */
-       if ELEM(NULL, fcu, fcu->modifiers.first)
-               return NULL;
-       
-       /* loop over modifiers until 'active' one is found */
-       for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-               if (fcm->flag & FMODIFIER_FLAG_ACTIVE)
-                       return fcm;
-       }
-       
-       /* no modifier is active */
-       return NULL;
-}
-
-/* Set the active F-Curve Modifier */
-void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm)
-{
-       FModifier *fm;
-       
-       /* sanity checks */
-       if ELEM(NULL, fcu, fcu->modifiers.first)
-               return;
-       
-       /* deactivate all, and set current one active */
-       for (fm= fcu->modifiers.first; fm; fm= fm->next)
-               fm->flag &= ~FMODIFIER_FLAG_ACTIVE;
-       
-       /* make given modifier active */
-       if (fcm)
-               fcm->flag |= FMODIFIER_FLAG_ACTIVE;
-}
-
 /* ***************************** F-Curve - Evaluation ********************************* */
 
 /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") 
@@ -2262,7 +1263,6 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm)
  */
 float evaluate_fcurve (FCurve *fcu, float evaltime) 
 {
-       FModifier *fcm;
        float cvalue= 0.0f;
        float devaltime;
        
@@ -2275,28 +1275,8 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
                evaltime= cvalue= evaluate_driver(fcu->driver, evaltime);
        }
        
-       /* evaluate time modifications imposed by some F-Curve Modifiers
-        *      - this step acts as an optimisation to prevent the F-Curve stack being evaluated 
-        *        several times by modifiers requesting the time be modified, as the final result
-        *        would have required using the modified time
-        *      - modifiers only ever recieve the unmodified time, as subsequent modifiers should be
-        *        working on the 'global' result of the modified curve, not some localised segment,
-        *        so nevaltime gets set to whatever the last time-modifying modifier likes...
-        *      - we start from the end of the stack, as only the last one matters for now
-        */
-       devaltime= evaltime;
-       
-       for (fcm= fcu->modifiers.last; fcm; fcm= fcm->prev) {
-               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-               
-               /* only evaluate if there's a callback for this */
-               // TODO: implement the 'influence' control feature...
-               if (fmi && fmi->evaluate_modifier_time) {
-                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
-                               devaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
-                       break;
-               }
-       }
+       /* evaluate modifiers which modify time to evaluate the base curve at */
+       devaltime= evaluate_time_fmodifiers(&fcu->modifiers, fcu, cvalue, evaltime);
        
        /* evaluate curve-data 
         *      - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying 
@@ -2308,16 +1288,7 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
                cvalue= fcurve_eval_samples(fcu, fcu->fpt, devaltime);
        
        /* evaluate modifiers */
-       for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-               
-               /* only evaluate if there's a callback for this */
-               // TODO: implement the 'influence' control feature...
-               if (fmi && fmi->evaluate_modifier) {
-                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
-                               fmi->evaluate_modifier(fcu, fcm, &cvalue, evaltime);
-               }
-       }
+       evaluate_value_fmodifiers(&fcu->modifiers, fcu, &cvalue, evaltime);
        
        /* if curve can only have integral values, perform truncation (i.e. drop the decimal part)
         * here so that the curve can be sampled correctly
@@ -2330,10 +1301,16 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
 }
 
 /* Calculate the value of the given F-Curve at the given frame, and set its curval */
-// TODO: will this be necessary?
 void calculate_fcurve (FCurve *fcu, float ctime)
 {
-       /* calculate and set curval (evaluates driver too) */
-       fcu->curval= evaluate_fcurve(fcu, ctime);
+       /* only calculate + set curval (overriding the existing value) if curve has 
+        * any data which warrants this...
+        */
+       if ( (fcu->totvert) || (fcu->driver && !(fcu->driver->flag & DRIVER_FLAG_INVALID)) ||
+                list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) )
+       {
+               /* calculate and set curval (evaluates driver too if necessary) */
+               fcu->curval= evaluate_fcurve(fcu, ctime);
+       }
 }
 
diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c
new file mode 100644 (file)
index 0000000..aa52149
--- /dev/null
@@ -0,0 +1,1197 @@
+/**
+ * $Id: fcurve.c 21299 2009-07-02 02:12:37Z aligorith $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ *
+ * Contributor(s): Joshua Leung (full recode)
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+
+#include <math.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <float.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_noise.h"
+
+#include "BKE_fcurve.h"
+#include "BKE_curve.h" 
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+#include "RNA_types.h"
+
+#ifndef DISABLE_PYTHON
+#include "BPY_extern.h" /* for BPY_pydriver_eval() */
+#endif
+
+#define SMALL -1.0e-10
+#define SELECT 1
+
+/* ******************************** F-Modifiers ********************************* */
+
+/* Info ------------------------------- */
+
+/* F-Modifiers are modifiers which operate on F-Curves. However, they can also be defined
+ * on NLA-Strips to affect all of the F-Curves referenced by the NLA-Strip. 
+ */
+
+/* Template --------------------------- */
+
+/* Each modifier defines a set of functions, which will be called at the appropriate
+ * times. In addition to this, each modifier should have a type-info struct, where
+ * its functions are attached for use. 
+ */
+/* Template for type-info data:
+ *     - make a copy of this when creating new modifiers, and just change the functions
+ *       pointed to as necessary
+ *     - although the naming of functions doesn't matter, it would help for code
+ *       readability, to follow the same naming convention as is presented here
+ *     - any functions that a constraint doesn't need to define, don't define
+ *       for such cases, just use NULL 
+ *     - these should be defined after all the functions have been defined, so that
+ *       forward-definitions/prototypes don't need to be used!
+ *     - keep this copy #if-def'd so that future constraints can get based off this
+ */
+#if 0
+static FModifierTypeInfo FMI_MODNAME = {
+       FMODIFIER_TYPE_MODNAME, /* type */
+       sizeof(FMod_ModName), /* size */
+       FMI_TYPE_SOME_ACTION, /* action type */
+       FMI_REQUIRES_SOME_REQUIREMENT, /* requirements */
+       "Modifier Name", /* name */
+       "FMod_ModName", /* struct name */
+       fcm_modname_free, /* free data */
+       fcm_modname_relink, /* relink data */
+       fcm_modname_copy, /* copy data */
+       fcm_modname_new_data, /* new data */
+       fcm_modname_verify, /* verify */
+       fcm_modname_time, /* evaluate time */
+       fcm_modname_evaluate /* evaluate */
+};
+#endif
+
+/* Generator F-Curve Modifier --------------------------- */
+
+/* Generators available:
+ *     1) simple polynomial generator:
+ *             - Exanded form - (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n])  
+ *             - Factorised form - (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1]))
+ */
+
+static void fcm_generator_free (FModifier *fcm)
+{
+       FMod_Generator *data= (FMod_Generator *)fcm->data;
+       
+       /* free polynomial coefficients array */
+       if (data->coefficients)
+               MEM_freeN(data->coefficients);
+}
+
+static void fcm_generator_copy (FModifier *fcm, FModifier *src)
+{
+       FMod_Generator *gen= (FMod_Generator *)fcm->data;
+       FMod_Generator *ogen= (FMod_Generator *)src->data;
+       
+       /* copy coefficients array? */
+       if (ogen->coefficients)
+               gen->coefficients= MEM_dupallocN(ogen->coefficients);
+}
+
+static void fcm_generator_new_data (void *mdata)
+{
+       FMod_Generator *data= (FMod_Generator *)mdata;
+       float *cp;
+       
+       /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */
+       data->poly_order= 1;
+       data->arraysize= 2;
+       cp= data->coefficients= MEM_callocN(sizeof(float)*2, "FMod_Generator_Coefs");
+       cp[0] = 0; // y-offset 
+       cp[1] = 1; // gradient
+}
+
+static void fcm_generator_verify (FModifier *fcm)
+{
+       FMod_Generator *data= (FMod_Generator *)fcm->data;
+       
+       /* requirements depend on mode */
+       switch (data->mode) {
+               case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
+               {
+                       /* arraysize needs to be order+1, so resize if not */
+                       if (data->arraysize != (data->poly_order+1)) {
+                               float *nc;
+                               
+                               /* make new coefficients array, and copy over as much data as can fit */
+                               nc= MEM_callocN(sizeof(float)*(data->poly_order+1), "FMod_Generator_Coefs");
+                               
+                               if (data->coefficients) {
+                                       if (data->arraysize > (data->poly_order+1))
+                                               memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order+1));
+                                       else
+                                               memcpy(nc, data->coefficients, sizeof(float)*data->arraysize);
+                                               
+                                       /* free the old data */
+                                       MEM_freeN(data->coefficients);
+                               }       
+                               
+                               /* set the new data */
+                               data->coefficients= nc;
+                               data->arraysize= data->poly_order+1;
+                       }
+               }
+                       break;
+               
+               case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */
+               {
+                       /* arraysize needs to be 2*order, so resize if not */
+                       if (data->arraysize != (data->poly_order * 2)) {
+                               float *nc;
+                               
+                               /* make new coefficients array, and copy over as much data as can fit */
+                               nc= MEM_callocN(sizeof(float)*(data->poly_order*2), "FMod_Generator_Coefs");
+                               
+                               if (data->coefficients) {
+                                       if (data->arraysize > (data->poly_order * 2))
+                                               memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order * 2));
+                                       else
+                                               memcpy(nc, data->coefficients, sizeof(float)*data->arraysize);
+                                               
+                                       /* free the old data */
+                                       MEM_freeN(data->coefficients);
+                               }       
+                               
+                               /* set the new data */
+                               data->coefficients= nc;
+                               data->arraysize= data->poly_order * 2;
+                       }
+               }
+                       break;  
+       }
+}
+
+static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       FMod_Generator *data= (FMod_Generator *)fcm->data;
+       
+       /* behaviour depends on mode 
+        * NOTE: the data in its default state is fine too
+        */
+       switch (data->mode) {
+               case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
+               {
+                       /* we overwrite cvalue with the sum of the polynomial */
+                       float *powers = MEM_callocN(sizeof(float)*data->arraysize, "Poly Powers");
+                       float value= 0.0f;
+                       unsigned int i;
+                       
+                       /* for each x^n, precalculate value based on previous one first... this should be 
+                        * faster that calling pow() for each entry
+                        */
+                       for (i=0; i < data->arraysize; i++) {
+                               /* first entry is x^0 = 1, otherwise, calculate based on previous */
+                               if (i)
+                                       powers[i]= powers[i-1] * evaltime;
+                               else
+                                       powers[0]= 1;
+                       }
+                       
+                       /* for each coefficient, add to value, which we'll write to *cvalue in one go */
+                       for (i=0; i < data->arraysize; i++)
+                               value += data->coefficients[i] * powers[i];
+                       
+                       /* only if something changed, write *cvalue in one go */
+                       if (data->poly_order) {
+                               if (data->flag & FCM_GENERATOR_ADDITIVE)
+                                       *cvalue += value;
+                               else
+                                       *cvalue= value;
+                       }
+                               
+                       /* cleanup */
+                       if (powers) 
+                               MEM_freeN(powers);
+               }
+                       break;
+                       
+               case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */
+               {
+                       float value= 1.0f, *cp=NULL;
+                       unsigned int i;
+                       
+                       /* for each coefficient pair, solve for that bracket before accumulating in value by multiplying */
+                       for (cp=data->coefficients, i=0; (cp) && (i < data->poly_order); cp+=2, i++) 
+                               value *= (cp[0]*evaltime + cp[1]);
+                               
+                       /* only if something changed, write *cvalue in one go */
+                       if (data->poly_order) {
+                               if (data->flag & FCM_GENERATOR_ADDITIVE)
+                                       *cvalue += value;
+                               else
+                                       *cvalue= value;
+                       }
+               }
+                       break;
+       }
+}
+
+static FModifierTypeInfo FMI_GENERATOR = {
+       FMODIFIER_TYPE_GENERATOR, /* type */
+       sizeof(FMod_Generator), /* size */
+       FMI_TYPE_GENERATE_CURVE, /* action type */
+       FMI_REQUIRES_NOTHING, /* requirements */
+       "Generator", /* name */
+       "FMod_Generator", /* struct name */
+       fcm_generator_free, /* free data */
+       fcm_generator_copy, /* copy data */
+       fcm_generator_new_data, /* new data */
+       fcm_generator_verify, /* verify */
+       NULL, /* evaluate time */
+       fcm_generator_evaluate /* evaluate */
+};
+
+/* Built-In Function Generator F-Curve Modifier --------------------------- */
+
+/* This uses the general equation for equations:
+ *             y = amplitude * fn(phase_multiplier*x + phase_offset) + y_offset
+ *
+ * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients,
+ * x is the evaluation 'time', and 'y' is the resultant value
+ *
+ * Functions available are
+ *     sin, cos, tan, sinc (normalised sin), natural log, square root 
+ */
+
+static void fcm_fn_generator_new_data (void *mdata)
+{
+       FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)mdata;
+       
+       /* set amplitude and phase multiplier to 1.0f so that something is generated */
+       data->amplitude= 1.0f;
+       data->phase_multiplier= 1.0f;
+}
+
+/* Unary 'normalised sine' function
+ *     y = sin(PI + x) / (PI * x),
+ * except for x = 0 when y = 1.
+ */
+static double sinc (double x)
+{
+    if (fabs(x) < 0.0001)
+        return 1.0;
+    else
+        return sin(M_PI * x) / (M_PI * x);
+}
+
+static void fcm_fn_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)fcm->data;
+       double arg= data->phase_multiplier*evaltime + data->phase_offset;
+       double (*fn)(double v) = NULL;
+       
+       /* get function pointer to the func to use:
+        * WARNING: must perform special argument validation hereto guard against crashes  
+        */
+       switch (data->type)
+       {
+               /* simple ones */                       
+               case FCM_GENERATOR_FN_SIN: /* sine wave */
+                       fn= sin;
+                       break;
+               case FCM_GENERATOR_FN_COS: /* cosine wave */
+                       fn= cos;
+                       break;
+               case FCM_GENERATOR_FN_SINC: /* normalised sine wave */
+                       fn= sinc;
+                       break;
+                       
+               /* validation required */
+               case FCM_GENERATOR_FN_TAN: /* tangent wave */
+               {
+                       /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */
+                       if IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0) {
+                               if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
+                                       *cvalue = 0.0f; /* no value possible here */
+                       }
+                       else
+                               fn= tan;
+               }
+                       break;
+               case FCM_GENERATOR_FN_LN: /* natural log */
+               {
+                       /* check that value is greater than 1? */
+                       if (arg > 1.0f) {
+                               fn= log;
+                       }
+                       else {
+                               if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
+                                       *cvalue = 0.0f; /* no value possible here */
+                       }
+               }
+                       break;
+               case FCM_GENERATOR_FN_SQRT: /* square root */
+               {
+                       /* no negative numbers */
+                       if (arg > 0.0f) {
+                               fn= sqrt;
+                       }
+                       else {
+                               if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
+                                       *cvalue = 0.0f; /* no value possible here */
+                       }
+               }
+                       break;
+               
+               default:
+                       printf("Invalid Function-Generator for F-Modifier - %d \n", data->type);
+       }
+       
+       /* execute function callback to set value if appropriate */
+       if (fn) {
+               float value= (float)(data->amplitude*fn(arg) + data->value_offset);
+               
+               if (data->flag & FCM_GENERATOR_ADDITIVE)
+                       *cvalue += value;
+               else
+                       *cvalue= value;
+       }
+}
+
+static FModifierTypeInfo FMI_FN_GENERATOR = {
+       FMODIFIER_TYPE_FN_GENERATOR, /* type */
+       sizeof(FMod_FunctionGenerator), /* size */
+       FMI_TYPE_GENERATE_CURVE, /* action type */
+       FMI_REQUIRES_NOTHING, /* requirements */
+       "Built-In Function", /* name */
+       "FMod_FunctionGenerator", /* struct name */
+       NULL, /* free data */
+       NULL, /* copy data */
+       fcm_fn_generator_new_data, /* new data */
+       NULL, /* verify */
+       NULL, /* evaluate time */
+       fcm_fn_generator_evaluate /* evaluate */
+};
+
+/* Envelope F-Curve Modifier --------------------------- */
+
+static void fcm_envelope_free (FModifier *fcm)
+{
+       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
+       
+       /* free envelope data array */
+       if (env->data)
+               MEM_freeN(env->data);
+}
+
+static void fcm_envelope_copy (FModifier *fcm, FModifier *src)
+{
+       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
+       FMod_Envelope *oenv= (FMod_Envelope *)src->data;
+       
+       /* copy envelope data array */
+       if (oenv->data)
+               env->data= MEM_dupallocN(oenv->data);
+}
+
+static void fcm_envelope_new_data (void *mdata)
+{
+       FMod_Envelope *env= (FMod_Envelope *)mdata;
+       
+       /* set default min/max ranges */
+       env->min= -1.0f;
+       env->max= 1.0f;
+}
+
+static void fcm_envelope_verify (FModifier *fcm)
+{
+       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
+       
+       /* if the are points, perform bubble-sort on them, as user may have changed the order */
+       if (env->data) {
+               // XXX todo...
+       }
+}
+
+static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       FMod_Envelope *env= (FMod_Envelope *)fcm->data;
+       FCM_EnvelopeData *fed, *prevfed, *lastfed;
+       float min=0.0f, max=0.0f, fac=0.0f;
+       int a;
+       
+       /* get pointers */
+       if (env->data == NULL) return;
+       prevfed= env->data;
+       fed= prevfed + 1;
+       lastfed= prevfed + (env->totvert-1);
+       
+       /* get min/max values for envelope at evaluation time (relative to mid-value) */
+       if (prevfed->time >= evaltime) {
+               /* before or on first sample, so just extend value */
+               min= prevfed->min;
+               max= prevfed->max;
+       }
+       else if (lastfed->time <= evaltime) {
+               /* after or on last sample, so just extend value */
+               min= lastfed->min;
+               max= lastfed->max;
+       }
+       else {
+               /* evaltime occurs somewhere between segments */
+               // TODO: implement binary search for this to make it faster?
+               for (a=0; prevfed && fed && (a < env->totvert-1); a++, prevfed=fed, fed++) {  
+                       /* evaltime occurs within the interval defined by these two envelope points */
+                       if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) {
+                               float afac, bfac, diff;
+                               
+                               diff= fed->time - prevfed->time;
+                               afac= (evaltime - prevfed->time) / diff;
+                               bfac= (fed->time - evaltime) / diff;
+                               
+                               min= bfac*prevfed->min + afac*fed->min;
+                               max= bfac*prevfed->max + afac*fed->max;
+                               
+                               break;
+                       }
+               }
+       }
+       
+       /* adjust *cvalue 
+        *      - fac is the ratio of how the current y-value corresponds to the reference range
+        *      - thus, the new value is found by mapping the old range to the new!
+        */
+       fac= (*cvalue - (env->midval + env->min)) / (env->max - env->min);
+       *cvalue= min + fac*(max - min); 
+}
+
+static FModifierTypeInfo FMI_ENVELOPE = {
+       FMODIFIER_TYPE_ENVELOPE, /* type */
+       sizeof(FMod_Envelope), /* size */
+       FMI_TYPE_REPLACE_VALUES, /* action type */
+       0, /* requirements */
+       "Envelope", /* name */
+       "FMod_Envelope", /* struct name */
+       fcm_envelope_free, /* free data */
+       fcm_envelope_copy, /* copy data */
+       fcm_envelope_new_data, /* new data */
+       fcm_envelope_verify, /* verify */
+       NULL, /* evaluate time */
+       fcm_envelope_evaluate /* evaluate */
+};
+
+/* Cycles F-Curve Modifier  --------------------------- */
+
+/* This modifier changes evaltime to something that exists within the curve's frame-range, 
+ * then re-evaluates modifier stack up to this point using the new time. This re-entrant behaviour
+ * is very likely to be more time-consuming than the original approach... (which was tighly integrated into 
+ * the calculation code...).
+ *
+ * NOTE: this needs to be at the start of the stack to be of use, as it needs to know the extents of the keyframes/sample-data
+ * Possible TODO - store length of cycle information that can be initialised from the extents of the keyframes/sample-data, and adjusted
+ *                             as appropriate
+ */
+
+/* temp data used during evaluation */
+typedef struct tFCMED_Cycles {
+       float cycyofs;          /* y-offset to apply */
+} tFCMED_Cycles;
+static void fcm_cycles_new_data (void *mdata)
+{
+       FMod_Cycles *data= (FMod_Cycles *)mdata;
+       
+       /* turn on cycles by default */
+       data->before_mode= data->after_mode= FCM_EXTRAPOLATE_CYCLIC;
+}
+
+static float fcm_cycles_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
+{
+       FMod_Cycles *data= (FMod_Cycles *)fcm->data;
+       float prevkey[2], lastkey[2], cycyofs=0.0f;
+       short side=0, mode=0;
+       int cycles=0;
+       
+       /* check if modifier is first in stack, otherwise disable ourself... */
+       // FIXME...
+       if (fcm->prev) {
+               fcm->flag |= FMODIFIER_FLAG_DISABLED;
+               return evaltime;
+       }
+       
+       /* calculate new evaltime due to cyclic interpolation */
+       if (fcu && fcu->bezt) {
+               BezTriple *prevbezt= fcu->bezt;
+               BezTriple *lastbezt= prevbezt + fcu->totvert-1;
+               
+               prevkey[0]= prevbezt->vec[1][0];
+               prevkey[1]= prevbezt->vec[1][1];
+               
+               lastkey[0]= lastbezt->vec[1][0];
+               lastkey[1]= lastbezt->vec[1][1];
+       }
+       else if (fcu && fcu->fpt) {
+               FPoint *prevfpt= fcu->fpt;
+               FPoint *lastfpt= prevfpt + fcu->totvert-1;
+               
+               prevkey[0]= prevfpt->vec[0];
+               prevkey[1]= prevfpt->vec[1];
+               
+               lastkey[0]= lastfpt->vec[0];
+               lastkey[1]= lastfpt->vec[1];
+       }
+       else
+               return evaltime;
+               
+       /* check if modifier will do anything
+        *      1) if in data range, definitely don't do anything
+        *      2) if before first frame or after last frame, make sure some cycling is in use
+        */
+       if (evaltime < prevkey[0]) {
+               if (data->before_mode)  {
+                       side= -1;
+                       mode= data->before_mode;
+                       cycles= data->before_cycles;
+               }
+       }
+       else if (evaltime > lastkey[0]) {
+               if (data->after_mode) {
+                       side= 1;
+                       mode= data->after_mode;
+                       cycles= data->after_cycles;
+               }
+       }
+       if ELEM(0, side, mode)
+               return evaltime;
+               
+       /* find relative place within a cycle */
+       {
+               float cycdx=0, cycdy=0, ofs=0;
+               float cycle= 0;
+               
+               /* ofs is start frame of cycle */
+               ofs= prevkey[0];
+               
+               /* calculate period and amplitude (total height) of a cycle */
+               cycdx= lastkey[0] - prevkey[0];
+               cycdy= lastkey[1] - prevkey[1];
+               
+               /* check if cycle is infinitely small, to be point of being impossible to use */
+               if (cycdx == 0)
+                       return evaltime;
+                       
+               /* calculate the 'number' of the cycle */
+               cycle= ((float)side * (evaltime - ofs) / cycdx);
+               
+               /* check that cyclic is still enabled for the specified time */
+               if (cycles == 0) {
+                       /* catch this case so that we don't exit when we have cycles=0
+                        * as this indicates infinite cycles...
+                        */
+               }
+               else if (cycle > (cycles+1)) {
+                       /* we are too far away from range to evaluate
+                        * TODO: but we should still hold last value... 
+                        */
+                       return evaltime;
+               }
+               
+               /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */
+               if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
+                       cycyofs = (float)floor((evaltime - ofs) / cycdx);
+                       cycyofs *= cycdy;
+               }
+               
+               /* calculate where in the cycle we are (overwrite evaltime to reflect this) */
+               if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)(cycle) % 2)) {
+                       /* when 'mirror' option is used and cycle number is odd, this cycle is played in reverse 
+                        *      - for 'before' extrapolation, we need to flip in a different way, otherwise values past
+                        *        then end of the curve get referenced (result of fmod will be negative, and with different phase)
+                        */
+                       if (side < 0)
+                               evaltime= (float)(prevkey[0] - fmod(evaltime-ofs, cycdx));
+                       else
+                               evaltime= (float)(lastkey[0] - fmod(evaltime-ofs, cycdx));
+               }
+               else {
+                       /* the cycle is played normally... */
+                       evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs);
+               }
+               if (evaltime < ofs) evaltime += cycdx;
+       }
+       
+       /* store temp data if needed */
+       if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
+               tFCMED_Cycles *edata;
+               
+               /* for now, this is just a float, but we could get more stuff... */
+               fcm->edata= edata= MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles");
+               edata->cycyofs= cycyofs;
+       }
+       
+       /* return the new frame to evaluate */
+       return evaltime;
+}
+static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       tFCMED_Cycles *edata= (tFCMED_Cycles *)fcm->edata;
+       
+       /* use temp data */
+       if (edata) {
+               /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */
+               *cvalue += edata->cycyofs;
+               
+               /* free temp data */
+               MEM_freeN(edata);
+               fcm->edata= NULL;
+       }
+}
+
+static FModifierTypeInfo FMI_CYCLES = {
+       FMODIFIER_TYPE_CYCLES, /* type */
+       sizeof(FMod_Cycles), /* size */
+       FMI_TYPE_EXTRAPOLATION, /* action type */
+       FMI_REQUIRES_ORIGINAL_DATA, /* requirements */
+       "Cycles", /* name */
+       "FMod_Cycles", /* struct name */
+       NULL, /* free data */
+       NULL, /* copy data */
+       fcm_cycles_new_data, /* new data */
+       NULL /*fcm_cycles_verify*/, /* verify */
+       fcm_cycles_time, /* evaluate time */
+       fcm_cycles_evaluate /* evaluate */
+};
+
+/* Noise F-Curve Modifier  --------------------------- */
+
+static void fcm_noise_new_data (void *mdata)
+{
+       FMod_Noise *data= (FMod_Noise *)mdata;
+       
+       /* defaults */
+       data->size= 1.0f;
+       data->strength= 1.0f;
+       data->phase= 1.0f;
+       data->depth = 0;
+       data->modification = FCM_NOISE_MODIF_REPLACE;
+}
+static void fcm_noise_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       FMod_Noise *data= (FMod_Noise *)fcm->data;
+       float noise;
+       
+       noise = BLI_turbulence(data->size, evaltime, data->phase, 0.f, data->depth);
+       
+       switch (data->modification) {
+               case FCM_NOISE_MODIF_ADD:
+                       *cvalue= *cvalue + noise * data->strength;
+                       break;
+               case FCM_NOISE_MODIF_SUBTRACT:
+                       *cvalue= *cvalue - noise * data->strength;
+                       break;
+               case FCM_NOISE_MODIF_MULTIPLY:
+                       *cvalue= *cvalue * noise * data->strength;
+                       break;
+               case FCM_NOISE_MODIF_REPLACE:
+               default:
+                       *cvalue= *cvalue + (noise - 0.5f) * data->strength;
+                       break;
+       }
+}
+
+static FModifierTypeInfo FMI_NOISE = {
+       FMODIFIER_TYPE_NOISE, /* type */
+       sizeof(FMod_Noise), /* size */
+       FMI_TYPE_REPLACE_VALUES, /* action type */
+       0, /* requirements */
+       "Noise", /* name */
+       "FMod_Noise", /* struct name */
+       NULL, /* free data */
+       NULL, /* copy data */
+       fcm_noise_new_data, /* new data */
+       NULL /*fcm_noise_verify*/, /* verify */
+       NULL, /* evaluate time */
+       fcm_noise_evaluate /* evaluate */
+};
+
+/* Filter F-Curve Modifier --------------------------- */
+
+#if 0 // XXX not yet implemented 
+static FModifierTypeInfo FMI_FILTER = {
+       FMODIFIER_TYPE_FILTER, /* type */
+       sizeof(FMod_Filter), /* size */
+       FMI_TYPE_REPLACE_VALUES, /* action type */
+       0, /* requirements */
+       "Filter", /* name */
+       "FMod_Filter", /* struct name */
+       NULL, /* free data */
+       NULL, /* copy data */
+       NULL, /* new data */
+       NULL /*fcm_filter_verify*/, /* verify */
+       NULL, /* evlauate time */
+       fcm_filter_evaluate /* evaluate */
+};
+#endif // XXX not yet implemented
+
+
+/* Python F-Curve Modifier --------------------------- */
+
+static void fcm_python_free (FModifier *fcm)
+{
+       FMod_Python *data= (FMod_Python *)fcm->data;
+       
+       /* id-properties */
+       IDP_FreeProperty(data->prop);
+       MEM_freeN(data->prop);
+}
+
+static void fcm_python_new_data (void *mdata) 
+{
+       FMod_Python *data= (FMod_Python *)mdata;
+       
+       /* everything should be set correctly by calloc, except for the prop->type constant.*/
+       data->prop = MEM_callocN(sizeof(IDProperty), "PyFModifierProps");
+       data->prop->type = IDP_GROUP;
+}
+
+static void fcm_python_copy (FModifier *fcm, FModifier *src)
+{
+       FMod_Python *pymod = (FMod_Python *)fcm->data;
+       FMod_Python *opymod = (FMod_Python *)src->data;
+       
+       pymod->prop = IDP_CopyProperty(opymod->prop);
+}
+
+static void fcm_python_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+#ifndef DISABLE_PYTHON
+       //FMod_Python *data= (FMod_Python *)fcm->data;
+       
+       /* FIXME... need to implement this modifier...
+        *      It will need it execute a script using the custom properties 
+        */
+#endif /* DISABLE_PYTHON */
+}
+
+static FModifierTypeInfo FMI_PYTHON = {
+       FMODIFIER_TYPE_PYTHON, /* type */
+       sizeof(FMod_Python), /* size */
+       FMI_TYPE_GENERATE_CURVE, /* action type */
+       FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
+       "Python", /* name */
+       "FMod_Python", /* struct name */
+       fcm_python_free, /* free data */
+       fcm_python_copy, /* copy data */
+       fcm_python_new_data, /* new data */
+       NULL /*fcm_python_verify*/, /* verify */
+       NULL /*fcm_python_time*/, /* evaluate time */
+       fcm_python_evaluate /* evaluate */
+};
+
+
+/* Limits F-Curve Modifier --------------------------- */
+
+static float fcm_limits_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
+{
+       FMod_Limits *data= (FMod_Limits *)fcm->data;
+       
+       /* check for the time limits */
+       if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin))
+               return data->rect.xmin;
+       if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax))
+               return data->rect.xmax;
+               
+       /* modifier doesn't change time */
+       return evaltime;
+}
+
+static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       FMod_Limits *data= (FMod_Limits *)fcm->data;
+       
+       /* value limits now */
+       if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin))
+               *cvalue= data->rect.ymin;
+       if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax))
+               *cvalue= data->rect.ymax;
+}
+
+static FModifierTypeInfo FMI_LIMITS = {
+       FMODIFIER_TYPE_LIMITS, /* type */
+       sizeof(FMod_Limits), /* size */
+       FMI_TYPE_GENERATE_CURVE, /* action type */  /* XXX... err... */   
+       FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
+       "Limits", /* name */
+       "FMod_Limits", /* struct name */
+       NULL, /* free data */
+       NULL, /* copy data */
+       NULL, /* new data */
+       NULL, /* verify */
+       fcm_limits_time, /* evaluate time */
+       fcm_limits_evaluate /* evaluate */
+};
+
+/* F-Curve Modifier API --------------------------- */
+/* All of the F-Curve Modifier api functions use FModifierTypeInfo structs to carry out
+ * and operations that involve F-Curve modifier specific code.
+ */
+
+/* These globals only ever get directly accessed in this file */
+static FModifierTypeInfo *fmodifiersTypeInfo[FMODIFIER_NUM_TYPES];
+static short FMI_INIT= 1; /* when non-zero, the list needs to be updated */
+
+/* This function only gets called when FMI_INIT is non-zero */
+static void fmods_init_typeinfo () 
+{
+       fmodifiersTypeInfo[0]=  NULL;                                   /* 'Null' F-Curve Modifier */
+       fmodifiersTypeInfo[1]=  &FMI_GENERATOR;                 /* Generator F-Curve Modifier */
+       fmodifiersTypeInfo[2]=  &FMI_FN_GENERATOR;              /* Built-In Function Generator F-Curve Modifier */
+       fmodifiersTypeInfo[3]=  &FMI_ENVELOPE;                  /* Envelope F-Curve Modifier */
+       fmodifiersTypeInfo[4]=  &FMI_CYCLES;                    /* Cycles F-Curve Modifier */
+       fmodifiersTypeInfo[5]=  &FMI_NOISE;                             /* Apply-Noise F-Curve Modifier */
+       fmodifiersTypeInfo[6]=  NULL/*&FMI_FILTER*/;                    /* Filter F-Curve Modifier */  // XXX unimplemented
+       fmodifiersTypeInfo[7]=  &FMI_PYTHON;                    /* Custom Python F-Curve Modifier */
+       fmodifiersTypeInfo[8]=  &FMI_LIMITS;                    /* Limits F-Curve Modifier */
+}
+
+/* This function should be used for getting the appropriate type-info when only
+ * a F-Curve modifier type is known
+ */
+FModifierTypeInfo *get_fmodifier_typeinfo (int type)
+{
+       /* initialise the type-info list? */
+       if (FMI_INIT) {
+               fmods_init_typeinfo();
+               FMI_INIT = 0;
+       }
+       
+       /* only return for valid types */
+       if ( (type >= FMODIFIER_TYPE_NULL) && 
+                (type <= FMODIFIER_NUM_TYPES ) ) 
+       {
+               /* there shouldn't be any segfaults here... */
+               return fmodifiersTypeInfo[type];
+       }
+       else {
+               printf("No valid F-Curve Modifier type-info data available. Type = %i \n", type);
+       }
+       
+       return NULL;
+} 
+/* This function should always be used to get the appropriate type-info, as it
+ * has checks which prevent segfaults in some weird cases.
+ */
+FModifierTypeInfo *fmodifier_get_typeinfo (FModifier *fcm)
+{
+       /* only return typeinfo for valid modifiers */
+       if (fcm)
+               return get_fmodifier_typeinfo(fcm->type);
+       else
+               return NULL;
+}
+
+/* API --------------------------- */
+
+/* Add a new F-Curve Modifier to the given F-Curve of a certain type */
+FModifier *add_fmodifier (ListBase *modifiers, int type)
+{
+       FModifierTypeInfo *fmi= get_fmodifier_typeinfo(type);
+       FModifier *fcm;
+       
+       /* sanity checks */
+       if ELEM(NULL, modifiers, fmi)
+               return NULL;
+       
+       /* special checks for whether modifier can be added */
+       if ((modifiers->first) && (type == FMODIFIER_TYPE_CYCLES)) {
+               /* cycles modifier must be first in stack, so for now, don't add if it can't be */
+               // TODO: perhaps there is some better way, but for now, 
+               printf("Error: Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be first in stack. \n");
+               return NULL;
+       }
+       
+       /* add modifier itself */
+       fcm= MEM_callocN(sizeof(FModifier), "F-Curve Modifier");
+       fcm->type = type;
+       fcm->flag = FMODIFIER_FLAG_EXPANDED;
+       BLI_addtail(modifiers, fcm);
+       
+       /* add modifier's data */
+       fcm->data= MEM_callocN(fmi->size, fmi->structName);
+               
+       /* init custom settings if necessary */
+       if (fmi->new_data)      
+               fmi->new_data(fcm->data);
+               
+       /* return modifier for further editing */
+       return fcm;
+}
+
+/* Duplicate all of the F-Modifiers in the Modifier stacks */
+void copy_fmodifiers (ListBase *dst, ListBase *src)
+{
+       FModifier *fcm, *srcfcm;
+       
+       if ELEM(NULL, dst, src)
+               return;
+       
+       dst->first= dst->last= NULL;
+       BLI_duplicatelist(dst, src);
+       
+       for (fcm=dst->first, srcfcm=src->first; fcm && srcfcm; srcfcm=srcfcm->next, fcm=fcm->next) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* make a new copy of the F-Modifier's data */
+               fcm->data = MEM_dupallocN(fcm->data);
+               
+               /* only do specific constraints if required */
+               if (fmi && fmi->copy_data)
+                       fmi->copy_data(fcm, srcfcm);
+       }
+}
+
+/* Remove and free the given F-Modifier from the given stack  */
+void remove_fmodifier (ListBase *modifiers, FModifier *fcm)
+{
+       FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+       
+       /* sanity check */
+       if (fcm == NULL)
+               return;
+       
+       /* free modifier's special data (stored inside fcm->data) */
+       if (fcm->data) {
+               if (fmi && fmi->free_data)
+                       fmi->free_data(fcm);
+                       
+               /* free modifier's data (fcm->data) */
+               MEM_freeN(fcm->data);
+       }
+       
+       /* remove modifier from stack */
+       if (modifiers)
+               BLI_freelinkN(modifiers, fcm);
+       else {
+               // XXX this case can probably be removed some day, as it shouldn't happen...
+               printf("remove_fmodifier() - no modifier stack given \n");
+               MEM_freeN(fcm);
+       }
+}
+
+/* Remove all of a given F-Curve's modifiers */
+void free_fmodifiers (ListBase *modifiers)
+{
+       FModifier *fcm, *fmn;
+       
+       /* sanity check */
+       if (modifiers == NULL)
+               return;
+       
+       /* free each modifier in order - modifier is unlinked from list and freed */
+       for (fcm= modifiers->first; fcm; fcm= fmn) {
+               fmn= fcm->next;
+               remove_fmodifier(modifiers, fcm);
+       }
+}
+
+/* Find the active F-Modifier */
+FModifier *find_active_fmodifier (ListBase *modifiers)
+{
+       FModifier *fcm;
+       
+       /* sanity checks */
+       if ELEM(NULL, modifiers, modifiers->first)
+               return NULL;
+       
+       /* loop over modifiers until 'active' one is found */
+       for (fcm= modifiers->first; fcm; fcm= fcm->next) {
+               if (fcm->flag & FMODIFIER_FLAG_ACTIVE)
+                       return fcm;
+       }
+       
+       /* no modifier is active */
+       return NULL;
+}
+
+/* Set the active F-Modifier */
+void set_active_fmodifier (ListBase *modifiers, FModifier *fcm)
+{
+       FModifier *fm;
+       
+       /* sanity checks */
+       if ELEM(NULL, modifiers, modifiers->first)
+               return;
+       
+       /* deactivate all, and set current one active */
+       for (fm= modifiers->first; fm; fm= fm->next)
+               fm->flag &= ~FMODIFIER_FLAG_ACTIVE;
+       
+       /* make given modifier active */
+       if (fcm)
+               fcm->flag |= FMODIFIER_FLAG_ACTIVE;
+}
+
+/* Do we have any modifiers which match certain criteria 
+ *     - mtype - type of modifier (if 0, doesn't matter)
+ *     - acttype - type of action to perform (if -1, doesn't matter)
+ */
+short list_has_suitable_fmodifier (ListBase *modifiers, int mtype, short acttype)
+{
+       FModifier *fcm;
+       
+       /* if there are no specific filtering criteria, just skip */
+       if ((mtype == 0) && (acttype == 0))
+               return (modifiers && modifiers->first);
+               
+       /* sanity checks */
+       if ELEM(NULL, modifiers, modifiers->first)
+               return 0;
+               
+       /* find the first mdifier fitting these criteria */
+       for (fcm= modifiers->first; fcm; fcm= fcm->next) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               short mOk=1, aOk=1; /* by default 1, so that when only one test, won't fail */
+               
+               /* check if applicable ones are fullfilled */
+               if (mtype)
+                       mOk= (fcm->type == mtype);
+               if (acttype > -1)
+                       aOk= (fmi->acttype == acttype);
+                       
+               /* if both are ok, we've found a hit */
+               if (mOk && aOk)
+                       return 1;
+       }
+       
+       /* no matches */
+       return 0;
+}  
+
+/* Evaluation API --------------------------- */
+
+/* evaluate time modifications imposed by some F-Curve Modifiers
+ *     - this step acts as an optimisation to prevent the F-Curve stack being evaluated 
+ *       several times by modifiers requesting the time be modified, as the final result
+ *       would have required using the modified time
+ *     - modifiers only ever recieve the unmodified time, as subsequent modifiers should be
+ *       working on the 'global' result of the modified curve, not some localised segment,
+ *       so nevaltime gets set to whatever the last time-modifying modifier likes...
+ *     - we start from the end of the stack, as only the last one matters for now
+ */
+float evaluate_time_fmodifiers (ListBase *modifiers, FCurve *fcu, float cvalue, float evaltime)
+{
+       FModifier *fcm;
+       float m_evaltime= evaltime;
+       
+       /* sanity checks */
+       if ELEM(NULL, modifiers, modifiers->last)
+               return evaltime;
+               
+       /* find the first modifier from end of stack that modifies time, and calculate the time the modifier
+        * would calculate time at
+        */
+       for (fcm= modifiers->last; fcm; fcm= fcm->prev) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* only evaluate if there's a callback for this */
+               // TODO: implement the 'influence' control feature...
+               if (fmi && fmi->evaluate_modifier_time) {
+                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
+                               m_evaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
+                       break;
+               }
+       }
+       
+       /* return the modified evaltime */
+       return m_evaltime;
+}
+
+/* Evalautes the given set of F-Curve Modifiers using the given data
+ * Should only be called after evaluate_time_fmodifiers() has been called...
+ */
+void evaluate_value_fmodifiers (ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime)
+{
+       FModifier *fcm;
+       
+       /* sanity checks */
+       if ELEM(NULL, modifiers, modifiers->first)
+               return;
+       
+       /* evaluate modifiers */
+       for (fcm= modifiers->first; fcm; fcm= fcm->next) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* only evaluate if there's a callback for this */
+               // TODO: implement the 'influence' control feature...
+               if (fmi && fmi->evaluate_modifier) {
+                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
+                               fmi->evaluate_modifier(fcu, fcm, cvalue, evaltime);
+               }
+       }
+} 
+
+/* ---------- */
+
+/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
+ * by start and end (inclusive).
+ */
+void fcurve_bake_modifiers (FCurve *fcu, int start, int end)
+{
+       ChannelDriver *driver;
+       
+       /* sanity checks */
+       // TODO: make these tests report errors using reports not printf's
+       if ELEM(NULL, fcu, fcu->modifiers.first) {
+               printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
+               return;
+       }
+       
+       /* temporarily, disable driver while we sample, so that they don't influence the outcome */
+       driver= fcu->driver;
+       fcu->driver= NULL;
+       
+       /* bake the modifiers, by sampling the curve at each frame */
+       fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
+       
+       /* free the modifiers now */
+       free_fmodifiers(&fcu->modifiers);
+       
+       /* restore driver */
+       fcu->driver= driver;
+}
index 54618813a0bcd105273a7620f6b90e498451c15e..cf7e486613bc8a5e4cda3150d4b76add3f932605 100644 (file)
@@ -58,6 +58,7 @@
 #include "DNA_key_types.h"
 #include "DNA_material_types.h"
 #include "DNA_mesh_types.h"
+#include "DNA_nla_types.h"
 #include "DNA_object_types.h"
 #include "DNA_object_force.h"
 #include "DNA_particle_types.h"
@@ -85,6 +86,7 @@
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_mesh.h"
+#include "BKE_nla.h"
 #include "BKE_object.h"
 
 
@@ -825,6 +827,10 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname
        char buf[512];
        int dummy_index= 0;
        
+       /* hack: if constname is set, we can only be dealing with an Constraint curve */
+       if (constname)
+               blocktype= ID_CO;
+       
        /* get property name based on blocktype */
        switch (blocktype) {
                case ID_OB: /* object */
@@ -840,7 +846,7 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname
                        break;
                        
                case ID_CO: /* constraint */
-                       propname= constraint_adrcodes_to_paths(adrcode, &dummy_index);
+                       propname= constraint_adrcodes_to_paths(adrcode, &dummy_index);  
                        break;
                        
                case ID_TE: /* texture */
@@ -870,7 +876,10 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname
                        
                /* XXX problematic blocktypes */
                case ID_CU: /* curve */
-                       propname= "speed"; // XXX this was a 'dummy curve' that didn't really correspond to any real var...
+                       /* this used to be a 'dummy' curve which got evaluated on the fly... 
+                        * now we've got real var for this!
+                        */
+                       propname= "eval_time";
                        break;
                        
                case ID_SEQ: /* sequencer strip */
@@ -1144,7 +1153,7 @@ static void icu_to_fcurves (ListBase *groups, ListBase *list, IpoCurve *icu, cha
                        /* Add a new FModifier (Cyclic) instead of setting extend value 
                         * as that's the new equivilant of that option. 
                         */
-                       FModifier *fcm= fcurve_add_modifier(fcu, FMODIFIER_TYPE_CYCLES);
+                       FModifier *fcm= add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES);
                        FMod_Cycles *data= (FMod_Cycles *)fcm->data;
                        
                        /* if 'offset' one is in use, set appropriate settings */
@@ -1463,6 +1472,87 @@ static void action_to_animdata (ID *id, bAction *act)
        action_to_animato(act, &adt->action->groups, &adt->action->curves, &adt->drivers);
 }
 
+/* ------------------------- */
+
+// TODO:
+//     - NLA group duplicators info
+//     - NLA curve/stride modifiers...
+
+/* Convert NLA-Strip to new system */
+static void nlastrips_to_animdata (ID *id, ListBase *strips)
+{
+       AnimData *adt= BKE_animdata_from_id(id);
+       NlaTrack *nlt = NULL;
+       NlaStrip *strip;
+       bActionStrip *as, *asn;
+       
+       /* for each one of the original strips, convert to a new strip and free the old... */
+       for (as= strips->first; as; as= asn) {
+               asn= as->next;
+               
+               /* this old strip is only worth something if it had an action... */
+               if (as->act) {
+                       /* convert Action data (if not yet converted), storing the results in the same Action */
+                       action_to_animato(as->act, &as->act->groups, &as->act->curves, &adt->drivers);
+                       
+                       /* create a new-style NLA-strip which references this Action, then copy over relevant settings */
+                       {
+                               /* init a new strip, and assign the action to it 
+                                *      - no need to muck around with the user-counts, since this is just 
+                                *        passing over the ref to the new owner, not creating an additional ref
+                                */
+                               strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip");
+                               strip->act= as->act;
+                               
+                                       /* endpoints */
+                               strip->start= as->start;
+                               strip->end= as->end;
+                               strip->actstart= as->actstart;
+                               strip->actend= as->actend;
+                               
+                                       /* action reuse */
+                               strip->repeat= as->repeat;
+                               strip->scale= as->scale;
+                               if (as->flag & ACTSTRIP_LOCK_ACTION)    strip->flag |= NLASTRIP_FLAG_SYNC_LENGTH;
+                               
+                                       /* blending */
+                               strip->blendin= as->blendin;
+                               strip->blendout= as->blendout;
+                               strip->blendmode= (as->mode==ACTSTRIPMODE_ADD) ? NLASTRIP_MODE_ADD : NLASTRIP_MODE_REPLACE;
+                               if (as->flag & ACTSTRIP_AUTO_BLENDS)    strip->flag |= NLASTRIP_FLAG_AUTO_BLENDS;
+                                       
+                                       /* assorted setting flags */
+                               if (as->flag & ACTSTRIP_SELECT)                 strip->flag |= NLASTRIP_FLAG_SELECT;
+                               if (as->flag & ACTSTRIP_ACTIVE)                 strip->flag |= NLASTRIP_FLAG_ACTIVE;
+                               
+                               if (as->flag & ACTSTRIP_MUTE)                   strip->flag |= NLASTRIP_FLAG_MUTED;
+                               if (as->flag & ACTSTRIP_REVERSE)                strip->flag |= NLASTRIP_FLAG_REVERSE;
+                               
+                                       /* by default, we now always extrapolate, while in the past this was optional */
+                               if ((as->flag & ACTSTRIP_HOLDLASTFRAME)==0) 
+                                       strip->extendmode= NLASTRIP_EXTEND_NOTHING;
+                       }       
+                       
+                       /* try to add this strip to the current NLA-Track (i.e. the 'last' one on the stack atm) */
+                       if (BKE_nlatrack_add_strip(nlt, strip) == 0) {
+                               /* trying to add to the current failed (no space), 
+                                * so add a new track to the stack, and add to that...
+                                */
+                               nlt= add_nlatrack(adt, NULL);
+                               BKE_nlatrack_add_strip(nlt, strip);
+                       }
+               }
+               
+               /* modifiers */
+               // FIXME: for now, we just free them...
+               if (as->modifiers.first)
+                       BLI_freelistN(&as->modifiers);
+               
+               /* free the old strip */
+               BLI_freelinkN(strips, as);
+       }
+}
+
 /* *************************************************** */
 /* External API - Only Called from do_versions() */
 
@@ -1509,7 +1599,30 @@ void do_versions_ipos_to_animato(Main *main)
                if (G.f & G_DEBUG) printf("\tconverting ob %s \n", id->name+2);
                
                /* check if object has any animation data */
-               if ((ob->ipo) || (ob->action) || (ob->nlastrips.first)) {
+               if (ob->nlastrips.first) {
+                       /* Add AnimData block */
+                       adt= BKE_id_add_animdata(id);
+                       
+                       /* IPO first to take into any non-NLA'd Object Animation */
+                       if (ob->ipo) {
+                               ipo_to_animdata(id, ob->ipo, NULL, NULL);
+                               
+                               ob->ipo->id.us--;
+                               ob->ipo= NULL;
+                       }
+                       
+                       /* Action is skipped since it'll be used by some strip in the NLA anyway, 
+                        * causing errors with evaluation in the new evaluation pipeline
+                        */
+                       if (ob->action) {
+                               ob->action->id.us--;
+                               ob->action= NULL;
+                       }
+                       
+                       /* finally NLA */
+                       nlastrips_to_animdata(id, &ob->nlastrips);
+               }
+               else if ((ob->ipo) || (ob->action)) {
                        /* Add AnimData block */
                        adt= BKE_id_add_animdata(id);
                        
@@ -1530,9 +1643,6 @@ void do_versions_ipos_to_animato(Main *main)
                                ob->ipo->id.us--;
                                ob->ipo= NULL;
                        }
-                       
-                       /* finally NLA */
-                       // XXX todo... for now, new NLA code not hooked up yet, so keep old stuff (but not for too long!)
                }
                
                /* check PoseChannels for constraints with local data */
index dc2bf26759f3b3c32cc0db9f18abdebc4b8f5767..457df9be7a9c5a4c519e49f938a4729789d78af0 100644 (file)
  * 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 <string.h>
+#include <math.h>
+#include <float.h>
 
 #include "MEM_guardedalloc.h"
 
 #include "BLI_blenlib.h"
+#include "BLI_ghash.h"
 
-#include "DNA_space_types.h"
-#include "DNA_nla_types.h"
+#include "DNA_anim_types.h"
 #include "DNA_action_types.h"
-#include "DNA_ID.h"
-#include "DNA_ipo_types.h"
-#include "DNA_object_types.h"
 
-#include "BKE_nla.h"
+#include "BKE_animsys.h"
 #include "BKE_action.h"
+#include "BKE_fcurve.h"
+#include "BKE_nla.h"
 #include "BKE_blender.h"
 #include "BKE_library.h"
-#include "BKE_object.h" /* for convert_action_to_strip(ob) */
+#include "BKE_object.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+#include "nla_private.h"
 
 
 #ifdef HAVE_CONFIG_H
 #include <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 ------------------------------------------- */
+
+/* Remove the given NLA strip from the NLA track it occupies, free the strip's data,
+ * and the strip itself. 
+ */
+void free_nlastrip (ListBase *strips, NlaStrip *strip)
+{
+       NlaStrip *cs, *csn;
+       
+       /* sanity checks */
+       if (strip == NULL)
+               return;
+               
+       /* free child-strips */
+       for (cs= strip->strips.first; cs; cs= csn) {
+               csn= cs->next;
+               free_nlastrip(&strip->strips, cs);
+       }
+               
+       /* 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 own F-Modifiers */
+       free_fmodifiers(&strip->modifiers);
+       
+       /* free the strip itself */
+       if (strips)
+               BLI_freelinkN(strips, strip);
+       else
+               MEM_freeN(strip);
+}
 
-       if (!*src){
-               *dst=NULL;
+/* 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);
+}
 
-       *dst = MEM_dupallocN(sstrip);
+/* 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;
+}
 
-       dstrip = *dst;
-       if (dstrip->act)
-               dstrip->act->id.us++;
+/* Copying ------------------------------------------- */
 
-       if (dstrip->ipo)
-               dstrip->ipo->id.us++;
+/* Copy NLA strip */
+NlaStrip *copy_nlastrip (NlaStrip *strip)
+{
+       NlaStrip *strip_d;
+       NlaStrip *cs, *cs_d;
        
-       if (dstrip->modifiers.first) {
-               BLI_duplicatelist (&dstrip->modifiers, &sstrip->modifiers);
+       /* sanity check */
+       if (strip == NULL)
+               return NULL;
+               
+       /* make a copy */
+       strip_d= MEM_dupallocN(strip);
+       strip_d->next= strip_d->prev= NULL;
+       
+       /* increase user-count of action */
+       if (strip_d->act)
+               strip_d->act->id.us++;
+               
+       /* copy F-Curves and modifiers */
+       copy_fcurves(&strip_d->fcurves, &strip->fcurves);
+       copy_fmodifiers(&strip_d->modifiers, &strip->modifiers);
+       
+       /* make a copy of all the child-strips, one at a time */
+       strip_d->strips.first= strip_d->strips.last= NULL;
+       
+       for (cs= strip->strips.first; cs; cs= cs->next) {
+               cs_d= copy_nlastrip(cs);
+               BLI_addtail(&strip_d->strips, cs_d);
        }
        
+       /* return the strip */
+       return strip_d;
 }
 
-void copy_nlastrips (ListBase *dst, ListBase *src)
+/* Copy NLA Track */
+NlaTrack *copy_nlatrack (NlaTrack *nlt)
 {
-       bActionStrip *strip;
+       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;
+}
 
-       dst->first=dst->last=NULL;
+/* Copy all NLA data */
+void copy_nladata (ListBase *dst, ListBase *src)
+{
+       NlaTrack *nlt, *nlt_d;
+       
+       /* sanity checks */
+       if ELEM(NULL, dst, src)
+               return;
+               
+       /* copy each NLA-track, one at a time */
+       for (nlt= src->first; nlt; nlt= nlt->next) {
+               /* make a copy, and add the copy to the destination list */
+               nlt_d= copy_nlatrack(nlt);
+               BLI_addtail(dst, nlt_d);
+       }
+}
 
-       BLI_duplicatelist (dst, src);
+/* Adding ------------------------------------------- */
 
-       /* Update specific data */
-       if (!dst->first)
-               return;
+/* Add a NLA Track to the given AnimData 
+ *     - prev: NLA-Track to add the new one after
+ */
+NlaTrack *add_nlatrack (AnimData *adt, NlaTrack *prev)
+{
+       NlaTrack *nlt;
+       
+       /* sanity checks */
+       if (adt == NULL)
+               return NULL;
+               
+       /* allocate new track */
+       nlt= MEM_callocN(sizeof(NlaTrack), "NlaTrack");
+       
+       /* set settings requiring the track to not be part of the stack yet */
+       nlt->flag = NLATRACK_SELECTED;
+       nlt->index= BLI_countlist(&adt->nla_tracks);
+       
+       /* add track to stack, and make it the active one */
+       if (prev)
+               BLI_insertlinkafter(&adt->nla_tracks, prev, nlt);
+       else
+               BLI_addtail(&adt->nla_tracks, nlt);
+       BKE_nlatrack_set_active(&adt->nla_tracks, nlt);
+       
+       /* must have unique name, but we need to seed this */
+       sprintf(nlt->name, "NlaTrack");
+       BLI_uniquename(&adt->nla_tracks, nlt, "NlaTrack", '.', offsetof(NlaTrack, name), 64);
+       
+       /* return the new track */
+       return nlt;
+}
+
+/* Add a NLA Strip referencing the given Action */
+NlaStrip *add_nlastrip (bAction *act)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks */
+       if (act == NULL)
+               return NULL;
+               
+       /* allocate new strip */
+       strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip");
+       
+       /* generic settings 
+        *      - selected flag to highlight this to the user
+        *      - auto-blends to ensure that blend in/out values are automatically 
+        *        determined by overlaps of strips
+        *      - (XXX) synchronisation of strip-length in accordance with changes to action-length
+        *        is not done though, since this should only really happens in editmode for strips now
+        *        though this decision is still subject to further review...
+        */
+       strip->flag = NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_AUTO_BLENDS;
+       
+       /* assign the action reference */
+       strip->act= act;
+       id_us_plus(&act->id);
+       
+       /* determine initial range 
+        *      - strip length cannot be 0... ever...
+        */
+       calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
+       
+       strip->start = strip->actstart;
+       strip->end = (IS_EQ(strip->actstart, strip->actend)) ?  (strip->actstart + 1.0f): (strip->actend);
+       
+       /* strip should be referenced as-is */
+       strip->scale= 1.0f;
+       strip->repeat = 1.0f;
+       
+       /* return the new strip */
+       return strip;
+}
 
-       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);
+       }
+       
+       /* automatically name it too */
+       BKE_nlastrip_validate_name(adt, strip);
+       
+       /* returns the strip added */
+       return strip;
+}
+
+/* *************************************************** */
+/* NLA Evaluation <-> Editing Stuff */
+
+/* Strip Mapping ------------------------------------- */
+
+/* non clipped mapping for strip-time <-> global time (for Action-Clips)
+ *     invert = convert action-strip time to global time 
+ */
+static float nlastrip_get_frame_actionclip (NlaStrip *strip, float cframe, short mode)
+{
+       float actlength, repeat, scale;
+       
+       /* get number of repeats */
+       if (IS_EQ(strip->repeat, 0.0f)) strip->repeat = 1.0f;
+       repeat = strip->repeat;
+       
+       /* scaling */
+       if (IS_EQ(strip->scale, 0.0f)) strip->scale= 1.0f;
+       scale = (float)fabs(strip->scale); /* scale must be positive - we've got a special flag for reversing */
+       
+       /* length of referenced action */
+       actlength = strip->actend - strip->actstart;
+       if (IS_EQ(actlength, 0.0f)) actlength = 1.0f;
+       
+       /* reversed = play strip backwards */
+       if (strip->flag & NLASTRIP_FLAG_REVERSE) {
+               // FIXME: this won't work right with Graph Editor?
+               if (mode == NLATIME_CONVERT_MAP) {
+                       return strip->end - scale*(cframe - strip->actstart);
+               }
+               else if (mode == NLATIME_CONVERT_UNMAP) {
+                       int repeatsNum = (int)((cframe - strip->start) / (actlength * scale));
+                       
+                       /* this method doesn't clip the values to lie within the action range only 
+                        *      - the '(repeatsNum * actlength * scale)' compensates for the fmod(...)
+                        *      - the fmod(...) works in the same way as for eval 
+                        */
+                       return strip->actend - (repeatsNum * actlength * scale) 
+                                       - (fmod(cframe - strip->start, actlength*scale) / scale);
+               }
+               else {
+                       if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) {
+                               /* this case prevents the motion snapping back to the first frame at the end of the strip 
+                                * by catching the case where repeats is a whole number, which means that the end of the strip
+                                * could also be interpreted as the end of the start of a repeat
+                                */
+                               return strip->actstart;
+                       }
+                       else {
+                               /* - the 'fmod(..., actlength*scale)' is needed to get the repeats working
+                                * - the '/ scale' is needed to ensure that scaling influences the timing within the repeat
+                                */
+                               return strip->actend - fmod(cframe - strip->start, actlength*scale) / scale; 
+                       }
+               }
+       }
+       else {
+               if (mode == NLATIME_CONVERT_MAP) {
+                       return strip->start + scale*(cframe - strip->actstart);
+               }
+               else if (mode == NLATIME_CONVERT_UNMAP) {
+                       int repeatsNum = (int)((cframe - strip->start) / (actlength * scale));
+                       
+                       /* this method doesn't clip the values to lie within the action range only 
+                        *      - the '(repeatsNum * actlength * scale)' compensates for the fmod(...)
+                        *      - the fmod(...) works in the same way as for eval 
+                        */
+                       return strip->actstart + (repeatsNum * actlength * scale) 
+                                       + (fmod(cframe - strip->start, actlength*scale) / scale);
+               }
+               else /* if (mode == NLATIME_CONVERT_EVAL) */{
+                       if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) {
+                               /* this case prevents the motion snapping back to the first frame at the end of the strip 
+                                * by catching the case where repeats is a whole number, which means that the end of the strip
+                                * could also be interpreted as the end of the start of a repeat
+                                */
+                               return strip->actend;
+                       }
+                       else {
+                               /* - the 'fmod(..., actlength*scale)' is needed to get the repeats working
+                                * - the '/ scale' is needed to ensure that scaling influences the timing within the repeat
+                                */
+                               return strip->actstart + fmod(cframe - strip->start, actlength*scale) / scale; 
+                       }
                }
        }
 }
 
-/* from editnla, for convert_action_to_strip -- no UI code so should be ok here.. */
-void find_stridechannel(Object *ob, bActionStrip *strip)
+/* non clipped mapping for strip-time <-> global time (for Transitions)
+ *     invert = convert action-strip time to global time 
+ */
+static float nlastrip_get_frame_transition (NlaStrip *strip, float cframe, short mode)
 {
-       if(ob && ob->pose) {
-               bPoseChannel *pchan;
-               for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
-                       if(pchan->flag & POSE_STRIDE)
-                               break;
-               if(pchan)
-                       BLI_strncpy(strip->stridechannel, pchan->name, 32);
+       float length;
+       
+       /* length of strip */
+       length= strip->end - strip->start;
+       
+       /* reversed = play strip backwards */
+       if (strip->flag & NLASTRIP_FLAG_REVERSE) {
+               if (mode == NLATIME_CONVERT_MAP)
+                       return strip->end - (length * cframe);
                else
-                       strip->stridechannel[0]= 0;
+                       return (strip->end - cframe) / length;
+       }
+       else {
+               if (mode == NLATIME_CONVERT_MAP)
+                       return (length * cframe) + strip->start;
+               else
+                       return (cframe - strip->start) / length;
        }
 }
 
-//called by convert_nla / bpy api with an object with the action to be converted to a new strip
-bActionStrip *convert_action_to_strip (Object *ob)
+/* non clipped mapping for strip-time <-> global time
+ *     mode = eNlaTime_ConvertModes[] -> NLATIME_CONVERT_*
+ *
+ * only secure for 'internal' (i.e. within AnimSys evaluation) operations,
+ * but should not be directly relied on for stuff which interacts with editors
+ */
+float nlastrip_get_frame (NlaStrip *strip, float cframe, short mode)
 {
-       bActionStrip *nstrip;
+       switch (strip->type) {
+               case NLASTRIP_TYPE_META: /* meta - for now, does the same as transition (is really just an empty container) */
+               case NLASTRIP_TYPE_TRANSITION: /* transition */
+                       return nlastrip_get_frame_transition(strip, cframe, mode);
+               
+               case NLASTRIP_TYPE_CLIP: /* action-clip (default) */
+               default:
+                       return nlastrip_get_frame_actionclip(strip, cframe, mode);
+       }       
+}
 
-       /* 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;
+
+/* Non clipped mapping for strip-time <-> global time
+ *     mode = eNlaTime_ConvertModesp[] -> NLATIME_CONVERT_*
+ *
+ * Public API method - perform this mapping using the given AnimData block
+ * and perform any necessary sanity checks on the value
+ */
+float BKE_nla_tweakedit_remap (AnimData *adt, float cframe, short mode)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks 
+        *      - obviously we've got to have some starting data
+        *      - when not in tweakmode, the active Action does not have any scaling applied :)
+        *      - when in tweakmode, if the no-mapping flag is set, do not map
+        */
+       if ((adt == NULL) || (adt->flag & ADT_NLA_EDIT_ON)==0 || (adt->flag & ADT_NLA_EDIT_NOMAP))
+               return cframe;
+               
+       /* if the active-strip info has been stored already, access this, otherwise look this up
+        * and store for (very probable) future usage
+        */
+       if (adt->actstrip == NULL) {
+               NlaTrack *nlt= BKE_nlatrack_find_active(&adt->nla_tracks);
+               adt->actstrip= BKE_nlastrip_find_active(nlt);
+       }
+       strip= adt->actstrip;
+       
+       /* sanity checks 
+        *      - in rare cases, we may not be able to find this strip for some reason (internal error)
+        *      - for now, if the user has defined a curve to control the time, this correction cannot be performed
+        *        reliably...
+        */
+       if ((strip == NULL) || (strip->flag & NLASTRIP_FLAG_USR_TIME))
+               return cframe;
+               
+       /* perform the correction now... */
+       return nlastrip_get_frame(strip, cframe, mode);
+}
+
+/* *************************************************** */
+/* NLA API */
+
+/* List of Strips ------------------------------------ */
+/* (these functions are used for NLA-Tracks and also for nested/meta-strips) */
+
+/* Check if there is any space in the given list to add the given strip */
+short BKE_nlastrips_has_space (ListBase *strips, float start, float end)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks */
+       if ((strips == NULL) || IS_EQ(start, end))
+               return 0;
+       if (start > end) {
+               puts("BKE_nlastrips_has_space() error... start and end arguments swapped");
+               SWAP(float, start, end);
+       }
+       
+       /* loop over NLA strips checking for any overlaps with this area... */
+       for (strip= strips->first; strip; strip= strip->next) {
+               /* if start frame of strip is past the target end-frame, that means that
+                * we've gone past the window we need to check for, so things are fine
+                */
+               if (strip->start > end)
+                       return 1;
+               
+               /* if the end of the strip is greater than either of the boundaries, the range
+                * must fall within the extents of the strip
+                */
+               if ((strip->end > start) || (strip->end > end))
+                       return 0;
+       }
+       
+       /* if we are still here, we haven't encountered any overlapping strips */
+       return 1;
+}
+
+/* Rearrange the strips in the track so that they are always in order 
+ * (usually only needed after a strip has been moved) 
+ */
+void BKE_nlastrips_sort_strips (ListBase *strips)
+{
+       ListBase tmp = {NULL, NULL};
+       NlaStrip *strip, *sstrip, *stripn;
+       
+       /* sanity checks */
+       if ELEM(NULL, strips, strips->first)
+               return;
+       
+       /* we simply perform insertion sort on this list, since it is assumed that per track,
+        * there are only likely to be at most 5-10 strips
+        */
+       for (strip= strips->first; strip; strip= stripn) {
+               short not_added = 1;
+               
+               stripn= strip->next;
+               
+               /* remove this strip from the list, and add it to the new list, searching from the end of 
+                * the list, assuming that the lists are in order 
+                */
+               BLI_remlink(strips, strip);
+               
+               for (sstrip= tmp.last; sstrip; sstrip= sstrip->prev) {
+                       /* check if add after */
+                       if (sstrip->end <= strip->start) {
+                               BLI_insertlinkafter(&tmp, sstrip, strip);
+                               not_added= 0;
+                               break;
+                       }
+               }
+               
+               /* add before first? */
+               if (not_added)
+                       BLI_addhead(&tmp, strip);
+       }
+       
+       /* reassign the start and end points of the strips */
+       strips->first= tmp.first;
+       strips->last= tmp.last;
+}
+
+/* Add the given NLA-Strip to the given list of strips, assuming that it 
+ * isn't currently a member of another list
+ */
+short BKE_nlastrips_add_strip (ListBase *strips, NlaStrip *strip)
+{
+       NlaStrip *ns;
+       short not_added = 1;
+       
+       /* sanity checks */
+       if ELEM(NULL, strips, strip)
+               return 0;
+               
+       /* check if any space to add */
+       if (BKE_nlastrips_has_space(strips, strip->start, strip->end)==0)
+               return 0;
+       
+       /* find the right place to add the strip to the nominated track */
+       for (ns= strips->first; ns; ns= ns->next) {
+               /* if current strip occurs after the new strip, add it before */
+               if (ns->start > strip->end) {
+                       BLI_insertlinkbefore(strips, ns, strip);
+                       not_added= 0;
+                       break;
+               }
+       }
+       if (not_added) {
+               /* just add to the end of the list of the strips then... */
+               BLI_addtail(strips, strip);
+       }
+       
+       /* added... */
+       return 1;
+}
+
+
+/* Meta-Strips ------------------------------------ */
+
+/* Convert 'islands' (i.e. continuous string of) selected strips to be
+ * contained within 'Meta-Strips' which act as strips which contain strips.
+ *     temp: are the meta-strips to be created 'temporary' ones used for transforms?
+ */
+void BKE_nlastrips_make_metas (ListBase *strips, short temp)
+{
+       NlaStrip *mstrip = NULL;
+       NlaStrip *strip, *stripn;
+       
+       /* sanity checks */
+       if ELEM(NULL, strips, strips->first)
+               return;
+       
+       /* group all continuous chains of selected strips into meta-strips */
+       for (strip= strips->first; strip; strip= stripn) {
+               stripn= strip->next;
+               
+               if (strip->flag & NLASTRIP_FLAG_SELECT) {
+                       /* if there is an existing meta-strip, add this strip to it, otherwise, create a new one */
+                       if (mstrip == NULL) {
+                               /* add a new meta-strip, and add it before the current strip that it will replace... */
+                               mstrip= MEM_callocN(sizeof(NlaStrip), "Meta-NlaStrip");
+                               mstrip->type = NLASTRIP_TYPE_META;
+                               BLI_insertlinkbefore(strips, strip, mstrip);
+                               
+                               /* set flags */
+                               mstrip->flag = NLASTRIP_FLAG_SELECT;
+                               
+                               /* set temp flag if appropriate (i.e. for transform-type editing) */
+                               if (temp)
+                                       mstrip->flag |= NLASTRIP_FLAG_TEMP_META;
+                                       
+                               /* set default repeat/scale values to prevent warnings */
+                               mstrip->repeat= mstrip->scale= 1.0f;
+                               
+                               /* make its start frame be set to the start frame of the current strip */
+                               mstrip->start= strip->start;
+                       }
                        
-       find_stridechannel(ob, nstrip);
-       //set_active_strip(ob, nstrip); /* is in editnla as does UI calls */
+                       /* remove the selected strips from the track, and add to the meta */
+                       BLI_remlink(strips, strip);
+                       BLI_addtail(&mstrip->strips, strip);
                        
-       nstrip->repeat = 1.0;
+                       /* expand the meta's dimensions to include the newly added strip- i.e. its last frame */
+                       mstrip->end= strip->end;
+               }
+               else {
+                       /* current strip wasn't selected, so the end of 'island' of selected strips has been reached,
+                        * so stop adding strips to the current meta
+                        */
+                       mstrip= NULL;
+               }
+       }
+}
 
-       if(ob->nlastrips.first == NULL)
-               ob->nlaflag |= OB_NLA_OVERRIDE;
+/* Split a meta-strip into a set of normal strips */
+void BKE_nlastrips_clear_metastrip (ListBase *strips, NlaStrip *strip)
+{
+       NlaStrip *cs, *csn;
        
-       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, strips, strip)
+               return;
        
+       /* move each one of the meta-strip's children before the meta-strip
+        * in the list of strips after unlinking them from the meta-strip
+        */
+       for (cs= strip->strips.first; cs; cs= csn) {
+               csn= cs->next;
+               BLI_remlink(&strip->strips, cs);
+               BLI_insertlinkbefore(strips, strip, cs);
+       }
+       
+       /* free the meta-strip now */
+       BLI_freelinkN(strips, strip);
 }
 
+/* Remove meta-strips (i.e. flatten the list of strips) from the top-level of the list of strips
+ *     sel: only consider selected meta-strips, otherwise all meta-strips are removed
+ *     onlyTemp: only remove the 'temporary' meta-strips used for transforms
+ */
+void BKE_nlastrips_clear_metas (ListBase *strips, short onlySel, short onlyTemp)
+{
+       NlaStrip *strip, *stripn;
+       
+       /* sanity checks */
+       if ELEM(NULL, strips, strips->first)
+               return;
+       
+       /* remove meta-strips fitting the criteria of the arguments */
+       for (strip= strips->first; strip; strip= stripn) {
+               stripn= strip->next;
+               
+               /* check if strip is a meta-strip */
+               if (strip->type == NLASTRIP_TYPE_META) {
+                       /* if check if selection and 'temporary-only' considerations are met */
+                       if ((onlySel==0) || (strip->flag & NLASTRIP_FLAG_SELECT)) {
+                               if ((!onlyTemp) || (strip->flag & NLASTRIP_FLAG_TEMP_META)) {
+                                       BKE_nlastrips_clear_metastrip(strips, strip);
+                               }
+                       }
+               }
+       }
+}
 
-/* not strip itself! */
-void free_actionstrip(bActionStrip* strip)
+/* Add the given NLA-Strip to the given Meta-Strip, assuming that the
+ * strip isn't attached to anyy list of strips 
+ */
+short BKE_nlameta_add_strip (NlaStrip *mstrip, NlaStrip *strip)
 {
-       if (!strip)
+       /* sanity checks */
+       if ELEM(NULL, mstrip, strip)
+               return 0;
+               
+       /* firstly, check if the meta-strip has space for this */
+       if (BKE_nlastrips_has_space(&mstrip->strips, strip->start, strip->end) == 0)
+               return 0;
+               
+       /* check if this would need to be added to the ends of the meta,
+        * and subsequently, if the neighbouring strips allow us enough room
+        */
+       if (strip->start < mstrip->start) {
+               /* check if strip to the left (if it exists) ends before the 
+                * start of the strip we're trying to add 
+                */
+               if ((mstrip->prev == NULL) || (mstrip->prev->end <= strip->start)) {
+                       /* add strip to start of meta's list, and expand dimensions */
+                       BLI_addhead(&mstrip->strips, strip);
+                       mstrip->start= strip->start;
+                       
+                       return 1;
+               }
+               else /* failed... no room before */
+                       return 0;
+       }
+       else if (strip->end > mstrip->end) {
+               /* check if strip to the right (if it exists) starts before the 
+                * end of the strip we're trying to add 
+                */
+               if ((mstrip->next == NULL) || (mstrip->next->start >= strip->end)) {
+                       /* add strip to end of meta's list, and expand dimensions */
+                       BLI_addtail(&mstrip->strips, strip);
+                       mstrip->end= strip->end;
+                       
+                       return 1;
+               }
+               else /* failed... no room after */
+                       return 0;
+       }
+       else {
+               /* just try to add to the meta-strip (no dimension changes needed) */
+               return BKE_nlastrips_add_strip(&mstrip->strips, strip);
+       }
+}
+
+/* Adjust the settings of NLA-Strips contained within a Meta-Strip (recursively), 
+ * until the Meta-Strips children all fit within the Meta-Strip's new dimensions
+ */
+void BKE_nlameta_flush_transforms (NlaStrip *mstrip) 
+{
+       NlaStrip *strip;
+       float oStart, oEnd, offset;
+       float oLen, nLen;
+       short scaleChanged= 0;
+       
+       /* sanity checks 
+        *      - strip must exist
+        *      - strip must be a meta-strip with some contents
+        */
+       if ELEM(NULL, mstrip, mstrip->strips.first)
+               return;
+       if (mstrip->type != NLASTRIP_TYPE_META)
                return;
+               
+       /* get the original start/end points, and calculate the start-frame offset
+        *      - these are simply the start/end frames of the child strips, 
+        *        since we assume they weren't transformed yet
+        */
+       oStart= ((NlaStrip *)mstrip->strips.first)->start;
+       oEnd= ((NlaStrip *)mstrip->strips.last)->end;
+       offset= mstrip->start - oStart;
+       
+       /* optimisation:
+        * don't flush if nothing changed yet
+        *      TODO: maybe we need a flag to say always flush?
+        */
+       if (IS_EQ(oStart, mstrip->start) && IS_EQ(oEnd, mstrip->end))
+               return;
+       
+       /* check if scale changed */
+       oLen = oEnd - oStart;
+       nLen = mstrip->end - mstrip->start;
+       if (IS_EQ(nLen, oLen) == 0)
+               scaleChanged= 1;
+       
+       /* for each child-strip, calculate new start/end points based on this new info */
+       for (strip= mstrip->strips.first; strip; strip= strip->next) {
+               if (scaleChanged) {
+                       PointerRNA ptr;
+                       float p1, p2, nStart, nEnd;
+                       
+                       /* compute positions of endpoints relative to old extents of strip */
+                       p1= (strip->start - oStart) / oLen;
+                       p2= (strip->end - oStart) / oLen;
+                       
+                       /* compute the new strip endpoints using the proportions */
+                       nStart= (p1 * nLen) + mstrip->start;
+                       nEnd= (p2 * nLen) + mstrip->start;
+                       
+                       /* firstly, apply the new positions manually, then apply using RNA 
+                        *      - first time is to make sure no truncation errors from one endpoint not being 
+                        *        set yet occur
+                        *      - second time is to make sure scale is computed properly...
+                        */
+                       strip->start= nStart;
+                       strip->end= nEnd;
+                       
+                       RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &ptr);
+                       RNA_float_set(&ptr, "start_frame", nStart);
+                       RNA_float_set(&ptr, "end_frame", nEnd);
+               }
+               else {
+                       /* just apply the changes in offset to both ends of the strip */
+                       strip->start += offset;
+                       strip->end += offset;
+               }
+               
+               /* finally, make sure the strip's children (if it is a meta-itself), get updated */
+               BKE_nlameta_flush_transforms(strip);
+       }
+}
 
-       if (strip->act){
-               strip->act->id.us--;
-               strip->act = NULL;
+/* NLA-Tracks ---------------------------------------- */
+
+/* Find the active NLA-track for the given stack */
+NlaTrack *BKE_nlatrack_find_active (ListBase *tracks)
+{
+       NlaTrack *nlt;
+       
+       /* sanity check */
+       if ELEM(NULL, tracks, tracks->first)
+               return NULL;
+               
+       /* try to find the first active track */
+       for (nlt= tracks->first; nlt; nlt= nlt->next) {
+               if (nlt->flag & NLATRACK_ACTIVE)
+                       return nlt;
        }
-       if (strip->ipo){
-               strip->ipo->id.us--;
-               strip->ipo = NULL;
+       
+       /* none found */
+       return NULL;
+}
+
+/* Toggle the 'solo' setting for the given NLA-track, making sure that it is the only one
+ * that has this status in its AnimData block.
+ */
+void BKE_nlatrack_solo_toggle (AnimData *adt, NlaTrack *nlt)
+{
+       NlaTrack *nt;
+       
+       /* sanity check */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return;
+               
+       /* firstly, make sure 'solo' flag for all tracks is disabled */
+       for (nt= adt->nla_tracks.first; nt; nt= nt->next) {
+               if (nt != nlt)
+                       nt->flag &= ~NLATRACK_SOLO;
        }
-       if (strip->modifiers.first) {
-               BLI_freelistN(&strip->modifiers);
+               
+       /* now, enable 'solo' for the given track if appropriate */
+       if (nlt) {
+               /* toggle solo status */
+               nlt->flag ^= NLATRACK_SOLO;
+               
+               /* set or clear solo-status on AnimData */
+               if (nlt->flag & NLATRACK_SOLO)
+                       adt->flag |= ADT_NLA_SOLO_TRACK;
+               else
+                       adt->flag &= ~ADT_NLA_SOLO_TRACK;
        }
+       else
+               adt->flag &= ~ADT_NLA_SOLO_TRACK;
+}
+
+/* Make the given NLA-track the active one for the given stack. If no track is provided, 
+ * this function can be used to simply deactivate all the NLA tracks in the given stack too.
+ */
+void BKE_nlatrack_set_active (ListBase *tracks, NlaTrack *nlt_a)
+{
+       NlaTrack *nlt;
        
+       /* sanity check */
+       if ELEM(NULL, tracks, tracks->first)
+               return;
+       
+       /* deactive all the rest */
+       for (nlt= tracks->first; nlt; nlt= nlt->next) 
+               nlt->flag &= ~NLATRACK_ACTIVE;
+               
+       /* set the given one as the active one */
+       if (nlt_a)
+               nlt_a->flag |= NLATRACK_ACTIVE;
 }
 
-void free_nlastrips (ListBase *nlalist)
+/* Check if there is any space in the given track to add a strip of the given length */
+short BKE_nlatrack_has_space (NlaTrack *nlt, float start, float end)
 {
-       bActionStrip *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);
+       }
+       
+       /* check if there's any space left in the track for a strip of the given length */
+       return BKE_nlastrips_has_space(&nlt->strips, start, end);
+}
 
-       if (!nlalist->first)
+/* Rearrange the strips in the track so that they are always in order 
+ * (usually only needed after a strip has been moved) 
+ */
+void BKE_nlatrack_sort_strips (NlaTrack *nlt)
+{
+       /* sanity checks */
+       if ELEM(NULL, nlt, nlt->strips.first)
                return;
+       
+       /* sort the strips with a more generic function */
+       BKE_nlastrips_sort_strips(&nlt->strips);
+}
 
-       /* Do any specific freeing */
-       for (strip=nlalist->first; strip; strip=strip->next)
+/* Add the given NLA-Strip to the given NLA-Track, assuming that it 
+ * isn't currently attached to another one 
+ */
+short BKE_nlatrack_add_strip (NlaTrack *nlt, NlaStrip *strip)
+{
+       /* sanity checks */
+       if ELEM(NULL, nlt, strip)
+               return 0;
+               
+       /* try to add the strip to the track using a more generic function */
+       return BKE_nlastrips_add_strip(&nlt->strips, strip);
+}
+
+/* NLA Strips -------------------------------------- */
+
+/* Find the active NLA-strip within the given track */
+NlaStrip *BKE_nlastrip_find_active (NlaTrack *nlt)
+{
+       NlaStrip *strip;
+       
+       /* sanity check */
+       if ELEM(NULL, nlt, nlt->strips.first)
+               return NULL;
+               
+       /* try to find the first active strip */
+       for (strip= nlt->strips.first; strip; strip= strip->next) {
+               if (strip->flag & NLASTRIP_FLAG_ACTIVE)
+                       return strip;
+       }
+       
+       /* none found */
+       return NULL;
+}
+
+/* Does the given NLA-strip fall within the given bounds (times)? */
+short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max)
+{
+       const float stripLen= (strip) ? strip->end - strip->start : 0.0f;
+       const float boundsLen= (float)fabs(max - min);
+       
+       /* sanity checks */
+       if ((strip == NULL) || IS_EQ(stripLen, 0.0f) || IS_EQ(boundsLen, 0.0f))
+               return 0;
+       
+       /* only ok if at least part of the strip is within the bounding window
+        *      - first 2 cases cover when the strip length is less than the bounding area
+        *      - second 2 cases cover when the strip length is greater than the bounding area
+        */
+       if ( (stripLen < boundsLen) && 
+                !(IN_RANGE(strip->start, min, max) ||
+                  IN_RANGE(strip->end, min, max)) )
+       {
+               return 0;
+       }
+       if ( (stripLen > boundsLen) && 
+                !(IN_RANGE(min, strip->start, strip->end) ||
+                  IN_RANGE(max, strip->start, strip->end)) )
        {
-               free_actionstrip (strip);
-       };
+               return 0;
+       }
+       
+       /* should be ok! */
+       return 1;
+}
 
-       /* Free the whole list */
-       BLI_freelistN(nlalist);
+/* Is the given NLA-strip the first one to occur for the given AnimData block */
+// TODO: make this an api method if necesary, but need to add prefix first
+short nlastrip_is_first (AnimData *adt, NlaStrip *strip)
+{
+       NlaTrack *nlt;
+       NlaStrip *ns;
+       
+       /* sanity checks */
+       if ELEM(NULL, adt, strip)
+               return 0;
+               
+       /* check if strip has any strips before it */
+       if (strip->prev)
+               return 0;
+               
+       /* check other tracks to see if they have a strip that's earlier */
+       // TODO: or should we check that the strip's track is also the first?
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               /* only check the first strip, assuming that they're all in order */
+               ns= nlt->strips.first;
+               if (ns) {
+                       if (ns->start < strip->start)
+                               return 0;
+               }
+       }       
+       
+       /* should be first now */
+       return 1;
 }
+
+/* Animated Strips ------------------------------------------- */
+
+/* Check if the given NLA-Track has any strips with own F-Curves */
+short BKE_nlatrack_has_animated_strips (NlaTrack *nlt)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks */
+       if ELEM(NULL, nlt, nlt->strips.first)
+               return 0;
+               
+       /* check each strip for F-Curves only (don't care about whether the flags are set) */
+       for (strip= nlt->strips.first; strip; strip= strip->next) {
+               if (strip->fcurves.first)
+                       return 1;
+       }
+       
+       /* none found */
+       return 0;
+}
+
+/* Check if given NLA-Tracks have any strips with own F-Curves */
+short BKE_nlatracks_have_animated_strips (ListBase *tracks)
+{
+       NlaTrack *nlt;
+       
+       /* sanity checks */
+       if ELEM(NULL, tracks, tracks->first)
+               return 0;
+               
+       /* check each track, stopping on the first hit */
+       for (nlt= tracks->first; nlt; nlt= nlt->next) {
+               if (BKE_nlatrack_has_animated_strips(nlt))
+                       return 1;
+       }
+       
+       /* none found */
+       return 0;
+}
+
+/* Validate the NLA-Strips 'control' F-Curves based on the flags set*/
+void BKE_nlastrip_validate_fcurves (NlaStrip *strip) 
+{
+       FCurve *fcu;
+       
+       /* sanity checks */
+       if (strip == NULL)
+               return;
+       
+       /* if controlling influence... */
+       if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
+               /* try to get F-Curve */
+               fcu= list_find_fcurve(&strip->fcurves, "influence", 0);
+               
+               /* add one if not found */
+               if (fcu == NULL) {
+                       /* make new F-Curve */
+                       fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve");
+                       BLI_addtail(&strip->fcurves, fcu);
+                       
+                       /* set default flags */
+                       fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+                       
+                       /* store path - make copy, and store that */
+                       fcu->rna_path= BLI_strdupn("influence", 9);
+                       
+                       // TODO: insert a few keyframes to ensure default behaviour?
+               }
+       }
+       
+       /* if controlling time... */
+       if (strip->flag & NLASTRIP_FLAG_USR_TIME) {
+               /* try to get F-Curve */
+               fcu= list_find_fcurve(&strip->fcurves, "strip_time", 0);
+               
+               /* add one if not found */
+               if (fcu == NULL) {
+                       /* make new F-Curve */
+                       fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve");
+                       BLI_addtail(&strip->fcurves, fcu);
+                       
+                       /* set default flags */
+                       fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+                       
+                       /* store path - make copy, and store that */
+                       fcu->rna_path= BLI_strdupn("strip_time", 10);
+                       
+                       // TODO: insert a few keyframes to ensure default behaviour?
+               }
+       }
+}
+
+/* Sanity Validation ------------------------------------ */
+
+/* Find (and set) a unique name for a strip from the whole AnimData block 
+ * Uses a similar method to the BLI method, but is implemented differently
+ * as we need to ensure that the name is unique over several lists of tracks,
+ * not just a single track.
+ */
+void BKE_nlastrip_validate_name (AnimData *adt, NlaStrip *strip)
+{
+       GHash *gh;
+       NlaStrip *tstrip;
+       NlaTrack *nlt;
+       
+       /* sanity checks */
+       if ELEM(NULL, adt, strip)
+               return;
+               
+       /* give strip a default name if none already */
+       if (strip->name[0]==0) {
+               switch (strip->type) {
+                       case NLASTRIP_TYPE_CLIP: /* act-clip */
+                               sprintf(strip->name, "Act: %s", (strip->act)?(strip->act->id.name+2):("<None>"));
+                               break;
+                       case NLASTRIP_TYPE_TRANSITION: /* transition */
+                               sprintf(strip->name, "Transition");
+                               break;
+                       case NLASTRIP_TYPE_META: /* meta */
+                               sprintf(strip->name, "Meta");
+                               break;
+                       default:
+                               sprintf(strip->name, "NLA Strip");
+                               break;
+               }
+       }
+       
+       /* build a hash-table of all the strips in the tracks 
+        *      - this is easier than iterating over all the tracks+strips hierarchy everytime
+        *        (and probably faster)
+        */
+       gh= BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp);
+       
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (tstrip= nlt->strips.first; tstrip; tstrip= tstrip->next) {
+                       /* don't add the strip of interest */
+                       if (tstrip == strip) 
+                               continue;
+                       
+                       /* use the name of the strip as the key, and the strip as the value, since we're mostly interested in the keys */
+                       BLI_ghash_insert(gh, tstrip->name, tstrip);
+               }
+       }
+       
+       /* if the hash-table has a match for this name, try other names... 
+        *      - in an extreme case, it might not be able to find a name, but then everything else in Blender would fail too :)
+        */
+       if (BLI_ghash_haskey(gh, strip->name)) {
+               char tempname[128];
+               int     number = 1;
+               char *dot;
+               
+               /* Strip off the suffix */
+               dot = strchr(strip->name, '.');
+               if (dot) *dot=0;
+               
+               /* Try different possibilities */
+               for (number = 1; number <= 999; number++) {
+                       /* assemble alternative name */
+                       BLI_snprintf(tempname, 128, "%s%c%03d", strip->name, ".", number);
+                       
+                       /* if hash doesn't have this, set it */
+                       if (BLI_ghash_haskey(gh, tempname) == 0) {
+                               BLI_strncpy(strip->name, tempname, sizeof(strip->name));
+                               break;
+                       }
+               }
+       }
+       
+       /* free the hash... */
+       BLI_ghash_free(gh, NULL, NULL);
+}
+
+/* ---- */
+
+/* Determine auto-blending for the given strip */
+void BKE_nlastrip_validate_autoblends (NlaTrack *nlt, NlaStrip *nls)
+{
+       NlaTrack *track;
+       NlaStrip *strip;
+       //float *ps=NULL, *pe=NULL;
+       //float *ns=NULL, *ne=NULL;
+       
+       /* sanity checks */
+       if ELEM(NULL, nls, nlt)
+               return;
+       if ((nlt->prev == NULL) && (nlt->next == NULL))
+               return;
+       if ((nls->flag & NLASTRIP_FLAG_AUTO_BLENDS)==0)
+               return;
+       
+       /* get test ranges */
+       if (nlt->prev) {
+               /* find strips that overlap over the start/end of the given strip,
+                * but which don't cover the entire length 
+                */
+               track= nlt->prev;
+               for (strip= track->strips.first; strip; strip= strip->next) {
+                       
+               }
+       }
+       if (nlt->next) {
+               /* find strips that overlap over the start/end of the given strip,
+                * but which don't cover the entire length 
+                */
+               track= nlt->next;
+               for (strip= track->strips.first; strip; strip= strip->next) {
+                       
+               }
+       }
+}
+
+/* Ensure that auto-blending and other settings are set correctly */
+void BKE_nla_validate_state (AnimData *adt)
+{
+       NlaStrip *strip;
+       NlaTrack *nlt;
+       
+       /* sanity checks */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return;
+               
+       /* adjust blending values for auto-blending */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       BKE_nlastrip_validate_autoblends(nlt, strip);
+               }
+       }
+}
+
+/* Core Tools ------------------------------------------- */
+
+/* For the given AnimData block, add the active action to the NLA
+ * stack (i.e. 'push-down' action). The UI should only allow this 
+ * for normal editing only (i.e. not in editmode for some strip's action),
+ * so no checks for this are performed.
+ */
+// TODO: maybe we should have checks for this too...
+void BKE_nla_action_pushdown (AnimData *adt)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks */
+       // TODO: need to report the error for this
+       if ELEM(NULL, adt, adt->action) 
+               return;
+               
+       /* if the action is empty, we also shouldn't try to add to stack, 
+        * as that will cause us grief down the track
+        */
+       // TODO: what about modifiers?
+       if (action_has_motion(adt->action) == 0) {
+               printf("BKE_nla_action_pushdown(): action has no data \n");
+               return;
+       }
+       
+       /* add a new NLA strip to the track, which references the active action */
+       strip= add_nlastrip_to_stack(adt, adt->action);
+       
+       /* do other necessary work on strip */  
+       if (strip) {
+               /* clear reference to action now that we've pushed it onto the stack */
+               adt->action->id.us--;
+               adt->action= NULL;
+               
+               /* if the strip is the first one in the track it lives in, check if there
+                * are strips in any other tracks that may be before this, and set the extend
+                * mode accordingly
+                */
+               if (nlastrip_is_first(adt, strip) == 0) {
+                       /* not first, so extend mode can only be NLASTRIP_EXTEND_HOLD_FORWARD not NLASTRIP_EXTEND_HOLD,
+                        * so that it doesn't override strips in previous tracks
+                        */
+                       // FIXME: this needs to be more automated, since user can rearrange strips
+                       strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD;
+               }
+       }
+}
+
+
+/* Find the active strip + track combo, and set them up as the tweaking track,
+ * and return if successful or not.
+ */
+short BKE_nla_tweakmode_enter (AnimData *adt)
+{
+       NlaTrack *nlt, *activeTrack=NULL;
+       NlaStrip *strip, *activeStrip=NULL;
+       
+       /* verify that data is valid */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return 0;
+               
+       /* if block is already in tweakmode, just leave, but we should report 
+        * that this block is in tweakmode (as our returncode)
+        */
+       if (adt->flag & ADT_NLA_EDIT_ON)
+               return 1;
+               
+       /* go over the tracks, finding the active one, and its active strip
+        *      - if we cannot find both, then there's nothing to do
+        */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               /* check if active */
+               if (nlt->flag & NLATRACK_ACTIVE) {
+                       /* store reference to this active track */
+                       activeTrack= nlt;
+                       
+                       /* now try to find active strip */
+                       activeStrip= BKE_nlastrip_find_active(nlt);
+                       break;
+               }       
+       }
+       if ELEM3(NULL, activeTrack, activeStrip, activeStrip->act) {
+               printf("NLA tweakmode enter - neither active requirement found \n");
+               return 0;
+       }
+               
+       /* go over all the tracks up to the active one, tagging each strip that uses the same 
+        * action as the active strip, but leaving everything else alone
+        */
+       for (nlt= activeTrack->prev; nlt; nlt= nlt->prev) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       if (strip->act == activeStrip->act)
+                               strip->flag |= NLASTRIP_FLAG_TWEAKUSER;
+                       else
+                               strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER;
+               }
+       }
+       
+       
+       /* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled 
+        *      - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on
+        */
+       for (nlt= activeTrack; nlt; nlt= nlt->next)
+               nlt->flag |= NLATRACK_DISABLED;
+       
+       /* handle AnimData level changes:
+        *      - 'real' active action to temp storage (no need to change user-counts)
+        *      - action of active strip set to be the 'active action', and have its usercount incremented
+        *      - editing-flag for this AnimData block should also get turned on (for more efficient restoring)
+        *      - take note of the active strip for mapping-correction of keyframes in the action being edited
+        */
+       adt->tmpact= adt->action;
+       adt->action= activeStrip->act;
+       adt->actstrip= activeStrip;
+       id_us_plus(&activeStrip->act->id);
+       adt->flag |= ADT_NLA_EDIT_ON;
+       
+       /* done! */
+       return 1;
+}
+
+/* Exit tweakmode for this AnimData block */
+void BKE_nla_tweakmode_exit (AnimData *adt)
+{
+       NlaStrip *strip;
+       NlaTrack *nlt;
+       
+       /* verify that data is valid */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return;
+               
+       /* hopefully the flag is correct - skip if not on */
+       if ((adt->flag & ADT_NLA_EDIT_ON) == 0)
+               return;
+               
+       // TODO: need to sync the user-strip with the new state of the action!
+               
+       /* for all Tracks, clear the 'disabled' flag
+        * for all Strips, clear the 'tweak-user' flag
+        */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               nlt->flag &= ~NLATRACK_DISABLED;
+               
+               for (strip= nlt->strips.first; strip; strip= strip->next) 
+                       strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER;
+       }
+       
+       /* handle AnimData level changes:
+        *      - 'temporary' active action needs its usercount decreased, since we're removing this reference
+        *      - 'real' active action is restored from storage
+        *      - storage pointer gets cleared (to avoid having bad notes hanging around)
+        *      - editing-flag for this AnimData block should also get turned off
+        *      - clear pointer to active strip
+        */
+       if (adt->action) adt->action->id.us--;
+       adt->action= adt->tmpact;
+       adt->tmpact= NULL;
+       adt->actstrip= NULL;
+       adt->flag &= ~ADT_NLA_EDIT_ON;
+}
+
+/* *************************************************** */
index 81bd78f18511e752005e2a39353a6b3aa64b7ce2..6490ff3c72482970a1fd10c7e6320afd486c791f 100644 (file)
@@ -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]);
        
@@ -1575,14 +1569,14 @@ static void ob_parcurve(Scene *scene, Object *ob, Object *par, float mat[][4])
        }
        /* catch exceptions: curve paths used as a duplicator */
        else if(enable_cu_speed) {
-               ctime= bsystem_time(scene, ob, (float)scene->r.cfra, 0.0);
-               
-#if 0 // XXX old animation system
-               if(calc_ipo_spec(cu->ipo, CU_SPEED, &ctime)==0) {
-                       ctime /= cu->pathlen;
-                       CLAMP(ctime, 0.0, 1.0);
-               }
-#endif // XXX old animation system
+               /* ctime is now a proper var setting of Curve which gets set by Animato like any other var that's animated,
+                * but this will only work if it actually is animated... 
+                *
+                * we firstly calculate the modulus of cu->ctime/cu->pathlen to clamp ctime within the 0.0 to 1.0 times pathlen
+                * range, then divide this (the modulus) by pathlen to get a value between 0.0 and 1.0
+                */
+               ctime= fmod(cu->ctime, cu->pathlen) / cu->pathlen;
+               CLAMP(ctime, 0.0, 1.0);
        }
        else {
                ctime= scene->r.cfra - give_timeoffset(ob);
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
new file mode 100644 (file)
index 0000000..df7ffaa
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * $Id: BKE_nla.h 20999 2009-06-19 04:45:56Z aligorith $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Joshua Leung (full recode)
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef NLA_PRIVATE
+#define NLA_PRIVATE
+
+/* --------------- NLA Evaluation DataTypes ----------------------- */
+
+/* used for list of strips to accumulate at current time */
+typedef struct NlaEvalStrip {
+       struct NlaEvalStrip *next, *prev;
+       
+       NlaTrack *track;                        /* track that this strip belongs to */
+       NlaStrip *strip;                        /* strip that's being used */
+       
+       short track_index;                      /* the index of the track within the list */
+       short strip_mode;                       /* which end of the strip are we looking at */
+       
+       float strip_time;                       /* time at which which strip is being evaluated */
+} NlaEvalStrip;
+
+/* NlaEvalStrip->strip_mode */
+enum {
+               /* standard evaluation */
+       NES_TIME_BEFORE = -1,
+       NES_TIME_WITHIN,
+       NES_TIME_AFTER,
+       
+               /* transition-strip evaluations */
+       NES_TIME_TRANSITION_START,
+       NES_TIME_TRANSITION_END,
+} eNlaEvalStrip_StripMode;
+
+
+/* temp channel for accumulating data from NLA (avoids needing to clear all values first) */
+// TODO: maybe this will be used as the 'cache' stuff needed for editable values too?
+typedef struct NlaEvalChannel {
+       struct NlaEvalChannel *next, *prev;
+       
+       PointerRNA ptr;                 /* pointer to struct containing property to use */
+       PropertyRNA *prop;              /* RNA-property type to use (should be in the struct given) */
+       int index;                              /* array index (where applicable) */
+       
+       float value;                    /* value of this channel */
+} NlaEvalChannel;
+
+/* --------------- NLA Functions (not to be used as a proper API) ----------------------- */
+
+/* convert from strip time <-> global time */
+float nlastrip_get_frame(NlaStrip *strip, float cframe, short mode);
+
+/* --------------- NLA Evaluation (very-private stuff) ----------------------- */
+/* these functions are only defined here to avoid problems with the order in which they get defined... */
+
+NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list, ListBase *strips, short index, float ctime);
+void nlastrip_evaluate(PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes);
+void nladata_flush_channels(ListBase *channels);
+
+#endif // NLA_PRIVATE
index 460a1dcb2f58fcc9871aacf15f167b9516be7b23..846e54526b86b35922cdcf2f777453c83daf72d9 100644 (file)
@@ -1667,10 +1667,26 @@ static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbas
 
 /* Data Linking ----------------------------- */
 
+static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list)
+{
+       FModifier *fcm;
+       
+       for (fcm= list->first; fcm; fcm= fcm->next) {
+               /* data for specific modifiers */
+               switch (fcm->type) {
+                       case FMODIFIER_TYPE_PYTHON:
+                       {
+                               FMod_Python *data= (FMod_Python *)fcm->data;
+                               data->script = newlibadr(fd, id->lib, data->script);
+                       }
+                               break;
+               }
+       }
+}
+
 static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list) 
 {
        FCurve *fcu;
-       FModifier *fcm;
        
        /* relink ID-block references... */
        for (fcu= list->first; fcu; fcu= fcu->next) {
@@ -1684,16 +1700,45 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
                }
                
                /* modifiers */
-               for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-                       /* data for specific modifiers */
-                       switch (fcm->type) {
-                               case FMODIFIER_TYPE_PYTHON:
-                               {
-                                       FMod_Python *data= (FMod_Python *)fcm->data;
-                                       data->script = newlibadr(fd, id->lib, data->script);
-                               }
-                                       break;
+               lib_link_fmodifiers(fd, id, &fcu->modifiers);
+       }
+}
+
+
+/* NOTE: this assumes that link_list has already been called on the list */
+static void direct_link_fmodifiers(FileData *fd, ListBase *list)
+{
+       FModifier *fcm;
+       
+       for (fcm= list->first; fcm; fcm= fcm->next) {
+               /* relink general data */
+               fcm->data = newdataadr(fd, fcm->data);
+               fcm->edata= NULL;
+               
+               /* do relinking of data for specific types */
+               switch (fcm->type) {
+                       case FMODIFIER_TYPE_GENERATOR:
+                       {
+                               FMod_Generator *data= (FMod_Generator *)fcm->data;
+                               
+                               data->coefficients= newdataadr(fd, data->coefficients);
+                       }
+                               break;
+                       case FMODIFIER_TYPE_ENVELOPE:
+                       {
+                               FMod_Envelope *data= (FMod_Envelope *)fcm->data;
+                               
+                               data->data= newdataadr(fd, data->data);
+                       }
+                               break;
+                       case FMODIFIER_TYPE_PYTHON:
+                       {
+                               FMod_Python *data= (FMod_Python *)fcm->data;
+                               
+                               data->prop = newdataadr(fd, data->prop);
+                               IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
                        }
+                               break;
                }
        }
 }
@@ -1702,7 +1747,6 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
 static void direct_link_fcurves(FileData *fd, ListBase *list)
 {
        FCurve *fcu;
-       FModifier *fcm;
        
        /* link F-Curve data to F-Curve again (non ID-libs) */
        for (fcu= list->first; fcu; fcu= fcu->next) {
@@ -1730,37 +1774,7 @@ static void direct_link_fcurves(FileData *fd, ListBase *list)
                
                /* modifiers */
                link_list(fd, &fcu->modifiers);
-               for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-                       /* relink general data */
-                       fcm->data = newdataadr(fd, fcm->data);
-                       fcm->edata= NULL;
-                       
-                       /* do relinking of data for specific types */
-                       switch (fcm->type) {
-                               case FMODIFIER_TYPE_GENERATOR:
-                               {
-                                       FMod_Generator *data= (FMod_Generator *)fcm->data;
-                                       
-                                       data->coefficients= newdataadr(fd, data->coefficients);
-                               }
-                                       break;
-                               case FMODIFIER_TYPE_ENVELOPE:
-                               {
-                                       FMod_Envelope *data= (FMod_Envelope *)fcm->data;
-                                       
-                                       data->data= newdataadr(fd, data->data);
-                               }
-                                       break;
-                               case FMODIFIER_TYPE_PYTHON:
-                               {
-                                       FMod_Python *data= (FMod_Python *)fcm->data;
-                                       
-                                       data->prop = newdataadr(fd, data->prop);
-                                       IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
-                               }
-                                       break;
-                       }
-               }
+               direct_link_fmodifiers(fd, &fcu->modifiers);
        }
 }
 
@@ -1812,6 +1826,65 @@ static void direct_link_action(FileData *fd, bAction *act)
        }
 }
 
+static void lib_link_nladata_strips(FileData *fd, ID *id, ListBase *list)
+{
+       NlaStrip *strip;
+       
+       for (strip= list->first; strip; strip= strip->next) {
+               /* check strip's children */
+               lib_link_nladata_strips(fd, id, &strip->strips);
+               
+               /* reassign the counted-reference to action */
+               strip->act = newlibadr_us(fd, id->lib, strip->act);
+       }
+}
+
+static void lib_link_nladata(FileData *fd, ID *id, ListBase *list)
+{
+       NlaTrack *nlt;
+       
+       /* we only care about the NLA strips inside the tracks */
+       for (nlt= list->first; nlt; nlt= nlt->next) {
+               lib_link_nladata_strips(fd, id, &nlt->strips);
+       }
+}
+
+/* This handles Animato NLA-Strips linking 
+ * NOTE: this assumes that link_list has already been called on the list 
+ */
+static void direct_link_nladata_strips(FileData *fd, ListBase *list)
+{
+       NlaStrip *strip;
+       
+       for (strip= list->first; strip; strip= strip->next) {
+               /* strip's child strips */
+               link_list(fd, &strip->strips);
+               direct_link_nladata_strips(fd, &strip->strips);
+               
+               /* strip's F-Curves */
+               link_list(fd, &strip->fcurves);
+               direct_link_fcurves(fd, &strip->fcurves);
+               
+               /* strip's F-Modifiers */
+               link_list(fd, &strip->modifiers);
+               direct_link_fcurves(fd, &strip->modifiers);
+       }
+}
+
+/* NOTE: this assumes that link_list has already been called on the list */
+static void direct_link_nladata(FileData *fd, ListBase *list)
+{
+       NlaTrack *nlt;
+       
+       for (nlt= list->first; nlt; nlt= nlt->next) {
+               /* relink list of strips */
+               link_list(fd, &nlt->strips);
+               
+               /* relink strip data */
+               direct_link_nladata_strips(fd, &nlt->strips);
+       }
+}
+
 /* ------- */
 
 static void lib_link_keyingsets(FileData *fd, ID *id, ListBase *list)
@@ -1854,6 +1927,7 @@ static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt)
        
        /* link action data */
        adt->action= newlibadr_us(fd, id->lib, adt->action);
+       adt->tmpact= newlibadr_us(fd, id->lib, adt->tmpact);
        
        /* link drivers */
        lib_link_fcurves(fd, id, &adt->drivers);
@@ -1861,7 +1935,7 @@ static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt)
        /* overrides don't have lib-link for now, so no need to do anything */
        
        /* link NLA-data */
-       // TODO... 
+       lib_link_nladata(fd, id, &adt->nla_tracks);
 }
 
 static void direct_link_animdata(FileData *fd, AnimData *adt)
@@ -1878,7 +1952,12 @@ static void direct_link_animdata(FileData *fd, AnimData *adt)
        // TODO...
        
        /* link NLA-data */
-       // TODO...
+       link_list(fd, &adt->nla_tracks);
+       direct_link_nladata(fd, &adt->nla_tracks);
+       
+       /* clear temp pointers that may have been set... */
+       // TODO: it's probably only a small cost to reload this anyway...
+       adt->actstrip= NULL;
 }      
 
 /* ************ READ NODE TREE *************** */
@@ -4708,6 +4787,11 @@ static void direct_link_screen(FileData *fd, bScreen *sc)
                                sipo->ads= newdataadr(fd, sipo->ads);
                                sipo->ghostCurves.first= sipo->ghostCurves.last= NULL;
                        }
+                       else if (sl->spacetype==SPACE_NLA) {
+                               SpaceNla *snla= (SpaceNla*)sl;
+                               
+                               snla->ads= newdataadr(fd, snla->ads);
+                       }
                        else if (sl->spacetype==SPACE_OUTLINER) {
                                SpaceOops *soops= (SpaceOops*) sl;
                                
@@ -9150,7 +9234,14 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                        if(ts->normalsize == 0.0 || !ts->uv_selectmode || ts->vgroup_weight == 0.0) {
                                ts->normalsize= 0.1f;
                                ts->selectmode= SCE_SELECT_VERTEX;
+                               
+                               /* autokeying - setting should be taken from the user-prefs
+                                * but the userprefs version may not have correct flags set 
+                                * (i.e. will result in blank box when enabled)
+                                */
                                ts->autokey_mode= U.autokey_mode;
+                               if (ts->autokey_mode == 0) 
+                                       ts->autokey_mode= 2; /* 'add/replace' but not on */
                                ts->uv_selectmode= UV_SELECT_VERTEX;
                                ts->vgroup_weight= 1.0f;
                        }
@@ -9485,12 +9576,27 @@ static void expand_keyingsets(FileData *fd, Main *mainvar, ListBase *list)
        }
 }
 
+static void expand_animdata_nlastrips(FileData *fd, Main *mainvar, ListBase *list)
+{
+       NlaStrip *strip;
+       
+       for (strip= list->first; strip; strip= strip->next) {
+               /* check child strips */
+               expand_animdata_nlastrips(fd, mainvar, &strip->strips);
+               
+               /* relink referenced action */
+               expand_doit(fd, mainvar, strip->act);
+       }
+}
+
 static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
 {
        FCurve *fcd;
+       NlaTrack *nlt;
        
        /* own action */
        expand_doit(fd, mainvar, adt->action);
+       expand_doit(fd, mainvar, adt->tmpact);
        
        /* drivers - assume that these F-Curves have driver data to be in this list... */
        for (fcd= adt->drivers.first; fcd; fcd= fcd->next) {
@@ -9500,6 +9606,10 @@ static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
                for (dtar= driver->targets.first; dtar; dtar= dtar->next)
                        expand_doit(fd, mainvar, dtar->id);
        }
+       
+       /* nla-data - referenced actions */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) 
+               expand_animdata_nlastrips(fd, mainvar, &nlt->strips);
 }      
 
 static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSettings *part)
index 9d35967c95d4bbd5399d3599b5de98d1287d0dca..4b52da830191a86daeb2d3e175cce204c185268a 100644 (file)
@@ -781,10 +781,59 @@ static void write_actuators(WriteData *wd, ListBase *lb)
        }
 }
 
+static void write_fmodifiers(WriteData *wd, ListBase *fmodifiers)
+{
+       FModifier *fcm;
+       
+       /* Modifiers */
+       for (fcm= fmodifiers->first; fcm; fcm= fcm->next) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* Write the specific data */
+               if (fmi && fcm->data) {
+                       /* firstly, just write the plain fmi->data struct */
+                       writestruct(wd, DATA, fmi->structName, 1, fcm->data);
+                       
+                       /* do any modifier specific stuff */
+                       switch (fcm->type) {
+                               case FMODIFIER_TYPE_GENERATOR:
+                               {
+                                       FMod_Generator *data= (FMod_Generator *)fcm->data;
+                                       
+                                       /* write coefficients array */
+                                       if (data->coefficients)
+                                               writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients);
+                               }
+                                       break;
+                               case FMODIFIER_TYPE_ENVELOPE:
+                               {
+                                       FMod_Envelope *data= (FMod_Envelope *)fcm->data;
+                                       
+                                       /* write envelope data */
+                                       if (data->data)
+                                               writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data);
+                               }
+                                       break;
+                               case FMODIFIER_TYPE_PYTHON:
+                               {
+                                       FMod_Python *data = (FMod_Python *)fcm->data;
+                                       
+                                       /* Write ID Properties -- and copy this comment EXACTLY for easy finding
+                                        of library blocks that implement this.*/
+                                       IDP_WriteProperty(data->prop, wd);
+                               }
+                                       break;
+                       }
+               }
+               
+               /* Write the modifier */
+               writestruct(wd, DATA, "FModifier", 1, fcm);
+       }
+}
+
 static void write_fcurves(WriteData *wd, ListBase *fcurves)
 {
        FCurve *fcu;
-       FModifier *fcm;
        
        for (fcu=fcurves->first; fcu; fcu=fcu->next) {
                /* F-Curve */
@@ -815,50 +864,8 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves)
                        }
                }
                
-               /* Modifiers */
-               for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-                       FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-                       
-                       /* Write the specific data */
-                       if (fmi && fcm->data) {
-                               /* firstly, just write the plain fmi->data struct */
-                               writestruct(wd, DATA, fmi->structName, 1, fcm->data);
-                               
-                               /* do any modifier specific stuff */
-                               switch (fcm->type) {
-                                       case FMODIFIER_TYPE_GENERATOR:
-                                       {
-                                               FMod_Generator *data= (FMod_Generator *)fcm->data;
-                                               
-                                               /* write coefficients array */
-                                               if (data->coefficients)
-                                                       writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients);
-                                       }
-                                               break;
-                                       case FMODIFIER_TYPE_ENVELOPE:
-                                       {
-                                               FMod_Envelope *data= (FMod_Envelope *)fcm->data;
-                                               
-                                               /* write envelope data */
-                                               if (data->data)
-                                                       writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data);
-                                       }
-                                               break;
-                                       case FMODIFIER_TYPE_PYTHON:
-                                       {
-                                               FMod_Python *data = (FMod_Python *)fcm->data;
-                                               
-                                               /* Write ID Properties -- and copy this comment EXACTLY for easy finding
-                                                of library blocks that implement this.*/
-                                               IDP_WriteProperty(data->prop, wd);
-                                       }
-                                               break;
-                               }
-                       }
-                       
-                       /* Write the modifier */
-                       writestruct(wd, DATA, "FModifier", 1, fcm);
-               }
+               /* write F-Modifiers */
+               write_fmodifiers(wd, &fcu->modifiers);
        }
 }
 
@@ -909,6 +916,37 @@ static void write_keyingsets(WriteData *wd, ListBase *list)
        }
 }
 
+static void write_nlastrips(WriteData *wd, ListBase *strips)
+{
+       NlaStrip *strip;
+       
+       for (strip= strips->first; strip; strip= strip->next) {
+               /* write the strip first */
+               writestruct(wd, DATA, "NlaStrip", 1, strip);
+               
+               /* write the strip's F-Curves and modifiers */
+               write_fcurves(wd, &strip->fcurves);
+               write_fmodifiers(wd, &strip->modifiers);
+               
+               /* write the strip's children */
+               write_nlastrips(wd, &strip->strips);
+       }
+}
+
+static void write_nladata(WriteData *wd, ListBase *nlabase)
+{
+       NlaTrack *nlt;
+       
+       /* write all the tracks */
+       for (nlt= nlabase->first; nlt; nlt= nlt->next) {
+               /* write the track first */
+               writestruct(wd, DATA, "NlaTrack", 1, nlt);
+               
+               /* write the track's strips */
+               write_nlastrips(wd, &nlt->strips);
+       }
+}
+
 static void write_animdata(WriteData *wd, AnimData *adt)
 {
        AnimOverride *aor;
@@ -920,14 +958,17 @@ static void write_animdata(WriteData *wd, AnimData *adt)
        write_fcurves(wd, &adt->drivers);
        
        /* write overrides */
+       // FIXME: are these needed?
        for (aor= adt->overrides.first; aor; aor= aor->next) {
                /* overrides consist of base data + rna_path */
                writestruct(wd, DATA, "AnimOverride", 1, aor);
                writedata(wd, DATA, strlen(aor->rna_path)+1, aor->rna_path);
        }
        
+       // TODO write the remaps (if they are needed)
+       
        /* write NLA data */
-       // XXX todo...
+       write_nladata(wd, &adt->nla_tracks);
 }
 
 static void write_constraints(WriteData *wd, ListBase *conlist)
@@ -1899,7 +1940,10 @@ static void write_screens(WriteData *wd, ListBase *scrbase)
                                        writestruct(wd, DATA, "SpaceSound", 1, sl);
                                }
                                else if(sl->spacetype==SPACE_NLA){
-                                       writestruct(wd, DATA, "SpaceNla", 1, sl);
+                                       SpaceNla *snla= (SpaceNla *)sl;
+                                       
+                                       writestruct(wd, DATA, "SpaceNla", 1, snla);
+                                       if(snla->ads) writestruct(wd, DATA, "bDopeSheet", 1, snla->ads);
                                }
                                else if(sl->spacetype==SPACE_TIME){
                                        writestruct(wd, DATA, "SpaceTime", 1, sl);
index 05d50f98e8e8e7e991d82d6c9fb8230854153979..5c31d00770509fb7b294d412905a49fe106ee6ed 100644 (file)
 #include "RNA_access.h"
 #include "RNA_define.h"
 
+#include "BKE_animsys.h"
 #include "BKE_action.h"
 #include "BKE_depsgraph.h"
-#include "BKE_ipo.h"
+#include "BKE_fcurve.h"
 #include "BKE_key.h"
 #include "BKE_material.h"
 #include "BKE_object.h"
 /* ************************************************************************** */
 /* CHANNELS API */
 
-/* -------------------------- Internal Macros ------------------------------- */
-
-/* set/clear/toggle macro 
- *     - channel - channel with a 'flag' member that we're setting
- *     - smode - 0=clear, 1=set, 2=toggle
- *     - sflag - bitflag to set
- */
-#define ACHANNEL_SET_FLAG(channel, smode, sflag) \
-       { \
-               if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
-               else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag |= (sflag); \
-               else                                                                    (channel)->flag &= ~(sflag); \
-       }
-       
-/* set/clear/toggle macro, where the flag is negative 
- *     - channel - channel with a 'flag' member that we're setting
- *     - smode - 0=clear, 1=set, 2=toggle
- *     - sflag - bitflag to set
- */
-#define ACHANNEL_SET_FLAG_NEG(channel, smode, sflag) \
-       { \
-               if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
-               else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag &= ~(sflag); \
-               else                                                                    (channel)->flag |= (sflag); \
-       }
-
 /* -------------------------- Exposed API ----------------------------------- */
 
 /* Set the given animation-channel as the active one for the active context */
-void ANIM_set_active_channel (void *data, short datatype, int filter, void *channel_data, short channel_type)
+void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
 {
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
        
        /* try to build list of filtered items */
-       // XXX we don't need/supply animcontext for now, since in this case, there's nothing really essential there that isn't already covered
-       ANIM_animdata_filter(NULL, &anim_data, filter, data, datatype);
+       ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
        if (anim_data.first == NULL)
                return;
                
@@ -149,11 +123,18 @@ void ANIM_set_active_channel (void *data, short datatype, int filter, void *chan
                                ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
                        }
                                break;
+                       case ANIMTYPE_NLATRACK:
+                       {
+                               NlaTrack *nlt= (NlaTrack *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
+                       }
+                               break;
                }
        }
        
        /* set active flag */
-       if (channel_data) {
+       if (channel_data != NULL) {
                switch (channel_type) {
                        case ANIMTYPE_GROUP:
                        {
@@ -167,6 +148,12 @@ void ANIM_set_active_channel (void *data, short datatype, int filter, void *chan
                                fcu->flag |= FCURVE_ACTIVE;
                        }
                                break;
+                       case ANIMTYPE_NLATRACK:
+                       {
+                               NlaTrack *nlt= (NlaTrack *)channel_data;
+                               nlt->flag |= NLATRACK_ACTIVE;
+                       }
+                               break;
                }
        }
        
@@ -217,6 +204,10 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
                                        if (ale->flag & FCURVE_SELECTED)
                                                sel= ACHANNEL_SETFLAG_CLEAR;
                                        break;
+                               case ANIMTYPE_NLATRACK:
+                                       if (ale->flag & NLATRACK_SELECTED)
+                                               sel= ACHANNEL_SETFLAG_CLEAR;
+                                       break;
                        }
                }
        }
@@ -263,6 +254,14 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
                                fcu->flag &= ~FCURVE_ACTIVE;
                        }
                                break;
+                       case ANIMTYPE_NLATRACK:
+                       {
+                               NlaTrack *nlt= (NlaTrack *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
+                               nlt->flag &= ~NLATRACK_ACTIVE;
+                       }
+                               break;
                }
        }
        
@@ -273,6 +272,47 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
 /* ************************************************************************** */
 /* OPERATORS */
 
+/* ****************** Operator Utilities ********************************** */
+
+/* poll callback for being in an Animation Editor channels list region */
+int animedit_poll_channels_active (bContext *C)
+{
+       ScrArea *sa= CTX_wm_area(C);
+       
+       /* channels region test */
+       // TODO: could enhance with actually testing if channels region?
+       if (ELEM(NULL, sa, CTX_wm_region(C)))
+               return 0;
+       /* animation editor test */
+       if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
+               return 0;
+               
+       return 1;
+}
+
+/* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
+int animedit_poll_channels_nla_tweakmode_off (bContext *C)
+{
+       ScrArea *sa= CTX_wm_area(C);
+       Scene *scene = CTX_data_scene(C);
+       
+       /* channels region test */
+       // TODO: could enhance with actually testing if channels region?
+       if (ELEM(NULL, sa, CTX_wm_region(C)))
+               return 0;
+       /* animation editor test */
+       if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
+               return 0;
+       
+       /* NLA TweakMode test */        
+       if (sa->spacetype == SPACE_NLA) {
+               if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
+                       return 0;
+       }
+               
+       return 1;
+}
+
 /* ****************** Rearrange Channels Operator ******************* */
 /* This operator only works for Action Editor mode for now, as having it elsewhere makes things difficult */
 
@@ -575,8 +615,8 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
        mode= RNA_enum_get(op->ptr, "direction");
        rearrange_action_channels(&ac, mode);
        
-       /* set notifier tha things have changed */
-       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL);
        
        return OPERATOR_FINISHED;
 }
@@ -652,6 +692,121 @@ void ANIM_OT_channels_move_bottom (wmOperatorType *ot)
 
 #endif // XXX old animation system - needs to be updated for new system...
 
+/* ******************** Delete Channel Operator *********************** */
+
+static int animchannels_delete_exec(bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* cannot delete in shapekey */
+       if (ac.datatype == ANIMCONT_SHAPEKEY) 
+               return OPERATOR_CANCELLED;
+               
+               
+       /* do groups only first (unless in Drivers mode, where there are none) */
+       if (ac.datatype != ANIMCONT_DRIVERS) {
+               /* filter data */
+               filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CHANNELS | ANIMFILTER_FOREDIT);
+               ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+               
+               /* delete selected groups and their associated channels */
+               for (ale= anim_data.first; ale; ale= ale->next) {
+                       /* only groups - don't check other types yet, since they may no-longer exist */
+                       if (ale->type == ANIMTYPE_GROUP) {
+                               bActionGroup *agrp= (bActionGroup *)ale->data;
+                               AnimData *adt= BKE_animdata_from_id(ale->id);
+                               FCurve *fcu, *fcn;
+                               
+                               /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
+                               if (adt == NULL)
+                                       continue;
+                               
+                               /* delete all of the Group's F-Curves, but no others */
+                               for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcn) {
+                                       fcn= fcu->next;
+                                       
+                                       /* remove from group and action, then free */
+                                       action_groups_remove_channel(adt->action, fcu);
+                                       free_fcurve(fcu);
+                               }
+                               
+                               /* free the group itself */
+                               if (adt->action)
+                                       BLI_freelinkN(&adt->action->groups, agrp);
+                               else
+                                       MEM_freeN(agrp);
+                       }
+               }
+               
+               /* cleanup */
+               BLI_freelistN(&anim_data);
+       }
+       
+       /* now do F-Curves */
+       if (ac.datatype != ANIMCONT_GPENCIL) {
+               /* filter data */
+               filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT);
+               ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+               
+               /* delete selected F-Curves */
+               for (ale= anim_data.first; ale; ale= ale->next) {
+                       /* only F-Curves, and only if we can identify its parent */
+                       if (ale->type == ANIMTYPE_FCURVE) {
+                               AnimData *adt= BKE_animdata_from_id(ale->id);
+                               FCurve *fcu= (FCurve *)ale->data;
+                               
+                               /* if no AnimData, we've got nowhere to remove the F-Curve from */
+                               if (adt == NULL)
+                                       continue;
+                                       
+                               /* remove from whatever list it came from
+                                *      - Action Group
+                                *      - Action
+                                *      - Drivers
+                                *      - TODO... some others?
+                                */
+                               if (fcu->grp)
+                                       action_groups_remove_channel(adt->action, fcu);
+                               else if (adt->action)
+                                       BLI_remlink(&adt->action->curves, fcu);
+                               else if (ac.datatype == ANIMCONT_DRIVERS)
+                                       BLI_remlink(&adt->drivers, fcu);
+                                       
+                               /* free the F-Curve itself */
+                               free_fcurve(fcu);
+                       }
+               }
+               
+               /* cleanup */
+               BLI_freelistN(&anim_data);
+       }
+       
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL);
+       
+       return OPERATOR_FINISHED;
+}
+void ANIM_OT_channels_delete (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Delete Channels";
+       ot->idname= "ANIM_OT_channels_delete";
+       
+       /* api callbacks */
+       ot->exec= animchannels_delete_exec;
+       ot->poll= animedit_poll_channels_active;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
 
 /* ******************** Toggle Channel Visibility Operator *********************** */
 
@@ -668,7 +823,7 @@ static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
                
        /* filter data */
-       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVESONLY);
+       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL);
        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
        
        /* See if we should be making showing all selected or hiding */
@@ -676,21 +831,35 @@ static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *op)
                if (vis == ACHANNEL_SETFLAG_CLEAR) 
                        break;
                
-               if (ale->flag & FCURVE_VISIBLE)
+               if ((ale->type == ANIMTYPE_FCURVE) && (ale->flag & FCURVE_VISIBLE))
+                       vis= ACHANNEL_SETFLAG_CLEAR;
+               else if ((ale->type == ANIMTYPE_GROUP) && !(ale->flag & AGRP_NOTVISIBLE))
                        vis= ACHANNEL_SETFLAG_CLEAR;
        }
                
        /* Now set the flags */
        for (ale= anim_data.first; ale; ale= ale->next) {
-               FCurve *fcu= (FCurve *)ale->data;
-               ACHANNEL_SET_FLAG(fcu, vis, FCURVE_VISIBLE);
+               switch (ale->type) {
+                       case ANIMTYPE_FCURVE: /* F-Curve */
+                       {
+                               FCurve *fcu= (FCurve *)ale->data;
+                               ACHANNEL_SET_FLAG(fcu, vis, FCURVE_VISIBLE);
+                       }
+                               break;
+                       case ANIMTYPE_GROUP: /* Group */
+                       {
+                               bActionGroup *agrp= (bActionGroup *)ale->data;
+                               ACHANNEL_SET_FLAG_NEG(agrp, vis, AGRP_NOTVISIBLE);
+                       }
+                               break;
+               }
        }
        
        /* cleanup */
        BLI_freelistN(&anim_data);
        
-       /* set notifier tha things have changed */
-       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL);
        
        return OPERATOR_FINISHED;
 }
@@ -889,6 +1058,12 @@ static void setflag_anim_channels (bAnimContext *ac, short setting, short mode,
                                        case ACHANNEL_SETTING_EXPAND:
                                                ACHANNEL_SET_FLAG(agrp, mode, AGRP_EXPANDED);
                                                break;
+                                       case ACHANNEL_SETTING_MUTE:
+                                               ACHANNEL_SET_FLAG(agrp, mode, AGRP_MUTED);
+                                               break;
+                                       case ACHANNEL_SETTING_VISIBLE:
+                                               ACHANNEL_SET_FLAG_NEG(agrp, mode, AGRP_NOTVISIBLE);
+                                               break;
                                }
                        }
                                break;
@@ -947,8 +1122,8 @@ static int animchannels_setflag_exec(bContext *C, wmOperator *op)
        /* modify setting */
        setflag_anim_channels(&ac, setting, mode, 1);
        
-       /* set notifier tha things have changed */
-       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL);
        
        return OPERATOR_FINISHED;
 }
@@ -963,7 +1138,7 @@ void ANIM_OT_channels_setting_enable (wmOperatorType *ot)
        /* api callbacks */
        ot->invoke= WM_menu_invoke;
        ot->exec= animchannels_setflag_exec;
-       ot->poll= ED_operator_areaactive;
+       ot->poll= animedit_poll_channels_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -984,7 +1159,7 @@ void ANIM_OT_channels_setting_disable (wmOperatorType *ot)
        /* api callbacks */
        ot->invoke= WM_menu_invoke;
        ot->exec= animchannels_setflag_exec;
-       ot->poll= ED_operator_areaactive;
+       ot->poll= animedit_poll_channels_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1005,7 +1180,7 @@ void ANIM_OT_channels_setting_toggle (wmOperatorType *ot)
        /* api callbacks */
        ot->invoke= WM_menu_invoke;
        ot->exec= animchannels_setflag_exec;
-       ot->poll= ED_operator_areaactive;
+       ot->poll= animedit_poll_channels_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1026,7 +1201,7 @@ void ANIM_OT_channels_editable_toggle (wmOperatorType *ot)
        
        /* api callbacks */
        ot->exec= animchannels_setflag_exec;
-       ot->poll= ED_operator_areaactive;
+       ot->poll= animedit_poll_channels_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1056,8 +1231,8 @@ static int animchannels_expand_exec (bContext *C, wmOperator *op)
        /* modify setting */
        setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel);
        
-       /* set notifier that things have changed */
-       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL);
        
        return OPERATOR_FINISHED;
 }
@@ -1070,7 +1245,7 @@ void ANIM_OT_channels_expand (wmOperatorType *ot)
        
        /* api callbacks */
        ot->exec= animchannels_expand_exec;
-       ot->poll= ED_operator_areaactive;
+       ot->poll= animedit_poll_channels_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1097,8 +1272,8 @@ static int animchannels_collapse_exec (bContext *C, wmOperator *op)
        /* modify setting */
        setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel);
        
-       /* set notifier that things have changed */
-       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_EDIT, NULL);
        
        return OPERATOR_FINISHED;
 }
@@ -1111,7 +1286,7 @@ void ANIM_OT_channels_collapse (wmOperatorType *ot)
        
        /* api callbacks */
        ot->exec= animchannels_collapse_exec;
-       ot->poll= ED_operator_areaactive;
+       ot->poll= animedit_poll_channels_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1136,8 +1311,8 @@ static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
        else
                ANIM_deselect_anim_channels(ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
        
-       /* set notifier tha things have changed */
-       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_SELECT, NULL);
        
        return OPERATOR_FINISHED;
 }
@@ -1150,7 +1325,7 @@ void ANIM_OT_channels_select_all_toggle (wmOperatorType *ot)
        
        /* api callbacks */
        ot->exec= animchannels_deselectall_exec;
-       ot->poll= ED_operator_areaactive;
+       ot->poll= animedit_poll_channels_nla_tweakmode_off;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1218,6 +1393,14 @@ static void borderselect_anim_channels (bAnimContext *ac, rcti *rect, short sele
                                        ACHANNEL_SET_FLAG(gpl, selectmode, GP_LAYER_SELECT);
                                }