Providing a bug fix?
Hello,
In using the Axiom math library I have found a bug (or, more accurately, a missing 'feature' for an edge case). If the transform includes negative scaling factors the current Decompose() method does not handle this correctly and will cause the resultant rotation and scale output to be incorrect. The fix is to check the determinant of the rotation matrix; if it is < 0, it means one of the axis has a negative scaling factor - in this case an axis is manually inverted, along with the rotation component derived from it.
It is not possible to determine which axis have negative scaling factors, but this is not a problem as two negative factors are equivalent to a 180 degree rotation. Inverting one scale axis and rotation matrix column will ensure that the quaternion extracted from the matrix is 'correct' and when recombined with the scale and translation will have a transform that is equivalent, if not identical, to that described by the original matrix. (This is explained in the post linked in the comment)
It is a very quick fix but it took me many hours to find it was needed; there is no equivalent in the trunk on SourceForge so I thought I would upload it to save anyone else the trouble! I am not sure how I would do that however, so perhaps I can leave it here and someone who knows the testing/check in processes etc can add it if they think it appropriate?
In using the Axiom math library I have found a bug (or, more accurately, a missing 'feature' for an edge case). If the transform includes negative scaling factors the current Decompose() method does not handle this correctly and will cause the resultant rotation and scale output to be incorrect. The fix is to check the determinant of the rotation matrix; if it is < 0, it means one of the axis has a negative scaling factor - in this case an axis is manually inverted, along with the rotation component derived from it.
It is not possible to determine which axis have negative scaling factors, but this is not a problem as two negative factors are equivalent to a 180 degree rotation. Inverting one scale axis and rotation matrix column will ensure that the quaternion extracted from the matrix is 'correct' and when recombined with the scale and translation will have a transform that is equivalent, if not identical, to that described by the original matrix. (This is explained in the post linked in the comment)
It is a very quick fix but it took me many hours to find it was needed; there is no equivalent in the trunk on SourceForge so I thought I would upload it to save anyone else the trouble! I am not sure how I would do that however, so perhaps I can leave it here and someone who knows the testing/check in processes etc can add it if they think it appropriate?
- Code:
public void Decompose( out Vector3 translation, out Vector3 scale, out Quaternion orientation )
{
scale = Vector3.UnitScale;
var rotation = Matrix3.Identity;
var axis = Vector3.Zero;
axis.x = this.m00;
axis.y = this.m10;
axis.z = this.m20;
scale.x = axis.Normalize(); // Normalize() returns the vector's length before it was normalized
rotation.m00 = axis.x;
rotation.m10 = axis.y;
rotation.m20 = axis.z;
axis.x = this.m01;
axis.y = this.m11;
axis.z = this.m21;
scale.y = axis.Normalize();
rotation.m01 = axis.x;
rotation.m11 = axis.y;
rotation.m21 = axis.z;
axis.x = this.m02;
axis.y = this.m12;
axis.z = this.m22;
scale.z = axis.Normalize();
rotation.m02 = axis.x;
rotation.m12 = axis.y;
rotation.m22 = axis.z;
/* http://www.robertblum.com/articles/2005/02/14/decomposing-matrices check to support transforms with negative scaling */
if (rotation.Determinant < 0)
{
rotation.m00 = -rotation.m00;
rotation.m10 = -rotation.m10;
rotation.m20 = -rotation.m20;
scale.x = -scale.x;
}
orientation = Quaternion.FromRotationMatrix( rotation );
translation = this.Translation;
}
