Quaterniong Matrix::CalculateQuaternion () { <span class="cpp-comment">//! The elements are stored in row-major order.</span> <span class="cpp-comment">/*! Layout is as follows: 00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33 */</span> Matrix m = *<span class="cpp-keyword">this</span>; Quaternion q; <span class="cpp-keyword">float</span> trace = m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">0</span>] + m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">1</span>] + m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">2</span>] + <span class="cpp-number">1</span>.0f; <span class="cpp-keyword">if</span> (trace > <span class="cpp-number">0</span>.00001f) { <span class="cpp-keyword">float</span> s = <span class="cpp-number">0</span>.5f / sqrt (trace); q.w = <span class="cpp-number">0</span>.25f / s; q.v.x = ( m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">1</span>] - m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">2</span>] ) * s; q.v.y = ( m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">2</span>] - m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">0</span>] ) * s; q.v.z = ( m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">0</span>] - m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">1</span>] ) * s; } <span class="cpp-keyword">else</span> { <span class="cpp-keyword">if</span> ( m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">0</span>] > m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">1</span>] && m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">0</span>] > m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">2</span>] ) { <span class="cpp-keyword">float</span> s = <span class="cpp-number">2</span>.0f * sqrt( <span class="cpp-number">1</span>.0f + m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">0</span>] - m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">1</span>] - m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">2</span>]); q.v.x = <span class="cpp-number">0</span>.25f * s; q.v.y = (m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">1</span>] + m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">0</span>] ) / s; q.v.z = (m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">2</span>] + m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">0</span>] ) / s; q.w = (m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">2</span>] - m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">1</span>] ) / s; } <span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span> (m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">1</span>] > m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">2</span>]) { <span class="cpp-keyword">float</span> s = <span class="cpp-number">2</span>.0f * sqrt( <span class="cpp-number">1</span>.0f + m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">1</span>] - m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">0</span>] - m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">2</span>]); q.v.x = (m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">1</span>] + m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">0</span>] ) / s; q.v.y = <span class="cpp-number">0</span>.25f * s; q.v.z = (m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">2</span>] + m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">1</span>] ) / s; q.w = (m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">2</span>] - m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">0</span>] ) / s; } <span class="cpp-keyword">else</span> { <span class="cpp-keyword">float</span> s = <span class="cpp-number">2</span>.0f * sqrt( <span class="cpp-number">1</span>.0f + m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">2</span>] - m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">0</span>] - m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">1</span>]); q.v.x = (m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">2</span>] + m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">0</span>] ) / s; q.v.y = (m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">2</span>] + m.m_fElement[<span class="cpp-number">2</span>][<span class="cpp-number">1</span>] ) / s; q.v.z = <span class="cpp-number">0</span>.25f * s; q.w = (m.m_fElement[<span class="cpp-number">0</span>][<span class="cpp-number">1</span>] - m.m_fElement[<span class="cpp-number">1</span>][<span class="cpp-number">0</span>] ) / s; } } <span class="cpp-keyword">return</span> (q); }

**0**

# Convert Matrix to Quaternion

###
#1
Members - Reputation: **616**

Posted 28 July 2008 - 01:08 AM

###
#2
Members - Reputation: **2078**

Posted 28 July 2008 - 01:16 AM

When you rotate the vector with the extracted quaternion, how are the results incorrect? I ask because if the axis and amount of rotation are correct but the rotation is in the 'wrong' direction, it may be that the function assumes the use of row vectors and your math library uses column vectors (or vice versa), or that you're applying the quaternion rotation incorrectly.

If you need more help, perhaps you could post the test code (where you extract the quaternion and apply the rotation), and also specify whether your library uses row or column vectors.

###
#3
Members - Reputation: **616**

Posted 28 July 2008 - 02:16 AM

Matrix m;

... set m to some rotation-matrix...

Quaternion q = m.CalculateQuaternion ();

Vector vm = m * v;

Vector vq = q * v;

After this vm is how it should be, but vq is incorrect. As far as i see it, the rotation is completely wrong, not simply in the wrong direction, or such.

As stated in my code, my Matrix-class is row-major. I also tried calculating the quaternion from the transposed matrix, but the results are still wrong.

I can create Quaternions in several different ways (e.g. from an axis/angle pair), and then everything works fine, so i know, that my quaternion/vector multiplication works as expected.

What puzzles me a bit, is that when i print out "trace" in the code, it is ALWAYS greater than 0, so always the first if is executed (at least i have not found yet a matrix that did otherwise). I don't know whether that is the usual expected behavior.

Does anyone use the same code (from that page)? Or does anyone have different code to convert a matrix into a quaternion? I don't need to figure out, what's wrong, i really only need it to work. So any help is appreciated.

Jan.

###
#4
Members - Reputation: **2078**

Posted 28 July 2008 - 06:37 AM

Quote:I didn't ask about the matrix storage (as you note, that is specified in your code), but rather whether the matrix is intended for use with row or column vectors. Your 'm * v' example uses column vectors, so you'll want to make sure that the example uses column vectors as well (I'm guessing it does, but I don't know for sure).

As stated in my code, my Matrix-class is row-major.

Another thing you might try is:

1. Create a matrix from an axis-angle pair

2. Create a quaternion (q1) from the same axis-angle pair

3. Extract a quaternion (q2) from the matrix created in step 1

4. Compare q1 and q2 and see if they're equal (to within a tolerance, of course)

Quote:IIRC, the trace will only be near zero when the rotation is near 180 degrees, so it's not surprising that you're not encountering that case often.

What puzzles me a bit, is that when i print out "trace" in the code, it is ALWAYS greater than 0, so always the first if is executed (at least i have not found yet a matrix that did otherwise). I don't know whether that is the usual expected behavior.

Quote:If you're still stuck on this later today, I can post some matrix-to-quaternion code that you can use as a reference.

Or does anyone have different code to convert a matrix into a quaternion?

###
#5
Members - Reputation: **616**

Posted 28 July 2008 - 07:26 AM

I tried your advice to compare quaternions created from matrices and from axis/angle pairs and got very interesting results:

I used random axises at first and, surprise, all the quaternions created directly from those and quaternions created from a matrix, were absolutely identical.

Now that is not the result, that i see in my application, so i dug a bit deeper.

When i create a rotation matrix around one of the main-axes, ie. around the X, Y or Z axis, my code to convert that into a quaternion fails badly. I don't know why, yet, but i assume it's some kind of instability that shows up, when the rotation-axis is close to one of the main-axis'es (damn, what's the plural of axis? and what's the genitive of that? and i thought German would be complicated...)

Well, i'll try to find out more about it.

Jan.

###
#6
Members - Reputation: **1168**

Posted 28 July 2008 - 07:28 AM

###
#7
Members - Reputation: **616**

Posted 28 July 2008 - 08:21 AM

http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55

http://www.gamasutra.com/features/19980703/quaternions_01.htm

http://cache-www.intel.com/cd/00/00/29/37/293748_293748.pdf

All are pretty close to each other, but still sometimes slightly different.

But the problem is, that no matter what you do, when there are zeros in you matrix, for example in a rotation-matrix around the X-axis, the formula always returns a quaternion with a bunch of zeroes in there, although a quaternion directly created from that axis and an angle, is different.

So, as i see it now, the formula is wrong by design. I just don't know, what you need to do, to get it right. And i don't understand, why it is used everywhere, i seem to overlook something.

And insights?

Jan.

###
#8
Members - Reputation: **1168**

Posted 28 July 2008 - 07:30 PM

Here's my code (tested and working), for reference. Matrices are row-major like yours (layed out in memory as m00,m01,m02,m10,m11,m12,m20,m21,m22). Quaternions are ordered x,y,z,w.

Quaternion QuaternionFromMatrix33(const Matrix33 *src)

{

const float trace = 1.0f + src->m00 + src->m11 + src->m22;

if (trace > 0.00001f)

{

const float s = sqrt(trace) * 2;

return Quaternion(

(src->m21 - src->m12) / s,

(src->m02 - src->m20) / s,

(src->m10 - src->m01) / s,

s / 4);

}

else if (src->m00 > src->m11 && src->m00 > src->m22)

{

const float s = sqrt(1.0f + src->m00 - src->m11 - src->m22) * 2;

return Quaternion(

s / 4,

(src->m10 + src->m01) / s,

(src->m02 + src->m20) / s,

(src->m21 - src->m12) / s);

}

else if (src->m11 > src->m22)

{

const float s = sqrt(1.0f + src->m11 - src->m00 - src->m22) * 2;

return Quaternion(

(src->m10 + src->m01) / s,

s / 4,

(src->m21 + src->m12) / s,

(src->m02 - src->m20) / s);

}

else

{

const float s = sqrt(1.0f + src->m22 - src->m00 - src->m11) * 2;

return Quaternion(

(src->m02 + src->m20) / s,

(src->m21 + src->m12) / s,

s / 4,

(src->m10 - src->m01) / s);

}

}

EDIT: Another thing try: Convert from matrix to quaternion, and then back to matrix. If it doesn't come back roughly the same, post a few before-and-after matrices. Might help track down exactly what kind of badness is happening to them.

[Edited by - DekuTree64 on July 29, 2008 1:30:01 AM]

###
#9
Members - Reputation: **616**

Posted 28 July 2008 - 08:49 PM

I have found a mistake, that i made during testing, it is working much better now, though not completely error-free, yet. I'll try your code or at least use it as a comparison. I hope to get everything working soon, then i'll post, what was wrong.

Thanks,

Jan.

###
#10
Members - Reputation: **616**

Posted 28 July 2008 - 11:35 PM

That suggests, that everything is working fine.

However, in the code, that uses the conversion, i use a look-at matrix, which does work fine, but i want it as a quaternion. Now, when i convert THAT matrix into a quaternion, the result is wrong. Here is an example:

LookAt Matrix:

0.97 | -0.08 | 0.21 | 0.00

-0.00 | 0.93 | 0.37 | 0.00

0.22 | 0.37 | -0.90 | 0.00

-0.00 | -0.00 | -0.00 | 1.00

Quaternion calculated from that matrix (x,y,z,w):

-0.00 | -0.01 | 0.04 | 1.00

That quaternion converted back into a matrix:

1.00 | -0.08 | -0.02 | 0.00

0.08 | 1.00 | 0.01 | 0.00

0.02 | -0.01 | 1.00 | 0.00

0.00 | 0.00 | 0.00 | 1.00

THAT matrix again converted into a Quaternion:

-0.00 | -0.01 | 0.04 | 1.00

As you can see, the quaternion does not convert back into the same matrix. However, that matrix, that it is converted to, DOES convert back into the SAME quaternion!

I also tested rotating a vector with all 4 variations, only the very first matrix rotates it correctly, the two (equal) quaternions and the matrix derived from the quaternion rotate the vector equally, but wrong. So it looks, as if the very first step, the conversion of the first matrix into a quaternion, is wrong. It yields a wrong quaternion. But it seems this only happens with certain matrices. Only "look-at" matrices yield such results, all other rotation-matrices seem to be convertible forwards and backwards correctly.

I am pretty much out of ideas, why this happens.

Jan.

EDIT: Note, that the quaternions given above are all normalized.

[Edited by - Jan K on July 29, 2008 5:35:00 AM]

###
#12
Members - Reputation: **616**

Posted 29 July 2008 - 01:09 AM

It seems my look-at matrix was incorrect. When i negate the 3rd column (the z-vector), everything (regarding quaternions) works, as expected. It seems i accidentally used a false sign in that function, but in all my code that used that matrix i just picked a look-at vector that corrected the problem. It was only the matrix-to-quaternion conversion code, that could not convert that matrix correctly. Damn.

Thanks for all your help and suggestions!

Jan.

###
#13
Members - Reputation: **154**

Posted 29 July 2008 - 05:00 AM

Quote:

Original post by Jan K

Oh, my god, i think i solved it.

It seems my look-at matrix was incorrect. When i negate the 3rd column (the z-vector), everything (regarding quaternions) works, as expected. It seems i accidentally used a false sign in that function, but in all my code that used that matrix i just picked a look-at vector that corrected the problem. It was only the matrix-to-quaternion conversion code, that could not convert that matrix correctly. Damn.

Thanks for all your help and suggestions!

Jan.

see also:

http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm

For this method matrix on input must be so called special orthogonal matrix (no reflection), while the matrix you posted didn't satisfy this property. I recommend you to guard this condition with good old assertion. This way you would be able find problem with that lookAt much more quickly.