PDA

View Full Version : OT: quaternion from matrix problem



rgreene
03-20-2003, 05:24 PM
I'm attempting to implement a look-at functionality for my animation system. It is certainly easy enough to build a matrix that specifies a stable basis for a given look-at vector, however since all rotations are defined as quats I need to convert this matrix back to a quat. I've tried some stock code to convert the matrix back to the quat, however it doesn't seem to be working since my matrix is not a pure rotation matrix. Is there a way around this? How would you implement a look-at functionality in a skeletal system where all bones define rotation in quats?

oliii
03-20-2003, 05:43 PM
You've got to make your matrix a pure rotational matrix, by recalculating the Up vector.

Say you have Eye, Target, Up

Vector Dir = (Target-Eye).Normalised();
Vector Side = (Up x Dir).Normalised();
Vector NormUp = (Dir ^ Side).Normalised();


Here is some code I've got if you want




Matrix &Matrix: http://www.opengl.org/discussion_boards/ubb/redface.gifperator =(const Quaternion& Quat)
{
float D1,D2,D3,D4,D5,D6,D7,D8,D9; //Dummy variables to hold precalcs

#ifdef _DEBUG
float QuatLenSq = Quat.GetLengthSquared();
if (QuatLenSq > MAX_QUAT_LENGTHSQR) TRAP("Q Not Normalised (too big)");
if (QuatLenSq < MIN_QUAT_LENGTHSQR) TRAP("Q Not Normalised (too small)");
#endif _DEBUG

D1 = (Quat.V.x * Quat.V.x) * 2.0f;
D2 = (Quat.V.y * Quat.V.y) * 2.0f;
D3 = (Quat.V.z * Quat.V.z) * 2.0f;

float RTimesTwo = Quat.R * 2.0f;
D4 = Quat.V.x * RTimesTwo;
D5 = Quat.V.y * RTimesTwo;
D6 = Quat.V.z * RTimesTwo;

D7 = (Quat.V.x * Quat.V.y) * 2.0f;
D8 = (Quat.V.x * Quat.V.z) * 2.0f;
D9 = (Quat.V.y * Quat.V.z) * 2.0f;

Mat[0][0] = 1.0f - D2 - D3;
Mat[0][1] = D7 - D6;
Mat[0][2] = D8 + D5;
Mat[1][0] = D7 + D6;
Mat[1][1] = 1.0f - D1 - D3;
Mat[1][2] = D9 - D4;
Mat[2][0] = D8 - D5;
Mat[2][1] = D9 + D4;
Mat[2][2] = 1.0f - D1 - D2;

return *this;
}


[This message has been edited by oliii (edited 03-20-2003).]

rgreene
03-20-2003, 05:48 PM
That code looks like quat->matrix; I need matrix->quat.

oliii
03-20-2003, 05:56 PM
doh. It's getting late http://www.opengl.org/discussion_boards/ubb/smile.gif

so you want to look in the direction of the bone?

rgreene
03-20-2003, 06:02 PM
Yea. I came up with a 'solution' however, it is incorrect. Basically, I get a vector which is the cross of the bone's neutral orientation (1, 0, 0) and the look-at vector. Then I use the acos of the dot between the neutral position and look-at vector to build my quat. This works, however since there are infinite solutions, this will result in my up vector twisting around the bone. I need to stablize the rotation, and haven't figured out how to handle that.

Ozzy
03-21-2003, 12:17 AM
This is an alternative from ken shoemake method it work for me and my probs but of course it is not accurate for some cases.

inline VR_VOID vrQuatFromMatrix(VR_QUAT *quat, VR_MATRIX *m)
{
VR_LONG ti, tj, th;
VR_FLOAT x0, y0, z0;
VR_FLOAT cy,ci, cj, ch, si, sj, sh, cc, cs, sc, ss;


cy = vrSqrt((m->m[2][2]*m->m[2][2] + m->m[1][2]*m->m[1][2]));

if (cy > 16*VR_EPSILON){
z0 = -vrAtan(m->m[0][1], m->m[0][0]);
y0 = -vrAtan(-m->m[0][2], cy);
x0 = -vrAtan(m->m[1][2], m->m[2][2]);
}
else{
z0 = -vrAtan(-m->m[1][0], m->m[1][1]);
y0 = -vrAtan(-m->m[0][2], cy);
x0 = 0.f;
}

th = vrRadToAngle(x0);
tj = -vrRadToAngle(y0);
ti = vrRadToAngle(z0);

ti >>= 1;
tj >>= 1;
th >>= 1;

ci = vrCos(ti); cj = vrCos(tj); ch = vrCos(th);
si = vrSin(ti); sj = vrSin(tj); sh = vrSin(th);
cc = ci*ch; cs = ci*sh; sc = si*ch; ss = si*sh;

quat->z = cj*sc - sj*cs;
quat->y = -(cj*ss + sj*cc);
quat->x = cj*cs - sj*sc;
quat->w = cj*cc + sj*ss;
}

hth

rgreene
03-21-2003, 12:54 AM
I'll give this a try tomorrow, a few questions:

what is VR_EPSILON supposed to be?
vrAtan is just atan () I assume?

Ozzy
03-21-2003, 06:47 AM
a small number ^^

#define VR_EPSILON 1.0E-6
vrAtan(x) equ atan(x)
vrAtan(y,x) equ atan2(y,x)

for the rest u can use standart math funcs and datatypes for 4x4 matrices & quaternions(x,y,z,w)

u should have a look to ken shoemake version also. this one is derived of course coz i met problems with the canonical version.

hth


[This message has been edited by Ozzy (edited 03-21-2003).]

rgreene
03-21-2003, 11:32 AM
Figured it out in my head on the way to work today. In case anybody is interested:

v = fLookAtPoint - offset;
v.mNormalize ();
dy = v.y;
v.y = 0;
v.mNormalize ();
S = v.mCrossProduct (tPoint3 (0, 1, 0));
U = v.mCrossProduct (tPoint3 (1, 0, 0));
U.mNormalize ();
fOutput.mCreateRotation (U, acos (v.x));
rotation.mCreateRotation (S, -asin (dy));
fOutput *= rotation;

where fOutput and rotation are quats