[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [11450] trunk/blender/source/blender:

Brecht Van Lommel brechtvanlommel at pandora.be
Tue Jul 31 21:28:53 CEST 2007


Revision: 11450
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=11450
Author:   blendix
Date:     2007-07-31 21:28:52 +0200 (Tue, 31 Jul 2007)

Log Message:
-----------

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.

Modified Paths:
--------------
    trunk/blender/source/blender/blenkernel/intern/armature.c
    trunk/blender/source/blender/blenlib/BLI_arithb.h
    trunk/blender/source/blender/blenlib/intern/arithb.c
    trunk/blender/source/blender/makesdna/DNA_action_types.h
    trunk/blender/source/blender/makesdna/DNA_armature_types.h
    trunk/blender/source/blender/src/buttons_editing.c

Modified: trunk/blender/source/blender/blenkernel/intern/armature.c
===================================================================
--- trunk/blender/source/blender/blenkernel/intern/armature.c	2007-07-31 17:45:26 UTC (rev 11449)
+++ trunk/blender/source/blender/blenkernel/intern/armature.c	2007-07-31 19:28:52 UTC (rev 11450)
@@ -499,11 +499,12 @@
 
 /* ************ 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 @@
 	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 @@
 
 		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 @@
 	   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 @@
 	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 @@
 		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 @@
 	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,11 +742,24 @@
 	/* 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)
 		if(defgrp_name && strcmp(defgrp_name, dg->name) == 0)
@@ -754,18 +806,26 @@
 
 	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 @@
 						                             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 @@
 				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,17 +888,22 @@
 			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;
+			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);
+			}
 
-			VecMulf(vec, scale);
-			VecAddf(co, vec, co);
-
 			if(defMats) {
 				float pre[3][3], post[3][3], tmpmat[3][3];
 
@@ -846,17 +911,19 @@
 				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 @@
 			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;
 	}
 }
 

Modified: trunk/blender/source/blender/blenlib/BLI_arithb.h
===================================================================
--- trunk/blender/source/blender/blenlib/BLI_arithb.h	2007-07-31 17:45:26 UTC (rev 11449)
+++ trunk/blender/source/blender/blenlib/BLI_arithb.h	2007-07-31 19:28:52 UTC (rev 11450)
@@ -128,6 +128,7 @@
 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 @@
 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);

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list