2.5
[blender.git] / source / blender / blenkernel / intern / action.c
index f64cc16702fb7c31e9d81f903447fb2ae4b95ede..98f8a83f8094203131313db04107d75f53f2e033 100644 (file)
@@ -1,15 +1,12 @@
 /**
  * $Id$
  *
- * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
+ * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version. The Blender
- * Foundation also sells licenses for use in proprietary software under
- * the Blender License.  See http://www.blender.org/BL/ for information
- * about this.
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,7 +22,7 @@
  *
  * Contributor(s): Full recode, Ton Roosendaal, Crete 2005
  *
- * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ * ***** END GPL LICENSE BLOCK *****
  */
 
 #ifdef HAVE_CONFIG_H
@@ -57,6 +54,7 @@
 #include "BKE_global.h"
 #include "BKE_ipo.h"
 #include "BKE_key.h"
+#include "BKE_lattice.h"
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_object.h"
@@ -64,8 +62,9 @@
 
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
+#include "BLI_ghash.h"
 
-#include "nla.h"
+//XXX #include "nla.h"
 
 /* *********************** NOTE ON POSE AND ACTION **********************
 
@@ -111,7 +110,6 @@ static void make_local_action_channels(bAction *act)
                        }
                }
        }
-                                       
 }
 
 void make_local_action(bAction *act)
@@ -164,37 +162,64 @@ void make_local_action(bAction *act)
 }
 
 
-void free_action(bAction *act)
+void free_action (bAction *act)
 {
        bActionChannel *chan;
        
        /* Free channels */
-       for (chan=act->chanbase.first; chan; chan=chan->next){
+       for (chan=act->chanbase.first; chan; chan=chan->next) {
                if (chan->ipo)
                        chan->ipo->id.us--;
                free_constraint_channels(&chan->constraintChannels);
        }
        
        if (act->chanbase.first)
-               BLI_freelistN (&act->chanbase);
+               BLI_freelistN(&act->chanbase);
+               
+       /* Free groups */
+       if (act->groups.first)
+               BLI_freelistN(&act->groups);
+               
+       /* Free pose-references (aka local markers) */
+       if (act->markers.first)
+               BLI_freelistN(&act->markers);
 }
 
-bAction* copy_action(bAction *src)
+bAction *copy_action (bAction *src)
 {
        bAction *dst = NULL;
        bActionChannel *dchan, *schan;
+       bActionGroup *dgrp, *sgrp;
        
-       if(!src) return NULL;
+       if (!src) return NULL;
        
        dst= copy_libblock(src);
-       duplicatelist(&(dst->chanbase), &(src->chanbase));
        
-       for (dchan=dst->chanbase.first, schan=src->chanbase.first; dchan; dchan=dchan->next, schan=schan->next){
+       BLI_duplicatelist(&(dst->chanbase), &(src->chanbase));
+       BLI_duplicatelist(&(dst->groups), &(src->groups));
+       BLI_duplicatelist(&(dst->markers), &(src->markers));
+       
+       for (dchan=dst->chanbase.first, schan=src->chanbase.first; dchan; dchan=dchan->next, schan=schan->next) {
+               for (dgrp=dst->groups.first, sgrp=src->groups.first; dgrp && sgrp; dgrp=dgrp->next, sgrp=sgrp->next) {
+                       if (dchan->grp == sgrp) {
+                               dchan->grp= dgrp;
+                               
+                               if (dgrp->channels.first == schan)
+                                       dgrp->channels.first= dchan;
+                               if (dgrp->channels.last == schan)
+                                       dgrp->channels.last= dchan;
+                                       
+                               break;
+                       }
+               }
+               
                dchan->ipo = copy_ipo(dchan->ipo);
                copy_constraint_channels(&dchan->constraintChannels, &schan->constraintChannels);
        }
+       
        dst->id.flag |= LIB_FAKEUSER;
        dst->id.us++;
+       
        return dst;
 }
 
@@ -224,12 +249,12 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name)
 {
        bPoseChannel *chan;
        
-       if (!pose){
+       if (!pose) {
                return NULL;
        }
        
        /*      See if this channel exists */
-       for (chan=pose->chanbase.first; chan; chan=chan->next){
+       for (chan=pose->chanbase.first; chan; chan=chan->next) {
                if (!strcmp (name, chan->name))
                        return chan;
        }
@@ -238,7 +263,7 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name)
        chan = MEM_callocN(sizeof(bPoseChannel), "verifyPoseChannel");
        
        strncpy (chan->name, name, 31);
-       /* init vars to prevent mat errors */
+       /* init vars to prevent math errors */
        chan->quat[0] = 1.0F;
        chan->size[0] = chan->size[1] = chan->size[2] = 1.0F;
        
@@ -246,6 +271,8 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name)
        chan->limitmax[0]= chan->limitmax[1]= chan->limitmax[2]= 180.0f;
        chan->stiffness[0]= chan->stiffness[1]= chan->stiffness[2]= 0.0f;
        
+       Mat4One(chan->constinv);
+       
        BLI_addtail (&pose->chanbase, chan);
        
        return chan;
@@ -264,9 +291,15 @@ void copy_pose(bPose **dst, bPose *src, int copycon)
                return;
        }
        
+       if (*dst==src) {
+               printf("copy_pose source and target are the same\n");
+               *dst=NULL;
+               return;
+       }
+       
        outPose= MEM_callocN(sizeof(bPose), "pose");
        
-       duplicatelist (&outPose->chanbase, &src->chanbase);
+       BLI_duplicatelist(&outPose->chanbase, &src->chanbase);
        
        if (copycon) {
                for (pchan=outPose->chanbase.first; pchan; pchan=pchan->next) {
@@ -283,13 +316,82 @@ void free_pose_channels(bPose *pose)
 {
        bPoseChannel *pchan;
        
-       if (pose->chanbase.first){
+       if (pose->chanbase.first) {
                for (pchan = pose->chanbase.first; pchan; pchan=pchan->next){
-                       if(pchan->path)
+                       if (pchan->path)
                                MEM_freeN(pchan->path);
                        free_constraints(&pchan->constraints);
                }
-               BLI_freelistN (&pose->chanbase);
+               BLI_freelistN(&pose->chanbase);
+       }
+}
+
+void free_pose(bPose *pose)
+{
+       if (pose) {
+               /* free pose-channels */
+               free_pose_channels(pose);
+               
+               /* free pose-groups */
+               if (pose->agroups.first)
+                       BLI_freelistN(&pose->agroups);
+               
+               /* free pose */
+               MEM_freeN(pose);
+       }
+}
+
+void game_copy_pose(bPose **dst, bPose *src)
+{
+       bPose *out;
+       bPoseChannel *pchan, *outpchan;
+       GHash *ghash;
+       
+       /* the game engine copies the current armature pose and then swaps
+        * the object pose pointer. this makes it possible to change poses
+        * without affecting the original blender data. */
+
+       if (!src) {
+               *dst=NULL;
+               return;
+       }
+       else if (*dst==src) {
+               printf("copy_pose source and target are the same\n");
+               *dst=NULL;
+               return;
+       }
+       
+       out= MEM_dupallocN(src);
+       out->agroups.first= out->agroups.last= NULL;
+       BLI_duplicatelist(&out->chanbase, &src->chanbase);
+
+       /* remap pointers */
+       ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+       pchan= src->chanbase.first;
+       outpchan= out->chanbase.first;
+       for (; pchan; pchan=pchan->next, outpchan=outpchan->next)
+               BLI_ghash_insert(ghash, pchan, outpchan);
+
+       for (pchan=out->chanbase.first; pchan; pchan=pchan->next) {
+               pchan->parent= BLI_ghash_lookup(ghash, pchan->parent);
+               pchan->child= BLI_ghash_lookup(ghash, pchan->child);
+               pchan->path= NULL;
+       }
+
+       BLI_ghash_free(ghash, NULL, NULL);
+       
+       *dst=out;
+}
+
+void game_free_pose(bPose *pose)
+{
+       if (pose) {
+               /* we don't free constraints, those are owned by the original pose */
+               if(pose->chanbase.first)
+                       BLI_freelistN(&pose->chanbase);
+               
+               MEM_freeN(pose);
        }
 }
 
@@ -299,15 +401,23 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan
        
        VECCOPY(pchan->loc, chan->loc);
        VECCOPY(pchan->size, chan->size);
+       VECCOPY(pchan->eul, chan->eul);
        QUATCOPY(pchan->quat, chan->quat);
+       pchan->rotmode= chan->rotmode;
+       Mat4CpyMat4(pchan->chan_mat, (float(*)[4])chan->chan_mat);
+       Mat4CpyMat4(pchan->pose_mat, (float(*)[4])chan->pose_mat);
        pchan->flag= chan->flag;
        
        con= chan->constraints.first;
-       for(pcon= pchan->constraints.first; pcon; pcon= pcon->next)
+       for(pcon= pchan->constraints.first; pcon; pcon= pcon->next, con= con->next) {
                pcon->enforce= con->enforce;
+               pcon->headtail= con->headtail;
+       }
 }
 
-/* checks for IK constraint, can do more constraints flags later */
+/* checks for IK constraint, and also for Follow-Path constraint.
+ * can do more constraints flags later 
+ */
 /* pose should be entirely OK */
 void update_pose_constraint_flags(bPose *pose)
 {
@@ -315,13 +425,15 @@ void update_pose_constraint_flags(bPose *pose)
        bConstraint *con;
        
        /* clear */
-       for (pchan = pose->chanbase.first; pchan; pchan=pchan->next) {
+       for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
                pchan->constflag= 0;
        }
+       pose->flag &= ~POSE_CONSTRAINTS_TIMEDEPEND;
+       
        /* detect */
-       for (pchan = pose->chanbase.first; pchan; pchan=pchan->next) {
-               for(con= pchan->constraints.first; con; con= con->next) {
-                       if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
+       for (pchan= pose->chanbase.first; pchan; pchan=pchan->next) {
+               for (con= pchan->constraints.first; con; con= con->next) {
+                       if (con->type==CONSTRAINT_TYPE_KINEMATIC) {
                                bKinematicConstraint *data = (bKinematicConstraint*)con->data;
                                
                                pchan->constflag |= PCHAN_HAS_IK;
@@ -344,11 +456,46 @@ void update_pose_constraint_flags(bPose *pose)
                                        }
                                }
                        }
-                       else pchan->constflag |= PCHAN_HAS_CONST;
+                       else if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) {
+                               bFollowPathConstraint *data= (bFollowPathConstraint *)con->data;
+                               
+                               /* for drawing constraint colors when color set allows this */
+                               pchan->constflag |= PCHAN_HAS_CONST;
+                               
+                               /* if we have a valid target, make sure that this will get updated on frame-change
+                                * (needed for when there is no anim-data for this pose)
+                                */
+                               if ((data->tar) && (data->tar->type==OB_CURVE))
+                                       pose->flag |= POSE_CONSTRAINTS_TIMEDEPEND;
+                       }
+                       else 
+                               pchan->constflag |= PCHAN_HAS_CONST;
                }
        }
 }
 
+/* Clears all BONE_UNKEYED flags for every pose channel in every pose 
+ * This should only be called on frame changing, when it is acceptable to
+ * do this. Otherwise, these flags should not get cleared as poses may get lost.
+ */
+void framechange_poses_clear_unkeyed(void)
+{
+       Object *ob;
+       bPose *pose;
+       bPoseChannel *pchan;
+       
+       /* This needs to be done for each object that has a pose */
+       // TODO: proxies may/may not be correctly handled here... (this needs checking) 
+       for (ob= G.main->object.first; ob; ob= ob->id.next) {
+               /* we only need to do this on objects with a pose */
+               if ( (pose= ob->pose) ) {
+                       for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
+                               if (pchan->bone) 
+                                       pchan->bone->flag &= ~BONE_UNKEYED;
+                       }
+               }
+       }
+}
 
 /* ************************ END Pose channels *************** */
 
@@ -359,10 +506,10 @@ bActionChannel *get_action_channel(bAction *act, const char *name)
 {
        bActionChannel *chan;
        
-       if (!act)
+       if (!act || !name)
                return NULL;
        
-       for (chan = act->chanbase.first; chan; chan=chan->next){
+       for (chan = act->chanbase.first; chan; chan=chan->next) {
                if (!strcmp (chan->name, name))
                        return chan;
        }
@@ -370,22 +517,81 @@ bActionChannel *get_action_channel(bAction *act, const char *name)
        return NULL;
 }
 
-/* returns existing channel, or adds new one. In latter case it doesnt activate it, context is required for that*/
+/* returns existing channel, or adds new one. In latter case it doesnt activate it, context is required for that */
 bActionChannel *verify_action_channel(bAction *act, const char *name)
 {
        bActionChannel *chan;
        
        chan= get_action_channel(act, name);
-       if(chan==NULL) {
-               if (!chan) {
-                       chan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
-                       strncpy (chan->name, name, 31);
-                       BLI_addtail (&act->chanbase, chan);
-               }
+       if (chan == NULL) {
+               chan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
+               strncpy(chan->name, name, 31);
+               BLI_addtail(&act->chanbase, chan);
        }
        return chan;
 }
 
+/* ************** time ****************** */
+
+static bActionStrip *get_active_strip(Object *ob)
+{
+       bActionStrip *strip;
+       
+       if(ob->action==NULL)
+               return NULL;
+       
+       for (strip=ob->nlastrips.first; strip; strip=strip->next)
+               if(strip->flag & ACTSTRIP_ACTIVE)
+                       break;
+       
+       if(strip && strip->act==ob->action)
+               return strip;
+       return NULL;
+}
+
+/* non clipped mapping of strip */
+static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert)
+{
+       float length, actlength, repeat, scale;
+       
+       if (strip->repeat == 0.0f) strip->repeat = 1.0f;
+       repeat = (strip->flag & ACTSTRIP_USESTRIDE) ? (1.0f) : (strip->repeat);
+       
+       if (strip->scale == 0.0f) strip->scale= 1.0f;
+       scale = fabs(strip->scale); /* scale must be positive (for now) */
+       
+       actlength = strip->actend-strip->actstart;
+       if (actlength == 0.0f) actlength = 1.0f;
+       length = repeat * scale * actlength;
+       
+       /* invert = convert action-strip time to global time */
+       if (invert)
+               return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start;
+       else
+               return repeat*actlength*(cframe - strip->start)/length + strip->actstart;
+}
+
+/* if the conditions match, it converts current time to strip time */
+float get_action_frame(Object *ob, float cframe)
+{
+       bActionStrip *strip= get_active_strip(ob);
+       
+       if(strip)
+               return get_actionstrip_frame(strip, cframe, 0);
+       return cframe;
+}
+
+/* inverted, strip time to current time */
+float get_action_frame_inv(Object *ob, float cframe)
+{
+       bActionStrip *strip= get_active_strip(ob);
+       
+       if(strip)
+               return get_actionstrip_frame(strip, cframe, 1);
+       return cframe;
+}
+
+
 /* ************************ Blending with NLA *************** */
 
 static void blend_pose_strides(bPose *dst, bPose *src, float srcweight, short mode)
@@ -393,10 +599,10 @@ static void blend_pose_strides(bPose *dst, bPose *src, float srcweight, short mo
        float dstweight;
        
        switch (mode){
-               case POSE_BLEND:
+               case ACTSTRIPMODE_BLEND:
                        dstweight = 1.0F - srcweight;
                        break;
-               case POSE_ADD:
+               case ACTSTRIPMODE_ADD:
                        dstweight = 1.0F;
                        break;
                default :
@@ -406,21 +612,101 @@ static void blend_pose_strides(bPose *dst, bPose *src, float srcweight, short mo
        VecLerpf(dst->stride_offset, dst->stride_offset, src->stride_offset, srcweight);
 }
 
+
+/* 
+
+bone matching diagram, strips A and B
+
+                 .------------------------.
+                 |         A              |
+                 '------------------------'
+                                .          .             b2
+                 .          .-------------v----------.
+                 .             |         B   .          |
+                 .          '------------------------'
+                 .          .             .
+                 .          .             .
+offset:          .    0     .    A-B      .  A-b2+B     
+                 .          .             .
+
+*/
+
+
+static void blend_pose_offset_bone(bActionStrip *strip, bPose *dst, bPose *src, float srcweight, short mode)
+{
+       /* matching offset bones */
+       /* take dst offset, and put src on on that location */
+       
+       if(strip->offs_bone[0]==0)
+               return;
+       
+       /* are we also blending with matching bones? */
+       if(strip->prev && strip->start>=strip->prev->start) {
+               bPoseChannel *dpchan= get_pose_channel(dst, strip->offs_bone);
+               if(dpchan) {
+                       bPoseChannel *spchan= get_pose_channel(src, strip->offs_bone);
+                       if(spchan) {
+                               float vec[3];
+                               
+                               /* dst->ctime has the internal strip->prev action time */
+                               /* map this time to nla time */
+                               
+                               float ctime= get_actionstrip_frame(strip, src->ctime, 1);
+                               
+                               if( ctime > strip->prev->end) {
+                                       bActionChannel *achan;
+                                       
+                                       /* add src to dest, minus the position of src on strip->prev->end */
+                                       
+                                       ctime= get_actionstrip_frame(strip, strip->prev->end, 0);
+                                       
+                                       achan= get_action_channel(strip->act, strip->offs_bone);
+                                       if(achan && achan->ipo) {
+                                               bPoseChannel pchan;
+                                               /* Evaluates and sets the internal ipo value */
+                                               calc_ipo(achan->ipo, ctime);
+                                               /* This call also sets the pchan flags */
+                                               execute_action_ipo(achan, &pchan);
+                                               
+                                               /* store offset that moves src to location of pchan */
+                                               VecSubf(vec, dpchan->loc, pchan.loc);
+                                               
+                                               Mat4Mul3Vecfl(dpchan->bone->arm_mat, vec);
+                                       }
+                               }
+                               else {
+                                       /* store offset that moves src to location of dst */
+                                       
+                                       VecSubf(vec, dpchan->loc, spchan->loc);
+                                       Mat4Mul3Vecfl(dpchan->bone->arm_mat, vec);
+                               }
+                               
+                               /* if blending, we only add with factor scrweight */
+                               VecMulf(vec, srcweight);
+                               
+                               VecAddf(dst->cyclic_offset, dst->cyclic_offset, vec);
+                       }
+               }
+       }
+       
+       VecAddf(dst->cyclic_offset, dst->cyclic_offset, src->cyclic_offset);
+}
+
+
 /* Only allowed for Poses with identical channels */
 void blend_poses(bPose *dst, bPose *src, float srcweight, short mode)
 {
        bPoseChannel *dchan;
        const bPoseChannel *schan;
        bConstraint *dcon, *scon;
-       float   dquat[4], squat[4];
        float dstweight;
        int i;
        
        switch (mode){
-       case POSE_BLEND:
+       case ACTSTRIPMODE_BLEND:
                dstweight = 1.0F - srcweight;
                break;
-       case POSE_ADD:
+       case ACTSTRIPMODE_ADD:
                dstweight = 1.0F;
                break;
        default :
@@ -434,21 +720,34 @@ void blend_poses(bPose *dst, bPose *src, float srcweight, short mode)
                        
                        /* Do the transformation blend */
                        if (schan->flag & POSE_ROT) {
-                               QUATCOPY(dquat, dchan->quat);
-                               QUATCOPY(squat, schan->quat);
-                               if(mode==POSE_BLEND)
-                                       QuatInterpol(dchan->quat, dquat, squat, srcweight);
-                               else
-                                       QuatAdd(dchan->quat, dquat, squat, srcweight);
-                               
-                               NormalQuat (dchan->quat);
+                               /* quat interpolation done separate */
+                               if (schan->rotmode == PCHAN_ROT_QUAT) {
+                                       float dquat[4], squat[4];
+                                       
+                                       QUATCOPY(dquat, dchan->quat);
+                                       QUATCOPY(squat, schan->quat);
+                                       if (mode==ACTSTRIPMODE_BLEND)
+                                               QuatInterpol(dchan->quat, dquat, squat, srcweight);
+                                       else {
+                                               QuatMulFac(squat, srcweight);
+                                               QuatMul(dchan->quat, dquat, squat);
+                                       }
+                                       
+                                       NormalQuat(dchan->quat);
+                               }
                        }
 
-                       for (i=0; i<3; i++){
+                       for (i=0; i<3; i++) {
+                               /* blending for loc and scale are pretty self-explanatory... */
                                if (schan->flag & POSE_LOC)
                                        dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight);
                                if (schan->flag & POSE_SIZE)
                                        dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight);
+                               
+                               /* euler-rotation interpolation done here instead... */
+                               // FIXME: are these results decent?
+                               if ((schan->flag & POSE_ROT) && (schan->rotmode))
+                                       dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight);
                        }
                        dchan->flag |= schan->flag;
                }
@@ -457,35 +756,38 @@ void blend_poses(bPose *dst, bPose *src, float srcweight, short mode)
                        dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight;
                }
        }
+       
+       /* this pose is now in src time */
+       dst->ctime= src->ctime;
 }
 
-
+/* Calculate the extents of given action */
 void calc_action_range(const bAction *act, float *start, float *end, int incl_hidden)
 {
-       const bActionChannel *chan;
-       const bConstraintChannel *conchan;
-       const IpoCurve  *icu;
-       float min=999999999.0f, max=-999999999.0;
+       bActionChannel *chan;
+       bConstraintChannel *conchan;
+       IpoCurve *icu;
+       float min=999999999.0f, max=-999999999.0f;
        int     foundvert=0;
 
-       if(act) {
+       if (act) {
                for (chan=act->chanbase.first; chan; chan=chan->next) {
-                       if(incl_hidden || (chan->flag & ACHAN_HIDDEN)==0) {
-                               if(chan->ipo) {
+                       if ((incl_hidden) || (chan->flag & ACHAN_HIDDEN)==0) {
+                               if (chan->ipo) {
                                        for (icu=chan->ipo->curve.first; icu; icu=icu->next) {
-                                               if(icu->totvert) {
-                                                       min= MIN2 (min, icu->bezt[0].vec[1][0]);
-                                                       max= MAX2 (max, icu->bezt[icu->totvert-1].vec[1][0]);
+                                               if (icu->totvert) {
+                                                       min= MIN2(min, icu->bezt[0].vec[1][0]);
+                                                       max= MAX2(max, icu->bezt[icu->totvert-1].vec[1][0]);
                                                        foundvert=1;
                                                }
                                        }
                                }
                                for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) {
-                                       if(conchan->ipo) {
+                                       if (conchan->ipo) {
                                                for (icu=conchan->ipo->curve.first; icu; icu=icu->next) {
-                                                       if(icu->totvert) {
-                                                               min= MIN2 (min, icu->bezt[0].vec[1][0]);
-                                                               max= MAX2 (max, icu->bezt[icu->totvert-1].vec[1][0]);
+                                                       if (icu->totvert) {
+                                                               min= MIN2(min, icu->bezt[0].vec[1][0]);
+                                                               max= MAX2(max, icu->bezt[icu->totvert-1].vec[1][0]);
                                                                foundvert=1;
                                                        }
                                                }
@@ -513,6 +815,11 @@ void extract_pose_from_pose(bPose *pose, const bPose *src)
        const bPoseChannel *schan;
        bPoseChannel *pchan= pose->chanbase.first;
 
+       if (pose==src) {
+               printf("extract_pose_from_pose source and target are the same\n");
+               return;
+       }
+
        for (schan=src->chanbase.first; schan; schan=schan->next, pchan= pchan->next) {
                copy_pose_channel_data(pchan, schan);
        }
@@ -532,9 +839,15 @@ void extract_pose_from_action(bPose *pose, bAction *act, float ctime)
        
        /* Copy the data from the action into the pose */
        for (pchan= pose->chanbase.first; pchan; pchan=pchan->next) {
+               /* skip this pose channel if it has been tagged as having unkeyed poses */
+               if ((pchan->bone) && (pchan->bone->flag & BONE_UNKEYED)) 
+                       continue;
+               
+               /* get action channel and clear pchan-transform flags */
                achan= get_action_channel(act, pchan->name);
                pchan->flag &= ~(POSE_LOC|POSE_ROT|POSE_SIZE);
-               if(achan) {
+               
+               if (achan) {
                        ipo = achan->ipo;
                        if (ipo) {
                                /* Evaluates and sets the internal ipo value */
@@ -542,13 +855,16 @@ void extract_pose_from_action(bPose *pose, bAction *act, float ctime)
                                /* This call also sets the pchan flags */
                                execute_action_ipo(achan, pchan);
                        }
-                       do_constraint_channels(&pchan->constraints, &achan->constraintChannels, ctime);
+                       /* 0 = do all ipos, not only drivers */
+                       do_constraint_channels(&pchan->constraints, &achan->constraintChannels, ctime, 0);
                }
        }
+       
+       pose->ctime= ctime;     /* used for cyclic offset matching */
 }
 
-/* for do_all_pose_actions, clears the pose */
-static void rest_pose(bPose *pose)
+/* for do_all_pose_actions, clears the pose. Now also exported for proxy and tools */
+void rest_pose(bPose *pose)
 {
        bPoseChannel *pchan;
        int i;
@@ -556,22 +872,55 @@ static void rest_pose(bPose *pose)
        if (!pose)
                return;
        
-       pose->stride_offset[0]= 0.0f;
-       pose->stride_offset[1]= 0.0f;
-       pose->stride_offset[2]= 0.0f;
+       memset(pose->stride_offset, 0, sizeof(pose->stride_offset));
+       memset(pose->cyclic_offset, 0, sizeof(pose->cyclic_offset));
        
-       for (pchan=pose->chanbase.first; pchan; pchan=pchan->next){
-               for (i=0; i<3; i++){
-                       pchan->loc[i]=0.0;
-                       pchan->quat[i+1]=0.0;
-                       pchan->size[i]=1.0;
+       for (pchan=pose->chanbase.first; pchan; pchan= pchan->next){
+               for (i=0; i<3; i++) {
+                       pchan->loc[i]= 0.0f;
+                       pchan->quat[i+1]= 0.0f;
+                       pchan->eul[i]= 0.0f;
+                       pchan->size[i]= 1.0f;
                }
-               pchan->quat[0]=1.0;
+               pchan->quat[0]= 1.0f;
                
                pchan->flag &= ~(POSE_LOC|POSE_ROT|POSE_SIZE);
        }
 }
 
+/* both poses should be in sync */
+void copy_pose_result(bPose *to, bPose *from)
+{
+       bPoseChannel *pchanto, *pchanfrom;
+       
+       if(to==NULL || from==NULL) {
+               printf("pose result copy error\n"); // debug temp
+               return;
+       }
+
+       if (to==from) {
+               printf("copy_pose_result source and target are the same\n");
+               return;
+       }
+
+
+       for(pchanfrom= from->chanbase.first; pchanfrom; pchanfrom= pchanfrom->next) {
+               pchanto= get_pose_channel(to, pchanfrom->name);
+               if(pchanto) {
+                       Mat4CpyMat4(pchanto->pose_mat, pchanfrom->pose_mat);
+                       Mat4CpyMat4(pchanto->chan_mat, pchanfrom->chan_mat);
+                       /* used for local constraints */
+                       VECCOPY(pchanto->loc, pchanfrom->loc);
+                       QUATCOPY(pchanto->quat, pchanfrom->quat);
+                       VECCOPY(pchanto->size, pchanfrom->size);
+                       
+                       VECCOPY(pchanto->pose_head, pchanfrom->pose_head);
+                       VECCOPY(pchanto->pose_tail, pchanfrom->pose_tail);
+                       pchanto->flag= pchanfrom->flag;
+               }
+       }
+}
+
 /* ********** NLA with non-poses works with ipo channels ********** */
 
 typedef struct NlaIpoChannel {
@@ -581,7 +930,7 @@ typedef struct NlaIpoChannel {
        int type;
 } NlaIpoChannel;
 
-static void extract_ipochannels_from_action(ListBase *lb, ID *id, bAction *act, char *name, float ctime)
+void extract_ipochannels_from_action(ListBase *lb, ID *id, bAction *act, const char *name, float ctime)
 {
        bActionChannel *achan= get_action_channel(act, name);
        IpoCurve *icu;
@@ -593,11 +942,13 @@ static void extract_ipochannels_from_action(ListBase *lb, ID *id, bAction *act,
                calc_ipo(achan->ipo, ctime);
                
                for(icu= achan->ipo->curve.first; icu; icu= icu->next) {
-                       
-                       nic= MEM_callocN(sizeof(NlaIpoChannel), "NlaIpoChannel");
-                       BLI_addtail(lb, nic);
-                       nic->val= icu->curval;
-                       nic->poin= get_ipo_poin(id, icu, &nic->type);
+                       /* skip IPO_BITS, is for layers and cannot be blended */
+                       if(icu->vartype != IPO_BITS) {
+                               nic= MEM_callocN(sizeof(NlaIpoChannel), "NlaIpoChannel");
+                               BLI_addtail(lb, nic);
+                               nic->val= icu->curval;
+                               nic->poin= get_ipo_poin(id, icu, &nic->type);
+                       }
                }
        }
        
@@ -612,7 +963,7 @@ static void extract_ipochannels_from_action(ListBase *lb, ID *id, bAction *act,
                        
                        if(conchan && conchan->ipo) {
                                calc_ipo(conchan->ipo, ctime);
-
+                               
                                icu= conchan->ipo->curve.first; // only one ipo now
                                if(icu) {
                                        nic= MEM_callocN(sizeof(NlaIpoChannel), "NlaIpoChannel constr");
@@ -646,10 +997,10 @@ static void blend_ipochannels(ListBase *dst, ListBase *src, float srcweight, int
        float dstweight;
        
        switch (mode){
-               case POSE_BLEND:
+               case ACTSTRIPMODE_BLEND:
                        dstweight = 1.0F - srcweight;
                        break;
-               case POSE_ADD:
+               case ACTSTRIPMODE_ADD:
                        dstweight = 1.0F;
                        break;
                default :
@@ -672,102 +1023,35 @@ static void blend_ipochannels(ListBase *dst, ListBase *src, float srcweight, int
        }
 }
 
-static void execute_ipochannels(ListBase *lb)
+int execute_ipochannels(ListBase *lb)
 {
        NlaIpoChannel *nic;
+       int count = 0;
        
        for(nic= lb->first; nic; nic= nic->next) {
                if(nic->poin) {
                        write_ipo_poin(nic->poin, nic->type, nic->val);
+                       count++;
                }
        }
+       return count;
 }
 
-
-/* ************** time ****************** */
-
-static bActionStrip *get_active_strip(Object *ob)
-{
-       bActionStrip *strip;
-       
-       if(ob->action==NULL)
-               return NULL;
-       
-       for (strip=ob->nlastrips.first; strip; strip=strip->next)
-               if(strip->flag & ACTSTRIP_ACTIVE)
-                       break;
-       
-       if(strip && strip->act==ob->action)
-               return strip;
-       return NULL;
-}
-
-/* non clipped mapping of strip */
-static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert)
-{
-       float length, actlength, repeat;
-       
-       if (strip->flag & ACTSTRIP_USESTRIDE)
-               repeat= 1.0f;
-       else
-               repeat= strip->repeat;
-       
-       length = strip->end-strip->start;
-       if(length==0.0f)
-               length= 1.0f;
-       actlength = strip->actend-strip->actstart;
-
-       
-       
-       if(invert)
-               return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start;
-       else
-               return repeat*actlength*(cframe - strip->start)/length + strip->actstart;
-}
-
-/* if the conditions match, it converts current time to strip time */
-float get_action_frame(Object *ob, float cframe)
-{
-       bActionStrip *strip= get_active_strip(ob);
-       
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 0);
-       return cframe;
-}
-
-/* inverted, strip time to current time */
-float get_action_frame_inv(Object *ob, float cframe)
-{
-       bActionStrip *strip= get_active_strip(ob);
-       
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 1);
-       return cframe;
-}
-
-
+/* nla timing */
 
 /* this now only used for repeating cycles, to enable fields and blur. */
 /* the whole time control in blender needs serious thinking... */
 static float nla_time(float cfra, float unit)
 {
        extern float bluroffs;  // bad construct, borrowed from object.c for now
+       extern float fieldoffs;
        
-       /* 2nd field */
-//     if(R.flag & R_SEC_FIELD) {
-//             if(R.r.mode & R_FIELDSTILL); else cfra+= 0.5f*unit;
-//     }
+       /* motion blur & fields */
+       cfra+= unit*(bluroffs+fieldoffs);
        
-       /* motion blur */
-       cfra+= unit*bluroffs;
-               
        /* global time */
        cfra*= G.scene->r.framelen;     
-
-
-       /* decide later... */
-//     if(no_speed_curve==0) if(ob && ob->ipo) cfra= calc_ipo_time(ob->ipo, cfra);
-
+       
        return cfra;
 }
 
@@ -777,7 +1061,7 @@ static float nla_time(float cfra, float unit)
 static float stridechannel_frame(Object *ob, float sizecorr, bActionStrip *strip, Path *path, float pathdist, float *stride_offset)
 {
        bAction *act= strip->act;
-       char *name= strip->stridechannel;
+       const char *name= strip->stridechannel;
        bActionChannel *achan= get_action_channel(act, name);
        int stride_axis= strip->stride_axis;
 
@@ -842,6 +1126,62 @@ static float stridechannel_frame(Object *ob, float sizecorr, bActionStrip *strip
        return 0.0f;
 }
 
+static void cyclic_offs_bone(Object *ob, bPose *pose, bActionStrip *strip, float time)
+{
+       /* only called when strip has cyclic, so >= 1.0f works... */
+       if(time >= 1.0f) {
+               bActionChannel *achan= get_action_channel(strip->act, strip->offs_bone);
+
+               if(achan && achan->ipo) {
+                       IpoCurve *icu= NULL;
+                       Bone *bone;
+                       float min[3]={0.0f, 0.0f, 0.0f}, max[3]={0.0f, 0.0f, 0.0f};
+                       int index=0, foundvert= 0;
+                       
+                       /* calculate the min/max */
+                       for (icu=achan->ipo->curve.first; icu; icu=icu->next) {
+                               if(icu->totvert>1) {
+                                       
+                                       if(icu->adrcode==AC_LOC_X)
+                                               index= 0;
+                                       else if(icu->adrcode==AC_LOC_Y)
+                                               index= 1;
+                                       else if(icu->adrcode==AC_LOC_Z)
+                                               index= 2;
+                                       else
+                                               continue;
+                               
+                                       foundvert= 1;
+                                       min[index]= icu->bezt[0].vec[1][1];
+                                       max[index]= icu->bezt[icu->totvert-1].vec[1][1];
+                               }
+                       }
+                       if(foundvert) {
+                               /* bring it into armature space */
+                               VecSubf(min, max, min);
+                               bone= get_named_bone(ob->data, strip->offs_bone);       /* weak */
+                               if(bone) {
+                                       Mat4Mul3Vecfl(bone->arm_mat, min);
+                                       
+                                       /* dominant motion, cyclic_offset was cleared in rest_pose */
+                                       if (strip->flag & (ACTSTRIP_CYCLIC_USEX | ACTSTRIP_CYCLIC_USEY | ACTSTRIP_CYCLIC_USEZ)) {
+                                               if (strip->flag & ACTSTRIP_CYCLIC_USEX) pose->cyclic_offset[0]= time*min[0];
+                                               if (strip->flag & ACTSTRIP_CYCLIC_USEY) pose->cyclic_offset[1]= time*min[1];
+                                               if (strip->flag & ACTSTRIP_CYCLIC_USEZ) pose->cyclic_offset[2]= time*min[2];
+                                       } else {
+                                               if( fabs(min[0]) >= fabs(min[1]) && fabs(min[0]) >= fabs(min[2]))
+                                                       pose->cyclic_offset[0]= time*min[0];
+                                               else if( fabs(min[1]) >= fabs(min[0]) && fabs(min[1]) >= fabs(min[2]))
+                                                       pose->cyclic_offset[1]= time*min[1];
+                                               else
+                                                       pose->cyclic_offset[2]= time*min[2];
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
 /* simple case for now; only the curve path with constraint value > 0.5 */
 /* blending we might do later... */
 static Object *get_parent_path(Object *ob)
@@ -864,6 +1204,47 @@ static Object *get_parent_path(Object *ob)
 
 /* ************** do the action ************ */
 
+/* For the calculation of the effects of an action at the given frame on an object 
+ * This is currently only used for the action constraint 
+ */
+void what_does_obaction (Object *ob, bAction *act, float cframe)
+{
+       ListBase tchanbase= {NULL, NULL};
+       
+       clear_workob();
+       Mat4CpyMat4(workob.obmat, ob->obmat);
+       Mat4CpyMat4(workob.parentinv, ob->parentinv);
+       Mat4CpyMat4(workob.constinv, ob->constinv);
+       workob.parent= ob->parent;
+       workob.track= ob->track;
+
+       workob.trackflag= ob->trackflag;
+       workob.upflag= ob->upflag;
+       
+       workob.partype= ob->partype;
+       workob.par1= ob->par1;
+       workob.par2= ob->par2;
+       workob.par3= ob->par3;
+
+       workob.constraints.first = ob->constraints.first;
+       workob.constraints.last = ob->constraints.last;
+
+       strcpy(workob.parsubstr, ob->parsubstr);
+       strcpy(workob.id.name, ob->id.name);
+       
+       /* extract_ipochannels_from_action needs id's! */
+       workob.action= act;
+       
+       extract_ipochannels_from_action(&tchanbase, &workob.id, act, "Object", bsystem_time(&workob, cframe, 0.0));
+       
+       if (tchanbase.first) {
+               execute_ipochannels(&tchanbase);
+               BLI_freelistN(&tchanbase);
+       }
+}
+
+/* ----- nla, etc. --------- */
+
 static void do_nla(Object *ob, int blocktype)
 {
        bPose *tpose= NULL;
@@ -872,7 +1253,7 @@ static void do_nla(Object *ob, int blocktype)
        bActionStrip *strip, *striplast=NULL, *stripfirst=NULL;
        float striptime, frametime, length, actlength;
        float blendfac, stripframe;
-       float scene_cfra= G.scene->r.cfra;
+       float scene_cfra= frame_to_float(G.scene->r.cfra); 
        int     doit, dostride;
        
        if(blocktype==ID_AR) {
@@ -902,24 +1283,24 @@ static void do_nla(Object *ob, int blocktype)
                }
        }
        if(strip==NULL) {       /* extend */
-               if(stripfirst)
-                       scene_cfra= stripfirst->start;
-               else if(striplast)
+               if(striplast)
                        scene_cfra= striplast->end;
+               else if(stripfirst)
+                       scene_cfra= stripfirst->start;
        }
        
        /* and now go over all strips */
        for (strip=ob->nlastrips.first; strip; strip=strip->next){
                doit=dostride= 0;
                
-               if (strip->act){        /* so theres an action */
+               if (strip->act && !(strip->flag & ACTSTRIP_MUTE)) {     /* so theres an action */
                        
                        /* Determine if the current frame is within the strip's range */
                        length = strip->end-strip->start;
                        actlength = strip->actend-strip->actstart;
                        striptime = (scene_cfra-(strip->start)) / length;
                        stripframe = (scene_cfra-(strip->start)) ;
-                       
+
                        if (striptime>=0.0){
                                
                                if(blocktype==ID_AR) 
@@ -942,7 +1323,7 @@ static void do_nla(Object *ob, int blocktype)
                                                                if(cu->path) {
                                                                        
                                                                        /* Find the position on the path */
-                                                                       ctime= bsystem_time(ob, parent, scene_cfra, 0.0);
+                                                                       ctime= bsystem_time(ob, scene_cfra, 0.0);
                                                                        
                                                                        if(calc_ipo_spec(cu->ipo, CU_SPEED, &ctime)==0) {
                                                                                /* correct for actions not starting on zero */
@@ -964,7 +1345,7 @@ static void do_nla(Object *ob, int blocktype)
                                                                        }
                                                                        
                                                                        frametime = (striptime * actlength) + strip->actstart;
-                                                                       frametime= bsystem_time(ob, 0, frametime, 0.0);
+                                                                       frametime= bsystem_time(ob, frametime, 0.0);
                                                                        
                                                                        if(blocktype==ID_AR) {
                                                                                extract_pose_from_action (tpose, strip->act, frametime);
@@ -984,31 +1365,38 @@ static void do_nla(Object *ob, int blocktype)
                                                
                                                /* Mod to repeat */
                                                if(strip->repeat!=1.0f) {
-                                                       striptime*= strip->repeat;
-                                                       striptime = (float)fmod (striptime, 1.0f + 0.1f/length);
+                                                       float cycle= striptime*strip->repeat;
+                                                       
+                                                       striptime = (float)fmod (cycle, 1.0f + 0.1f/length);
+                                                       cycle-= striptime;
+                                                       
+                                                       if(blocktype==ID_AR)
+                                                               cyclic_offs_bone(ob, tpose, strip, cycle);
                                                }
 
                                                frametime = (striptime * actlength) + strip->actstart;
                                                frametime= nla_time(frametime, (float)strip->repeat);
                                                        
-                                               if(blocktype==ID_AR)
+                                               if(blocktype==ID_AR) {
                                                        extract_pose_from_action (tpose, strip->act, frametime);
+                                               }
                                                else if(blocktype==ID_OB) {
                                                        extract_ipochannels_from_action(&tchanbase, &ob->id, strip->act, "Object", frametime);
                                                        if(key)
                                                                extract_ipochannels_from_action(&tchanbase, &key->id, strip->act, "Shape", frametime);
-                                               }                                               
+                                               }
+                                               
                                                doit=1;
                                        }
                                }
                                /* Handle extend */
-                               else{
+                               else {
                                        if (strip->flag & ACTSTRIP_HOLDLASTFRAME){
                                                /* we want the strip to hold on the exact fraction of the repeat value */
                                                
                                                frametime = actlength * (strip->repeat-(int)strip->repeat);
                                                if(frametime<=0.000001f) frametime= actlength;  /* rounding errors... */
-                                               frametime= bsystem_time(ob, 0, frametime+strip->actstart, 0.0);
+                                               frametime= bsystem_time(ob, frametime+strip->actstart, 0.0);
                                                
                                                if(blocktype==ID_AR)
                                                        extract_pose_from_action (tpose, strip->act, frametime);
@@ -1017,6 +1405,13 @@ static void do_nla(Object *ob, int blocktype)
                                                        if(key)
                                                                extract_ipochannels_from_action(&tchanbase, &key->id, strip->act, "Shape", frametime);
                                                }
+                                               
+                                               /* handle cycle hold */
+                                               if(strip->repeat!=1.0f) {
+                                                       if(blocktype==ID_AR)
+                                                               cyclic_offs_bone(ob, tpose, strip, strip->repeat-1.0f);
+                                               }
+                                               
                                                doit=1;
                                        }
                                }
@@ -1035,6 +1430,9 @@ static void do_nla(Object *ob, int blocktype)
                                                blendfac = 1;
                                        
                                        if(blocktype==ID_AR) {/* Blend this pose with the accumulated pose */
+                                               /* offset bone, for matching cycles */
+                                               blend_pose_offset_bone (strip, ob->pose, tpose, blendfac, strip->mode);
+                                               
                                                blend_poses (ob->pose, tpose, blendfac, strip->mode);
                                                if(dostride)
                                                        blend_pose_strides (ob->pose, tpose, blendfac, strip->mode);
@@ -1057,23 +1455,19 @@ static void do_nla(Object *ob, int blocktype)
        }
        
        /* free */
-       if (tpose){
-               free_pose_channels(tpose);
-               MEM_freeN(tpose);
-       }
+       if (tpose)
+               free_pose(tpose);
        if(chanbase.first)
                BLI_freelistN(&chanbase);
-       
 }
 
 void do_all_pose_actions(Object *ob)
 {
-
-       // only to have safe calls from editor
+       /* only to have safe calls from editor */
        if(ob==NULL) return;
        if(ob->type!=OB_ARMATURE || ob->pose==NULL) return;
 
-       if(ob->pose->flag & POSE_LOCKED) {  // no actions to execute while transform
+       if(ob->pose->flag & POSE_LOCKED) {  /*  no actions to execute while transform */
                if(ob->pose->flag & POSE_DO_UNLOCK)
                        ob->pose->flag &= ~(POSE_LOCKED|POSE_DO_UNLOCK);
        }
@@ -1082,11 +1476,14 @@ void do_all_pose_actions(Object *ob)
                
                cframe= get_action_frame(ob, cframe);
                
-               extract_pose_from_action (ob->pose, ob->action, bsystem_time(ob, 0, cframe, 0.0));
+               extract_pose_from_action (ob->pose, ob->action, bsystem_time(ob, cframe, 0.0));
        }
        else if(ob->nlastrips.first) {
                do_nla(ob, ID_AR);
        }
+       
+       /* clear POSE_DO_UNLOCK flags that might have slipped through (just in case) */
+       ob->pose->flag &= ~POSE_DO_UNLOCK;
 }
 
 /* called from where_is_object */
@@ -1103,9 +1500,9 @@ void do_all_object_actions(Object *ob)
                
                cframe= get_action_frame(ob, cframe);
                
-               extract_ipochannels_from_action(&tchanbase, &ob->id, ob->action, "Object", bsystem_time(ob, 0, cframe, 0.0));
+               extract_ipochannels_from_action(&tchanbase, &ob->id, ob->action, "Object", bsystem_time(ob, cframe, 0.0));
                if(key)
-                       extract_ipochannels_from_action(&tchanbase, &key->id, ob->action, "Shape", bsystem_time(ob, 0, cframe, 0.0));
+                       extract_ipochannels_from_action(&tchanbase, &key->id, ob->action, "Shape", bsystem_time(ob, cframe, 0.0));
                
                if(tchanbase.first) {
                        execute_ipochannels(&tchanbase);
@@ -1116,5 +1513,3 @@ void do_all_object_actions(Object *ob)
                do_nla(ob, ID_OB);
        }
 }
-
-