use static functions where possible for some local functions.
[blender.git] / source / blender / blenkernel / intern / ipo.c
index 103e2f7edac03462859656b0374a88d20a6e238d..62f44d92d25c10f83fc7db18d7375527ec0546d7 100644 (file)
@@ -39,6 +39,7 @@
 #include <math.h>
 #include <stdio.h>
 #include <string.h>
+#include <stddef.h>
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -57,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"
@@ -84,6 +86,7 @@
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_mesh.h"
+#include "BKE_nla.h"
 #include "BKE_object.h"
 
 
@@ -157,9 +160,9 @@ static AdrBit2Path ma_mode_bits[]= {
 //     {MA_SHADOW, "shadow", 0},
 //     {MA_SHLESS, "shadeless", 0},
 //     ...
-       {MA_RAYTRANSP, "raytrace_transparency.enabled", 0},
+       {MA_RAYTRANSP, "transparency", 0},
        {MA_RAYMIRROR, "raytrace_mirror.enabled", 0},
-       {MA_HALO, "halo.enabled", 0}
+//     {MA_HALO, "type", MA_TYPE_HALO}
 };
 
 /* ----------------- */
@@ -234,17 +237,15 @@ static char *ob_adrcodes_to_paths (int adrcode, int *array_index)
                        *array_index= 1; return "delta_scale";
                case OB_DSIZE_Z:
                        *array_index= 2; return "delta_scale";
-       
-#if 0  
-               case OB_COL_R:  
-                       poin= &(ob->col[0]); break;
+               case OB_COL_R:
+                       *array_index= 0; return "color";
                case OB_COL_G:
-                       poin= &(ob->col[1]); break;
+                       *array_index= 1; return "color";
                case OB_COL_B:
-                       poin= &(ob->col[2]); break;
+                       *array_index= 2; return "color";
                case OB_COL_A:
-                       poin= &(ob->col[3]); break;
-                       
+                       *array_index= 3; return "color";
+#if 0
                case OB_PD_FSTR:
                        if (ob->pd) poin= &(ob->pd->f_strength);
                        break;
@@ -295,8 +296,8 @@ static char *pchan_adrcodes_to_paths (int adrcode, int *array_index)
                case AC_EUL_Z:
                        *array_index= 2; return "euler_rotation";
                        
-               case -1: // XXX special case for rotation drivers... until eulers are added...
-                       *array_index= 0; return "rotation";
+               case -1: /* special case for euler-rotations used by old drivers */
+                       *array_index= 0; return "euler_rotation";
                        
                case AC_LOC_X:
                        *array_index= 0; return "location";
@@ -544,7 +545,7 @@ static char *material_adrcodes_to_paths (int adrcode, int *array_index)
                        return "ambient";
                
                case MA_SPEC:
-                       return "specularity";
+                       return "specular_reflection";
                
                case MA_HARD:
                        return "specular_hardness";
@@ -743,6 +744,72 @@ static char *world_adrcodes_to_paths (int adrcode, int *array_index)
        return NULL;    
 }
 
+/* Particle Types */
+static char *particle_adrcodes_to_paths (int adrcode, int *array_index)
+{
+       /* set array index like this in-case nothing sets it correctly  */
+       *array_index= 0;
+       
+       /* result depends on adrcode */
+       switch (adrcode) {
+               case PART_CLUMP:
+                       return "settings.clump_factor";
+               case PART_AVE:
+                       return "settings.angular_velocity_factor";
+               case PART_SIZE:
+                       return "settings.particle_size";
+               case PART_DRAG:
+                       return "settings.drag_factor";
+               case PART_BROWN:
+                       return "settings.brownian_factor";
+               case PART_DAMP:
+                       return "settings.damp_factor";
+               case PART_LENGTH:
+                       return "settings.length";
+               case PART_GRAV_X:
+                       *array_index= 0; return "settings.acceleration";
+               case PART_GRAV_Y:
+                       *array_index= 1; return "settings.acceleration";
+               case PART_GRAV_Z:
+                       *array_index= 2; return "settings.acceleration";
+               case PART_KINK_AMP:
+                       return "settings.kink_amplitude";
+               case PART_KINK_FREQ:
+                       return "settings.kink_frequency";
+               case PART_KINK_SHAPE:
+                       return "settings.kink_shape";
+               case PART_BB_TILT:
+                       return "settings.billboard_tilt";
+               
+               /* PartDeflect needs to be sorted out properly in rna_object_force;
+                  If anyone else works on this, but is unfamiliar, these particular
+                       settings reference the particles of the system themselves
+                       being used as forces -- it will use the same rna structure
+                       as the similar object forces                            */
+               /*case PART_PD_FSTR:
+                       if (part->pd) poin= &(part->pd->f_strength);
+                       break;
+               case PART_PD_FFALL:
+                       if (part->pd) poin= &(part->pd->f_power);
+                       break;
+               case PART_PD_FMAXD:
+                       if (part->pd) poin= &(part->pd->maxdist);
+                       break;
+               case PART_PD2_FSTR:
+                       if (part->pd2) poin= &(part->pd2->f_strength);
+                       break;
+               case PART_PD2_FFALL:
+                       if (part->pd2) poin= &(part->pd2->f_power);
+                       break;
+               case PART_PD2_FMAXD:
+                       if (part->pd2) poin= &(part->pd2->maxdist);
+                       break;*/
+
+               }
+               
+       return NULL;    
+}
+
 /* ------- */
 
 /* Allocate memory for RNA-path for some property given a blocktype, adrcode, and 'root' parts of path
@@ -753,13 +820,17 @@ static char *world_adrcodes_to_paths (int adrcode, int *array_index)
  *             - array_index                   - index in property's array (if applicable) to use
  *             - return                                - the allocated path...
  */
-char *get_rna_access (int blocktype, int adrcode, char actname[], char constname[], int *array_index)
+static char *get_rna_access (int blocktype, int adrcode, char actname[], char constname[], int *array_index)
 {
        DynStr *path= BLI_dynstr_new();
        char *propname=NULL, *rpath=NULL;
        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 */
@@ -775,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 */
@@ -799,10 +870,16 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname
                
                case ID_WO: /* world */
                        propname= world_adrcodes_to_paths(adrcode, &dummy_index);
+
+               case ID_PA: /* particle */
+                       propname= particle_adrcodes_to_paths(adrcode, &dummy_index);
                        
                /* 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 */
@@ -880,6 +957,7 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname
 static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver)
 {
        ChannelDriver *cdriver;
+       DriverTarget *dtar=NULL, *dtar2=NULL;
        
        /* allocate memory for new driver */
        cdriver= MEM_callocN(sizeof(ChannelDriver), "ChannelDriver");
@@ -887,6 +965,7 @@ static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver)
        /* if 'pydriver', just copy data across */
        if (idriver->type == IPO_DRIVER_TYPE_PYTHON) {
                /* PyDriver only requires the expression to be copied */
+               // TODO: but the expression will be useless...
                cdriver->type = DRIVER_TYPE_PYTHON;
                strcpy(cdriver->expression, idriver->name); // XXX is this safe? 
        }
@@ -895,12 +974,15 @@ static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver)
                if (idriver->blocktype == ID_AR) {
                        /* ID_PO */
                        if (idriver->adrcode == OB_ROT_DIFF) {
-                               if (G.f & G_DEBUG) printf("idriver_to_cdriver - rotdiff %p \n", idriver->ob);
                                /* Rotational Difference is a special type of driver now... */
                                cdriver->type= DRIVER_TYPE_ROTDIFF;
                                
+                               /* make 2 driver targets */
+                               dtar= driver_add_new_target(cdriver);
+                               dtar2= driver_add_new_target(cdriver);
+                               
                                /* driver must use bones from same armature... */
-                               cdriver->id= cdriver->id2= (ID *)idriver->ob;
+                               dtar->id= dtar2->id= (ID *)idriver->ob;
                                
                                /* paths for the two targets get the pointers to the relevant Pose-Channels 
                                 *      - return pointers to Pose-Channels not rotation channels, as calculation code is picky
@@ -909,44 +991,45 @@ static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver)
                                 *      - we use several hacks here - blocktype == -1 specifies that no property needs to be found, and
                                 *        providing a name for 'actname' will automatically imply Pose-Channel with name 'actname'
                                 */
-                               cdriver->rna_path= get_rna_access(-1, -1, idriver->name, NULL, NULL);
-                               cdriver->rna_path2= get_rna_access(-1, -1, idriver->name+DRIVER_NAME_OFFS, NULL, NULL);
+                               dtar->rna_path= get_rna_access(-1, -1, idriver->name, NULL, NULL);
+                               dtar2->rna_path= get_rna_access(-1, -1, idriver->name+DRIVER_NAME_OFFS, NULL, NULL);
                        }
                        else {
-                               if (G.f & G_DEBUG) printf("idriver_to_cdriver - arm  %p \n", idriver->ob);
                                /* 'standard' driver */
-                               cdriver->type= DRIVER_TYPE_CHANNEL;
-                               cdriver->id= (ID *)idriver->ob;
+                               cdriver->type= DRIVER_TYPE_AVERAGE;
+                               
+                               /* make 1 driver target */
+                               dtar= driver_add_new_target(cdriver);
+                               dtar->id= (ID *)idriver->ob;
                                
                                switch (idriver->adrcode) {
                                        case OB_LOC_X:  /* x,y,z location are quite straightforward */
-                                               cdriver->rna_path= get_rna_access(ID_PO, AC_LOC_X, idriver->name, NULL, &cdriver->array_index);
+                                               dtar->rna_path= get_rna_access(ID_PO, AC_LOC_X, idriver->name, NULL, &dtar->array_index);
                                                break;
                                        case OB_LOC_Y:
-                                               cdriver->rna_path= get_rna_access(ID_PO, AC_LOC_Y, idriver->name, NULL, &cdriver->array_index);
+                                               dtar->rna_path= get_rna_access(ID_PO, AC_LOC_Y, idriver->name, NULL, &dtar->array_index);
                                                break;
                                        case OB_LOC_Z:
-                                               cdriver->rna_path= get_rna_access(ID_PO, AC_LOC_Z, idriver->name, NULL, &cdriver->array_index);
+                                               dtar->rna_path= get_rna_access(ID_PO, AC_LOC_Z, idriver->name, NULL, &dtar->array_index);
                                                break;
                                                
                                        case OB_SIZE_X: /* x,y,z scaling are also quite straightforward */
-                                               cdriver->rna_path= get_rna_access(ID_PO, AC_SIZE_X, idriver->name, NULL, &cdriver->array_index);
+                                               dtar->rna_path= get_rna_access(ID_PO, AC_SIZE_X, idriver->name, NULL, &dtar->array_index);
                                                break;
                                        case OB_SIZE_Y:
-                                               cdriver->rna_path= get_rna_access(ID_PO, AC_SIZE_Y, idriver->name, NULL, &cdriver->array_index);
+                                               dtar->rna_path= get_rna_access(ID_PO, AC_SIZE_Y, idriver->name, NULL, &dtar->array_index);
                                                break;
                                        case OB_SIZE_Z:
-                                               cdriver->rna_path= get_rna_access(ID_PO, AC_SIZE_Z, idriver->name, NULL, &cdriver->array_index);
+                                               dtar->rna_path= get_rna_access(ID_PO, AC_SIZE_Z, idriver->name, NULL, &dtar->array_index);
                                                break;  
                                                
-                                       case OB_ROT_X:  /* rotation - we need to be careful with this... XXX (another reason why we need eulers) */     
+                                       case OB_ROT_X:  /* rotation - we need to be careful with this... */     
                                        case OB_ROT_Y:
                                        case OB_ROT_Z:
                                        {
-                                               // XXX this is not yet a 1:1 map, since we'd need euler rotations to make this work nicely (unless we make some hacks)
-                                               // XXX -1 here is a special hack...
-                                               cdriver->rna_path= get_rna_access(ID_PO, -1, idriver->name, NULL, NULL);
-                                               cdriver->array_index= idriver->adrcode - OB_ROT_X;
+                                               /* -1 here, not rotation code, since old system didn't have eulers */
+                                               dtar->rna_path= get_rna_access(ID_PO, -1, idriver->name, NULL, NULL);
+                                               dtar->array_index= idriver->adrcode - OB_ROT_X;
                                        }
                                                break;
                                }
@@ -954,14 +1037,16 @@ static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver)
                }
                else {
                        /* ID_OB */
-                       if (G.f & G_DEBUG) printf("idriver_to_cdriver  - ob %p \n", idriver->ob);
-                       cdriver->type= DRIVER_TYPE_CHANNEL;
-                       cdriver->id= (ID *)idriver->ob;
-                       cdriver->rna_path= get_rna_access(ID_OB, idriver->adrcode, NULL, NULL, &cdriver->array_index);
+                       cdriver->type= DRIVER_TYPE_AVERAGE;
+                       
+                       /* make 1 driver target */
+                       dtar= driver_add_new_target(cdriver);
+                       
+                       dtar->id= (ID *)idriver->ob;
+                       dtar->rna_path= get_rna_access(ID_OB, idriver->adrcode, NULL, NULL, &dtar->array_index);
                }
        }
        
-       if (G.f & G_DEBUG) printf("\tcdriver -> id = %p \n", cdriver->id);
        
        /* free old driver */
        MEM_freeN(idriver);
@@ -970,12 +1055,65 @@ static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver)
        return cdriver;
 }
 
+/* Add F-Curve to the correct list 
+ *     - grpname is needed to be used as group name where relevant, and is usually derived from actname
+ */
+static void fcurve_add_to_list (ListBase *groups, ListBase *list, FCurve *fcu, char *grpname)
+{
+       /* If we're adding to an action, we will have groups to write to... */
+       if (groups && grpname) {
+               /* wrap the pointers given into a dummy action that we pass to the API func
+                * and extract the resultant lists...
+                */
+               bAction tmp_act;
+               bActionGroup *agrp= NULL;
+               
+               /* init the temp action */
+               //memset(&tmp_act, 0, sizeof(bAction)); // XXX only enable this line if we get errors
+               tmp_act.groups.first= groups->first;
+               tmp_act.groups.last= groups->last;
+               tmp_act.curves.first= list->first;
+               tmp_act.curves.last= list->last;
+               /* ... xxx, the other vars don't need to be filled in */
+               
+               /* get the group to use */
+               agrp= action_groups_find_named(&tmp_act, grpname);
+               if (agrp == NULL) {
+                       /* no matching group, so add one */
+                       if (agrp == NULL) {
+                               /* Add a new group, and make it active */
+                               agrp= MEM_callocN(sizeof(bActionGroup), "bActionGroup");
+                               
+                               agrp->flag = AGRP_SELECTED;
+                               BLI_snprintf(agrp->name, 64, grpname);
+                               
+                               BLI_addtail(&tmp_act.groups, agrp);
+                               BLI_uniquename(&tmp_act.groups, agrp, "Group", '.', offsetof(bActionGroup, name), 64);
+                       }
+               }
+               
+               /* add F-Curve to group */
+               /* WARNING: this func should only need to look at the stuff we initialised, if not, things may crash */
+               action_groups_add_channel(&tmp_act, agrp, fcu);
+               
+               /* set the output lists based on the ones in the temp action */
+               groups->first= tmp_act.groups.first;
+               groups->last= tmp_act.groups.last;
+               list->first= tmp_act.curves.first;
+               list->last= tmp_act.curves.last;
+       }
+       else {
+               /* simply add the F-Curve to the end of the given list */
+               BLI_addtail(list, fcu);
+       }
+}
+
 /* Convert IPO-Curve to F-Curve (including Driver data), and free any of the old data that 
  * is not relevant, BUT do not free the IPO-Curve itself...
  *     actname: name of Action-Channel (if applicable) that IPO-Curve's IPO-block belonged to
  *     constname: name of Constraint-Channel (if applicable) that IPO-Curve's IPO-block belonged to
  */
-static void icu_to_fcurves (ListBase *list, IpoCurve *icu, char *actname, char *constname)
+static void icu_to_fcurves (ListBase *groups, ListBase *list, IpoCurve *icu, char *actname, char *constname)
 {
        AdrBit2Path *abp;
        FCurve *fcu;
@@ -1014,7 +1152,7 @@ static void icu_to_fcurves (ListBase *list, IpoCurve *icu, char *actname, char *
                        /* 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 */
@@ -1037,7 +1175,7 @@ static void icu_to_fcurves (ListBase *list, IpoCurve *icu, char *actname, char *
                if (G.f & G_DEBUG) printf("\tconvert bitflag ipocurve, totbits = %d \n", totbits);
                
                /* add the 'only int values' flag */
-               fcu->flag |= FCURVE_INT_VALUES;         
+               fcu->flag |= (FCURVE_INT_VALUES|FCURVE_DISCRETE_VALUES);                
                
                /* for each bit we have to remap + check for:
                 * 1) we need to make copy the existing F-Curve data (fcu -> fcurve),
@@ -1090,7 +1228,7 @@ static void icu_to_fcurves (ListBase *list, IpoCurve *icu, char *actname, char *
                        }
                        
                        /* add new F-Curve to list */
-                       BLI_addtail(list, fcurve);
+                       fcurve_add_to_list(groups, list, fcurve, actname);
                }
                
                /* free old data of curve now that it's no longer needed for converting any more curves */
@@ -1137,6 +1275,26 @@ static void icu_to_fcurves (ListBase *list, IpoCurve *icu, char *actname, char *
                                        dst->vec[1][1] *= fac;
                                        dst->vec[2][1] *= fac;
                                }
+                               
+                               /* correct times for rotation drivers 
+                                *      - need to go from degrees to radians...
+                                *      - there's only really 1 target to worry about 
+                                */
+                               if (fcu->driver && fcu->driver->targets.first) {
+                                       DriverTarget *dtar= fcu->driver->targets.first;
+                                       
+                                       /* since drivers could only be for objects, we should just check for 'rotation' being 
+                                        * in the name of the path given
+                                        *      - WARNING: this will break if we encounter a bone or object explictly named in that way...
+                                        */
+                                       if ((dtar && dtar->rna_path) && strstr(dtar->rna_path, "rotation")) {
+                                               const float fac= (float)M_PI / 180.0f;
+                                               
+                                               dst->vec[0][0] *= fac;
+                                               dst->vec[1][0] *= fac;
+                                               dst->vec[2][0] *= fac;
+                                       }
+                               }
                        }
                        
                        /* free this data now */
@@ -1149,7 +1307,7 @@ static void icu_to_fcurves (ListBase *list, IpoCurve *icu, char *actname, char *
                }
                
                /* add new F-Curve to list */
-               BLI_addtail(list, fcu);
+               fcurve_add_to_list(groups, list, fcu, actname);
        }
 }
 
@@ -1159,7 +1317,7 @@ static void icu_to_fcurves (ListBase *list, IpoCurve *icu, char *actname, char *
  * This does not assume that any ID or AnimData uses it, but does assume that
  * it is given two lists, which it will perform driver/animation-data separation.
  */
-static void ipo_to_animato (Ipo *ipo, char actname[], char constname[], ListBase *anim, ListBase *drivers)
+static void ipo_to_animato (Ipo *ipo, char actname[], char constname[], ListBase *animgroups, ListBase *anim, ListBase *drivers)
 {
        IpoCurve *icu, *icn;
        
@@ -1190,10 +1348,15 @@ static void ipo_to_animato (Ipo *ipo, char actname[], char constname[], ListBase
                /* Since an IPO-Curve may end up being made into many F-Curves (i.e. bitflag curves), 
                 * we figure out the best place to put the channel, then tell the curve-converter to just dump there
                 */
-               if (icu->driver)
-                       icu_to_fcurves(drivers, icu, actname, constname);
+               if (icu->driver) {
+                       /* Blender 2.4x allowed empty drivers, but we don't now, since they cause more trouble than they're worth */
+                       if ((icu->driver->ob) || (icu->driver->type == IPO_DRIVER_TYPE_PYTHON))
+                               icu_to_fcurves(NULL, drivers, icu, actname, constname);
+                       else
+                               MEM_freeN(icu->driver);
+               }
                else
-                       icu_to_fcurves(anim, icu, actname, constname);
+                       icu_to_fcurves(animgroups, anim, icu, actname, constname);
                
                /* free this IpoCurve now that it's been converted */
                BLI_freelinkN(&ipo->curve, icu);
@@ -1205,7 +1368,7 @@ static void ipo_to_animato (Ipo *ipo, char actname[], char constname[], ListBase
  * to Objects, where ob->ipo and ob->action need to be combined).
  * NOTE: we need to be careful here, as same data-structs are used for new system too!
  */
-static void action_to_animato (bAction *act, ListBase *curves, ListBase *drivers)
+static void action_to_animato (bAction *act, ListBase *groups, ListBase *curves, ListBase *drivers)
 {
        bActionChannel *achan, *achann;
        bConstraintChannel *conchan, *conchann;
@@ -1226,7 +1389,7 @@ static void action_to_animato (bAction *act, ListBase *curves, ListBase *drivers
                
                /* convert Action Channel's IPO data */
                if (achan->ipo) {
-                       ipo_to_animato(achan->ipo, achan->name, NULL, curves, drivers);
+                       ipo_to_animato(achan->ipo, achan->name, NULL, groups, curves, drivers);
                        achan->ipo->id.us--;
                        achan->ipo= NULL;
                }
@@ -1238,7 +1401,7 @@ static void action_to_animato (bAction *act, ListBase *curves, ListBase *drivers
                        
                        /* convert Constraint Channel's IPO data */
                        if (conchan->ipo) {
-                               ipo_to_animato(conchan->ipo, achan->name, conchan->name, curves, drivers);
+                               ipo_to_animato(conchan->ipo, achan->name, conchan->name, groups, curves, drivers);
                                conchan->ipo->id.us--;
                                conchan->ipo= NULL;
                        }
@@ -1282,7 +1445,8 @@ static void ipo_to_animdata (ID *id, Ipo *ipo, char actname[], char constname[])
        /* Convert curves to animato system (separated into separate lists of F-Curves for animation and drivers),
         * and the try to put these lists in the right places, but do not free the lists here
         */
-       ipo_to_animato(ipo, actname, constname, &anim, &drivers);
+       // XXX there shouldn't be any need for the groups, so don't supply pointer for that now... 
+       ipo_to_animato(ipo, actname, constname, NULL, &anim, &drivers);
        
        /* deal with animation first */
        if (anim.first) {
@@ -1324,7 +1488,88 @@ static void action_to_animdata (ID *id, bAction *act)
        }
        
        /* convert Action data */
-       action_to_animato(act, &adt->action->curves, &adt->drivers);
+       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);
+       }
 }
 
 /* *************************************************** */
@@ -1373,18 +1618,34 @@ 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 */
+                       /* 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;
                        }
                        
-                       /* now Action */
+                       /* 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);
+                       
+                       /* Action first - so that Action name get conserved */
                        if (ob->action) {
                                action_to_animdata(id, ob->action);
                                
@@ -1395,12 +1656,19 @@ void do_versions_ipos_to_animato(Main *main)
                                }
                        }
                        
-                       /* finally NLA */
-                       // XXX todo... for now, new NLA code not hooked up yet, so keep old stuff (but not for too long!)
+                       /* IPO second... */
+                       if (ob->ipo) {
+                               ipo_to_animdata(id, ob->ipo, NULL, NULL);
+                               ob->ipo->id.us--;
+                               ob->ipo= NULL;
+                       }
                }
                
                /* check PoseChannels for constraints with local data */
                if (ob->pose) {
+                       /* Verify if there's AnimData block */
+                       BKE_id_add_animdata(id);
+                       
                        for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
                                for (con= pchan->constraints.first; con; con= con->next) {
                                        /* if constraint has own IPO, convert add these to Object 
@@ -1424,6 +1692,9 @@ void do_versions_ipos_to_animato(Main *main)
                         * (NOTE: they're most likely to be drivers too) 
                         */
                        if (con->ipo) {
+                               /* Verify if there's AnimData block, just in case */
+                               BKE_id_add_animdata(id);
+                               
                                /* although this was the constraint's local IPO, we still need to provide con 
                                 * so that drivers can be added properly...
                                 */
@@ -1437,19 +1708,24 @@ void do_versions_ipos_to_animato(Main *main)
                }
                
                /* check constraint channels - we need to remove them anyway... */
-               for (conchan= ob->constraintChannels.first; conchan; conchan= conchann) {
-                       /* get pointer to next Constraint Channel */
-                       conchann= conchan->next;
+               if (ob->constraintChannels.first) {
+                       /* Verify if there's AnimData block */
+                       BKE_id_add_animdata(id);
                        
-                       /* convert Constraint Channel's IPO data */
-                       if (conchan->ipo) {
-                               ipo_to_animdata(id, conchan->ipo, NULL, conchan->name);
-                               conchan->ipo->id.us--;
-                               conchan->ipo= NULL;
+                       for (conchan= ob->constraintChannels.first; conchan; conchan= conchann) {
+                               /* get pointer to next Constraint Channel */
+                               conchann= conchan->next;
+                               
+                               /* convert Constraint Channel's IPO data */
+                               if (conchan->ipo) {
+                                       ipo_to_animdata(id, conchan->ipo, NULL, conchan->name);
+                                       conchan->ipo->id.us--;
+                                       conchan->ipo= NULL;
+                               }
+                               
+                               /* free Constraint Channel */
+                               BLI_freelinkN(&ob->constraintChannels, conchan);
                        }
-                       
-                       /* free Constraint Channel */
-                       BLI_freelinkN(&ob->constraintChannels, conchan);
                }
        }
        
@@ -1564,7 +1840,7 @@ void do_versions_ipos_to_animato(Main *main)
                if (G.f & G_DEBUG) printf("\tconverting action %s \n", id->name+2);
                
                /* be careful! some of the actions we encounter will be converted ones... */
-               action_to_animato(act, &act->curves, &drivers);
+               action_to_animato(act, &act->groups, &act->curves, &drivers);
        }
        
        /* ipo's */
@@ -1579,7 +1855,7 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* add a new action for this, and convert all data into that action */
                        new_act= add_empty_action("ConvIPO_Action"); // XXX need a better name...
-                       ipo_to_animato(ipo, NULL, NULL, &new_act->curves, &drivers);
+                       ipo_to_animato(ipo, NULL, NULL, NULL, &new_act->curves, &drivers);
                }
                
                /* clear fake-users, and set user-count to zero to make sure it is cleared on file-save */
@@ -1590,117 +1866,6 @@ void do_versions_ipos_to_animato(Main *main)
        /* free unused drivers from actions + ipos */
        free_fcurves(&drivers);
        
-       printf("INFO: animato convert done \n"); // xxx debug
+       printf("INFO: Animato convert done \n"); // xxx debug
 }
 
-
-
-#if 0 // XXX old animation system
-
-/* ***************************** IPO - DataAPI ********************************* */
-
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!! FIXME - BAD CRUFT WARNING !!!!!!!!!!!!!!!!!!!!!!!
-
-/* These functions here should be replaced eventually by the Data API, as this is 
- * inflexible duplication...
- */
-
-/* --------------------- Get Pointer API ----------------------------- */ 
-
-
-/* GS reads the memory pointed at in a specific ordering. There are,
- * however two definitions for it. I have jotted them down here, both,
- * but I think the first one is actually used. The thing is that
- * big-endian systems might read this the wrong way round. OTOH, we
- * constructed the IDs that are read out with this macro explicitly as
- * well. I expect we'll sort it out soon... */
-
-/* from blendef: */
-#define GS(a)  (*((short *)(a)))
-
-/* from misc_util: flip the bytes from x  */
-/*  #define GS(x) (((unsigned char *)(x))[0] << 8 | ((unsigned char *)(x))[1]) */
-
-
-/* general function to get pointer to source/destination data  */
-void *get_ipo_poin (ID *id, IpoCurve *icu, int *type)
-{
-       void *poin= NULL;
-       MTex *mtex= NULL;
-
-       /* most channels will have float data, but those with other types will override this */
-       *type= IPO_FLOAT;
-
-       /* data is divided into 'blocktypes' based on ID-codes */
-       switch (GS(id->name)) {
-               case ID_PA: /* particle channels -----------------------------  */
-               {
-                       ParticleSettings *part= (ParticleSettings *)id;
-                       
-                       switch (icu->adrcode) {
-                       case PART_EMIT_FREQ:
-                       case PART_EMIT_LIFE:
-                       case PART_EMIT_VEL:
-                       case PART_EMIT_AVE:
-                       case PART_EMIT_SIZE:
-                               poin= NULL; 
-                               break;
-                       
-                       case PART_CLUMP:
-                               poin= &(part->clumpfac); break;
-                       case PART_AVE:
-                               poin= &(part->avefac); break;
-                       case PART_SIZE:
-                               poin= &(part->size); break;
-                       case PART_DRAG:
-                               poin= &(part->dragfac); break;
-                       case PART_BROWN:
-                               poin= &(part->brownfac); break;
-                       case PART_DAMP:
-                               poin= &(part->dampfac); break;
-                       case PART_LENGTH:
-                               poin= &(part->length); break;
-                       case PART_GRAV_X:
-                               poin= &(part->acc[0]); break;
-                       case PART_GRAV_Y:
-                               poin= &(part->acc[1]); break;
-                       case PART_GRAV_Z:
-                               poin= &(part->acc[2]); break;
-                       case PART_KINK_AMP:
-                               poin= &(part->kink_amp); break;
-                       case PART_KINK_FREQ:
-                               poin= &(part->kink_freq); break;
-                       case PART_KINK_SHAPE:
-                               poin= &(part->kink_shape); break;
-                       case PART_BB_TILT:
-                               poin= &(part->bb_tilt); break;
-                               
-                       case PART_PD_FSTR:
-                               if (part->pd) poin= &(part->pd->f_strength);
-                               break;
-                       case PART_PD_FFALL:
-                               if (part->pd) poin= &(part->pd->f_power);
-                               break;
-                       case PART_PD_FMAXD:
-                               if (part->pd) poin= &(part->pd->maxdist);
-                               break;
-                       case PART_PD2_FSTR:
-                               if (part->pd2) poin= &(part->pd2->f_strength);
-                               break;
-                       case PART_PD2_FFALL:
-                               if (part->pd2) poin= &(part->pd2->f_power);
-                               break;
-                       case PART_PD2_FMAXD:
-                               if (part->pd2) poin= &(part->pd2->maxdist);
-                               break;
-                       }
-               }
-                       break;
-       }
-
-       /* return pointer */
-       return poin;
-}
-
-
-#endif // XXX old animation system