Logo Search packages:      
Sourcecode: blender version File versions

action.c

/**
 * $Id: action.c,v 1.14 2004/11/11 15:31:44 ton Exp $
 *
 * ***** BEGIN GPL/BL DUAL 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <math.h>
#include <stdlib.h>     /* for NULL */

#include "MEM_guardedalloc.h"
#include "BLI_arithb.h"
#include "BLI_blenlib.h"

#include "BKE_action.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_utildefines.h"

#include "DNA_object_types.h"
#include "DNA_ipo_types.h"
#include "DNA_curve_types.h"
#include "DNA_scene_types.h"
#include "DNA_action_types.h"
#include "DNA_nla_types.h"

#include "BKE_blender.h"
#include "BKE_ipo.h"
#include "BKE_object.h"
#include "BKE_library.h"
#include "BKE_anim.h"
#include "BKE_armature.h"

#include "nla.h"

#include "BKE_constraint.h"
#include "DNA_constraint_types.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/* Local function prototypes */

static void do_pose_constraint_channels(bPose *pose, bAction *act, 
                                                            float ctime);

static void rest_pose(bPose *pose, int clearflag);

/* Implementation */

bPoseChannel *get_pose_channel(const bPose *pose, const char *name)
{
      bPoseChannel *chan;

      for (chan=pose->chanbase.first; chan; chan=chan->next){
            if (!strcmp (chan->name, name))
                  return chan;
      }

      return NULL;
}

static void rest_pose(bPose *pose, int clearflag)
{
      bPoseChannel *chan;
      int i;

      if (!pose)
            return;

      for (chan=pose->chanbase.first; chan; chan=chan->next){
            for (i=0; i<3; i++){
                  chan->loc[i]=0.0;
                  chan->quat[i+1]=0.0;
                  chan->size[i]=1.0;
            }
            chan->quat[0]=1.0;
            if (clearflag)
                  chan->flag =0;
      }
}

#ifdef __NLA_BLENDCON
static void blend_constraints(ListBase *dst, const ListBase *src,
                                            float srcweight, short mode)
{
      bConstraint *dcon;
      const bConstraint *scon;
      float dstweight = 0;

      switch (mode){
      case POSE_BLEND:
            dstweight = 1.0F - srcweight;
            break;
      case POSE_ADD:
            dstweight = 1.0F;
            break;
      }

      /* Blend constraints */
      for (dcon=dst->first; dcon; dcon=dcon->next){
            for (scon = src->first; scon; scon=scon->next){
                  if (!strcmp(scon->name, dcon->name))
                        break;
            }
            
            if (scon){
                  dcon->enforce = (dcon->enforce*dstweight) + (scon->enforce*srcweight);
                  if (mode == POSE_BLEND)
                        dcon->enforce/=2.0;
                  
                  if (dcon->enforce>1.0)
                        dcon->enforce=1.0;
                  if (dcon->enforce<0.0)
                        dcon->enforce=0.0;

            }
      }
}
#endif

void blend_poses(bPose *dst, const bPose *src, float srcweight, short mode)
{
      bPoseChannel *dchan;
      const bPoseChannel *schan;
      float dquat[4], squat[4]; //, mat[3][3];
      float dstweight;
      int i;
      
      switch (mode){
      case POSE_BLEND:
            dstweight = 1.0F - srcweight;
            break;
      case POSE_ADD:
            dstweight = 1.0F;
            break;
      default :
            dstweight = 1.0F;
      }
      
      for (dchan = dst->chanbase.first; dchan; dchan=dchan->next){
            schan = get_pose_channel(src, dchan->name);
            if (schan){
                  if (schan->flag & (POSE_ROT|POSE_LOC|POSE_SIZE)){
                        
                        /* Convert both quats to matrices and then back again. 
                         * This prevents interpolation problems
                         * This sucks because it is slow and stupid
                         */
                        
                        //QuatToMat3(dchan->quat, mat);
                        //Mat3ToQuat(mat, dquat);
                        //QuatToMat3(schan->quat, mat);
                        //Mat3ToQuat(mat, squat);
                        
                        /* replaced quat->matrix->quat conversion with decent quaternion interpol (ton) */
                        /* 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);
                        }

                        for (i=0; i<3; i++){
                              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);
                              //if (schan->flag & POSE_ROT)
                              //    dchan->quat[i+1] = (dquat[i+1]*dstweight) + (squat[i+1]*srcweight);
                        }
                        
                        /* Do one more iteration for the quaternions only and normalize the quaternion if needed */
                        //if (schan->flag & POSE_ROT)
                        //    dchan->quat[0] = 1.0f + ((dquat[0]-1.0f)*dstweight) + ((squat[0]-1.0f)*srcweight);
                        //if (mode==POSE_BLEND)
                        //    NormalQuat (dchan->quat);
                        
                        dchan->flag |= schan->flag;
                  }
            }
      }
}

void clear_pose_constraint_status(Object *ob)
{
      bPoseChannel *chan;

      if (!ob)
            return;
      if (!ob->pose)
            return;

      for (chan = ob->pose->chanbase.first; chan; chan=chan->next){
            chan->flag &= ~PCHAN_DONE;
      }
}

float calc_action_start(const bAction *act)
{
      const bActionChannel *chan;
      const IpoCurve    *icu;
      float size=999999999.0f;
      int   i;
      int   foundvert=0;
      const bConstraintChannel *conchan;


      if (!act)
            return 0;

      for (chan=act->chanbase.first; chan; chan=chan->next){
            for (icu=chan->ipo->curve.first; icu; icu=icu->next)
                  for (i=0; i<icu->totvert; i++){
                        size = MIN2 (size, icu->bezt[i].vec[1][0]);
                        foundvert=1;
                        
                  }
                  for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
                        for (icu=conchan->ipo->curve.first; icu; icu=icu->next)
                              for (i=0; i<icu->totvert; i++){
                                    size = MIN2 (size, icu->bezt[i].vec[1][0]);
                                    foundvert=1;
                              }
                  }
      }
      
      if (!foundvert)
            return 0;
      else
            return size;
}

float calc_action_end(const bAction *act)
{
      const bActionChannel    *chan;
      const bConstraintChannel *conchan;
      const IpoCurve          *icu;
      float size=0;
      int   i;

      if (!act)
            return 0;

      for (chan=act->chanbase.first; chan; chan=chan->next){
            for (icu=chan->ipo->curve.first; icu; icu=icu->next)
                  for (i=0; i<icu->totvert; i++)
                        size = MAX2 (size, icu->bezt[i].vec[1][0]);
                  
                  for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
                        for (icu=conchan->ipo->curve.first; icu; icu=icu->next)
                              for (i=0; i<icu->totvert; i++)
                                    size = MAX2 (size, icu->bezt[i].vec[1][0]);
                  }
      }
      return size;
}

bPoseChannel *verify_pose_channel(bPose* pose, const char* name)
{
      bPoseChannel *chan;

      if (!pose){
            return NULL;
      }

      /*      See if this channel exists */
      for (chan=pose->chanbase.first; chan; chan=chan->next){
            if (!strcmp (name, chan->name))
                  return chan;
      }

      /* If not, create it and add it */
      chan = MEM_callocN(sizeof(bPoseChannel), "verifyPoseChannel");

      strcpy (chan->name, name);
      chan->loc[0] = chan->loc[1] = chan->loc[2] = 0.0F;
      chan->quat[1] = chan->quat[2] = chan->quat[3] = 0.0F;
      chan->quat[0] = 1.0F;
      chan->size[0] = chan->size[1] = chan->size[2] = 1.0F;

      chan->flag |= POSE_ROT|POSE_SIZE|POSE_LOC;

      BLI_addtail (&pose->chanbase, chan);

      return chan;
}

void get_pose_from_pose(bPose **pose, const bPose *src)
{
      const bPoseChannel      *pchan;
      bPoseChannel *newchan;

      if (!src)
            return;
      if (!pose)
            return;

      /* If there is no pose, create one */
      if (!*pose){
            *pose=MEM_callocN (sizeof(bPose), "pose");
      }

      /* Copy the data from the action into the pose */
      for (pchan=src->chanbase.first; pchan; pchan=pchan->next){
            newchan = copy_pose_channel(pchan);
            verify_pose_channel(*pose, pchan->name);
            set_pose_channel(*pose, newchan);
      }
}

#ifdef __NLA_BLENDCON
static void get_constraint_influence_from_pose(bPose *dst, bPose *src)
{
      bConstraint *dcon, *scon;

      if (!src || !dst)
            return;

      for (dcon = dst->chanbase.first; dcon; dcon=dcon->next){
            for (scon=src->chanbase.first; scon; scon=scon->next){
                  if (!strcmp(scon->name, dcon->name))
                        break;
            }
            if (scon){
                  dcon->enforce = scon->enforce;
            }
      }
}
#endif

/* If the pose does not exist, a new one is created */

void get_pose_from_action(bPose **pose, bAction *act, float ctime) 
{
      bActionChannel *achan;
      bPoseChannel      *pchan;
      Ipo                     *ipo;
      IpoCurve          *curve;
      

      if (!act)
            return;
      if (!pose)
            return;

      /* If there is no pose, create one */
      if (!*pose){
            *pose=MEM_callocN (sizeof(bPose), "pose");
      }

      /* Copy the data from the action into the pose */
      for (achan=act->chanbase.first; achan; achan=achan->next){
            act->achan= achan;

            ipo = achan->ipo;
            if (ipo){
                  pchan=MEM_callocN (sizeof(bPoseChannel), "gpfa_poseChannel");
                  strcpy (pchan->name, achan->name);

                  act->pchan=pchan;
                  /* Evaluates and sets the internal ipo value */
                  calc_ipo(ipo, ctime);

                  /* Set the pchan flags */
                  for (curve = achan->ipo->curve.first; curve; curve=curve->next){
                        /*    Skip empty curves */
                        if (!curve->totvert)
                              continue;

                        switch (curve->adrcode){
                        case AC_QUAT_X:
                        case AC_QUAT_Y:
                        case AC_QUAT_Z:
                        case AC_QUAT_W:
                              pchan->flag |= POSE_ROT;
                              break;
                        case AC_LOC_X:
                        case AC_LOC_Y:
                        case AC_LOC_Z:
                              pchan->flag |= POSE_LOC;
                              break;
                        case AC_SIZE_X:
                        case AC_SIZE_Y:
                        case AC_SIZE_Z:
                              pchan->flag |= POSE_SIZE;
                              break;
                        }
                  }

                  execute_ipo((ID*)act, achan->ipo);
            
                  set_pose_channel(*pose, pchan);
            }
      }
}

void do_all_actions()
{
      Base *base;
      bPose *apose=NULL;
      bPose *tpose=NULL;
      Object *ob;
      bActionStrip *strip;
      int   doit;
      float striptime, frametime, length, actlength;
      float blendfac, stripframe;

      int set;

      /* NEW: current scene ob ipo's */
      base= G.scene->base.first;
      set= 0;

      while(base) {
            
            ob = base->object;

            /* Retrieve data from the NLA */
            if(ob->type==OB_ARMATURE){

                  doit=0;

                  /* Clear pose */
                  if (apose){
                        clear_pose(apose);
                        MEM_freeN(apose);
                  }
                  /* Clear pose */
                  if (tpose){
                        clear_pose(tpose);
                        MEM_freeN(tpose);
                  }

                  copy_pose(&apose, ob->pose, 1);
                  copy_pose(&tpose, ob->pose, 1);
                  rest_pose(apose, 1);
 
                  if (base->object->nlastrips.first){
                        rest_pose(base->object->pose, 0);
                  }

                  for (strip=base->object->nlastrips.first; strip; strip=strip->next){
                        doit = 0;
                        if (strip->act){
                  
                              /* Determine if the current frame is within the strip's range */
                              length = strip->end-strip->start;
                              actlength = strip->actend-strip->actstart;
                              striptime = (G.scene->r.cfra-(strip->start)) / length;
                              stripframe = (G.scene->r.cfra-(strip->start)) ;


                              if (striptime>=0.0){
                                    
                                    rest_pose(tpose, 1);

                                    /* Handle path */
                                    if (strip->flag & ACTSTRIP_USESTRIDE){
                                          if (ob->parent && ob->parent->type==OB_CURVE){
                                                Curve *cu = ob->parent->data;
                                                float ctime, pdist;

                                                if (cu->flag & CU_PATH){
                                                      /* Ensure we have a valid path */
                                                      if(cu->path==0 || cu->path->data==0) calc_curvepath(ob->parent);

                                                      /* Find the position on the path */
                                                      ctime= bsystem_time(ob, ob->parent, (float)G.scene->r.cfra, 0.0);
                                                      
                                                      if(calc_ipo_spec(cu->ipo, CU_SPEED, &ctime)==0) {
                                                            ctime /= cu->pathlen;
                                                            CLAMP(ctime, 0.0, 1.0);
                                                      }
                                                      pdist = ctime*cu->path->totdist;
                                                      
                                                      if (strip->stridelen)
                                                            striptime = pdist / strip->stridelen;
                                                      else
                                                            striptime = 0;
                                                      
                                                      striptime = (float)fmod (striptime, 1.0);
                                                      
                                                      frametime = (striptime * actlength) + strip->actstart;
                                                      get_pose_from_action (&tpose, strip->act, bsystem_time(ob, 0, frametime, 0.0));
#ifdef __NLA_BLENDCON
                                                      do_pose_constraint_channels(tpose, strip->act, bsystem_time(ob, 0, frametime, 0.0));
#endif
                                                      doit=1;
                                                }
                                          }
                                    }

                                    /* Handle repeat */
            
                                    else if (striptime < 1.0){
                                          /* Mod to repeat */
                                          striptime*=strip->repeat;
                                          striptime = (float)fmod (striptime, 1.0);
                                          
                                          frametime = (striptime * actlength) + strip->actstart;
                                          get_pose_from_action (&tpose, strip->act, bsystem_time(ob, 0, frametime, 0.0));
#ifdef __NLA_BLENDCON
                                          do_pose_constraint_channels(tpose, strip->act, bsystem_time(ob, 0, frametime, 0.0));
#endif
                                          doit=1;
                                    }
                                    /* Handle extend */
                                    else{
                                          if (strip->flag & ACTSTRIP_HOLDLASTFRAME){
                                                striptime = 1.0;
                                                frametime = (striptime * actlength) + strip->actstart;
                                                get_pose_from_action (&tpose, strip->act, bsystem_time(ob, 0, frametime, 0.0));
#ifdef __NLA_BLENDCON
                                                do_pose_constraint_channels(tpose, strip->act, bsystem_time(ob, 0, frametime, 0.0));
#endif
                                                doit=1;
                                          }
                                    }

                                    /* Handle blendin & blendout */
                                    if (doit){
                                          /* Handle blendin */

                                          if (strip->blendin>0.0 && stripframe<=strip->blendin && G.scene->r.cfra>=strip->start){
                                                blendfac = stripframe/strip->blendin;
                                          }
                                          else if (strip->blendout>0.0 && stripframe>=(length-strip->blendout) && G.scene->r.cfra<=strip->end){
                                                blendfac = (length-stripframe)/(strip->blendout);
                                          }
                                          else
                                                blendfac = 1;

                                          /* Blend this pose with the accumulated pose */
                                          blend_poses (apose, tpose, blendfac, strip->mode);
#ifdef __NLA_BLENDCON
                                          blend_constraints(&apose->chanbase, &tpose->chanbase, blendfac, strip->mode);
#endif
                                    }
                              }                             
                              if (apose){
                                    get_pose_from_pose(&ob->pose, apose);
#ifdef __NLA_BLENDCON
                                    get_constraint_influence_from_pose(ob->pose, apose);
#endif
                              }
                        }
                        
                  }

                  /* Do local action (always overrides the nla actions) */
                  /*    At the moment, only constraint ipos on the local action have any effect */
                  if(base->object->action) {
                        get_pose_from_action (&ob->pose, ob->action, bsystem_time(ob, 0, (float) G.scene->r.cfra, 0.0));
                        do_pose_constraint_channels(ob->pose, ob->action, bsystem_time(ob, 0, (float) G.scene->r.cfra, 0.0));
                        doit = 1;
                  } 
                  
                  if (doit)
                        apply_pose_armature(get_armature(ob), ob->pose, 1);

            }
            base= base->next;
            if(base==0 && set==0 && G.scene->set) {
                  set= 1;
                  base= G.scene->set->base.first; 
            }
            
      }

      if (apose){
            clear_pose(apose);
            MEM_freeN(apose);
            apose = NULL;
      }     
      if (tpose){
            clear_pose(tpose);
            MEM_freeN(tpose);
            apose = NULL;
      }

}

static void do_pose_constraint_channels(bPose *pose, bAction *act, float ctime)
{
      bPoseChannel *pchan;
      bActionChannel *achan;

      if (!pose || !act)
            return;
      
      for (pchan=pose->chanbase.first; pchan; pchan=pchan->next){
            achan=get_named_actionchannel(act, pchan->name);
            if (achan)
                  do_constraint_channels(&pchan->constraints, &achan->constraintChannels, ctime);
      }
}

bActionChannel *get_named_actionchannel(bAction *act, const char *name)
{
      bActionChannel *chan;

      if (!act)
            return NULL;

      for (chan = act->chanbase.first; chan; chan=chan->next){
            if (!strcmp (chan->name, name))
                  return chan;
      }

      return NULL;
}

void clear_pose(bPose *pose) 
{
      bPoseChannel *chan;
      
      if (pose->chanbase.first){
            for (chan = pose->chanbase.first; chan; chan=chan->next){
                  free_constraints(&chan->constraints);
            }
            BLI_freelistN (&pose->chanbase);
      }
}

void make_local_action(bAction *act)
{
      Object *ob;
      bAction *actn;
      int local=0, lib=0;

      if(act->id.lib==0) return;
      if(act->id.us==1) {
            act->id.lib= 0;
            act->id.flag= LIB_LOCAL;
            new_id(0, (ID *)act, 0);
            return;
      }
      
      ob= G.main->object.first;
      while(ob) {
            if(ob->action==act) {
                  if(ob->id.lib) lib= 1;
                  else local= 1;
            }
            ob= ob->id.next;
      }
      
      if(local && lib==0) {
            act->id.lib= 0;
            act->id.flag= LIB_LOCAL;
            new_id(0, (ID *)act, 0);
      }
      else if(local && lib) {
            actn= copy_action(act);
            actn->id.us= 0;
            
            ob= G.main->object.first;
            while(ob) {
                  if(ob->action==act) {
                        
                        if(ob->id.lib==0) {
                              ob->action = actn;
                              ob->activecon = NULL;
                              actn->id.us++;
                              act->id.us--;
                        }
                  }
                  ob= ob->id.next;
            }
      }
}


void free_action(bAction *act)
{
      bActionChannel *chan;

      /* Free channels */
      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);
}

bAction* copy_action(bAction *src)
{
      bAction *dst = NULL;
      bActionChannel *dchan, *schan;
      
      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){
            dchan->ipo = copy_ipo(dchan->ipo);
            copy_constraint_channels(&dchan->constraintChannels, &schan->constraintChannels);
      }
      dst->id.flag |= LIB_FAKEUSER;
      dst->id.us++;
      return dst;
}

bPoseChannel *copy_pose_channel(const bPoseChannel* src)
{
      bPoseChannel *dst;

      if (!src)
            return NULL;

      dst = MEM_callocN (sizeof(bPoseChannel), "copyPoseChannel");
      memcpy (dst, src, sizeof(bPoseChannel));
      dst->prev=dst->next=NULL;

      return dst;
}

void copy_pose(bPose **dst, bPose *src, int copycon)
{
      bPose *outPose;
      bPose * inPose;
      bPoseChannel      *newChan;
      bPoseChannel *curChan;

      inPose = src;
      
      if (!inPose){
            *dst=NULL;
            return;
      }

      outPose=MEM_callocN(sizeof(bPose), "pose");

      for (curChan=inPose->chanbase.first; curChan; curChan=curChan->next){
            newChan=MEM_callocN(sizeof(bPoseChannel), "copyposePoseChannel");

            strcpy (newChan->name, curChan->name);
            newChan->flag=curChan->flag;

            memcpy (newChan->loc, curChan->loc, sizeof (curChan->loc));
            memcpy (newChan->size, curChan->size, sizeof (curChan->size));
            memcpy (newChan->quat, curChan->quat, sizeof (curChan->quat));
            Mat4CpyMat4 (newChan->obmat, (void *)curChan->obmat);

            BLI_addtail (&outPose->chanbase, newChan);
            if (copycon){
                  copy_constraints(&newChan->constraints, &curChan->constraints);
            }
      }
      
      *dst=outPose;
}

bPoseChannel *set_pose_channel(bPose *pose, bPoseChannel *chan)
{
      /*    chan is no longer valid for the calling function.
            and should not be used by that function after calling
            this one
      */
      bPoseChannel      *curChan;

      /*    Determine if an equivalent channel exists already */
      for (curChan=pose->chanbase.first; curChan; curChan=curChan->next){
            if (!strcmp (curChan->name, chan->name)){
                  if (chan->flag & POSE_ROT)
                        memcpy (curChan->quat, chan->quat, sizeof(chan->quat)); 
                  if (chan->flag & POSE_SIZE) 
                        memcpy (curChan->size, chan->size, sizeof(chan->size));
                  if (chan->flag & POSE_LOC)
                        memcpy (curChan->loc, chan->loc, sizeof(chan->loc));
                  if (chan->flag & PCHAN_DONE)
                        Mat4CpyMat4 (curChan->obmat, chan->obmat);

                  curChan->flag |= chan->flag;
                  MEM_freeN (chan);
                  return curChan;
            }
      }

      MEM_freeN (chan);
      return NULL;
      /* If an equivalent channel doesn't exist, then don't bother setting it. */
}



Generated by  Doxygen 1.6.0   Back to index