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:
authorJoshua Leung <aligorith@gmail.com>2008-12-19 14:45:46 +0300
committerJoshua Leung <aligorith@gmail.com>2008-12-19 14:45:46 +0300
commitc752ec9fc458f6fd1fc0ae7a156d33bf86e73eff (patch)
tree8f559ef2cd1ce8a1c4ccee72096f34b1caf3c46f /source/blender/blenkernel
parent242695011e71a358f466d4f78df3b9c66d739a64 (diff)
2.5
Merged 'backend' changes from AnimSys2. Many of these changes are necessary for the Dopesheet and other changes I'm currently still stabilising. Those will come in due course.
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_ipo.h6
-rw-r--r--source/blender/blenkernel/BKE_key.h1
-rw-r--r--source/blender/blenkernel/intern/action.c67
-rw-r--r--source/blender/blenkernel/intern/armature.c26
-rw-r--r--source/blender/blenkernel/intern/constraint.c66
-rw-r--r--source/blender/blenkernel/intern/ipo.c87
-rw-r--r--source/blender/blenkernel/intern/key.c16
7 files changed, 185 insertions, 84 deletions
diff --git a/source/blender/blenkernel/BKE_ipo.h b/source/blender/blenkernel/BKE_ipo.h
index fdd176e0e64..f0c530808a7 100644
--- a/source/blender/blenkernel/BKE_ipo.h
+++ b/source/blender/blenkernel/BKE_ipo.h
@@ -24,7 +24,7 @@
*
* The Original Code is: all of this file.
*
- * Contributor(s): none yet.
+ * Contributor(s): 2008, Joshua Leung (Animation Cleanup)
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -87,6 +87,8 @@ void testhandles_ipocurve(struct IpoCurve *icu);
void sort_time_ipocurve(struct IpoCurve *icu);
int test_time_ipocurve(struct IpoCurve *icu);
+void set_interpolation_ipocurve(struct IpoCurve *icu, short ipo);
+
/* -------- IPO-Curve (Bezier) Calculations ---------- */
void correct_bezpart(float *v1, float *v2, float *v3, float *v4);
@@ -101,6 +103,8 @@ void calc_icu(struct IpoCurve *icu, float ctime);
float calc_ipo_time(struct Ipo *ipo, float ctime);
void calc_ipo(struct Ipo *ipo, float ctime);
+void calc_ipo_range(struct Ipo *ipo, float *start, float *end);
+
/* ------------ Keyframe Column Tools -------------- */
void add_to_cfra_elem(struct ListBase *lb, struct BezTriple *bezt);
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h
index faf8692b89a..0557120a3aa 100644
--- a/source/blender/blenkernel/BKE_key.h
+++ b/source/blender/blenkernel/BKE_key.h
@@ -62,6 +62,7 @@ int do_ob_key(struct Object *ob);
struct Key *ob_get_key(struct Object *ob);
struct KeyBlock *ob_get_keyblock(struct Object *ob);
struct KeyBlock *key_get_keyblock(struct Key *key, int index);
+struct KeyBlock *key_get_named_keyblock(struct Key *key, const char name[]);
// needed for the GE
void do_rel_key(int start, int end, int tot, char *basispoin, struct Key *key, int mode);
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 1c720cea21f..98f8a83f809 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -299,7 +299,7 @@ void copy_pose(bPose **dst, bPose *src, int copycon)
outPose= MEM_callocN(sizeof(bPose), "pose");
- BLI_duplicatelist (&outPose->chanbase, &src->chanbase);
+ BLI_duplicatelist(&outPose->chanbase, &src->chanbase);
if (copycon) {
for (pchan=outPose->chanbase.first; pchan; pchan=pchan->next) {
@@ -401,7 +401,9 @@ 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;
@@ -697,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;
@@ -719,23 +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 {
- QuatMulFac(squat, srcweight);
- QuatMul(dchan->quat, dquat, squat);
+ /* 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);
}
-
- 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;
}
@@ -749,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;
}
}
@@ -867,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;
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 8bb694f45e8..0b5925a0b38 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -180,7 +180,7 @@ static void copy_bonechildren (Bone* newBone, Bone* oldBone)
Bone *curBone, *newChildBone;
/* Copy this bone's list*/
- BLI_duplicatelist (&newBone->childbase, &oldBone->childbase);
+ BLI_duplicatelist(&newBone->childbase, &oldBone->childbase);
/* For each child in the list, update it's children*/
newChildBone=newBone->childbase.first;
@@ -1216,7 +1216,10 @@ void armature_mat_pose_to_bone(bPoseChannel *pchan, float inmat[][4], float outm
if (pchan==NULL) return;
/* get the inverse matrix of the pchan's transforms */
- LocQuatSizeToMat4(pc_trans, pchan->loc, pchan->quat, pchan->size);
+ if (pchan->rotmode)
+ LocEulSizeToMat4(pc_trans, pchan->loc, pchan->eul, pchan->size);
+ else
+ LocQuatSizeToMat4(pc_trans, pchan->loc, pchan->quat, pchan->size);
Mat4Invert(inv_trans, pc_trans);
/* Remove the pchan's transforms from it's pose_mat.
@@ -1967,22 +1970,29 @@ void chan_calc_mat(bPoseChannel *chan)
float rmat[3][3];
float tmat[3][3];
+ /* get scaling matrix */
SizeToMat3(chan->size, smat);
- NormalQuat(chan->quat);
-
- QuatToMat3(chan->quat, rmat);
+ /* rotations may either be quats or eulers (no rotation modes for now...) */
+ if (chan->rotmode) {
+ /* euler rotations (will cause gimble lock... no rotation order to solve that yet) */
+ EulToMat3(chan->eul, rmat);
+ }
+ else {
+ /* quats are normalised before use to eliminate scaling issues */
+ NormalQuat(chan->quat);
+ QuatToMat3(chan->quat, rmat);
+ }
+ /* calculate matrix of bone (as 3x3 matrix, but then copy the 4x4) */
Mat3MulMat3(tmat, rmat, smat);
-
Mat4CpyMat3(chan->chan_mat, tmat);
/* prevent action channels breaking chains */
/* need to check for bone here, CONSTRAINT_TYPE_ACTION uses this call */
- if (chan->bone==NULL || !(chan->bone->flag & BONE_CONNECTED)) {
+ if ((chan->bone==NULL) || !(chan->bone->flag & BONE_CONNECTED)) {
VECCOPY(chan->chan_mat[3], chan->loc);
}
-
}
/* transform from bone(b) to bone(b+1), store in chan_mat */
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index b8bfb002075..cbdcfa56ff2 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -69,6 +69,7 @@
#include "BPY_extern.h"
#endif
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -3021,44 +3022,53 @@ static void clampto_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *ta
float len= (curveMax[clamp_axis] - curveMin[clamp_axis]);
float offset;
- /* find bounding-box range where target is located */
- if (ownLoc[clamp_axis] < curveMin[clamp_axis]) {
- /* bounding-box range is before */
- offset= curveMin[clamp_axis];
-
- while (ownLoc[clamp_axis] < offset)
- offset -= len;
-
- /* now, we calculate as per normal, except using offset instead of curveMin[clamp_axis] */
- curvetime = (ownLoc[clamp_axis] - offset) / (len);
- }
- else if (ownLoc[clamp_axis] > curveMax[clamp_axis]) {
- /* bounding-box range is after */
- offset= curveMax[clamp_axis];
-
- while (ownLoc[clamp_axis] > offset) {
- if ((offset + len) > ownLoc[clamp_axis])
- break;
- else
- offset += len;
+ /* check to make sure len is not so close to zero that it'll cause errors */
+ if (IS_EQ(len, 0) == 0) {
+ /* find bounding-box range where target is located */
+ if (ownLoc[clamp_axis] < curveMin[clamp_axis]) {
+ /* bounding-box range is before */
+ offset= curveMin[clamp_axis];
+
+ while (ownLoc[clamp_axis] < offset)
+ offset -= len;
+
+ /* now, we calculate as per normal, except using offset instead of curveMin[clamp_axis] */
+ curvetime = (ownLoc[clamp_axis] - offset) / (len);
+ }
+ else if (ownLoc[clamp_axis] > curveMax[clamp_axis]) {
+ /* bounding-box range is after */
+ offset= curveMax[clamp_axis];
+
+ while (ownLoc[clamp_axis] > offset) {
+ if ((offset + len) > ownLoc[clamp_axis])
+ break;
+ else
+ offset += len;
+ }
+
+ /* now, we calculate as per normal, except using offset instead of curveMax[clamp_axis] */
+ curvetime = (ownLoc[clamp_axis] - offset) / (len);
+ }
+ else {
+ /* as the location falls within bounds, just calculate */
+ curvetime = (ownLoc[clamp_axis] - curveMin[clamp_axis]) / (len);
}
-
- /* now, we calculate as per normal, except using offset instead of curveMax[clamp_axis] */
- curvetime = (ownLoc[clamp_axis] - offset) / (len);
}
else {
- /* as the location falls within bounds, just calculate */
- curvetime = (ownLoc[clamp_axis] - curveMin[clamp_axis]) / (len);
+ /* as length is close to zero, curvetime by default should be 0 (i.e. the start) */
+ curvetime= 0.0f;
}
}
else {
/* no cyclic, so position is clamped to within the bounding box */
if (ownLoc[clamp_axis] <= curveMin[clamp_axis])
- curvetime = 0.0;
+ curvetime = 0.0f;
else if (ownLoc[clamp_axis] >= curveMax[clamp_axis])
- curvetime = 1.0;
- else
+ curvetime = 1.0f;
+ else if ( IS_EQ((curveMax[clamp_axis] - curveMin[clamp_axis]), 0) == 0 )
curvetime = (ownLoc[clamp_axis] - curveMin[clamp_axis]) / (curveMax[clamp_axis] - curveMin[clamp_axis]);
+ else
+ curvetime = 0.0f;
}
/* 3. position on curve */
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index 8407c66d584..e5c58860cf5 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -102,8 +102,9 @@ int ob_ar[OB_TOTIPO]= {
};
int ac_ar[AC_TOTIPO]= {
- AC_LOC_X, AC_LOC_Y, AC_LOC_Z,
- AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z,
+ AC_LOC_X, AC_LOC_Y, AC_LOC_Z,
+ AC_EUL_X, AC_EUL_Y, AC_EUL_Z,
+ AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z,
AC_SIZE_X, AC_SIZE_Y, AC_SIZE_Z
};
@@ -584,6 +585,35 @@ float frame_to_float (int cfra) /* see also bsystem_time in object.c */
return ctime;
}
+/* Calculate the extents of IPO block's keyframes */
+void calc_ipo_range (Ipo *ipo, float *start, float *end)
+{
+ IpoCurve *icu;
+ float min=999999999.0f, max=-999999999.0f;
+ short foundvert=0;
+
+ if (ipo) {
+ for (icu=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]);
+ foundvert=1;
+ }
+ }
+ }
+
+ /* minimum length is 1 frame */
+ if (foundvert) {
+ if (min == max) max += 1.0f;
+ *start= min;
+ *end= max;
+ }
+ else {
+ *start= 0.0f;
+ *end= 1.0f;
+ }
+}
+
/* ***************************** IPO Curve Sanity ********************************* */
/* The functions here are used in various parts of Blender, usually after some editing
* of keyframe data has occurred. They ensure that keyframe data is properly ordered and
@@ -803,7 +833,6 @@ void correct_bezpart (float *v1, float *v2, float *v3, float *v4)
}
}
-#if 0 // TODO: enable when we have per-segment interpolation
/* This function sets the interpolation mode for an entire Ipo-Curve.
* It is primarily used for patching old files, but is also used in the interface
* to make sure that all segments of the curve use the same interpolation.
@@ -824,7 +853,6 @@ void set_interpolation_ipocurve (IpoCurve *icu, short ipo)
for (a=0, bezt=icu->bezt; a<icu->totvert; a++, bezt++)
bezt->ipo= ipo;
}
-#endif // TODO: enable when we have per-segment interpolation
/* ***************************** Curve Calculations ********************************* */
@@ -1028,7 +1056,6 @@ static float eval_driver (IpoDriver *driver, float ipotime)
else
#endif /* DISABLE_PYTHON */
{
-
Object *ob= driver->ob;
/* must have an object to evaluate */
@@ -1140,7 +1167,7 @@ static float eval_driver (IpoDriver *driver, float ipotime)
}
/* evaluate and return the value of the given IPO-curve at the specified frame ("evaltime") */
-float eval_icu(IpoCurve *icu, float evaltime)
+float eval_icu (IpoCurve *icu, float evaltime)
{
float cvalue = 0.0f;
@@ -1157,7 +1184,7 @@ float eval_icu(IpoCurve *icu, float evaltime)
/* get pointers */
BezTriple *bezt, *prevbezt, *lastbezt;
float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac;
- float cycdx, cycdy, ofs, cycyofs= 0.0;
+ float cycdx, cycdy, ofs, cycyofs= 0.0f;
int a, b;
/* get pointers */
@@ -1192,12 +1219,11 @@ float eval_icu(IpoCurve *icu, float evaltime)
}
/* evaluation time at or past endpoints? */
- // TODO: for per-bezt interpolation, replace all icu->ipo with (bezt)->ipo
if (prevbezt->vec[1][0] >= evaltime) {
/* before or on first keyframe */
- if ((icu->extrap & IPO_DIR) && (icu->ipo != IPO_CONST)) {
+ if ((icu->extrap & IPO_DIR) && (prevbezt->ipo != IPO_CONST)) {
/* linear or bezier interpolation */
- if (icu->ipo==IPO_LIN) {
+ if (prevbezt->ipo==IPO_LIN) {
/* Use the next center point instead of our own handle for
* linear interpolated extrapolate
*/
@@ -1242,9 +1268,9 @@ float eval_icu(IpoCurve *icu, float evaltime)
}
else if (lastbezt->vec[1][0] <= evaltime) {
/* after or on last keyframe */
- if( (icu->extrap & IPO_DIR) && (icu->ipo != IPO_CONST)) {
+ if( (icu->extrap & IPO_DIR) && (lastbezt->ipo != IPO_CONST)) {
/* linear or bezier interpolation */
- if (icu->ipo==IPO_LIN) {
+ if (lastbezt->ipo==IPO_LIN) {
/* Use the next center point instead of our own handle for
* linear interpolated extrapolate
*/
@@ -1289,16 +1315,15 @@ float eval_icu(IpoCurve *icu, float evaltime)
}
else {
/* evaltime occurs somewhere in the middle of the curve */
- // TODO: chould be optimised by using a binary search instead???
for (a=0; prevbezt && bezt && (a < icu->totvert-1); a++, prevbezt=bezt, bezt++) {
/* evaltime occurs within the interval defined by these two keyframes */
if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) {
/* value depends on interpolation mode */
- if (icu->ipo == IPO_CONST) {
+ if (prevbezt->ipo == IPO_CONST) {
/* constant (evaltime not relevant, so no interpolation needed) */
cvalue= prevbezt->vec[1][1];
}
- else if (icu->ipo == IPO_LIN) {
+ else if (prevbezt->ipo == IPO_LIN) {
/* linear - interpolate between values of the two keyframes */
fac= bezt->vec[1][0] - prevbezt->vec[1][0];
@@ -1339,7 +1364,7 @@ float eval_icu(IpoCurve *icu, float evaltime)
}
/* apply y-offset (for 'cyclic extrapolation') to calculated value */
- cvalue+= cycyofs;
+ cvalue += cycyofs;
}
/* clamp evaluated value to lie within allowable value range for this channel */
@@ -1467,12 +1492,17 @@ void execute_action_ipo (bActionChannel *achan, bPoseChannel *pchan)
if (achan && achan->ipo && pchan) {
IpoCurve *icu;
- /* loop over IPO-curves, getting a pointer to pchan var to write to
- * - assume for now that only 'float' channels will ever get written into
- */
+ /* loop over IPO-curves, getting a pointer to pchan var to write to */
for (icu= achan->ipo->curve.first; icu; icu= icu->next) {
void *poin= get_pchan_ipo_poin(pchan, icu->adrcode);
- if (poin) write_ipo_poin(poin, IPO_FLOAT, icu->curval);
+
+ if (poin) {
+ /* only euler-rotations are of type float-degree, all others are 'float' only */
+ if (ELEM3(icu->adrcode, AC_EUL_X, AC_EUL_Y, AC_EUL_Z))
+ write_ipo_poin(poin, IPO_FLOAT_DEGR, icu->curval);
+ else
+ write_ipo_poin(poin, IPO_FLOAT, icu->curval);
+ }
}
}
}
@@ -1791,6 +1821,7 @@ void clear_delta_obipo(Ipo *ipo)
/* --------------------- Get Pointer API ----------------------------- */
/* get pointer to pose-channel's channel, but set appropriate flags first */
+// TODO: most channels (except euler rots, which are float-degr) are floats, so do we need type arg?
void *get_pchan_ipo_poin (bPoseChannel *pchan, int adrcode)
{
void *poin= NULL;
@@ -1813,6 +1844,22 @@ void *get_pchan_ipo_poin (bPoseChannel *pchan, int adrcode)
pchan->flag |= POSE_ROT;
break;
+ case AC_EUL_X:
+ poin= &(pchan->eul[0]);
+ pchan->flag |= POSE_ROT;
+ //type= IPO_FLOAT_DEGR;
+ break;
+ case AC_EUL_Y:
+ poin= &(pchan->eul[1]);
+ pchan->flag |= POSE_ROT;
+ //type= IPO_FLOAT_DEGR;
+ break;
+ case AC_EUL_Z:
+ poin= &(pchan->eul[2]);
+ pchan->flag |= POSE_ROT;
+ //type= IPO_FLOAT_DEGR;
+ break;
+
case AC_LOC_X:
poin= &(pchan->loc[0]);
pchan->flag |= POSE_LOC;
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index a34da4377b1..5540a262a1d 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -59,6 +59,7 @@
#include "BLI_blenlib.h"
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -1404,3 +1405,18 @@ KeyBlock *key_get_keyblock(Key *key, int index)
return NULL;
}
+
+/* get the appropriate KeyBlock given a name to search for */
+KeyBlock *key_get_named_keyblock(Key *key, const char name[])
+{
+ KeyBlock *kb;
+
+ if (key && name) {
+ for (kb= key->block.first; kb; kb= kb->next) {
+ if (strcmp(name, kb->name)==0)
+ return kb;
+ }
+ }
+
+ return NULL;
+}