diff options
Diffstat (limited to 'source/blender/blenkernel/intern/action.c')
-rw-r--r-- | source/blender/blenkernel/intern/action.c | 873 |
1 files changed, 873 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c new file mode 100644 index 00000000000..9f5de3f34fa --- /dev/null +++ b/source/blender/blenkernel/intern/action.c @@ -0,0 +1,873 @@ +/** + * $Id$ + * + * ***** 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 ***** + */ + +#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" + +/* Local function prototypes */ +static + void +do_pose_constraint_channels( + bPose *pose, + bAction *act, + float ctime +); + +static + void +get_constraint_influence_from_pose ( + bPose *dst, + bPose *src +); + +static + void +blend_constraints( + ListBase *dst, + const ListBase *src, + float srcweight, + short mode +); + +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; + } +} + +static + void +blend_constraints( + ListBase *dst, + const ListBase *src, + float srcweight, + short mode +){ + bConstraint *dcon; + const bConstraint *scon; + float dstweight; + + 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; + + } + } +} + + 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); + + /* Do the transformation blend */ + 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; +} + + void +verify_pose_channel ( + bPose* pose, + const char* name +) { + bPoseChannel *chan; + + if (!pose){ + return; + } + + /* See if this channel exists */ + for (chan=pose->chanbase.first; chan; chan=chan->next){ + if (!strcmp (name, chan->name)) + return; + } + + /* 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); +} + + 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); + } +} + +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; + } + } +} + +/* 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 ( + const 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, + const bPose *src, + int copycon +){ + bPose *outPose; + const bPose * inPose; + bPoseChannel *newChan; + const 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, 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. */ +} + + |