Thursday, July 24, 2014

SingularMatrixException: cannot invert matrix - solved

I have following problem in my Java3D application:

javax.vecmath.SingularMatrixException: cannot invert matrix
 at javax.media.j3d.Transform3D.invertGeneral(Transform3D.java:3099)
 at javax.media.j3d.Transform3D.invert(Transform3D.java:2864)

Occurring of this problem is deterministic and it happen always in same scene processing. Source code of java class Transform3D.java is here. Piece of code where is problem rooted is:

  final void invertGeneral(Transform3D t1) {
 double tmp[] = new double[16];
 int row_perm[] = new int[4];
 int i, r, c;

 // Use LU decomposition and backsubstitution code specifically
 // for floating-point 4x4 matrices.

 // Copy source matrix to tmp
 System.arraycopy(t1.mat, 0, tmp, 0, tmp.length);

 // Calculate LU decomposition: Is the matrix singular?
 if (!luDecomposition(tmp, row_perm)) {
     // Matrix has no inverse
     throw new SingularMatrixException(J3dI18N.getString("Transform3D1"));
 }

 // Perform back substitution on the identity matrix
 // luDecomposition will set rot[] & scales[] for use
 // in luBacksubstituation
 mat[0] = 1.0;  mat[1] = 0.0;  mat[2] = 0.0;  mat[3] = 0.0;
 mat[4] = 0.0;  mat[5] = 1.0;  mat[6] = 0.0;  mat[7] = 0.0;
 mat[8] = 0.0;  mat[9] = 0.0;  mat[10] = 1.0; mat[11] = 0.0;
 mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0;
 luBacksubstitution(tmp, row_perm, this.mat);

 type = 0;
 dirtyBits = ALL_DIRTY;
}

Obviously piece of resource bundle defining error messages looks like:

Transform3D1=cannot invert matrix

The root of the problem is using of class Transform3D. In my code is this class used like this:

Point3d center = new Point3d();
boundingSphere.getCenter(center);

Transform3D lookAt = new Transform3D();
lookAt.lookAt(new Point3d(0.0, 0.0, -40.0), center, new Vector3d(0.0, 1.0, 0.0));
lookAt.invert();

Than it's easy. Method lookAt have following parameters:

  • eye - the location of the eye (Point3d)
  • center - a point in the virtual world where the eye is looking (Point3d)
  • up - an up vector specifying the frustum's up direction (Vector3d)
When one of eye, scene and center vectors is linear combination of others than matrix is singular.Detect that transformation is singular matrix could be done for example by determinant. So code could be easily fixed. Nice think is that it works fine in Java3D 1.5 release by Sun. This doesn't work in Java3D 1.6 which could be found at github.com/hharrison/java3d-core/.

In my case is solution pretty simple. When center and eye are same points slightly adjust one of them like this:

Point3d center = new Point3d(0.0, 0.0, -40.0);
Point3d eye = new Point3d(0.0, 0.0, -40.0);
Vector3d up = new Vector3d(0.0, 1.0, 0.0);
Transform3D lookAt = new Transform3D();
lookAt.lookAt(eye, center, up);
if (Double.compare(Double.NaN, lookAt.determinant()) == 0) {
    eye = new Point3d(eye.x + 0.001F, eye.y + 0.001F, eye.z + 0.001F);
    lookAt.lookAt(eye, center, up);
}
lookAt.invert();

And than it works fine. One think is strange. Determinant of singular matrix should be zero not specific value "Not a Number". Probably it's because of determinant computing implementation.

No comments:

Post a Comment