2.5
[blender.git] / source / blender / blenkernel / intern / action.c
index b04e0240873c50d188f3d2948c71e85ae212876b..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
@@ -65,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 **********************
 
@@ -177,24 +175,51 @@ void free_action (bAction *act)
        
        if (act->chanbase.first)
                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 *dst = NULL;
        bActionChannel *dchan, *schan;
+       bActionGroup *dgrp, *sgrp;
        
        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;
 }
 
@@ -266,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) {
@@ -285,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);
        }
 }
 
@@ -301,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)
 {
@@ -317,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;
@@ -346,7 +456,20 @@ 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;
                }
        }
 }
@@ -386,7 +509,7 @@ bActionChannel *get_action_channel(bAction *act, const char *name)
        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;
        }
@@ -394,18 +517,16 @@ 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;
 }
@@ -431,21 +552,20 @@ static bActionStrip *get_active_strip(Object *ob)
 /* non clipped mapping of strip */
 static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert)
 {
-       float length, actlength, repeat;
+       float length, actlength, repeat, scale;
        
-       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 (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;
        
-       if(invert)
+       /* 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;
@@ -579,7 +699,6 @@ 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;
        
@@ -601,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==ACTSTRIPMODE_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;
                }
@@ -629,33 +761,33 @@ void blend_poses(bPose *dst, bPose *src, float srcweight, short mode)
        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;
                                                        }
                                                }
@@ -683,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);
        }
@@ -742,6 +879,7 @@ void rest_pose(bPose *pose)
                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.0f;
@@ -760,6 +898,12 @@ void copy_pose_result(bPose *to, bPose *from)
                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) {
@@ -786,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;
@@ -819,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");
@@ -879,15 +1023,18 @@ 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;
 }
 
 /* nla timing */
@@ -904,11 +1051,7 @@ static float nla_time(float cfra, float unit)
        
        /* 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;
 }
 
@@ -918,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;
 
@@ -1086,12 +1229,13 @@ void what_does_obaction (Object *ob, bAction *act, float cframe)
        workob.constraints.first = ob->constraints.first;
        workob.constraints.last = ob->constraints.last;
 
-       strcpy(workob.parsubstr, ob->parsubstr); 
+       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, &ob->id, act, "Object", bsystem_time(&workob, cframe, 0.0));
+       extract_ipochannels_from_action(&tchanbase, &workob.id, act, "Object", bsystem_time(&workob, cframe, 0.0));
        
        if (tchanbase.first) {
                execute_ipochannels(&tchanbase);
@@ -1311,10 +1455,8 @@ 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);
 }