Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/action.c')
-rw-r--r--source/blender/blenkernel/intern/action.c873
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. */
+}
+
+