2.5 - Animation Playback Tweaks
[blender.git] / source / blender / blenkernel / intern / anim.c
index 08648aa6e107b59cf2f17191f056ccf76c7b46ae..6c1b8eb9000cb192e7954bec3aedf235a47b7bcf 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "MEM_guardedalloc.h"
 #include "BLI_blenlib.h"
+#include "BLI_editVert.h"
 #include "BLI_arithb.h"
 #include "BLI_rand.h"
 #include "DNA_listBase.h"
@@ -44,6 +45,7 @@
 #include "DNA_key_types.h"
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
 #include "DNA_object_types.h"
 #include "DNA_particle_types.h"
 #include "DNA_scene_types.h"
@@ -51,6 +53,7 @@
 #include "DNA_vfont_types.h"
 
 #include "BKE_anim.h"
+#include "BKE_curve.h"
 #include "BKE_DerivedMesh.h"
 #include "BKE_displist.h"
 #include "BKE_effect.h"
@@ -61,6 +64,7 @@
 #include "BKE_key.h"
 #include "BKE_lattice.h"
 #include "BKE_main.h"
+#include "BKE_mesh.h"
 #include "BKE_object.h"
 #include "BKE_particle.h"
 #include "BKE_utildefines.h"
 #include <config.h>
 #endif
 
+#include "ED_mesh.h"
+
+static void object_duplilist_recursive(ID *id, Scene *scene, Object *ob, ListBase *duplilist, float par_space_mat[][4], int level, int animated);
+
 void free_path(Path *path)
 {
        if(path->data) MEM_freeN(path->data);
@@ -93,8 +101,10 @@ void calc_curvepath(Object *ob)
        
        if(ob==NULL || ob->type != OB_CURVE) return;
        cu= ob->data;
-       //XXX if(ob==G.obedit) nu= editNurb.first;
-       //XXX else nu= cu->nurb.first;
+       if(cu->editnurb) 
+               nu= cu->editnurb->first;
+       else 
+               nu= cu->nurb.first;
        
        if(cu->path) free_path(cu->path);
        cu->path= NULL;
@@ -112,7 +122,7 @@ void calc_curvepath(Object *ob)
        
        path->len= tot+1;
        /* exception: vector handle paths and polygon paths should be subdivided at least a factor resolu */
-       if(path->len<nu->resolu*nu->pntsu) path->len= nu->resolu*nu->pntsu;
+       if(path->len<nu->resolu*SEGMENTSU(nu)) path->len= nu->resolu*SEGMENTSU(nu);
        
        dist= (float *)MEM_mallocN((tot+1)*4, "calcpathdist");
 
@@ -216,14 +226,16 @@ int where_on_path(Object *ob, float ctime, float *vec, float *dir)        /* returns OK
        cu= ob->data;
        if(cu->path==NULL || cu->path->data==NULL) {
                printf("no path!\n");
+               return 0;
        }
        path= cu->path;
        fp= path->data;
        
        /* test for cyclic */
        bl= cu->bev.first;
+       if (!bl) return 0;
        if (!bl->nr) return 0;
-       if(bl && bl->poly> -1) cycl= 1;
+       if(bl->poly> -1) cycl= 1;
 
        ctime *= (path->len-1);
        
@@ -275,7 +287,7 @@ int where_on_path(Object *ob, float ctime, float *vec, float *dir)  /* returns OK
 
 /* ****************** DUPLICATOR ************** */
 
-static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], int lay, int index)
+static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], int lay, int index, int type, int animated)
 {
        DupliObject *dob= MEM_callocN(sizeof(DupliObject), "dupliobject");
        
@@ -285,94 +297,120 @@ static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], i
        Mat4CpyMat4(dob->omat, ob->obmat);
        dob->origlay= ob->lay;
        dob->index= index;
+       dob->type= type;
+       dob->animated= (type == OB_DUPLIGROUP) && animated;
        ob->lay= lay;
        
        return dob;
 }
 
-static void group_duplilist(ListBase *lb, Object *ob, int level)
+static void group_duplilist(ListBase *lb, Scene *scene, Object *ob, int level, int animated)
 {
        DupliObject *dob;
        Group *group;
        GroupObject *go;
-       float mat[4][4];
+       float mat[4][4], tmat[4][4];
        
        if(ob->dup_group==NULL) return;
        group= ob->dup_group;
        
        /* simple preventing of too deep nested groups */
-       if(level>4) return;
+       if(level>MAX_DUPLI_RECUR) return;
        
        /* handles animated groups, and */
        /* we need to check update for objects that are not in scene... */
-       group_handle_recalc_and_update(ob, group);
+       group_handle_recalc_and_update(scene, ob, group);
+       animated= animated || group_is_animated(ob, group);
        
        for(go= group->gobject.first; go; go= go->next) {
                /* note, if you check on layer here, render goes wrong... it still deforms verts and uses parent imat */
                if(go->ob!=ob) {
-                       Mat4MulMat4(mat, go->ob->obmat, ob->obmat);
-                       dob= new_dupli_object(lb, go->ob, mat, ob->lay, 0);
+                       
+                       /* Group Dupli Offset, should apply after everything else */
+                       if (group->dupli_ofs[0] || group->dupli_ofs[1] || group->dupli_ofs[2]) {
+                               Mat4CpyMat4(tmat, go->ob->obmat);
+                               VecSubf(tmat[3], tmat[3], group->dupli_ofs);
+                               Mat4MulMat4(mat, tmat, ob->obmat);
+                       } else {
+                               Mat4MulMat4(mat, go->ob->obmat, ob->obmat);
+                       }
+                       
+                       dob= new_dupli_object(lb, go->ob, mat, ob->lay, 0, OB_DUPLIGROUP, animated);
                        dob->no_draw= (dob->origlay & group->layer)==0;
                        
-                       if(go->ob->dup_group && (go->ob->transflag & OB_DUPLIGROUP)) {
+                       if(go->ob->transflag & OB_DUPLI) {
                                Mat4CpyMat4(dob->ob->obmat, dob->mat);
-                               group_duplilist(lb, go->ob, level+1);
+                               object_duplilist_recursive((ID *)group, scene, go->ob, lb, ob->obmat, level+1, animated);
                                Mat4CpyMat4(dob->ob->obmat, dob->omat);
                        }
                }
        }
 }
 
-static void frames_duplilist(ListBase *lb, Object *ob)
+static void frames_duplilist(ListBase *lb, Scene *scene, Object *ob, int level, int animated)
 {
        extern int enable_cu_speed;     /* object.c */
        Object copyob;
+       DupliObject *dob;
        int cfrao, ok;
        
-       cfrao= G.scene->r.cfra;
+       /* simple preventing of too deep nested groups */
+       if(level>MAX_DUPLI_RECUR) return;
+       
+       cfrao= scene->r.cfra;
        if(ob->parent==NULL && ob->track==NULL && ob->ipo==NULL && ob->constraints.first==NULL) return;
 
        if(ob->transflag & OB_DUPLINOSPEED) enable_cu_speed= 0;
        copyob= *ob;    /* store transform info */
 
-       for(G.scene->r.cfra= ob->dupsta; G.scene->r.cfra<=ob->dupend; G.scene->r.cfra++) {
+       for(scene->r.cfra= ob->dupsta; scene->r.cfra<=ob->dupend; scene->r.cfra++) {
 
                ok= 1;
                if(ob->dupoff) {
-                       ok= G.scene->r.cfra - ob->dupsta;
+                       ok= scene->r.cfra - ob->dupsta;
                        ok= ok % (ob->dupon+ob->dupoff);
                        if(ok < ob->dupon) ok= 1;
                        else ok= 0;
                }
                if(ok) {
-                       do_ob_ipo(ob);
-                       where_is_object_time(ob, (float)G.scene->r.cfra);
-                       new_dupli_object(lb, ob, ob->obmat, ob->lay, G.scene->r.cfra);
+#if 0 // XXX old animation system
+                       do_ob_ipo(scene, ob);
+#endif // XXX old animation system
+                       where_is_object_time(scene, ob, (float)scene->r.cfra);
+                       dob= new_dupli_object(lb, ob, ob->obmat, ob->lay, scene->r.cfra, OB_DUPLIFRAMES, animated);
+                       Mat4CpyMat4(dob->omat, copyob.obmat);
                }
        }
 
        *ob= copyob;    /* restore transform info */
-       G.scene->r.cfra= cfrao;
+       scene->r.cfra= cfrao;
        enable_cu_speed= 1;
 }
 
 struct vertexDupliData {
+       ID *id; /* scene or group, for recursive loops */
+       int level;
+       int animated;
        ListBase *lb;
        float pmat[4][4];
+       float obmat[4][4]; /* Only used for dupliverts inside dupligroups, where the ob->obmat is modified */
+       Scene *scene;
        Object *ob, *par;
+       float (*orco)[3];
 };
 
 static void vertex_dupli__mapFunc(void *userData, int index, float *co, float *no_f, short *no_s)
 {
+       DupliObject *dob;
        struct vertexDupliData *vdd= userData;
-       float vec[3], *q2, mat[3][3], tmat[4][4], obmat[4][4];
+       float vec[3], q2[4], mat[3][3], tmat[4][4], obmat[4][4];
        
        VECCOPY(vec, co);
        Mat4MulVecfl(vdd->pmat, vec);
        VecSubf(vec, vec, vdd->pmat[3]);
-       VecAddf(vec, vec, vdd->ob->obmat[3]);
+       VecAddf(vec, vec, vdd->obmat[3]);
        
-       Mat4CpyMat4(obmat, vdd->ob->obmat);
+       Mat4CpyMat4(obmat, vdd->obmat);
        VECCOPY(obmat[3], vec);
        
        if(vdd->par->transflag & OB_DUPLIROT) {
@@ -383,53 +421,111 @@ static void vertex_dupli__mapFunc(void *userData, int index, float *co, float *n
                        vec[0]= -no_s[0]; vec[1]= -no_s[1]; vec[2]= -no_s[2];
                }
                
-               q2= vectoquat(vec, vdd->ob->trackflag, vdd->ob->upflag);
+               vectoquat(vec, vdd->ob->trackflag, vdd->ob->upflag, q2);
                
                QuatToMat3(q2, mat);
                Mat4CpyMat4(tmat, obmat);
                Mat4MulMat43(obmat, tmat, mat);
        }
-       new_dupli_object(vdd->lb, vdd->ob, obmat, vdd->par->lay, index);
+       dob= new_dupli_object(vdd->lb, vdd->ob, obmat, vdd->par->lay, index, OB_DUPLIVERTS, vdd->animated);
+       if(vdd->orco)
+               VECCOPY(dob->orco, vdd->orco[index]);
+       
+       if(vdd->ob->transflag & OB_DUPLI) {
+               float tmpmat[4][4];
+               Mat4CpyMat4(tmpmat, vdd->ob->obmat);
+               Mat4CpyMat4(vdd->ob->obmat, obmat); /* pretend we are really this mat */
+               object_duplilist_recursive((ID *)vdd->id, vdd->scene, vdd->ob, vdd->lb, obmat, vdd->level+1, vdd->animated);
+               Mat4CpyMat4(vdd->ob->obmat, tmpmat);
+       }
 }
 
-static void vertex_duplilist(ListBase *lb, Scene *sce, Object *par)
+static void vertex_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], int level, int animated)
 {
-       Object *ob;
-       Base *base;
-       float vec[3], no[3], pmat[4][4];
-       int lay, totvert, a;
+       Object *ob, *ob_iter;
+       Mesh *me= par->data;
+       Base *base = NULL;
        DerivedMesh *dm;
+       struct vertexDupliData vdd;
+       Scene *sce = NULL;
+       Group *group = NULL;
+       GroupObject * go = NULL;
+       EditMesh *em;
+       float vec[3], no[3], pmat[4][4];
+       int lay, totvert, a, oblay;
        
        Mat4CpyMat4(pmat, par->obmat);
        
-       lay= G.scene->lay;
+       /* simple preventing of too deep nested groups */
+       if(level>MAX_DUPLI_RECUR) return;
+       
+       em = BKE_mesh_get_editmesh(me);
+       
+       if(em) {
+               dm= editmesh_get_derived_cage(scene, par, em, CD_MASK_BAREMESH);
+               BKE_mesh_end_editmesh(me, em);
+       } else
+               dm= mesh_get_derived_deform(scene, par, CD_MASK_BAREMESH);
        
-       if(par==G.obedit)
-               dm= editmesh_get_derived_cage(CD_MASK_BAREMESH);
+       if(G.rendering) {
+               vdd.orco= (float(*)[3])get_mesh_orco_verts(par);
+               transform_mesh_orco_verts(me, vdd.orco, me->totvert, 0);
+       }
        else
-               dm = mesh_get_derived_deform(par, CD_MASK_BAREMESH);
+               vdd.orco= NULL;
        
        totvert = dm->getNumVerts(dm);
 
-       base= sce->base.first;
-       while(base) {
-
-               if(base->object->type>0 && (lay & base->lay) && G.obedit!=base->object) {
-                       ob= base->object->parent;
+       /* having to loop on scene OR group objects is NOT FUN */
+       if (GS(id->name) == ID_SCE) {
+               sce = (Scene *)id;
+               lay= sce->lay;
+               base= sce->base.first;
+       } else {
+               group = (Group *)id;
+               lay= group->layer;
+               go = group->gobject.first;
+       }
+       
+       /* Start looping on Scene OR Group objects */
+       while (base || go) { 
+               if (sce) {
+                       ob_iter= base->object;
+                       oblay = base->lay;
+               } else {
+                       ob_iter= go->ob;
+                       oblay = ob_iter->lay;
+               }
+               
+               if (lay & oblay && scene->obedit!=ob_iter) {
+                       ob=ob_iter->parent;
                        while(ob) {
                                if(ob==par) {
-                                       struct vertexDupliData vdd;
+                                       ob = ob_iter;
+       /* End Scene/Group object loop, below is generic */
                                        
-                                       ob= base->object;
+                                       
+                                       /* par_space_mat - only used for groups so we can modify the space dupli's are in
+                                          when par_space_mat is NULL ob->obmat can be used instead of ob__obmat
+                                       */
+                                       if(par_space_mat)
+                                               Mat4MulMat4(vdd.obmat, ob->obmat, par_space_mat);
+                                       else
+                                               Mat4CpyMat4(vdd.obmat, ob->obmat);
+
+                                       vdd.id= id;
+                                       vdd.level= level;
+                                       vdd.animated= animated;
                                        vdd.lb= lb;
                                        vdd.ob= ob;
+                                       vdd.scene= scene;
                                        vdd.par= par;
                                        Mat4CpyMat4(vdd.pmat, pmat);
                                        
                                        /* mballs have a different dupli handling */
                                        if(ob->type!=OB_MBALL) ob->flag |= OB_DONE;     /* doesnt render */
 
-                                       if(par==G.obedit) {
+                                       if(par==scene->obedit) {
                                                dm->foreachMappedVert(dm, vertex_dupli__mapFunc, (void*) &vdd);
                                        }
                                        else {
@@ -446,29 +542,43 @@ static void vertex_duplilist(ListBase *lb, Scene *sce, Object *par)
                                ob= ob->parent;
                        }
                }
-               base= base->next;
+               if (sce)        base= base->next;       /* scene loop */
+               else            go= go->next;           /* group loop */
        }
 
+       if(vdd.orco)
+               MEM_freeN(vdd.orco);
        dm->release(dm);
 }
 
-static void face_duplilist(ListBase *lb, Scene *sce, Object *par)
+static void face_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], int level, int animated)
 {
-       Object *ob;
-       Base *base;
+       Object *ob, *ob_iter;
+       Base *base = NULL;
+       DupliObject *dob;
        DerivedMesh *dm;
+       Mesh *me= par->data;
+       MTFace *mtface;
        MFace *mface;
        MVert *mvert;
-       float pmat[4][4], imat[3][3];
-       int lay, totface, a;
+       float pmat[4][4], imat[3][3], (*orco)[3] = NULL, w;
+       int lay, oblay, totface, a;
+       Scene *sce = NULL;
+       Group *group = NULL;
+       GroupObject *go = NULL;
+       EditMesh *em;
+       float ob__obmat[4][4]; /* needed for groups where the object matrix needs to be modified */
        
-       Mat4CpyMat4(pmat, par->obmat);
+       /* simple preventing of too deep nested groups */
+       if(level>MAX_DUPLI_RECUR) return;
        
-       lay= G.scene->lay;
+       Mat4CpyMat4(pmat, par->obmat);
        
-       if(par==G.obedit) {
+       em = BKE_mesh_get_editmesh(me);
+       if(em) {
                int totvert;
-               dm= editmesh_get_derived_cage(CD_MASK_BAREMESH);
+               
+               dm= editmesh_get_derived_cage(scene, par, em, CD_MASK_BAREMESH);
                
                totface= dm->getNumFaces(dm);
                mface= MEM_mallocN(sizeof(MFace)*totface, "mface temp");
@@ -476,34 +586,78 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par)
                totvert= dm->getNumVerts(dm);
                mvert= MEM_mallocN(sizeof(MVert)*totvert, "mvert temp");
                dm->copyVertArray(dm, mvert);
+
+               BKE_mesh_end_editmesh(me, em);
        }
        else {
-               dm = mesh_get_derived_deform(par, CD_MASK_BAREMESH);
+               dm = mesh_get_derived_deform(scene, par, CD_MASK_BAREMESH);
                
                totface= dm->getNumFaces(dm);
                mface= dm->getFaceArray(dm);
                mvert= dm->getVertArray(dm);
        }
+
+       if(G.rendering) {
+
+               orco= (float(*)[3])get_mesh_orco_verts(par);
+               transform_mesh_orco_verts(me, orco, me->totvert, 0);
+               mtface= me->mtface;
+       }
+       else {
+               orco= NULL;
+               mtface= NULL;
+       }
        
+       /* having to loop on scene OR group objects is NOT FUN */
+       if (GS(id->name) == ID_SCE) {
+               sce = (Scene *)id;
+               lay= sce->lay;
+               base= sce->base.first;
+       } else {
+               group = (Group *)id;
+               lay= group->layer;
+               go = group->gobject.first;
+       }
        
-       for(base= sce->base.first; base; base= base->next) {
+       /* Start looping on Scene OR Group objects */
+       while (base || go) { 
+               if (sce) {
+                       ob_iter= base->object;
+                       oblay = base->lay;
+               } else {
+                       ob_iter= go->ob;
+                       oblay = ob_iter->lay;
+               }
                
-               if(base->object->type>0 && (lay & base->lay) && G.obedit!=base->object) {
-                       ob= base->object->parent;
+               if (lay & oblay && scene->obedit!=ob_iter) {
+                       ob=ob_iter->parent;
                        while(ob) {
                                if(ob==par) {
+                                       ob = ob_iter;
+       /* End Scene/Group object loop, below is generic */
                                        
-                                       ob= base->object;
-                                       Mat3CpyMat4(imat, ob->parentinv);
+                                       /* par_space_mat - only used for groups so we can modify the space dupli's are in
+                                          when par_space_mat is NULL ob->obmat can be used instead of ob__obmat
+                                       */
+                                       if(par_space_mat)
+                                               Mat4MulMat4(ob__obmat, ob->obmat, par_space_mat);
+                                       else
+                                               Mat4CpyMat4(ob__obmat, ob->obmat);
                                        
+                                       Mat3CpyMat4(imat, ob->parentinv);
+                                               
                                        /* mballs have a different dupli handling */
                                        if(ob->type!=OB_MBALL) ob->flag |= OB_DONE;     /* doesnt render */
 
                                        for(a=0; a<totface; a++) {
-                                               float *v1= mvert[ mface[a].v1 ].co;
-                                               float *v2= mvert[ mface[a].v2 ].co;
-                                               float *v3= mvert[ mface[a].v3 ].co;
-                                               float *v4= mface[a].v4?mvert[ mface[a].v4 ].co:NULL;
+                                               int mv1 = mface[a].v1;
+                                               int mv2 = mface[a].v2;
+                                               int mv3 = mface[a].v3;
+                                               int mv4 = mface[a].v4;
+                                               float *v1= mvert[mv1].co;
+                                               float *v2= mvert[mv2].co;
+                                               float *v3= mvert[mv3].co;
+                                               float *v4= (mv4)? mvert[mv4].co: NULL;
                                                float cent[3], quat[4], mat[3][3], mat3[3][3], tmat[4][4], obmat[4][4];
 
                                                /* translation */
@@ -514,9 +668,10 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par)
                                                Mat4MulVecfl(pmat, cent);
                                                
                                                VecSubf(cent, cent, pmat[3]);
-                                               VecAddf(cent, cent, ob->obmat[3]);
+                                               VecAddf(cent, cent, ob__obmat[3]);
+                                               
+                                               Mat4CpyMat4(obmat, ob__obmat);
                                                
-                                               Mat4CpyMat4(obmat, ob->obmat);
                                                VECCOPY(obmat[3], cent);
                                                
                                                /* rotation */
@@ -536,8 +691,40 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par)
                                                Mat4CpyMat4(tmat, obmat);
                                                Mat4MulMat43(obmat, tmat, mat);
                                                
-                                               new_dupli_object(lb, ob, obmat, lay, a);
-
+                                               dob= new_dupli_object(lb, ob, obmat, lay, a, OB_DUPLIFACES, animated);
+                                               if(G.rendering) {
+                                                       w= (mv4)? 0.25f: 1.0f/3.0f;
+
+                                                       if(orco) {
+                                                               VECADDFAC(dob->orco, dob->orco, orco[mv1], w);
+                                                               VECADDFAC(dob->orco, dob->orco, orco[mv2], w);
+                                                               VECADDFAC(dob->orco, dob->orco, orco[mv3], w);
+                                                               if(mv4)
+                                                                       VECADDFAC(dob->orco, dob->orco, orco[mv4], w);
+                                                       }
+
+                                                       if(mtface) {
+                                                               dob->uv[0] += w*mtface[a].uv[0][0];
+                                                               dob->uv[1] += w*mtface[a].uv[0][1];
+                                                               dob->uv[0] += w*mtface[a].uv[1][0];
+                                                               dob->uv[1] += w*mtface[a].uv[1][1];
+                                                               dob->uv[0] += w*mtface[a].uv[2][0];
+                                                               dob->uv[1] += w*mtface[a].uv[2][1];
+
+                                                               if(mv4) {
+                                                                       dob->uv[0] += w*mtface[a].uv[3][0];
+                                                                       dob->uv[1] += w*mtface[a].uv[3][1];
+                                                               }
+                                                       }
+                                               }
+                                               
+                                               if(ob->transflag & OB_DUPLI) {
+                                                       float tmpmat[4][4];
+                                                       Mat4CpyMat4(tmpmat, ob->obmat);
+                                                       Mat4CpyMat4(ob->obmat, obmat); /* pretend we are really this mat */
+                                                       object_duplilist_recursive((ID *)id, scene, ob, lb, ob->obmat, level+1, animated);
+                                                       Mat4CpyMat4(ob->obmat, tmpmat);
+                                               }
                                        }
                                        
                                        break;
@@ -545,152 +732,222 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par)
                                ob= ob->parent;
                        }
                }
+               if (sce)        base= base->next;       /* scene loop */
+               else            go= go->next;           /* group loop */
        }
        
-       if(par==G.obedit) {
+       if(par==scene->obedit) {
                MEM_freeN(mface);
                MEM_freeN(mvert);
        }
+
+       if(orco)
+               MEM_freeN(orco);
        
        dm->release(dm);
 }
 
-static void new_particle_duplilist(ListBase *lb, Scene *sce, Object *par, ParticleSystem *psys)
+static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *par, float par_space_mat[][4], ParticleSystem *psys, int level, int animated)
 {
        GroupObject *go;
-       Object *ob, **oblist=0;
+       Object *ob=0, **oblist=0, obcopy, *obcopylist=0;
+       DupliObject *dob;
        ParticleSettings *part;
        ParticleData *pa;
+       ChildParticle *cpa=0;
        ParticleKey state;
-       float ctime, pa_time;
-       float tmat[4][4], mat[3][3], obrotmat[3][3], parotmat[3][3], size=0.0;
-       float xvec[3] = {-1.0, 0.0, 0.0}, *q;
-       int lay, a, k, step_nbr = 0, counter;
+       ParticleCacheKey *cache;
+       ParticleSystemModifierData *psmd;
+       float ctime, pa_time, scale = 1.0f;
+       float tmat[4][4], mat[4][4], pamat[4][4], size=0.0;
+       float (*obmat)[4], (*oldobmat)[4];
+       int lay, a, b, counter, hair = 0;
        int totpart, totchild, totgroup=0, pa_num;
 
        if(psys==0) return;
        
+       /* simple preventing of too deep nested groups */
+       if(level>MAX_DUPLI_RECUR) return;
+       
        part=psys->part;
+       psmd= psys_get_modifier(par, psys);
 
-       if(part==0) return;
+       if(part==0)
+               return;
 
-       ctime = bsystem_time(par, (float)G.scene->r.cfra, 0.0);
+       if(!psys_check_enabled(par, psys))
+               return;
+
+       ctime = bsystem_time(scene, par, (float)scene->r.cfra, 0.0);
 
        totpart = psys->totpart;
        totchild = psys->totchild;
 
        BLI_srandom(31415926 + psys->seed);
-               
-       lay= G.scene->lay;
-       if((part->draw_as == PART_DRAW_OB && part->dup_ob) ||
-               (part->draw_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first)) {
-
-               if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) && part->draw & PART_DRAW_KEYS)
-                       step_nbr = part->keys_step;
-               else
-                       step_nbr = 0;
+       
+       lay= scene->lay;
+       if((psys->renderdata || part->draw_as==PART_DRAW_REND) &&
+               ((part->ren_as == PART_DRAW_OB && part->dup_ob) ||
+               (part->ren_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first))) {
+
+               /* if we have a hair particle system, use the path cache */
+               if(part->type == PART_HAIR) {
+                       if(psys->flag & PSYS_HAIR_DONE)
+                               hair= (totchild == 0 || psys->childcache) && psys->pathcache;
+                       if(!hair)
+                               return;
+                       
+                       /* we use cache, update totchild according to cached data */
+                       totchild = psys->totchildcache;
+                       totpart = psys->totcached;
+               }
 
-               psys->lattice = psys_get_lattice(par, psys);
+               psys->lattice = psys_get_lattice(scene, par, psys);
 
-               if(part->draw_as==PART_DRAW_GR) {
-                       group_handle_recalc_and_update(par, part->dup_group);
+               /* gather list of objects or single object */
+               if(part->ren_as==PART_DRAW_GR) {
+                       group_handle_recalc_and_update(scene, par, part->dup_group);
 
-                       go= part->dup_group->gobject.first;
-                       while(go) {
-                               go=go->next;
+                       for(go=part->dup_group->gobject.first; go; go=go->next)
                                totgroup++;
-                       }
 
-                       oblist= MEM_callocN(totgroup*sizeof(Object *), "dupgroup object list");
-                       go= part->dup_group->gobject.first;
-                       for(a=0; a<totgroup; a++, go=go->next)
-                               oblist[a]=go->ob;
+                       /* we also copy the actual objects to restore afterwards, since
+                        * where_is_object_time will change the object which breaks transform */
+                       oblist = MEM_callocN(totgroup*sizeof(Object *), "dupgroup object list");
+                       obcopylist = MEM_callocN(totgroup*sizeof(Object), "dupgroup copy list");
+
+                       go = part->dup_group->gobject.first;
+                       for(a=0; a<totgroup; a++, go=go->next) {
+                               oblist[a] = go->ob;
+                               obcopylist[a] = *go->ob;
+                       }
+               }
+               else {
+                       ob = part->dup_ob;
+                       obcopy = *ob;
                }
 
                if(totchild==0 || part->draw & PART_DRAW_PARENT)
-                       a=0;
+                       a = 0;
                else
-                       a=totpart;
+                       a = totpart;
 
                for(pa=psys->particles,counter=0; a<totpart+totchild; a++,pa++,counter++) {
                        if(a<totpart) {
-                               if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue;
-
-                               pa_num=pa->num;
+                               /* handle parent particle */
+                               if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP))
+                                       continue;
 
-                               pa_time=pa->time;
-
-                               size=pa->size;
+                               pa_num = pa->num;
+                               pa_time = pa->time;
+                               size = pa->size;
                        }
                        else {
-                               /* TODO: figure these two out */
-                               pa_num = a;
-                               pa_time = psys->particles[psys->child[a - totpart].parent].time;
+                               /* handle child particle */
+                               cpa = &psys->child[a - totpart];
 
-                               size=psys_get_child_size(psys, &psys->child[a - totpart], ctime, 0);
+                               pa_num = a;
+                               pa_time = psys->particles[cpa->parent].time;
+                               size = psys_get_child_size(psys, cpa, ctime, 0);
                        }
 
-                       if(part->draw_as==PART_DRAW_GR) {
+                       if(part->ren_as==PART_DRAW_GR) {
+                               /* for groups, pick the object based on settings */
                                if(part->draw&PART_DRAW_RAND_GR)
-                                       ob = oblist[BLI_rand() % totgroup];
+                                       b= BLI_rand() % totgroup;
                                else if(part->from==PART_FROM_PARTICLE)
-                                       ob = oblist[pa_num % totgroup];
+                                       b= pa_num % totgroup;
                                else
-                                       ob = oblist[a % totgroup];
+                                       b= a % totgroup;
+
+                               ob = oblist[b];
+                               obmat = oblist[b]->obmat;
+                               oldobmat = obcopylist[b].obmat;
+                       }
+                       else {
+                               obmat= ob->obmat;
+                               oldobmat= obcopy.obmat;
                        }
-                       else
-                               ob = part->dup_ob;
 
-                       for(k=0; k<=step_nbr; k++, counter++) {
-                               if(step_nbr) {
-                                       state.time = (float)k / (float)step_nbr;
-                                       psys_get_particle_on_path(par, psys, a, &state, 0);
+                       if(hair) {
+                               /* hair we handle separate and compute transform based on hair keys */
+                               if(a < totpart) {
+                                       cache = psys->pathcache[a];
+                                       psys_get_dupli_path_transform(par, psys, psmd, pa, 0, cache, pamat, &scale);
                                }
                                else {
-                                       state.time = -1.0;
-                                       if(psys_get_particle_state(par, psys, a, &state, 0) == 0)
-                                               continue;
+                                       cache = psys->childcache[a-totpart];
+                                       psys_get_dupli_path_transform(par, psys, psmd, 0, cpa, cache, pamat, &scale);
                                }
 
-                               QuatToMat3(state.rot, parotmat);
-
-                               if(part->draw_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) {
-                                       for(go= part->dup_group->gobject.first; go; go= go->next) {
-
-                                               Mat4CpyMat4(tmat, go->ob->obmat);
-                                               Mat4MulMat43(tmat, go->ob->obmat, parotmat);
-                                               Mat4MulFloat3((float *)tmat, size);
+                               VECCOPY(pamat[3], cache->co);
+                               pamat[3][3]= 1.0f;
+                               
+                       }
+                       else {
+                               /* first key */
+                               state.time = ctime;
+                               if(psys_get_particle_state(scene, par, psys, a, &state, 0) == 0)
+                                       continue;
+
+                               QuatToMat4(state.rot, pamat);
+                               VECCOPY(pamat[3], state.co);
+                               pamat[3][3]= 1.0f;
+                       }
 
-                                               VECADD(tmat[3], go->ob->obmat[3], state.co);
+                       if(part->ren_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) {
+                               for(go= part->dup_group->gobject.first, b=0; go; go= go->next, b++) {
+                                       Mat4MulMat4(tmat, oblist[b]->obmat, pamat);
+                                       Mat4MulFloat3((float *)tmat, size*scale);
+                                       if(par_space_mat)
+                                               Mat4MulMat4(mat, tmat, par_space_mat);
+                                       else
+                                               Mat4CpyMat4(mat, tmat);
 
-                                               new_dupli_object(lb, go->ob, tmat, par->lay, counter);
-                                       }
+                                       dob= new_dupli_object(lb, go->ob, mat, par->lay, counter, OB_DUPLIPARTS, animated);
+                                       Mat4CpyMat4(dob->omat, obcopylist[b].obmat);
+                                       if(G.rendering)
+                                               psys_get_dupli_texture(par, part, psmd, pa, cpa, dob->uv, dob->orco);
                                }
-                               else {
-                                       /* to give ipos in object correct offset */
-                                       where_is_object_time(ob, ctime-pa_time);
-                                       
-                                       q = vectoquat(xvec, ob->trackflag, ob->upflag);
-                                       QuatToMat3(q, obrotmat);
-
-                                       Mat3MulMat3(mat, parotmat, obrotmat);
-                                       Mat4CpyMat4(tmat, ob->obmat);
-                                       Mat4MulMat43(tmat, ob->obmat, mat);
-                                       Mat4MulFloat3((float *)tmat, size);
-
-                                       VECCOPY(tmat[3], state.co);
+                       }
+                       else {
+                               /* to give ipos in object correct offset */
+                               where_is_object_time(scene, ob, ctime-pa_time);
+                               
+                               Mat4CpyMat4(mat, pamat);
+
+                               Mat4MulMat4(tmat, obmat, mat);
+                               Mat4MulFloat3((float *)tmat, size*scale);
+                               if(par_space_mat)
+                                       Mat4MulMat4(mat, tmat, par_space_mat);
+                               else
+                                       Mat4CpyMat4(mat, tmat);
 
-                                       new_dupli_object(lb, ob, tmat, par->lay, counter);
-                               }
+                               dob= new_dupli_object(lb, ob, mat, ob->lay, counter, OB_DUPLIPARTS, animated);
+                               Mat4CpyMat4(dob->omat, oldobmat);
+                               if(G.rendering)
+                                       psys_get_dupli_texture(par, part, psmd, pa, cpa, dob->uv, dob->orco);
                        }
                }
+
+               /* restore objects since they were changed in where_is_object_time */
+               if(part->ren_as==PART_DRAW_GR) {
+                       for(a=0; a<totgroup; a++)
+                               *(oblist[a])= obcopylist[a];
+               }
+               else
+                       *ob= obcopy;
        }
+
+       /* clean up */
        if(oblist)
                MEM_freeN(oblist);
+       if(obcopylist)
+               MEM_freeN(obcopylist);
 
        if(psys->lattice) {
-               end_latt_deform();
-               psys->lattice = 0;
+               end_latt_deform(psys->lattice);
+               psys->lattice = NULL;
        }
 }
 
@@ -699,7 +956,7 @@ static Object *find_family_object(Object **obar, char *family, char ch)
        Object *ob;
        int flen;
        
-       if( obar[ch] ) return obar[ch];
+       if( obar[(int)ch] ) return obar[(int)ch];
        
        flen= strlen(family);
        
@@ -711,13 +968,13 @@ static Object *find_family_object(Object **obar, char *family, char ch)
                ob= ob->id.next;
        }
        
-       obar[ch]= ob;
+       obar[(int)ch]= ob;
        
        return ob;
 }
 
 
-static void font_duplilist(ListBase *lb, Object *par)
+static void font_duplilist(ListBase *lb, Scene *scene, Object *par, int level, int animated)
 {
        Object *ob, *obar[256];
        Curve *cu;
@@ -725,11 +982,14 @@ static void font_duplilist(ListBase *lb, Object *par)
        float vec[3], obmat[4][4], pmat[4][4], fsize, xof, yof;
        int slen, a;
        
+       /* simple preventing of too deep nested groups */
+       if(level>MAX_DUPLI_RECUR) return;
+       
        Mat4CpyMat4(pmat, par->obmat);
        
        /* in par the family name is stored, use this to find the other objects */
        
-       chartransdata= text_to_curve(par, FO_DUPLI);
+       chartransdata= BKE_text_to_curve(scene, par, FO_DUPLI);
        if(chartransdata==0) return;
        
        memset(obar, 0, 256*sizeof(void *));
@@ -755,54 +1015,73 @@ static void font_duplilist(ListBase *lb, Object *par)
                        Mat4CpyMat4(obmat, par->obmat);
                        VECCOPY(obmat[3], vec);
                        
-                       new_dupli_object(lb, ob, obmat, par->lay, a);
+                       new_dupli_object(lb, ob, obmat, par->lay, a, OB_DUPLIVERTS, animated);
                }
-               
        }
        
        MEM_freeN(chartransdata);
 }
 
 /* ***************************** */
+static void object_duplilist_recursive(ID *id, Scene *scene, Object *ob, ListBase *duplilist, float par_space_mat[][4], int level, int animated)
+{      
+       if((ob->transflag & OB_DUPLI)==0)
+               return;
+       
+       /* Should the dupli's be generated for this object? - Respect restrict flags */
+       if (G.rendering) {
+               if (ob->restrictflag & OB_RESTRICT_RENDER) {
+                       return;
+               }
+       } else {
+               if (ob->restrictflag & OB_RESTRICT_VIEW) {
+                       return;
+               }
+       }
 
-/* note; group dupli's already set transform matrix. see note in group_duplilist() */
-ListBase *object_duplilist(Scene *sce, Object *ob)
-{
-       ListBase *duplilist= MEM_mallocN(sizeof(ListBase), "duplilist");
-       duplilist->first= duplilist->last= NULL;
-
-       if(ob->transflag & OB_DUPLI) {
-               if(ob->transflag & OB_DUPLIPARTS) {
-                       ParticleSystem *psys = ob->particlesystem.first;
-                       for(; psys; psys=psys->next)
-                               new_particle_duplilist(duplilist, sce, ob, psys);
+       if(ob->transflag & OB_DUPLIPARTS) {
+               ParticleSystem *psys = ob->particlesystem.first;
+               for(; psys; psys=psys->next)
+                       new_particle_duplilist(duplilist, id, scene, ob, par_space_mat, psys, level+1, animated);
+       }
+       else if(ob->transflag & OB_DUPLIVERTS) {
+               if(ob->type==OB_MESH) {
+                       vertex_duplilist(duplilist, id, scene, ob, par_space_mat, level+1, animated);
                }
-               else if(ob->transflag & OB_DUPLIVERTS) {
-                       if(ob->type==OB_MESH) {
-                               vertex_duplilist(duplilist, sce, ob);
-                       }
-                       else if(ob->type==OB_FONT) {
-                               font_duplilist(duplilist, ob);
+               else if(ob->type==OB_FONT) {
+                       if (GS(id->name)==ID_SCE) { /* TODO - support dupligroups */
+                               font_duplilist(duplilist, scene, ob, level+1, animated);
                        }
                }
-               else if(ob->transflag & OB_DUPLIFACES) {
-                       if(ob->type==OB_MESH)
-                               face_duplilist(duplilist, sce, ob);
+       }
+       else if(ob->transflag & OB_DUPLIFACES) {
+               if(ob->type==OB_MESH)
+                       face_duplilist(duplilist, id, scene, ob, par_space_mat, level+1, animated);
+       }
+       else if(ob->transflag & OB_DUPLIFRAMES) {
+               if (GS(id->name)==ID_SCE) { /* TODO - support dupligroups */
+                       frames_duplilist(duplilist, scene, ob, level+1, animated);
                }
-               else if(ob->transflag & OB_DUPLIFRAMES) 
-                       frames_duplilist(duplilist, ob);
-               else if(ob->transflag & OB_DUPLIGROUP) {
-                       DupliObject *dob;
-                       
-                       group_duplilist(duplilist, ob, 0); /* now recursive */
+       } else if(ob->transflag & OB_DUPLIGROUP) {
+               DupliObject *dob;
+               
+               group_duplilist(duplilist, scene, ob, level+1, animated); /* now recursive */
 
-                       /* make copy already, because in group dupli's deform displists can be made, requiring parent matrices */
+               if (level==0) {
                        for(dob= duplilist->first; dob; dob= dob->next)
-                               Mat4CpyMat4(dob->ob->obmat, dob->mat);
+                               if(dob->type == OB_DUPLIGROUP)
+                                       Mat4CpyMat4(dob->ob->obmat, dob->mat);
                }
-               
        }
-       
+}
+
+/* Returns a list of DupliObject
+ * note; group dupli's already set transform matrix. see note in group_duplilist() */
+ListBase *object_duplilist(Scene *sce, Object *ob)
+{
+       ListBase *duplilist= MEM_mallocN(sizeof(ListBase), "duplilist");
+       duplilist->first= duplilist->last= NULL;
+       object_duplilist_recursive((ID *)sce, sce, ob, duplilist, NULL, 0, 0);
        return duplilist;
 }