Merged changes in the trunk up to revision 35203.
[blender.git] / source / blender / blenkernel / intern / anim_sys.c
index c617ca33e8a27fde0c6a9b77efa2eded61382d6c..2268a1535bd77092f87ac5f08eae87f11408f504 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/*
  * $Id$
  *
  * ***** BEGIN GPL LICENSE BLOCK *****
@@ -15,7 +15,7 @@
  *
  * 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.
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
  * All rights reserved.
 #include "MEM_guardedalloc.h"
 
 #include "BLI_blenlib.h"
-#include "BLI_math.h"
 #include "BLI_dynstr.h"
+#include "BLI_utildefines.h"
 
 #include "DNA_anim_types.h"
+#include "DNA_material_types.h"
 #include "DNA_scene_types.h"
+#include "DNA_texture_types.h"
 
 #include "BKE_animsys.h"
 #include "BKE_action.h"
 #include "BKE_nla.h"
 #include "BKE_global.h"
 #include "BKE_main.h"
+#include "BKE_library.h"
 #include "BKE_utildefines.h"
 
 #include "RNA_access.h"
-#include "RNA_types.h"
 
 #include "nla_private.h"
 
@@ -60,8 +62,8 @@
 
 /* Getter/Setter -------------------------------------------- */
 
-/* Internal utility to check if ID can have AnimData */
-static short id_has_animdata (ID *id)
+/* Check if ID can have AnimData */
+short id_type_can_have_animdata (ID *id)
 {
        /* sanity check */
        if (id == NULL)
@@ -72,11 +74,12 @@ static short id_has_animdata (ID *id)
        switch (GS(id->name)) {
                        /* has AnimData */
                case ID_OB:
-               case ID_ME: case ID_MB: case ID_CU: case ID_AR:
+               case ID_ME: case ID_MB: case ID_CU: case ID_AR: case ID_LT:
                case ID_KE:
                case ID_PA:
                case ID_MA: case ID_TE: case ID_NT:
                case ID_LA: case ID_CA: case ID_WO:
+               case ID_LS:
                case ID_SCE:
                {
                        return 1;
@@ -99,7 +102,7 @@ AnimData *BKE_animdata_from_id (ID *id)
         * types that do to be of type IdAdtTemplate, and extract the
         * AnimData that way
         */
-       if (id_has_animdata(id)) {
+       if (id_type_can_have_animdata(id)) {
                IdAdtTemplate *iat= (IdAdtTemplate *)id;
                return iat->adt;
        }
@@ -117,7 +120,7 @@ AnimData *BKE_id_add_animdata (ID *id)
         * types that do to be of type IdAdtTemplate, and add the AnimData
         * to it using the template
         */
-       if (id_has_animdata(id)) {
+       if (id_type_can_have_animdata(id)) {
                IdAdtTemplate *iat= (IdAdtTemplate *)id;
                
                /* check if there's already AnimData, in which case, don't add */
@@ -145,7 +148,7 @@ void BKE_free_animdata (ID *id)
        /* Only some ID-blocks have this info for now, so we cast the 
         * types that do to be of type IdAdtTemplate
         */
-       if (id_has_animdata(id)) {
+       if (id_type_can_have_animdata(id)) {
                IdAdtTemplate *iat= (IdAdtTemplate *)id;
                AnimData *adt= iat->adt;
                
@@ -177,7 +180,7 @@ void BKE_free_animdata (ID *id)
 /* Freeing -------------------------------------------- */
 
 /* Make a copy of the given AnimData - to be used when copying datablocks */
-AnimData *BKE_copy_animdata (AnimData *adt)
+AnimData *BKE_copy_animdata (AnimData *adt, const short do_action)
 {
        AnimData *dadt;
        
@@ -187,9 +190,15 @@ AnimData *BKE_copy_animdata (AnimData *adt)
        dadt= MEM_dupallocN(adt);
        
        /* make a copy of action - at worst, user has to delete copies... */
-       dadt->action= copy_action(adt->action);
-       dadt->tmpact= copy_action(adt->tmpact);
-       
+       if(do_action) {
+               dadt->action= copy_action(adt->action);
+               dadt->tmpact= copy_action(adt->tmpact);
+       }
+       else {
+               id_us_plus((ID *)dadt->action);
+               id_us_plus((ID *)dadt->tmpact);
+       }
+
        /* duplicate NLA data */
        copy_nladata(&dadt->nla_tracks, &adt->nla_tracks);
        
@@ -203,7 +212,7 @@ AnimData *BKE_copy_animdata (AnimData *adt)
        return dadt;
 }
 
-int BKE_copy_animdata_id(struct ID *id_to, struct ID *id_from)
+int BKE_copy_animdata_id(struct ID *id_to, struct ID *id_from, const short do_action)
 {
        AnimData *adt;
 
@@ -215,13 +224,26 @@ int BKE_copy_animdata_id(struct ID *id_to, struct ID *id_from)
        adt = BKE_animdata_from_id(id_from);
        if (adt) {
                IdAdtTemplate *iat = (IdAdtTemplate *)id_to;
-               iat->adt= BKE_copy_animdata(adt);
+               iat->adt= BKE_copy_animdata(adt, do_action);
        }
 
        return 1;
 }
 
-
+void BKE_copy_animdata_id_action(struct ID *id)
+{
+       AnimData *adt= BKE_animdata_from_id(id);
+       if(adt) {
+               if(adt->action) {
+                       ((ID *)adt->action)->us--;
+                       adt->action= copy_action(adt->action);
+               }
+               if(adt->tmpact) {
+                       ((ID *)adt->tmpact)->us--;
+                       adt->tmpact= copy_action(adt->tmpact);
+               }
+       }
+}
 
 /* Make Local -------------------------------------------- */
 
@@ -256,6 +278,176 @@ void BKE_animdata_make_local(AnimData *adt)
                make_local_strips(&nlt->strips);
 }
 
+/* Sub-ID Regrouping ------------------------------------------- */
+
+/* helper heuristic for determining if a path is compatible with the basepath 
+ * < path: (str) full RNA-path from some data (usually an F-Curve) to compare
+ * < basepath: (str) shorter path fragment to look for
+ * > returns (bool) whether there is a match
+ */
+static short animpath_matches_basepath (const char path[], const char basepath[])
+{
+       /* we need start of path to be basepath */
+       return (path && basepath) && (strstr(path, basepath) == path);
+}
+
+/* Move F-Curves in src action to dst action, setting up all the necessary groups 
+ * for this to happen, but only if the F-Curves being moved have the appropriate 
+ * "base path". 
+ *     - This is used when data moves from one datablock to another, causing the
+ *       F-Curves to need to be moved over too
+ */
+void action_move_fcurves_by_basepath (bAction *srcAct, bAction *dstAct, const char basepath[])
+{
+       FCurve *fcu, *fcn=NULL;
+       
+       /* sanity checks */
+       if ELEM3(NULL, srcAct, dstAct, basepath) {
+               if (G.f & G_DEBUG) {
+                       printf("ERROR: action_partition_fcurves_by_basepath(%p, %p, %p) has insufficient info to work with\n",
+                                       srcAct, dstAct, basepath);
+               }
+               return;
+       }
+               
+       /* clear 'temp' flags on all groups in src, as we'll be needing them later 
+        * to identify groups that we've managed to empty out here
+        */
+       action_groups_clear_tempflags(srcAct);
+       
+       /* iterate over all src F-Curves, moving over the ones that need to be moved */
+       for (fcu = srcAct->curves.first; fcu; fcu = fcn) {
+               /* store next pointer in case we move stuff */
+               fcn = fcu->next;
+               
+               /* should F-Curve be moved over?
+                *      - we only need the start of the path to match basepath
+                */
+               if (animpath_matches_basepath(fcu->rna_path, basepath)) {                       
+                       bActionGroup *agrp = NULL;
+                       
+                       /* if grouped... */
+                       if (fcu->grp) {
+                               /* make sure there will be a matching group on the other side for the migrants */
+                               agrp = action_groups_find_named(dstAct, fcu->grp->name);
+                               
+                               if (agrp == NULL) {
+                                       /* add a new one with a similar name (usually will be the same though) */
+                                       agrp = action_groups_add_new(dstAct, fcu->grp->name);
+                               }
+                               
+                               /* old groups should be tagged with 'temp' flags so they can be removed later
+                                * if we remove everything from them
+                                */
+                               fcu->grp->flag |= AGRP_TEMP;
+                       }
+                       
+                       /* perform the migration now */
+                       action_groups_remove_channel(srcAct, fcu);
+                       
+                       if (agrp)
+                               action_groups_add_channel(dstAct, agrp, fcu);
+                       else
+                               BLI_addtail(&dstAct->curves, fcu);
+               }
+       }
+       
+       /* cleanup groups (if present) */
+       if (srcAct->groups.first) {
+               bActionGroup *agrp, *grp=NULL;
+               
+               for (agrp = srcAct->groups.first; agrp; agrp = grp) {
+                       grp = agrp->next;
+                       
+                       /* only tagged groups need to be considered - clearing these tags or removing them */
+                       if (agrp->flag & AGRP_TEMP) {
+                               /* if group is empty and tagged, then we can remove as this operation
+                                * moved out all the channels that were formerly here
+                                */
+                               if (agrp->channels.first == NULL)
+                                       BLI_freelinkN(&srcAct->groups, agrp);
+                               else
+                                       agrp->flag &= ~AGRP_TEMP;
+                       }
+               }
+       }
+}
+
+/* Transfer the animation data from srcID to dstID where the srcID
+ * animation data is based off "basepath", creating new AnimData and
+ * associated data as necessary
+ */
+void BKE_animdata_separate_by_basepath (ID *srcID, ID *dstID, ListBase *basepaths)
+{
+       AnimData *srcAdt=NULL, *dstAdt=NULL;
+       LinkData *ld;
+       
+       /* sanity checks */
+       if ELEM(NULL, srcID, dstID) {
+               if (G.f & G_DEBUG)
+                       printf("ERROR: no source or destination ID to separate AnimData with\n");
+               return;
+       }
+       
+       /* get animdata from src, and create for destination (if needed) */
+       srcAdt = BKE_animdata_from_id(srcID);
+       dstAdt = BKE_id_add_animdata(dstID);
+       
+       if ELEM(NULL, srcAdt, dstAdt) {
+               if (G.f & G_DEBUG)
+                       printf("ERROR: no AnimData for this pair of ID's\n");
+               return;
+       }
+       
+       /* active action */
+       if (srcAdt->action) {
+               /* set up an action if necessary, and name it in a similar way so that it can be easily found again */
+               if (dstAdt->action == NULL) {
+                       dstAdt->action = add_empty_action(srcAdt->action->id.name+2);
+               }
+               else if (dstAdt->action == srcAdt->action) {
+                       printf("Argh! Source and Destination share animation! ('%s' and '%s' both use '%s') Making new empty action\n",
+                               srcID->name, dstID->name, srcAdt->action->id.name);
+                       
+                       // TODO: review this...
+                       id_us_min(&dstAdt->action->id);
+                       dstAdt->action = add_empty_action(dstAdt->action->id.name+2);
+               }
+                       
+               /* loop over base paths, trying to fix for each one... */
+               for (ld = basepaths->first; ld; ld = ld->next) {
+                       const char *basepath = (const char *)ld->data;
+                       action_move_fcurves_by_basepath(srcAdt->action, dstAdt->action, basepath);
+               }
+       }
+       
+       /* drivers */
+       if (srcAdt->drivers.first) {
+               FCurve *fcu, *fcn=NULL;
+               
+               /* check each driver against all the base paths to see if any should go */
+               for (fcu = srcAdt->drivers.first; fcu; fcu = fcn) {
+                       fcn = fcu->next;
+                       
+                       /* try each basepath in turn, but stop on the first one which works */
+                       for (ld = basepaths->first; ld; ld = ld->next) {
+                               const char *basepath = (const char *)ld->data;
+                               
+                               if (animpath_matches_basepath(fcu->rna_path, basepath)) {
+                                       /* just need to change lists */
+                                       BLI_remlink(&srcAdt->drivers, fcu);
+                                       BLI_addtail(&dstAdt->drivers, fcu);
+                                       
+                                       // TODO: add depsgraph flushing calls?
+                                       
+                                       /* can stop now, as moved already */
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
 /* Path Validation -------------------------------------------- */
 
 /* Check if a given RNA Path is valid, by tracing it from the given ID, and seeing if we can resolve it */
@@ -274,7 +466,7 @@ static short check_rna_path_is_valid (ID *owner_id, char *path)
 /* Check if some given RNA Path needs fixing - free the given path and set a new one as appropriate 
  * NOTE: we assume that oldName and newName have [" "] padding around them
  */
-static char *rna_path_rename_fix (ID *owner_id, char *prefix, char *oldName, char *newName, char *oldpath)
+static char *rna_path_rename_fix (ID *owner_id, const char *prefix, char *oldName, char *newName, char *oldpath, int verify_paths)
 {
        char *prefixPtr= strstr(oldpath, prefix);
        char *oldNamePtr= strstr(oldpath, oldName);
@@ -286,7 +478,7 @@ static char *rna_path_rename_fix (ID *owner_id, char *prefix, char *oldName, cha
         */
        if ( (prefixPtr && oldNamePtr) && (prefixPtr+prefixLen == oldNamePtr) ) {
                /* if we haven't aren't able to resolve the path now, try again after fixing it */
-               if (check_rna_path_is_valid(owner_id, oldpath) == 0) {          
+               if (!verify_paths || check_rna_path_is_valid(owner_id, oldpath) == 0) {         
                        DynStr *ds= BLI_dynstr_new();
                        char *postfixPtr= oldNamePtr+oldNameLen;
                        char *newPath = NULL;
@@ -315,7 +507,7 @@ static char *rna_path_rename_fix (ID *owner_id, char *prefix, char *oldName, cha
                        
                        /* check if the new path will solve our problems */
                        // TODO: will need to check whether this step really helps in practice
-                       if (check_rna_path_is_valid(owner_id, newPath)) {
+                       if (!verify_paths || check_rna_path_is_valid(owner_id, newPath)) {
                                /* free the old path, and return the new one, since we've solved the issues */
                                MEM_freeN(oldpath);
                                return newPath;
@@ -332,7 +524,7 @@ static char *rna_path_rename_fix (ID *owner_id, char *prefix, char *oldName, cha
 }
 
 /* Check RNA-Paths for a list of F-Curves */
-static void fcurves_path_rename_fix (ID *owner_id, char *prefix, char *oldName, char *newName, ListBase *curves)
+static void fcurves_path_rename_fix (ID *owner_id, const char *prefix, char *oldName, char *newName, ListBase *curves, int verify_paths)
 {
        FCurve *fcu;
        
@@ -340,7 +532,20 @@ static void fcurves_path_rename_fix (ID *owner_id, char *prefix, char *oldName,
        for (fcu= curves->first; fcu; fcu= fcu->next) {
                /* firstly, handle the F-Curve's own path */
                if (fcu->rna_path)
-                       fcu->rna_path= rna_path_rename_fix(owner_id, prefix, oldName, newName, fcu->rna_path);
+                       fcu->rna_path= rna_path_rename_fix(owner_id, prefix, oldName, newName, fcu->rna_path, verify_paths);
+       }
+}
+
+/* Check RNA-Paths for a list of Drivers */
+static void drivers_path_rename_fix (ID *owner_id, const char *prefix, char *oldName, char *newName, char *oldKey, char *newKey, ListBase *curves, int verify_paths)
+{
+       FCurve *fcu;
+       
+       /* we need to check every curve - drivers are F-Curves too! */
+       for (fcu= curves->first; fcu; fcu= fcu->next) {
+               /* firstly, handle the F-Curve's own path */
+               if (fcu->rna_path)
+                       fcu->rna_path= rna_path_rename_fix(owner_id, prefix, oldKey, newKey, fcu->rna_path, verify_paths);
                
                /* driver? */
                if (fcu->driver) {
@@ -354,15 +559,16 @@ static void fcurves_path_rename_fix (ID *owner_id, char *prefix, char *oldName,
                                {
                                        /* rename RNA path */
                                        if (dtar->rna_path)
-                                               dtar->rna_path= rna_path_rename_fix(dtar->id, prefix, oldName, newName, dtar->rna_path);
+                                               dtar->rna_path= rna_path_rename_fix(dtar->id, prefix, oldKey, newKey, dtar->rna_path, verify_paths);
                                        
                                        /* also fix the bone-name (if applicable) */
-                                       // XXX this has been disabled because the old/new names have padding which means this check will fail
-                                       //if ( ((dtar->id) && (GS(dtar->id->name) == ID_OB)) &&
-                                       //       (dtar->pchan_name[0]) && (strcmp(oldName, dtar->pchan_name)==0) )
-                                       //{
-                                       //      BLI_strncpy(dtar->pchan_name, newName, sizeof(dtar->pchan_name));
-                                       //}
+                                       if (strstr(prefix, "bones")) {
+                                               if ( ((dtar->id) && (GS(dtar->id->name) == ID_OB)) &&
+                                                        (dtar->pchan_name[0]) && (strcmp(oldName, dtar->pchan_name)==0) )
+                                               {
+                                                       BLI_strncpy(dtar->pchan_name, newName, sizeof(dtar->pchan_name));
+                                               }
+                                       }
                                }
                                DRIVER_TARGETS_LOOPER_END
                        }
@@ -371,7 +577,7 @@ static void fcurves_path_rename_fix (ID *owner_id, char *prefix, char *oldName,
 }
 
 /* Fix all RNA-Paths for Actions linked to NLA Strips */
-static void nlastrips_path_rename_fix (ID *owner_id, char *prefix, char *oldName, char *newName, ListBase *strips)
+static void nlastrips_path_rename_fix (ID *owner_id, const char *prefix, char *oldName, char *newName, ListBase *strips, int verify_paths)
 {
        NlaStrip *strip;
        
@@ -379,11 +585,11 @@ static void nlastrips_path_rename_fix (ID *owner_id, char *prefix, char *oldName
        for (strip= strips->first; strip; strip= strip->next) {
                /* fix strip's action */
                if (strip->act)
-                       fcurves_path_rename_fix(owner_id, prefix, oldName, newName, &strip->act->curves);
+                       fcurves_path_rename_fix(owner_id, prefix, oldName, newName, &strip->act->curves, verify_paths);
                /* ignore own F-Curves, since those are local...  */
                
                /* check sub-strips (if metas) */
-               nlastrips_path_rename_fix(owner_id, prefix, oldName, newName, &strip->strips);
+               nlastrips_path_rename_fix(owner_id, prefix, oldName, newName, &strip->strips, verify_paths);
        }
 }
 
@@ -391,41 +597,123 @@ static void nlastrips_path_rename_fix (ID *owner_id, char *prefix, char *oldName
  * NOTE: it is assumed that the structure we're replacing is <prefix><["><name><"]>
  *             i.e. pose.bones["Bone"]
  */
-void BKE_animdata_fix_paths_rename (ID *owner_id, AnimData *adt, char *prefix, char *oldName, char *newName)
+void BKE_animdata_fix_paths_rename (ID *owner_id, AnimData *adt, const char *prefix, char *oldName, char *newName, int oldSubscript, int newSubscript, int verify_paths)
 {
        NlaTrack *nlt;
        char *oldN, *newN;
        
        /* if no AnimData, no need to proceed */
-       if (ELEM4(NULL, owner_id, adt, oldName, newName))
+       if (ELEM(NULL, owner_id, adt))
                return;
        
-       /* pad the names with [" "] so that only exact matches are made */
-       oldN= BLI_sprintfN("[\"%s\"]", oldName);
-       newN= BLI_sprintfN("[\"%s\"]", newName);
+       if ((oldName != NULL) && (newName != NULL)) {
+               /* pad the names with [" "] so that only exact matches are made */
+               oldN= BLI_sprintfN("[\"%s\"]", oldName);
+               newN= BLI_sprintfN("[\"%s\"]", newName);
+       } 
+       else {
+               oldN= BLI_sprintfN("[%d]", oldSubscript);
+               newN= BLI_sprintfN("[%d]", newSubscript);
+       }
        
        /* Active action and temp action */
        if (adt->action)
-               fcurves_path_rename_fix(owner_id, prefix, oldN, newN, &adt->action->curves);
+               fcurves_path_rename_fix(owner_id, prefix, oldN, newN, &adt->action->curves, verify_paths);
        if (adt->tmpact)
-               fcurves_path_rename_fix(owner_id, prefix, oldN, newN, &adt->tmpact->curves);
+               fcurves_path_rename_fix(owner_id, prefix, oldN, newN, &adt->tmpact->curves, verify_paths);
                
        /* Drivers - Drivers are really F-Curves */
-       fcurves_path_rename_fix(owner_id, prefix, oldN, newN, &adt->drivers);
+       drivers_path_rename_fix(owner_id, prefix, oldName, newName, oldN, newN, &adt->drivers, verify_paths);
        
        /* NLA Data - Animation Data for Strips */
        for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next)
-               nlastrips_path_rename_fix(owner_id, prefix, oldN, newN, &nlt->strips);
+               nlastrips_path_rename_fix(owner_id, prefix, oldN, newN, &nlt->strips, verify_paths);
                
        /* free the temp names */
        MEM_freeN(oldN);
        MEM_freeN(newN);
 }
 
+/* Whole Database Ops -------------------------------------------- */
+
+/* apply the given callback function on all data in main database */
+void BKE_animdata_main_cb (Main *mainptr, ID_AnimData_Edit_Callback func, void *user_data)
+{
+       ID *id;
+
+       /* standard data version */
+#define ANIMDATA_IDS_CB(first) \
+       for (id= first; id; id= id->next) { \
+               AnimData *adt= BKE_animdata_from_id(id); \
+               if (adt) func(id, adt, user_data); \
+       }
+
+       /* "embedded" nodetree cases (i.e. scene/material/texture->nodetree) */
+#define ANIMDATA_NODETREE_IDS_CB(first, NtId_Type) \
+       for (id= first; id; id= id->next) { \
+               AnimData *adt= BKE_animdata_from_id(id); \
+               NtId_Type *ntp= (NtId_Type *)id; \
+               if (ntp->nodetree) { \
+                       AnimData *adt2= BKE_animdata_from_id((ID *)ntp); \
+                       if (adt2) func(id, adt2, user_data); \
+               } \
+               if (adt) func(id, adt, user_data); \
+       }
+
+       /* nodes */
+       ANIMDATA_IDS_CB(mainptr->nodetree.first);
+               
+       /* textures */
+       ANIMDATA_NODETREE_IDS_CB(mainptr->tex.first, Tex);
+               
+       /* lamps */
+       ANIMDATA_IDS_CB(mainptr->lamp.first);
+       
+       /* materials */
+       ANIMDATA_NODETREE_IDS_CB(mainptr->mat.first, Material);
+       
+       /* cameras */
+       ANIMDATA_IDS_CB(mainptr->camera.first);
+       
+       /* shapekeys */
+       ANIMDATA_IDS_CB(mainptr->key.first);
+       
+       /* metaballs */
+       ANIMDATA_IDS_CB(mainptr->mball.first);
+       
+       /* curves */
+       ANIMDATA_IDS_CB(mainptr->curve.first);
+       
+       /* armatures */
+       ANIMDATA_IDS_CB(mainptr->armature.first);
+       
+       /* lattices */
+       ANIMDATA_IDS_CB(mainptr->latt.first);
+       
+       /* meshes */
+       ANIMDATA_IDS_CB(mainptr->mesh.first);
+       
+       /* particles */
+       ANIMDATA_IDS_CB(mainptr->particle.first);
+       
+       /* objects */
+       ANIMDATA_IDS_CB(mainptr->object.first);
+       
+       /* worlds */
+       ANIMDATA_IDS_CB(mainptr->world.first);
+       
+       /* scenes */
+       ANIMDATA_NODETREE_IDS_CB(mainptr->scene.first, Scene);
+       
+       /* line styles */
+       ANIMDATA_IDS_CB(mainptr->linestyle.first);
+       }
+
 /* Fix all RNA-Paths throughout the database (directly access the Global.main version)
  * NOTE: it is assumed that the structure we're replacing is <prefix><["><name><"]>
  *             i.e. pose.bones["Bone"]
  */
+/* TODO: use BKE_animdata_main_cb for looping over all data  */
 void BKE_all_animdata_fix_paths_rename (char *prefix, char *oldName, char *newName)
 {
        Main *mainptr= G.main;
@@ -438,20 +726,32 @@ void BKE_all_animdata_fix_paths_rename (char *prefix, char *oldName, char *newNa
 #define RENAMEFIX_ANIM_IDS(first) \
        for (id= first; id; id= id->next) { \
                AnimData *adt= BKE_animdata_from_id(id); \
-               BKE_animdata_fix_paths_rename(id, adt, prefix, oldName, newName);\
+               BKE_animdata_fix_paths_rename(id, adt, prefix, oldName, newName, 0, 0, 1);\
+       }
+       
+       /* another version of this macro for nodetrees */
+#define RENAMEFIX_ANIM_NODETREE_IDS(first, NtId_Type) \
+       for (id= first; id; id= id->next) { \
+               AnimData *adt= BKE_animdata_from_id(id); \
+               NtId_Type *ntp= (NtId_Type *)id; \
+               if (ntp->nodetree) { \
+                       AnimData *adt2= BKE_animdata_from_id((ID *)ntp); \
+                       BKE_animdata_fix_paths_rename((ID *)ntp, adt2, prefix, oldName, newName, 0, 0, 1);\
+               } \
+               BKE_animdata_fix_paths_rename(id, adt, prefix, oldName, newName, 0, 0, 1);\
        }
        
        /* nodes */
        RENAMEFIX_ANIM_IDS(mainptr->nodetree.first);
        
        /* textures */
-       RENAMEFIX_ANIM_IDS(mainptr->tex.first);
+       RENAMEFIX_ANIM_NODETREE_IDS(mainptr->tex.first, Tex);
        
        /* lamps */
        RENAMEFIX_ANIM_IDS(mainptr->lamp.first);
        
        /* materials */
-       RENAMEFIX_ANIM_IDS(mainptr->mat.first);
+       RENAMEFIX_ANIM_NODETREE_IDS(mainptr->mat.first, Material);
        
        /* cameras */
        RENAMEFIX_ANIM_IDS(mainptr->camera.first);
@@ -468,8 +768,11 @@ void BKE_all_animdata_fix_paths_rename (char *prefix, char *oldName, char *newNa
        /* armatures */
        RENAMEFIX_ANIM_IDS(mainptr->armature.first);
        
+       /* lattices */
+       RENAMEFIX_ANIM_IDS(mainptr->latt.first);
+       
        /* meshes */
-       // TODO...
+       RENAMEFIX_ANIM_IDS(mainptr->mesh.first);
        
        /* particles */
        RENAMEFIX_ANIM_IDS(mainptr->particle.first);
@@ -480,22 +783,13 @@ void BKE_all_animdata_fix_paths_rename (char *prefix, char *oldName, char *newNa
        /* worlds */
        RENAMEFIX_ANIM_IDS(mainptr->world.first);
        
+       /* linestyles */
+       RENAMEFIX_ANIM_IDS(mainptr->linestyle.first);
+       
        /* scenes */
-       for (id= mainptr->scene.first; id; id= id->next) {
-               AnimData *adt= BKE_animdata_from_id(id);
-               Scene *scene= (Scene *)id;
-               
-               /* do compositing nodes first (since these aren't included in main tree) */
-               if (scene->nodetree) {
-                       AnimData *adt2= BKE_animdata_from_id((ID *)scene->nodetree);
-                       BKE_animdata_fix_paths_rename((ID *)scene->nodetree, adt2, prefix, oldName, newName);
+       RENAMEFIX_ANIM_NODETREE_IDS(mainptr->scene.first, Scene);
                }
                
-               /* now fix scene animation data as per normal */
-               BKE_animdata_fix_paths_rename((ID *)id, adt, prefix, oldName, newName);
-       }
-}
-
 /* *********************************** */ 
 /* KeyingSet API */
 
@@ -503,20 +797,14 @@ void BKE_all_animdata_fix_paths_rename (char *prefix, char *oldName, char *newNa
 
 /* Find the first path that matches the given criteria */
 // TODO: do we want some method to perform partial matches too?
-KS_Path *BKE_keyingset_find_destination (KeyingSet *ks, ID *id, const char group_name[], const char rna_path[], int array_index, int group_mode)
+KS_Path *BKE_keyingset_find_path (KeyingSet *ks, ID *id, const char group_name[], const char rna_path[], int array_index, int UNUSED(group_mode))
 {
        KS_Path *ksp;
        
        /* sanity checks */
-       if ELEM(NULL, ks, rna_path)
+       if ELEM3(NULL, ks, rna_path, id)
                return NULL;
        
-       /* ID is optional for relative KeyingSets, but is necessary for absolute KeyingSets */
-       if (id == NULL) {
-               if (ks->flag & KEYINGSET_ABSOLUTE)
-                       return NULL;
-       }
-       
        /* loop over paths in the current KeyingSet, finding the first one where all settings match 
         * (i.e. the first one where none of the checks fail and equal 0)
         */
@@ -524,11 +812,11 @@ KS_Path *BKE_keyingset_find_destination (KeyingSet *ks, ID *id, const char group
                short eq_id=1, eq_path=1, eq_index=1, eq_group=1;
                
                /* id */
-               if ((ks->flag & KEYINGSET_ABSOLUTE) && (id != ksp->id))
+               if (id != ksp->id)
                        eq_id= 0;
                
                /* path */
-               if ((ksp->rna_path==0) || strcmp(rna_path, ksp->rna_path))
+               if ((ksp->rna_path==NULL) || strcmp(rna_path, ksp->rna_path))
                        eq_path= 0;
                        
                /* index - need to compare whole-array setting too... */
@@ -558,12 +846,9 @@ KeyingSet *BKE_keyingset_add (ListBase *list, const char name[], short flag, sho
        
        /* allocate new KeyingSet */
        ks= MEM_callocN(sizeof(KeyingSet), "KeyingSet");
-       
-       if (name)
-               BLI_snprintf(ks->name, 64, name);
-       else
-               strcpy(ks->name, "KeyingSet");
-       
+
+       BLI_strncpy(ks->name, name ? name : "KeyingSet", sizeof(ks->name));
+
        ks->flag= flag;
        ks->keyingflag= keyingflag;
        
@@ -571,59 +856,54 @@ KeyingSet *BKE_keyingset_add (ListBase *list, const char name[], short flag, sho
        BLI_addtail(list, ks);
        
        /* make sure KeyingSet has a unique name (this helps with identification) */
-       BLI_uniquename(list, ks, "KeyingSet", '.', offsetof(KeyingSet, name), 64);
+       BLI_uniquename(list, ks, "KeyingSet", '.', offsetof(KeyingSet, name), sizeof(ks->name));
        
        /* return new KeyingSet for further editing */
        return ks;
 }
 
-/* Add a destination to a KeyingSet. Nothing is returned for now...
+/* Add a path to a KeyingSet. Nothing is returned for now...
  * Checks are performed to ensure that destination is appropriate for the KeyingSet in question
  */
-void BKE_keyingset_add_destination (KeyingSet *ks, ID *id, const char group_name[], const char rna_path[], int array_index, short flag, short groupmode)
+KS_Path *BKE_keyingset_add_path (KeyingSet *ks, ID *id, const char group_name[], const char rna_path[], int array_index, short flag, short groupmode)
 {
        KS_Path *ksp;
        
        /* sanity checks */
        if ELEM(NULL, ks, rna_path) {
-               printf("ERROR: no Keying Set and/or RNA Path to add destination with \n");
-               return;
+               printf("ERROR: no Keying Set and/or RNA Path to add path with \n");
+               return NULL;
        }
        
-       /* ID is optional for relative KeyingSets, but is necessary for absolute KeyingSets */
+       /* ID is required for all types of KeyingSets */
        if (id == NULL) {
-               if (ks->flag & KEYINGSET_ABSOLUTE) {
-                       printf("ERROR: No ID provided for absolute destination. \n");
-                       return;
-               }
+               printf("ERROR: No ID provided for Keying Set Path. \n");
+               return NULL;
        }
        
        /* don't add if there is already a matching KS_Path in the KeyingSet */
-       if (BKE_keyingset_find_destination(ks, id, group_name, rna_path, array_index, groupmode)) {
+       if (BKE_keyingset_find_path(ks, id, group_name, rna_path, array_index, groupmode)) {
                if (G.f & G_DEBUG)
                        printf("ERROR: destination already exists in Keying Set \n");
-               return;
+               return NULL;
        }
        
        /* allocate a new KeyingSet Path */
        ksp= MEM_callocN(sizeof(KS_Path), "KeyingSet Path");
        
        /* just store absolute info */
-       if (ks->flag & KEYINGSET_ABSOLUTE) {
-               ksp->id= id;
-               if (group_name)
-                       BLI_snprintf(ksp->group, 64, group_name);
-               else
-                       strcpy(ksp->group, "");
-       }
+       ksp->id= id;
+       if (group_name)
+               BLI_strncpy(ksp->group, group_name, sizeof(ksp->group));
+       else
+               ksp->group[0]= '\0';
        
        /* store additional info for relative paths (just in case user makes the set relative) */
        if (id)
                ksp->idtype= GS(id->name);
        
        /* just copy path info */
-       // XXX no checks are performed for templates yet
-       // should array index be checked too?
+       // TODO: should array index be checked too?
        ksp->rna_path= BLI_strdupn(rna_path, strlen(rna_path));
        ksp->array_index= array_index;
        
@@ -633,20 +913,38 @@ void BKE_keyingset_add_destination (KeyingSet *ks, ID *id, const char group_name
        
        /* add KeyingSet path to KeyingSet */
        BLI_addtail(&ks->paths, ksp);
+       
+       /* return this path */
+       return ksp;
 }      
 
+/* Free the given Keying Set path */
+void BKE_keyingset_free_path (KeyingSet *ks, KS_Path *ksp)
+{
+       /* sanity check */
+       if ELEM(NULL, ks, ksp)
+               return;
+
+       /* free RNA-path info */
+       if(ksp->rna_path)
+               MEM_freeN(ksp->rna_path);
+
+       /* free path itself */
+       BLI_freelinkN(&ks->paths, ksp);
+}
+
 /* Copy all KeyingSets in the given list */
-void BKE_keyingsets_copy(ListBase *newlist, ListBase *list)
+void BKE_keyingsets_copy (ListBase *newlist, ListBase *list)
 {
        KeyingSet *ksn;
        KS_Path *kspn;
-
+       
        BLI_duplicatelist(newlist, list);
 
-       for(ksn=newlist->first; ksn; ksn=ksn->next) {
+       for (ksn=newlist->first; ksn; ksn=ksn->next) {
                BLI_duplicatelist(&ksn->paths, &ksn->paths);
-
-               for(kspn=ksn->paths.first; kspn; kspn=kspn->next)
+               
+               for (kspn=ksn->paths.first; kspn; kspn=kspn->next)
                        kspn->rna_path= MEM_dupallocN(kspn->rna_path);
        }
 }
@@ -665,12 +963,7 @@ void BKE_keyingset_free (KeyingSet *ks)
        /* free each path as we go to avoid looping twice */
        for (ksp= ks->paths.first; ksp; ksp= kspn) {
                kspn= ksp->next;
-               
-               /* free RNA-path info */
-               MEM_freeN(ksp->rna_path);
-               
-               /* free path itself */
-               BLI_freelinkN(&ks->paths, ksp);
+               BKE_keyingset_free_path(ks, ksp);
        }
 }
 
@@ -704,7 +997,7 @@ void BKE_keyingsets_free (ListBase *list)
  *     - path: original path string (as stored in F-Curve data)
  *     - dst: destination string to write data to
  */
-static short animsys_remap_path (AnimMapper *remap, char *path, char **dst)
+static short animsys_remap_path (AnimMapper *UNUSED(remap), char *path, char **dst)
 {
        /* is there a valid remapping table to use? */
        //if (remap) {
@@ -724,28 +1017,43 @@ static short animsys_write_rna_setting (PointerRNA *ptr, char *path, int array_i
        PropertyRNA *prop;
        PointerRNA new_ptr;
        
+       //printf("%p %s %i %f\n", ptr, path, array_index, value);
+       
        /* get property to write to */
        if (RNA_path_resolve(ptr, path, &new_ptr, &prop)) 
        {
                /* set value - only for animatable numerical values */
                if (RNA_property_animateable(&new_ptr, prop)) 
                {
+                       int array_len= RNA_property_array_length(&new_ptr, prop);
+                       
+                       if(array_len && array_index >= array_len)
+                       {
+                               if (G.f & G_DEBUG) {
+                                       printf("Animato: Invalid array index. ID = '%s',  '%s[%d]', array length is %d \n",
+                                               (ptr && ptr->id.data) ? (((ID *)ptr->id.data)->name+2) : "<No ID>",
+                                               path, array_index, array_len-1);
+                               }
+                               
+                               return 0;
+                       }
+                       
                        switch (RNA_property_type(prop)) 
                        {
                                case PROP_BOOLEAN:
-                                       if (RNA_property_array_length(&new_ptr, prop))
+                                       if (array_len)
                                                RNA_property_boolean_set_index(&new_ptr, prop, array_index, (int)value);
                                        else
                                                RNA_property_boolean_set(&new_ptr, prop, (int)value);
                                        break;
                                case PROP_INT:
-                                       if (RNA_property_array_length(&new_ptr, prop))
+                                       if (array_len)
                                                RNA_property_int_set_index(&new_ptr, prop, array_index, (int)value);
                                        else
                                                RNA_property_int_set(&new_ptr, prop, (int)value);
                                        break;
                                case PROP_FLOAT:
-                                       if (RNA_property_array_length(&new_ptr, prop))
+                                       if (array_len)
                                                RNA_property_float_set_index(&new_ptr, prop, array_index, value);
                                        else
                                                RNA_property_float_set(&new_ptr, prop, value);
@@ -767,7 +1075,7 @@ static short animsys_write_rna_setting (PointerRNA *ptr, char *path, int array_i
                // XXX don't tag as failed yet though, as there are some legit situations (Action Constraint) 
                // where some channels will not exist, but shouldn't lock up Action
                if (G.f & G_DEBUG) {
-                       printf("Animato: Invalid path. ID = '%s',  '%s [%d]' \n", 
+                       printf("Animato: Invalid path. ID = '%s',  '%s[%d]' \n",
                                (ptr && ptr->id.data) ? (((ID *)ptr->id.data)->name+2) : "<No ID>", 
                                path, array_index);
                }
@@ -939,6 +1247,14 @@ static void nlastrip_evaluate_controls (NlaStrip *strip, float ctime)
                /* execute these settings as per normal */
                animsys_evaluate_fcurves(&strip_ptr, &strip->fcurves, NULL, ctime);
        }
+
+       /* if user can control the evaluation time (using F-Curves), consider the option which allows this time to be clamped 
+        * to lie within extents of the action-clip, so that a steady changing rate of progress through several cycles of the clip
+        * can be achieved easily
+        */
+       // NOTE: if we add any more of these special cases, we better group them up nicely...
+       if ((strip->flag & NLASTRIP_FLAG_USR_TIME) && (strip->flag & NLASTRIP_FLAG_USR_TIME_CYCLIC))
+               strip->strip_time= fmod(strip->strip_time - strip->actstart, strip->actend - strip->actstart);
 }
 
 /* gets the strip active at the current time for a list of strips for evaluation purposes */
@@ -1505,9 +1821,6 @@ void nladata_flush_channels (ListBase *channels)
  */
 static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
 {
-       ListBase dummy_trackslist = {NULL, NULL};
-       NlaStrip dummy_strip;
-       
        NlaTrack *nlt;
        short track_index=0;
        short has_strips = 0;
@@ -1521,7 +1834,7 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
        
        /* 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++) { 
-               /* if tweaking is on and this strip is the tweaking track, stop on this one */
+               /* stop here if tweaking is on and this strip is the tweaking track (it will be the first one that's 'disabled')... */
                if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED))
                        break;
                        
@@ -1548,22 +1861,32 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
         */
        if ((adt->action) && !(adt->flag & ADT_NLA_SOLO_TRACK)) {
                /* if there are strips, evaluate action as per NLA rules */
-               if (has_strips) {
+               if ((has_strips) || (adt->actstrip)) {
                        /* make dummy NLA strip, and add that to the stack */
-                       memset(&dummy_strip, 0, sizeof(NlaStrip));
-                       dummy_trackslist.first= dummy_trackslist.last= &dummy_strip;
+                       NlaStrip dummy_strip= {NULL};
+                       ListBase dummy_trackslist;
                        
-                       dummy_strip.act= adt->action;
-                       dummy_strip.remap= adt->remap;
-                       
-                       /* action range is calculated taking F-Modifiers into account (which making new strips doesn't do due to the troublesome nature of that) */
-                       calc_action_range(dummy_strip.act, &dummy_strip.actstart, &dummy_strip.actend, 1);
-                       dummy_strip.start = dummy_strip.actstart;
-                       dummy_strip.end = (IS_EQ(dummy_strip.actstart, dummy_strip.actend)) ?  (dummy_strip.actstart + 1.0f): (dummy_strip.actend);
+                       dummy_trackslist.first= dummy_trackslist.last= &dummy_strip;
                        
-                       dummy_strip.blendmode= adt->act_blendmode;
-                       dummy_strip.extendmode= adt->act_extendmode;
-                       dummy_strip.influence= adt->act_influence;
+                       if ((nlt) && !(adt->flag & ADT_NLA_EDIT_NOMAP)) {
+                               /* edit active action in-place according to its active strip, so copy the data  */
+                               memcpy(&dummy_strip, adt->actstrip, sizeof(NlaStrip));
+                               dummy_strip.next = dummy_strip.prev = NULL;
+                       }
+                       else {
+                               /* set settings of dummy NLA strip from AnimData settings */
+                               dummy_strip.act= adt->action;
+                               dummy_strip.remap= adt->remap;
+                               
+                               /* action range is calculated taking F-Modifiers into account (which making new strips doesn't do due to the troublesome nature of that) */
+                               calc_action_range(dummy_strip.act, &dummy_strip.actstart, &dummy_strip.actend, 1);
+                               dummy_strip.start = dummy_strip.actstart;
+                               dummy_strip.end = (IS_EQ(dummy_strip.actstart, dummy_strip.actend)) ?  (dummy_strip.actstart + 1.0f): (dummy_strip.actend);
+                               
+                               dummy_strip.blendmode= adt->act_blendmode;
+                               dummy_strip.extendmode= adt->act_extendmode;
+                               dummy_strip.influence= adt->act_influence;
+                       }
                        
                        /* add this to our list of evaluation strips */
                        nlastrips_ctime_get_strip(&estrips, &dummy_trackslist, -1, ctime);
@@ -1599,16 +1922,18 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
 /* Clear all overides */
 
 /* Add or get existing Override for given setting */
-AnimOverride *BKE_animsys_validate_override (PointerRNA *ptr, char *path, int array_index)
+#if 0
+AnimOverride *BKE_animsys_validate_override (PointerRNA *UNUSED(ptr), char *UNUSED(path), int UNUSED(array_index))
 {
        // FIXME: need to define how to get overrides
        return NULL;
 } 
+#endif
 
 /* -------------------- */
 
 /* Evaluate Overrides */
-static void animsys_evaluate_overrides (PointerRNA *ptr, AnimData *adt, float ctime)
+static void animsys_evaluate_overrides (PointerRNA *ptr, AnimData *adt)
 {
        AnimOverride *aor;
        
@@ -1674,7 +1999,7 @@ void BKE_animsys_evaluate_animdata (ID *id, AnimData *adt, float ctime, short re
         */
        // 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))
                {
@@ -1707,7 +2032,7 @@ void BKE_animsys_evaluate_animdata (ID *id, AnimData *adt, float ctime, short re
         *      - Overrides are cleared upon frame change and/or keyframing
         *      - It is best that we execute this everytime, so that no errors are likely to occur.
         */
-       animsys_evaluate_overrides(&id_ptr, adt, ctime);
+       animsys_evaluate_overrides(&id_ptr, adt);
        
        /* clear recalc flag now */
        adt->recalc= 0;
@@ -1727,16 +2052,35 @@ void BKE_animsys_evaluate_all_animation (Main *main, float ctime)
        if (G.f & G_DEBUG)
                printf("Evaluate all animation - %f \n", ctime);
        
-       /* macro for less typing 
+       /* macros for less typing 
         *      - only evaluate animation data for id if it has users (and not just fake ones)
         *      - whether animdata exists is checked for by the evaluation function, though taking 
         *        this outside of the function may make things slightly faster?
         */
 #define EVAL_ANIM_IDS(first, aflag) \
        for (id= first; id; id= id->next) { \
-               AnimData *adt= BKE_animdata_from_id(id); \
-               if ( (id->us > 1) || (id->us && !(id->flag & LIB_FAKEUSER)) ) \
+               if (ID_REAL_USERS(id) > 0) { \
+                       AnimData *adt= BKE_animdata_from_id(id); \
+                       BKE_animsys_evaluate_animdata(id, adt, ctime, aflag); \
+               } \
+       }
+       /* another macro for the "embedded" nodetree cases 
+        *      - this is like EVAL_ANIM_IDS, but this handles the case "embedded nodetrees" 
+        *        (i.e. scene/material/texture->nodetree) which we need a special exception
+        *        for, otherwise they'd get skipped
+        *      - ntp = "node tree parent" = datablock where node tree stuff resides
+        */
+#define EVAL_ANIM_NODETREE_IDS(first, NtId_Type, aflag) \
+       for (id= first; id; id= id->next) { \
+               if (ID_REAL_USERS(id) > 0) { \
+                       AnimData *adt= BKE_animdata_from_id(id); \
+                       NtId_Type *ntp= (NtId_Type *)id; \
+                       if (ntp->nodetree) { \
+                               AnimData *adt2= BKE_animdata_from_id((ID *)ntp->nodetree); \
+                               BKE_animsys_evaluate_animdata((ID *)ntp->nodetree, adt2, ctime, ADT_RECALC_ANIM); \
+                       } \
                        BKE_animsys_evaluate_animdata(id, adt, ctime, aflag); \
+               } \
        }
        
        /* optimisation: 
@@ -1758,51 +2102,41 @@ void BKE_animsys_evaluate_all_animation (Main *main, float ctime)
        EVAL_ANIM_IDS(main->nodetree.first, ADT_RECALC_ANIM);
        
        /* textures */
-       EVAL_ANIM_IDS(main->tex.first, ADT_RECALC_ANIM);
+       EVAL_ANIM_NODETREE_IDS(main->tex.first, Tex, ADT_RECALC_ANIM);
        
        /* lamps */
        EVAL_ANIM_IDS(main->lamp.first, ADT_RECALC_ANIM);
        
        /* materials */
-       EVAL_ANIM_IDS(main->mat.first, ADT_RECALC_ANIM);
+       EVAL_ANIM_NODETREE_IDS(main->mat.first, Material, ADT_RECALC_ANIM);
        
        /* cameras */
        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);
        
        /* metaballs */
        EVAL_ANIM_IDS(main->mball.first, ADT_RECALC_ANIM);
        
        /* curves */
-               /* 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...
-                *      - it shouldn't get set when calculating drivers...
-                */
-       for (id= main->curve.first; id; id= id->next) {
-               AnimData *adt= BKE_animdata_from_id(id);
-               Curve *cu= (Curve *)id;
-               
-               /* set ctime variable for curve */
-               cu->ctime= ctime;
-               
-               /* now execute animation data on top of this as per normal */
-               BKE_animsys_evaluate_animdata(id, adt, ctime, ADT_RECALC_ANIM);
-       }
+       EVAL_ANIM_IDS(main->curve.first, ADT_RECALC_ANIM);
        
        /* armatures */
        EVAL_ANIM_IDS(main->armature.first, ADT_RECALC_ANIM);
        
+       /* lattices */
+       EVAL_ANIM_IDS(main->latt.first, ADT_RECALC_ANIM);
+       
        /* meshes */
        EVAL_ANIM_IDS(main->mesh.first, ADT_RECALC_ANIM);
        
        /* particles */
        EVAL_ANIM_IDS(main->particle.first, ADT_RECALC_ANIM);
        
+       /* linestyles */
+       EVAL_ANIM_IDS(main->linestyle.first, ADT_RECALC_ANIM);
+       
        /* objects */
                /* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets 
                 * this tagged by Depsgraph on framechange. This optimisation means that objects
@@ -1814,19 +2148,7 @@ void BKE_animsys_evaluate_all_animation (Main *main, float ctime)
        EVAL_ANIM_IDS(main->world.first, ADT_RECALC_ANIM);
        
        /* scenes */
-       for (id= main->scene.first; id; id= id->next) {
-               AnimData *adt= BKE_animdata_from_id(id);
-               Scene *scene= (Scene *)id;
-               
-               /* do compositing nodes first (since these aren't included in main tree) */
-               if (scene->nodetree) {
-                       AnimData *adt2= BKE_animdata_from_id((ID *)scene->nodetree);
-                       BKE_animsys_evaluate_animdata((ID *)scene->nodetree, adt2, ctime, ADT_RECALC_ANIM);
-               }
-               
-               /* now execute scene animation data as per normal */
-               BKE_animsys_evaluate_animdata(id, adt, ctime, ADT_RECALC_ANIM);
-       }
+       EVAL_ANIM_NODETREE_IDS(main->scene.first, Scene, ADT_RECALC_ANIM);
 }
 
 /* ***************************************** */