datenwolf / linmath.h

a lean linear math library, aimed at graphics programming. Supports vec3, vec4, mat4x4 and quaternions
Do What The F*ck You Want To Public License
922 stars 134 forks source link

quat_from_mat4 not working #37

Open Mikepicker opened 4 years ago

Mikepicker commented 4 years ago

Unfortunately, the function doesn't give correct results.

Here's a proposal to change it (taken from https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm):

float tr = M[0][0] + M[1][1] + M[2][2];

  if (tr > 0) { 
    float S = sqrt(tr+1.0) * 2; // S=4*qw 
    q[3] = 0.25 * S;
    q[0] = (M[1][2] - M[2][1]) / S;
    q[1] = (M[2][0] - M[0][2]) / S; 
    q[2] = (M[0][1] - M[1][0]) / S; 
  } else if ((M[0][0] > M[1][1])&(M[0][0] > M[2][2])) { 
    float S = sqrt(1.0 + M[0][0] - M[1][1] - M[2][2]) * 2; // S=4*qx 
    q[3] = (M[1][2] - M[2][1]) / S;
    q[0] = 0.25 * S;
    q[1] = (M[1][0] + M[0][1]) / S; 
    q[2] = (M[2][0] + M[0][2]) / S; 
  } else if (M[1][1] > M[2][2]) { 
    float S = sqrt(1.0 + M[1][1] - M[0][0] - M[2][2]) * 2; // S=4*qy
    q[3] = (M[2][0] - M[0][2]) / S;
    q[0] = (M[1][0] + M[0][1]) / S; 
    q[1] = 0.25 * S;
    q[2] = (M[2][1] + M[1][2]) / S; 
  } else { 
    float S = sqrt(1.0 + M[2][2] - M[0][0] - M[1][1]) * 2; // S=4*qz
    q[3] = (M[0][1] - M[1][0]) / S;
    q[0] = (M[2][0] + M[0][2]) / S;
    q[1] = (M[2][1] + M[1][2]) / S;
    q[2] = 0.25 * S;
  }
datenwolf commented 4 years ago

Did you check that the order of the quaternion elements is correct? linmath.h uses a slightly different convention and uses the last element for the real part, i.e. the ordering of the quaternions is i,j,k,r.

Mikepicker commented 4 years ago

I've also tried shifting the elements, but it didn't work either. Does it work fine in your case?

I did a simple test:

  1. Initialize an identity matrix
  2. Extract quaternion with quat_from_mat
  3. Create a new matrix with mat_from_quat
  4. Realized that it's not the identity matrix

Can you do this test? If it works for you maybe it's something I messed up :)

datenwolf commented 4 years ago

I've also tried shifting the elements, but it didn't work either. Does it work fine in your case?

I did a simple test:

1. Initialize an identity matrix

2. Extract quaternion with quat_from_mat

There's your problem right there. An identity matrix has no distinct eigenvector, i.e. axis of rotation: Every vector is an eigenvector of an identity matrix.

Which in turn means, that there's no unique quaternion associated with it.

You must test with a rotation matrix.

Mikepicker commented 4 years ago

Oh, thanks for pointing me that out!

By the way, I've tested it with some transforms coming from an animated data (which contains rotation matrices) and I couldn't get good results, while using the approach I posted it worked smoothly.

Have you had any issue with the function?

monolifed commented 4 years ago

https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf note: row-major post-multiplied

datenwolf commented 3 years ago

Yeah, I'll have to look at this. A couple of weeks ago I got a bunch of pull requests, and I was so swamped with work, that I didn't check them properly. Mea culpa.

TTBT when I published linmath.h I never expected to take off like that. There are a few warts and pimples I'd like to clean up, but wonder how much that would break for existing codebases.