[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [33523] trunk/blender/source/blender: Added an assert() check for normalized quats which exposed a number of bugs where normalized quat was incorrectly assumed .

Campbell Barton ideasman42 at gmail.com
Tue Dec 7 02:56:32 CET 2010


Revision: 33523
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=33523
Author:   campbellbarton
Date:     2010-12-07 02:56:32 +0100 (Tue, 07 Dec 2010)

Log Message:
-----------
Added an assert() check for normalized quats which exposed a number of bugs where normalized quat was incorrectly assumed.
This would have made bug #25003 very simple to find.

- Objects had their quats normalized when calculating their matrix, this is inconstant with pose bones and isn't useful for animation.
  Also it wasn't normalizing the delta rotation so these would give bad rotations.

- Converting between rotation modes BKE_rotMode_change_values() assumed normal length quat. changing quat to euler rotation for eg could change the bone.

- Clear rotation and transform were not normalizing the quat when 4d loc was disabled on quat rotation, corrected and also made it so the quat scale is restored after conversion so animations curves dont jump.

There is 1 case in mat3_to_quat_is_ok() where quat_to_mat3 on an unnormalized quat is needed, for this I had to add an ugly static function quat_to_mat3_no_assert(), but overall its worthwhile IMHO to be able to find incorrect use of rotation conversion.

Modified Paths:
--------------
    trunk/blender/source/blender/blenkernel/intern/anim.c
    trunk/blender/source/blender/blenkernel/intern/armature.c
    trunk/blender/source/blender/blenkernel/intern/object.c
    trunk/blender/source/blender/blenlib/BLI_math_rotation.h
    trunk/blender/source/blender/blenlib/intern/math_rotation.c
    trunk/blender/source/blender/editors/armature/editarmature.c
    trunk/blender/source/blender/editors/transform/transform.c

Modified: trunk/blender/source/blender/blenkernel/intern/anim.c
===================================================================
--- trunk/blender/source/blender/blenkernel/intern/anim.c	2010-12-07 01:54:25 UTC (rev 33522)
+++ trunk/blender/source/blender/blenkernel/intern/anim.c	2010-12-07 01:56:32 UTC (rev 33523)
@@ -1339,12 +1339,16 @@
 			else {
 				/* first key */
 				state.time = ctime;
-				if(psys_get_particle_state(&sim, a, &state, 0) == 0)
+				if(psys_get_particle_state(&sim, a, &state, 0) == 0) {
 					continue;
-
-				quat_to_mat4( pamat,state.rot);
-				VECCOPY(pamat[3], state.co);
-				pamat[3][3]= 1.0f;
+				}
+				else {
+					float tquat[4];
+					normalize_qt_qt(tquat, state.rot);
+					quat_to_mat4(pamat, tquat);
+					copy_v3_v3(pamat[3], state.co);
+					pamat[3][3]= 1.0f;
+				}
 			}
 
 			if(part->ren_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) {

Modified: trunk/blender/source/blender/blenkernel/intern/armature.c
===================================================================
--- trunk/blender/source/blender/blenkernel/intern/armature.c	2010-12-07 01:54:25 UTC (rev 33522)
+++ trunk/blender/source/blender/blenkernel/intern/armature.c	2010-12-07 01:56:32 UTC (rev 33523)
@@ -1248,6 +1248,7 @@
 		}
 		else if (oldMode == ROT_MODE_QUAT) {
 			/* quat to euler */
+			normalize_qt(quat);
 			quat_to_eulO( eul, newMode,quat);
 		}
 		/* else { no conversion needed } */
@@ -1270,6 +1271,7 @@
 		}
 		else if (oldMode == ROT_MODE_QUAT) {
 			/* quat to axis angle */
+			normalize_qt(quat);
 			quat_to_axis_angle( axis, angle,quat);
 		}
 		
@@ -2092,8 +2094,7 @@
 		 * but if this proves to be too problematic, switch back to the old system of operating directly on 
 		 * the stored copy
 		 */
-		QUATCOPY(quat, pchan->quat);
-		normalize_qt(quat);
+		normalize_qt_qt(quat, pchan->quat);
 		quat_to_mat3(rmat, quat);
 	}
 	

Modified: trunk/blender/source/blender/blenkernel/intern/object.c
===================================================================
--- trunk/blender/source/blender/blenkernel/intern/object.c	2010-12-07 01:54:25 UTC (rev 33522)
+++ trunk/blender/source/blender/blenkernel/intern/object.c	2010-12-07 01:56:32 UTC (rev 33523)
@@ -1668,9 +1668,13 @@
 	}
 	else {
 		/* quats are normalised before use to eliminate scaling issues */
-		normalize_qt(ob->quat);
-		quat_to_mat3( rmat,ob->quat);
-		quat_to_mat3( dmat,ob->dquat);
+		float tquat[4];
+
+		normalize_qt_qt(tquat, ob->quat);
+		quat_to_mat3(rmat, tquat);
+
+		normalize_qt_qt(tquat, ob->quat);
+		quat_to_mat3(dmat, tquat);
 	}
 	
 	/* combine these rotations */
@@ -1818,8 +1822,8 @@
 #else
 			quat_apply_track(quat, ob->trackflag, ob->upflag);
 #endif
-
-			quat_to_mat4(mat,quat);			
+			normalize_qt(quat);
+			quat_to_mat4(mat, quat);
 		}
 		
 		if(cu->flag & CU_PATH_RADIUS) {

Modified: trunk/blender/source/blender/blenlib/BLI_math_rotation.h
===================================================================
--- trunk/blender/source/blender/blenlib/BLI_math_rotation.h	2010-12-07 01:54:25 UTC (rev 33522)
+++ trunk/blender/source/blender/blenlib/BLI_math_rotation.h	2010-12-07 01:56:32 UTC (rev 33523)
@@ -51,10 +51,11 @@
 void sub_qt_qtqt(float q[4], const float a[4], const float b[4]);
 
 void invert_qt(float q[4]);
-void invert_qt_qt(float *q1, const float *q2);
+void invert_qt_qt(float q1[4], const float q2[4]);
 void conjugate_qt(float q[4]);
 float dot_qtqt(const float a[4], const float b[4]);
-void normalize_qt(float q[4]);
+float normalize_qt(float q[4]);
+float normalize_qt_qt(float q1[4], const float q2[4]);
 
 /* comparison */
 int is_zero_qt(float q[4]);

Modified: trunk/blender/source/blender/blenlib/intern/math_rotation.c
===================================================================
--- trunk/blender/source/blender/blenlib/intern/math_rotation.c	2010-12-07 01:54:25 UTC (rev 33522)
+++ trunk/blender/source/blender/blenlib/intern/math_rotation.c	2010-12-07 01:56:32 UTC (rev 33523)
@@ -31,6 +31,9 @@
 
 /******************************** Quaternions ********************************/
 
+/* used to test is a quat is not normalized */
+#define QUAT_EPSILON 0.00001
+
 void unit_qt(float *q)
 {
 	q[0]= 1.0f;
@@ -144,7 +147,8 @@
 	mul_v3_fl(q+1, si);
 }
 
-void quat_to_mat3(float m[][3], const float q[4])
+/* skip error check, currently only needed by mat3_to_quat_is_ok */
+static void quat_to_mat3_no_assert(float m[][3], const float q[4])
 {
 	double q0, q1, q2, q3, qda,qdb,qdc,qaa,qab,qac,qbb,qbc,qcc;
 
@@ -176,10 +180,23 @@
 	m[2][2]= (float)(1.0-qaa-qbb);
 }
 
+
+void quat_to_mat3(float m[][3], const float q[4])
+{
+	/* throw an error if the quat isn't normalized */
+	float f;
+	assert((f=dot_qtqt(q, q))==0.0 || (fabs(f-1.0) < QUAT_EPSILON));
+
+	quat_to_mat3_no_assert(m, q);
+}
+
 void quat_to_mat4(float m[][4], const float q[4])
 {
 	double q0, q1, q2, q3, qda,qdb,qdc,qaa,qab,qac,qbb,qbc,qcc;
 
+	/* throw an error if the quat isn't normalized */
+	assert((q0=dot_qtqt(q, q))==0.0 || (fabs(q0-1.0) < QUAT_EPSILON));
+
 	q0= M_SQRT2 * q[0];
 	q1= M_SQRT2 * q[1];
 	q2= M_SQRT2 * q[2];
@@ -300,7 +317,7 @@
 	q1[3]= -nor[2]*si;
 
 	/* rotate back x-axis from mat, using inverse q1 */
-	quat_to_mat3( matr,q1);
+	quat_to_mat3_no_assert( matr,q1);
 	invert_m3_m3(matn, matr);
 	mul_m3_v3(matn, mat[0]);
 	
@@ -318,7 +335,7 @@
 }
 
 
-void normalize_qt(float *q)
+float normalize_qt(float *q)
 {
 	float len;
 	
@@ -330,8 +347,16 @@
 		q[1]= 1.0f;
 		q[0]= q[2]= q[3]= 0.0f;			
 	}
+
+	return len;
 }
 
+float normalize_qt_qt(float r[4], const float q[4])
+{
+	copy_qt_qt(r, q);
+	return normalize_qt(r);
+}
+
 /* note: expects vectors to be normalized */
 void rotation_between_vecs_to_quat(float *q, const float v1[3], const float v2[3])
 {
@@ -619,7 +644,10 @@
 void quat_to_axis_angle(float axis[3], float *angle, const float q[4])
 {
 	float ha, si;
-	
+
+	/* throw an error if the quat isn't normalized */
+	assert((ha=dot_qtqt(q, q))==0.0 || (fabs(ha-1.0) < QUAT_EPSILON));
+
 	/* calculate angle/2, and sin(angle/2) */
 	ha= (float)acos(q[0]);
 	si= (float)sin(ha);
@@ -925,7 +953,7 @@
 void quat_to_eul(float *eul, const float quat[4])
 {
 	float mat[3][3];
-	
+
 	quat_to_mat3(mat,quat);
 	mat3_to_eul(eul,mat);
 }

Modified: trunk/blender/source/blender/editors/armature/editarmature.c
===================================================================
--- trunk/blender/source/blender/editors/armature/editarmature.c	2010-12-07 01:54:25 UTC (rev 33522)
+++ trunk/blender/source/blender/editors/armature/editarmature.c	2010-12-07 01:56:32 UTC (rev 33523)
@@ -5026,10 +5026,11 @@
 			else {
 				/* perform clamping using euler form (3-components) */
 				float eul[3], oldeul[3], quat1[4] = {0};
+				float qlen;
 				
 				if (pchan->rotmode == ROT_MODE_QUAT) {
-					copy_qt_qt(quat1, pchan->quat);
-					quat_to_eul( oldeul,pchan->quat);
+					qlen= normalize_qt_qt(quat1, pchan->quat);
+					quat_to_eul(oldeul, quat1);
 				}
 				else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
 					axis_angle_to_eulO( oldeul, EULER_ORDER_DEFAULT,pchan->rotAxis, pchan->rotAngle);
@@ -5048,7 +5049,11 @@
 					eul[2]= oldeul[2];
 				
 				if (pchan->rotmode == ROT_MODE_QUAT) {
-					eul_to_quat( pchan->quat,eul);
+					eul_to_quat(pchan->quat, eul);
+
+					/* restore original quat size */
+					mul_qt_fl(pchan->quat, qlen);
+
 					/* quaternions flip w sign to accumulate rotations correctly */
 					if ((quat1[0]<0.0f && pchan->quat[0]>0.0f) || (quat1[0]>0.0f && pchan->quat[0]<0.0f)) {
 						mul_qt_fl(pchan->quat, -1.0f);
@@ -5064,8 +5069,7 @@
 		}						// Duplicated in source/blender/editors/object/object_transform.c
 		else { 
 			if (pchan->rotmode == ROT_MODE_QUAT) {
-				pchan->quat[1]=pchan->quat[2]=pchan->quat[3]= 0.0f; 
-				pchan->quat[0]= 1.0f;
+				unit_qt(pchan->quat);
 			}
 			else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
 				/* by default, make rotation of 0 radians around y-axis (roll) */

Modified: trunk/blender/source/blender/editors/transform/transform.c
===================================================================
--- trunk/blender/source/blender/editors/transform/transform.c	2010-12-07 01:54:25 UTC (rev 33522)
+++ trunk/blender/source/blender/editors/transform/transform.c	2010-12-07 01:56:32 UTC (rev 33523)
@@ -1917,23 +1917,29 @@
 	}
 	else {
 		/* quaternions get limited with euler... (compatability mode) */
-		float eul[3], oldeul[3], quat1[4];
-		
-		QUATCOPY(quat1, quat);
-		quat_to_eul( eul,quat);
-		quat_to_eul( oldeul,oldquat);
-		
+		float eul[3], oldeul[3], nquat[4], noldquat[4];
+		float qlen;
+
+		qlen= normalize_qt_qt(nquat, quat);
+		normalize_qt_qt(noldquat, oldquat);
+
+		quat_to_eul(eul, nquat);
+		quat_to_eul(oldeul, noldquat);
+
 		if (protectflag & OB_LOCK_ROTX)
 			eul[0]= oldeul[0];
 		if (protectflag & OB_LOCK_ROTY)
 			eul[1]= oldeul[1];
 		if (protectflag & OB_LOCK_ROTZ)
 			eul[2]= oldeul[2];
-		
+
 		eul_to_quat( quat,eul);
+
+		/* restore original quat size */
+		mul_qt_fl(quat, qlen);
 		
 		/* quaternions flip w sign to accumulate rotations correctly */
-		if ( (quat1[0]<0.0f && quat[0]>0.0f) || (quat1[0]>0.0f && quat[0]<0.0f) ) {
+		if ( (nquat[0]<0.0f && quat[0]>0.0f) || (nquat[0]>0.0f && quat[0]<0.0f) ) {
 			mul_qt_fl(quat, -1.0f);
 		}
 	}
@@ -2013,8 +2019,7 @@
 			   we don't necessarily end up with a rotation matrix, and
 			   then conversion back to quat gives a different result */
 			float quat[4];
-			copy_qt_qt(quat, td->ext->quat);
-			normalize_qt(quat);
+			normalize_qt_qt(quat, td->ext->quat);
 			quat_to_mat4(cob->matrix, quat);
 		}
 		else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {





More information about the Bf-blender-cvs mailing list