Quaternion Deform Interpolation

===============================

This is a new armature deform interpolation method using Dual Quaternions,
which reduces the artifacts of linear blend skinning:

http://www.blender.org/development/current-projects/changes-since-244/skinning/

Based on the paper and provided code:

Skinning with Dual Quaternions
Ladislav Kavan, Steven Collins, Jiri Zara, Carol O'Sullivan.
Symposium on Interactive 3D Graphics and Games, 2007.
This commit is contained in:
Brecht Van Lommel
2007-07-31 19:28:52 +00:00
parent 876cfc837e
commit c0e0f4698f
6 changed files with 371 additions and 70 deletions

View File

@@ -499,11 +499,12 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan)
/* ************ Armature Deform ******************* */
static void pchan_b_bone_defmats(bPoseChannel *pchan)
static void pchan_b_bone_defmats(bPoseChannel *pchan, int use_quaternion)
{
Bone *bone= pchan->bone;
Mat4 *b_bone= b_bone_spline_setup(pchan);
Mat4 *b_bone_mats;
DualQuat *b_bone_dual_quats= NULL;
float tmat[4][4];
int a;
@@ -511,6 +512,11 @@ static void pchan_b_bone_defmats(bPoseChannel *pchan)
b_bone_mats= MEM_mallocN((1+bone->segments)*sizeof(Mat4), "BBone defmats");
pchan->b_bone_mats= b_bone_mats;
if(use_quaternion) {
b_bone_dual_quats= MEM_mallocN((bone->segments)*sizeof(DualQuat), "BBone dqs");
pchan->b_bone_dual_quats= b_bone_dual_quats;
}
/* first matrix is the inverse arm_mat, to bring points in local bone space
for finding out which segment it belongs to */
Mat4Invert(b_bone_mats[0].mat, bone->arm_mat);
@@ -527,10 +533,13 @@ static void pchan_b_bone_defmats(bPoseChannel *pchan)
Mat4MulSerie(b_bone_mats[a+1].mat, pchan->chan_mat, bone->arm_mat,
b_bone[a].mat, tmat, b_bone_mats[0].mat, NULL, NULL, NULL);
if(use_quaternion)
Mat4ToDQuat(bone->arm_mat, b_bone_mats[a+1].mat, &b_bone_dual_quats[a]);
}
}
static void b_bone_deform(bPoseChannel *pchan, Bone *bone, float *co, float defmat[][3])
static void b_bone_deform(bPoseChannel *pchan, Bone *bone, float *co, DualQuat *dq, float defmat[][3])
{
Mat4 *b_bone= pchan->b_bone_mats;
float (*mat)[4]= b_bone[0].mat;
@@ -548,10 +557,15 @@ static void b_bone_deform(bPoseChannel *pchan, Bone *bone, float *co, float defm
straight joints in restpos. */
CLAMP(a, 0, bone->segments-1);
Mat4MulVecfl(b_bone[a+1].mat, co);
if(dq) {
DQuatCpyDQuat(dq, &((DualQuat*)pchan->b_bone_dual_quats)[a]);
}
else {
Mat4MulVecfl(b_bone[a+1].mat, co);
if(defmat)
Mat3CpyMat4(defmat, b_bone[a+1].mat);
if(defmat)
Mat3CpyMat4(defmat, b_bone[a+1].mat);
}
}
/* using vec with dist to bone b1 - b2 */
@@ -618,11 +632,12 @@ static void pchan_deform_mat_add(bPoseChannel *pchan, float weight, float bbonem
Mat3AddMat3(mat, mat, wmat);
}
static float dist_bone_deform(bPoseChannel *pchan, float *vec, float mat[][3], float *co)
static float dist_bone_deform(bPoseChannel *pchan, float *vec, DualQuat *dq, float mat[][3], float *co)
{
Bone *bone= pchan->bone;
float fac, contrib=0.0;
float cop[3], bbonemat[3][3];
DualQuat bbonedq;
if(bone==NULL) return 0.0f;
@@ -635,46 +650,67 @@ static float dist_bone_deform(bPoseChannel *pchan, float *vec, float mat[][3], f
fac*=bone->weight;
contrib= fac;
if(contrib>0.0) {
if(bone->segments>1)
// applies on cop and bbonemat
b_bone_deform(pchan, bone, cop, (mat)?bbonemat:NULL);
else
Mat4MulVecfl(pchan->chan_mat, cop);
if(vec) {
if(bone->segments>1)
// applies on cop and bbonemat
b_bone_deform(pchan, bone, cop, NULL, (mat)?bbonemat:NULL);
else
Mat4MulVecfl(pchan->chan_mat, cop);
// Make this a delta from the base position
VecSubf (cop, cop, co);
cop[0]*=fac; cop[1]*=fac; cop[2]*=fac;
VecAddf (vec, vec, cop);
// Make this a delta from the base position
VecSubf (cop, cop, co);
cop[0]*=fac; cop[1]*=fac; cop[2]*=fac;
VecAddf (vec, vec, cop);
if(mat)
pchan_deform_mat_add(pchan, fac, bbonemat, mat);
if(mat)
pchan_deform_mat_add(pchan, fac, bbonemat, mat);
}
else {
if(bone->segments>1) {
b_bone_deform(pchan, bone, cop, &bbonedq, NULL);
DQuatAddWeighted(dq, &bbonedq, fac);
}
else
DQuatAddWeighted(dq, pchan->dual_quat, fac);
}
}
}
return contrib;
}
static void pchan_bone_deform(bPoseChannel *pchan, float weight, float *vec, float mat[][3], float *co, float *contrib)
static void pchan_bone_deform(bPoseChannel *pchan, float weight, float *vec, DualQuat *dq, float mat[][3], float *co, float *contrib)
{
float cop[3], bbonemat[3][3];
DualQuat bbonedq;
if (!weight)
return;
VECCOPY(cop, co);
if(pchan->bone->segments>1)
// applies on cop and bbonemat
b_bone_deform(pchan, pchan->bone, cop, (mat)?bbonemat:NULL);
else
Mat4MulVecfl(pchan->chan_mat, cop);
vec[0]+=(cop[0]-co[0])*weight;
vec[1]+=(cop[1]-co[1])*weight;
vec[2]+=(cop[2]-co[2])*weight;
if(vec) {
if(pchan->bone->segments>1)
// applies on cop and bbonemat
b_bone_deform(pchan, pchan->bone, cop, NULL, (mat)?bbonemat:NULL);
else
Mat4MulVecfl(pchan->chan_mat, cop);
vec[0]+=(cop[0]-co[0])*weight;
vec[1]+=(cop[1]-co[1])*weight;
vec[2]+=(cop[2]-co[2])*weight;
if(mat)
pchan_deform_mat_add(pchan, weight, bbonemat, mat);
if(mat)
pchan_deform_mat_add(pchan, weight, bbonemat, mat);
}
else {
if(pchan->bone->segments>1) {
b_bone_deform(pchan, pchan->bone, cop, &bbonedq, NULL);
DQuatAddWeighted(dq, &bbonedq, weight);
}
else
DQuatAddWeighted(dq, pchan->dual_quat, weight);
}
(*contrib)+=weight;
}
@@ -686,12 +722,15 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
bPoseChannel *pchan, **defnrToPC = NULL;
MDeformVert *dverts = NULL;
bDeformGroup *dg;
DualQuat *dualquats= NULL;
float obinv[4][4], premat[4][4], postmat[4][4];
int use_envelope = deformflag & ARM_DEF_ENVELOPE;
int use_quaternion = deformflag & ARM_DEF_QUATERNION;
int numGroups = 0; /* safety for vertexgroup index overflow */
int i, target_totvert = 0; /* safety for vertexgroup overflow */
int use_dverts = 0;
int armature_def_nr = -1;
int totchan;
if(armOb == G.obedit) return;
@@ -703,10 +742,23 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
/* bone defmats are already in the channels, chan_mat */
/* initialize B_bone matrices and dual quaternions */
for(pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next)
if(!(pchan->bone->flag & BONE_NO_DEFORM))
if(use_quaternion) {
totchan= BLI_countlist(&armOb->pose->chanbase);
dualquats= MEM_callocN(sizeof(DualQuat)*totchan, "dualquats");
}
totchan= 0;
for(pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next) {
if(!(pchan->bone->flag & BONE_NO_DEFORM)) {
if(pchan->bone->segments > 1)
pchan_b_bone_defmats(pchan);
pchan_b_bone_defmats(pchan, use_quaternion);
if(use_quaternion) {
pchan->dual_quat= &dualquats[totchan++];
Mat4ToDQuat(pchan->bone->arm_mat, pchan->chan_mat, pchan->dual_quat);
}
}
}
/* get the def_nr for the overall armature vertex group if present */
for(i = 0, dg = target->defbase.first; dg; i++, dg = dg->next)
@@ -754,18 +806,26 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
for(i = 0; i < numVerts; i++) {
MDeformVert *dvert;
DualQuat sumdq, *dq = NULL;
float *co = vertexCos[i];
float summat[3][3], (*smat)[3] = NULL;
float vec[3];
float sumvec[3], summat[3][3];
float *vec = NULL, (*smat)[3] = NULL;
float contrib = 0.0f;
float armature_weight = 1.0f; /* default to 1 if no overall def group */
int j;
vec[0] = vec[1] = vec[2] = 0.0f;
if(use_quaternion) {
memset(&sumdq, 0, sizeof(DualQuat));
dq= &sumdq;
}
else {
sumvec[0] = sumvec[1] = sumvec[2] = 0.0f;
vec= sumvec;
if(defMats) {
Mat3Clr((float*)summat);
smat = summat;
if(defMats) {
Mat3Clr((float*)summat);
smat = summat;
}
}
if(use_dverts || armature_def_nr >= 0) {
@@ -810,7 +870,7 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
bone->rad_tail,
bone->dist);
}
pchan_bone_deform(pchan, weight, vec, smat, co, &contrib);
pchan_bone_deform(pchan, weight, vec, dq, smat, co, &contrib);
}
}
/* if there are vertexgroups but not groups with bones
@@ -820,7 +880,7 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
for(pchan = armOb->pose->chanbase.first; pchan;
pchan = pchan->next) {
if(!(pchan->bone->flag & BONE_NO_DEFORM))
contrib += dist_bone_deform(pchan, vec, smat, co);
contrib += dist_bone_deform(pchan, vec, dq, smat, co);
}
}
}
@@ -828,16 +888,21 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
for(pchan = armOb->pose->chanbase.first; pchan;
pchan = pchan->next) {
if(!(pchan->bone->flag & BONE_NO_DEFORM))
contrib += dist_bone_deform(pchan, vec, smat, co);
contrib += dist_bone_deform(pchan, vec, dq, smat, co);
}
}
/* actually should be EPSILON? weight values and contrib can be like 10e-39 small */
if(contrib > 0.0001f) {
float scale = armature_weight/contrib;
VecMulf(vec, scale);
VecAddf(co, vec, co);
if(use_quaternion) {
DQuatNormalize(dq, contrib, armature_weight);
DQuatMulVecfl(dq, co, (defMats)? summat: NULL);
smat = summat;
}
else {
VecMulf(vec, armature_weight/contrib);
VecAddf(co, vec, co);
}
if(defMats) {
float pre[3][3], post[3][3], tmpmat[3][3];
@@ -846,17 +911,19 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
Mat3CpyMat4(post, postmat);
Mat3CpyMat3(tmpmat, defMats[i]);
Mat3MulFloat((float*)smat, scale);
if(!use_quaternion) /* quaternion already is scale corrected */
Mat3MulFloat((float*)smat, armature_weight/contrib);
Mat3MulSerie(defMats[i], tmpmat, pre, smat, post,
NULL, NULL, NULL, NULL);
}
}
/* always, check above code */
Mat4MulVecfl(postmat, co);
}
if(dualquats) MEM_freeN(dualquats);
if(defnrToPC) MEM_freeN(defnrToPC);
/* free B_bone matrices */
@@ -865,6 +932,12 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
MEM_freeN(pchan->b_bone_mats);
pchan->b_bone_mats = NULL;
}
if(pchan->b_bone_dual_quats) {
MEM_freeN(pchan->b_bone_dual_quats);
pchan->b_bone_dual_quats = NULL;
}
pchan->dual_quat = NULL;
}
}

View File

@@ -128,6 +128,7 @@ void QuatConj(float *q);
void QuatInv(float *q);
void QuatMulf(float *q, float f);
float QuatDot(float *q1, float *q2);
void QuatCopy(float *q1, float *q2);
void printquat(char *str, float q[4]);
@@ -353,6 +354,21 @@ void spheremap(float x, float y, float z, float *u, float *v);
int LineIntersectsTriangle(float p1[3], float p2[3], float v0[3], float v1[3], float v2[3], float *lambda);
int point_in_tri_prism(float p[3], float v1[3], float v2[3], float v3[3]);
typedef struct DualQuat {
float quat[4];
float trans[4];
float scale[4][4];
float scale_weight;
} DualQuat;
void Mat4ToDQuat(float basemat[][4], float mat[][4], DualQuat *dq);
void DQuatToMat4(DualQuat *dq, float mat[][4]);
void DQuatAddWeighted(DualQuat *dqsum, DualQuat *dq, float weight);
void DQuatNormalize(DualQuat *dq, float totweight, float factor);
void DQuatMulVecfl(DualQuat *dq, float *co, float mat[][3]);
void DQuatCpyDQuat(DualQuat *dq1, DualQuat *dq2);
#ifdef __cplusplus
}
#endif

View File

@@ -936,7 +936,7 @@ void Mat4MulFloat(float *m, float f)
{
int i;
for(i=0;i<12;i++) m[i]*=f; /* count to 12: without vector component */
for(i=0;i<16;i++) m[i]*=f; /* count to 12: without vector component */
}
@@ -1597,6 +1597,221 @@ void QuatAdd(float *result, float *quat1, float *quat2, float t)
result[3]= quat1[3] + t*quat2[3];
}
void QuatCopy(float *q1, float *q2)
{
q1[0]= q2[0];
q1[1]= q2[1];
q1[2]= q2[2];
q1[3]= q2[3];
}
/* **************** DUAL QUATERNIONS ************** */
/*
Conversion routines between (regular quaternion, translation) and
dual quaternion.
Version 1.0.0, February 7th, 2007
Copyright (C) 2006-2007 University of Dublin, Trinity College, All Rights
Reserved
This software is provided 'as-is', without any express or implied
warranty. In no event will the author(s) be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Author: Ladislav Kavan, kavanl@cs.tcd.ie
Changes for Blender:
- renaming, style changes and optimizations
- added support for scaling
*/
void Mat4ToDQuat(float basemat[][4], float mat[][4], DualQuat *dq)
{
float *t, *q, dscale[3], scale[3], basequat[4];
float baseRS[4][4], baseinv[4][4], baseR[4][4], baseRinv[4][4];
float R[4][4], S[4][4];
/* split scaling and rotation, there is probably a faster way to do
this, it's done like this now to correctly get negative scaling */
Mat4MulMat4(baseRS, basemat, mat);
Mat4ToSize(baseRS, scale);
VecCopyf(dscale, scale);
dscale[0] -= 1.0f; dscale[1] -= 1.0f; dscale[2] -= 1.0f;
if((Det4x4(mat) < 0.0f) || VecLength(dscale) > 1e-4) {
/* extract R and S */
Mat4ToQuat(baseRS, basequat);
QuatToMat4(basequat, baseR);
VecCopyf(baseR[3], baseRS[3]);
Mat4Invert(baseinv, basemat);
Mat4MulMat4(R, baseinv, baseR);
Mat4Invert(baseRinv, baseR);
Mat4MulMat4(S, baseRS, baseRinv);
/* set scaling part */
Mat4MulSerie(dq->scale, basemat, S, baseinv, 0, 0, 0, 0, 0);
dq->scale_weight= 1.0f;
}
else {
/* matrix does not contain scaling */
Mat4CpyMat4(R, mat);
dq->scale_weight= 0.0f;
}
/* non-dual part */
Mat4ToQuat(R, dq->quat);
/* dual part */
t= R[3];
q= dq->quat;
dq->trans[0]= -0.5f*( t[0]*q[1] + t[1]*q[2] + t[2]*q[3]);
dq->trans[1]= 0.5f*( t[0]*q[0] + t[1]*q[3] - t[2]*q[2]);
dq->trans[2]= 0.5f*(-t[0]*q[3] + t[1]*q[0] + t[2]*q[1]);
dq->trans[3]= 0.5f*( t[0]*q[2] - t[1]*q[1] + t[2]*q[0]);
}
void DQuatToMat4(DualQuat *dq, float mat[][4])
{
float len, *t, q0[4];
/* regular quaternion */
QuatCopy(q0, dq->quat);
/* normalize */
len= sqrt(QuatDot(q0, q0));
if(len != 0.0f)
QuatMulf(q0, 1.0f/len);
/* rotation */
QuatToMat4(q0, mat);
/* translation */
t= dq->trans;
mat[3][0]= 2.0*(-t[0]*q0[1] + t[1]*q0[0] - t[2]*q0[3] + t[3]*q0[2]);
mat[3][1]= 2.0*(-t[0]*q0[2] + t[1]*q0[3] + t[2]*q0[0] - t[3]*q0[1]);
mat[3][2]= 2.0*(-t[0]*q0[3] - t[1]*q0[2] + t[2]*q0[1] + t[3]*q0[0]);
/* note: this does not handle scaling */
}
void DQuatAddWeighted(DualQuat *dqsum, DualQuat *dq, float weight)
{
/* make sure we interpolate quats in the right direction */
if (QuatDot(dq->quat, dqsum->quat) < 0)
weight = -weight;
/* interpolate rotation and translation */
dqsum->quat[0] += weight*dq->quat[0];
dqsum->quat[1] += weight*dq->quat[1];
dqsum->quat[2] += weight*dq->quat[2];
dqsum->quat[3] += weight*dq->quat[3];
dqsum->trans[0] += weight*dq->trans[0];
dqsum->trans[1] += weight*dq->trans[1];
dqsum->trans[2] += weight*dq->trans[2];
dqsum->trans[3] += weight*dq->trans[3];
/* interpolate scale - but only if needed */
if (dq->scale_weight) {
float wmat[4][4];
Mat4CpyMat4(wmat, dq->scale);
Mat4MulFloat((float*)wmat, weight);
Mat4AddMat4(dqsum->scale, dqsum->scale, wmat);
dqsum->scale_weight += weight;
}
}
void DQuatNormalize(DualQuat *dq, float totweight, float factor)
{
float scale= factor/totweight;
QuatMulf(dq->quat, scale);
QuatMulf(dq->trans, scale);
if(dq->scale_weight) {
float addweight= totweight - dq->scale_weight;
if(addweight) {
dq->scale[0][0] += addweight;
dq->scale[1][1] += addweight;
dq->scale[2][2] += addweight;
dq->scale[3][3] += addweight;
}
Mat4MulFloat((float*)dq->scale, scale);
dq->scale_weight= 1.0f;
}
}
void DQuatMulVecfl(DualQuat *dq, float *co, float mat[][3])
{
float M[3][3], t[3], scalemat[3][3], len2;
float w= dq->quat[0], x= dq->quat[1], y= dq->quat[2], z= dq->quat[3];
float t0= dq->trans[0], t1= dq->trans[1], t2= dq->trans[2], t3= dq->trans[3];
/* rotation matrix */
M[0][0]= w*w + x*x - y*y - z*z;
M[1][0]= 2*(x*y - w*z);
M[2][0]= 2*(x*z + w*y);
M[0][1]= 2*(x*y + w*z);
M[1][1]= w*w + y*y - x*x - z*z;
M[2][1]= 2*(y*z - w*x);
M[0][2]= 2*(x*z - w*y);
M[1][2]= 2*(y*z + w*x);
M[2][2]= w*w + z*z - x*x - y*y;
len2= QuatDot(dq->quat, dq->quat);
if(len2 > 0.0f)
len2= 1.0f/len2;
/* translation */
t[0]= 2*(-t0*x + w*t1 - t2*z + y*t3);
t[1]= 2*(-t0*y + t1*z - x*t3 + w*t2);
t[2]= 2*(-t0*z + x*t2 + w*t3 - t1*y);
/* apply scaling */
if(dq->scale_weight)
Mat4MulVecfl(dq->scale, co);
/* apply rotation and translation */
Mat3MulVecfl(M, co);
co[0]= (co[0] + t[0])*len2;
co[1]= (co[1] + t[1])*len2;
co[2]= (co[2] + t[2])*len2;
/* compute crazyspace correction mat */
if(mat) {
Mat3CpyMat4(scalemat, dq->scale);
Mat3MulMat3(mat, M, scalemat);
Mat3MulFloat((float*)mat, len2);
}
}
void DQuatCpyDQuat(DualQuat *dq1, DualQuat *dq2)
{
memcpy(dq1, dq2, sizeof(DualQuat));
}
/* **************** VIEW / PROJECTION ******************************** */
@@ -2635,28 +2850,16 @@ void SizeToMat4( float *size, float mat[][4])
void Mat3ToSize( float mat[][3], float *size)
{
float vec[3];
VecCopyf(vec, mat[0]);
size[0]= Normalize(vec);
VecCopyf(vec, mat[1]);
size[1]= Normalize(vec);
VecCopyf(vec, mat[2]);
size[2]= Normalize(vec);
size[0]= VecLength(mat[0]);
size[1]= VecLength(mat[1]);
size[2]= VecLength(mat[2]);
}
void Mat4ToSize( float mat[][4], float *size)
{
float vec[3];
VecCopyf(vec, mat[0]);
size[0]= Normalize(vec);
VecCopyf(vec, mat[1]);
size[1]= Normalize(vec);
VecCopyf(vec, mat[2]);
size[2]= Normalize(vec);
size[0]= VecLength(mat[0]);
size[1]= VecLength(mat[1]);
size[2]= VecLength(mat[2]);
}
/* ************* SPECIALS ******************* */

View File

@@ -61,7 +61,12 @@ typedef struct bPoseChannel {
struct bPoseChannel *parent; /* set on read file or rebuild pose */
struct bPoseChannel *child; /* set on read file or rebuild pose, the 'ik' child, for b-bones */
struct ListBase iktree; /* only while evaluating pose */
void *b_bone_mats; /* only while deform, stores precalculated b_bone deform mats */
/* only while deform, stores precalculated b_bone deform mats,
dual quaternions */
void *b_bone_mats;
void *dual_quat;
void *b_bone_dual_quats;
float loc[3]; /* written in by actions or transform */
float size[3];

View File

@@ -113,6 +113,7 @@ typedef struct bArmature {
/* armature->deformflag */
#define ARM_DEF_VGROUP 1
#define ARM_DEF_ENVELOPE 2
#define ARM_DEF_QUATERNION 4
/* armature->pathflag */
#define ARM_PATH_FNUMS 0x001

View File

@@ -1592,7 +1592,7 @@ static void draw_modifier(uiBlock *block, Object *ob, ModifierData *md, int *xco
if(wmd->flag & MOD_WAVE_NORM)
height += 19;
} else if (md->type==eModifierType_Armature) {
height = 67;
height = 86;
} else if (md->type==eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData*) md;
height = 86;
@@ -1913,6 +1913,8 @@ static void draw_modifier(uiBlock *block, Object *ob, ModifierData *md, int *xco
uiButSetCompleteFunc(but, autocomplete_vgroup, (void *)ob);
uiDefButBitS(block, TOG, ARM_DEF_VGROUP, B_ARM_RECALCDATA, "Vert.Groups", lx,cy-=19,buttonWidth/2,20, &amd->deformflag, 0, 0, 0, 0, "Enable VertexGroups defining deform");
uiDefButBitS(block, TOG, ARM_DEF_ENVELOPE, B_ARM_RECALCDATA, "Envelopes", lx+buttonWidth/2,cy,(buttonWidth + 1)/2,20, &amd->deformflag, 0, 0, 0, 0, "Enable Bone Envelopes defining deform");
uiDefButBitS(block, TOG, ARM_DEF_QUATERNION, B_ARM_RECALCDATA, "Quaternion", lx,(cy-=19),buttonWidth + 1,20, &amd->deformflag, 0, 0, 0, 0, "Enable deform rotation interpolation with Quaternions");
} else if (md->type==eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData*) md;
@@ -3624,8 +3626,9 @@ static void editing_panel_armature_type(Object *ob, bArmature *arm)
uiDefBut(block, LABEL, 0, "Deform Options", 10,40,150,20, 0, 0, 0, 0, 0, "");
uiBlockBeginAlign(block);
uiDefButBitS(block, TOG, ARM_DEF_VGROUP, B_ARM_RECALCDATA, "Vertex Groups", 10, 20,150,20, &arm->deformflag, 0, 0, 0, 0, "Enable VertexGroups defining deform (not for Modifiers)");
uiDefButBitS(block, TOG, ARM_DEF_ENVELOPE, B_ARM_RECALCDATA, "Envelopes", 160,20,150,20, &arm->deformflag, 0, 0, 0, 0, "Enable Bone Envelopes defining deform (not for Modifiers)");
uiDefButBitS(block, TOG, ARM_DEF_VGROUP, B_ARM_RECALCDATA, "Vertex Groups", 10, 20,100,20, &arm->deformflag, 0, 0, 0, 0, "Enable VertexGroups defining deform (not for Modifiers)");
uiDefButBitS(block, TOG, ARM_DEF_ENVELOPE, B_ARM_RECALCDATA, "Envelopes", 110,20,100,20, &arm->deformflag, 0, 0, 0, 0, "Enable Bone Envelopes defining deform (not for Modifiers)");
uiDefButBitS(block, TOG, ARM_DEF_QUATERNION, B_ARM_RECALCDATA, "Quaternion", 210,20,100,20, &arm->deformflag, 0, 0, 0, 0, "Enable deform rotation interpolation with Quaternions (not for Modifiers)");
uiDefButBitI(block, TOG, ARM_RESTPOS, B_ARM_RECALCDATA,"Rest Position", 10,0,150,20, &arm->flag, 0, 0, 0, 0, "Show armature rest position, no posing possible");
uiDefButBitI(block, TOG, ARM_DELAYDEFORM, REDRAWVIEW3D, "Delay Deform", 160,0,150,20, &arm->flag, 0, 0, 0, 0, "Don't deform children when manipulating bones in pose mode");
uiBlockEndAlign(block);