View Full Version : OT: quaternion from matrix problem

rgreene

03-20-2003, 04: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, 04: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, 04:48 PM

That code looks like quat->matrix; I need matrix->quat.

oliii

03-20-2003, 04: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, 05: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.

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-20-2003, 11:54 PM

I'll give this a try tomorrow, a few questions:

what is VR_EPSILON supposed to be?

vrAtan is just atan () I assume?

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, 10: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

Powered by vBulletin® Version 4.2.5 Copyright © 2018 vBulletin Solutions Inc. All rights reserved.