I've recently written two quite different implementations of firstperson camera that use Quaternions to perform rotations.
Neither worked, they appeared to suffer a common bug (the results onscreen were identical). The only thing they had in common was a code module providing quaternion math functions that I'd written earlier.

For starters, here is the formula for QuatMultiply (gleaned from the net) apon which I based my code - is it correct?



Assuming two input quaternions (A and B), find C = A . B

C.x = | A.w*B.x + A.x*B.w + A.y*B.z - A.z*B.y |
C.y = | A.w*B.y - A.x*B.z + A.y*B.w + A.z*B.x |
C.z = | A.w*B.z + A.x*B.y - A.y*B.x + A.z*B.w |
C.w = | A.w*B.w - A.x*B.x - A.y*B.y - A.z*B.z |


Now, here is my implementation of the above formula. Is it correct?


QuaternionMultiply proc uses esi edi ecx pA, pB, pC
mov esi,pA
mov edi,pB
mov ecx,pC
assume esi:ptr CQuaternion
assume edi:ptr CQuaternion
assume ecx:ptr CQuaternion

;C.x = | A.w*B.x + A.x*B.w + A.y*B.z - A.z*B.y |
fld [esi].fW
fmul [edi].fX
fld [esi].fX
fmul [edi].fW
fadd
fld [esi].fY
fmul [edi].fZ
fadd
fld [esi].fZ
fmul [edi].fY
fsub
fstp [ecx].fX

;C.y = | A.w*B.y - A.x*B.z + A.y*B.w + A.z*B.x |
fld [esi].fW
fmul [edi].fY
fld [esi].fX
fmul [edi].fZ
fsub
fld [esi].fY
fmul [edi].fW
fadd
fld [esi].fZ
fmul [edi].fX
fadd
fstp [ecx].fY

;C.z = | A.w*B.z + A.x*B.y - A.y*B.x + A.z*B.w |
fld [esi].fW
fmul [edi].fZ
fld [esi].fX
fmul [edi].fY
fadd
fld [esi].fY
fmul [edi].fX
fsub
fld [esi].fZ
fmul [edi].fW
fadd
fstp [ecx].fZ

;C.w = | A.w*B.w - A.x*B.x - A.y*B.y - A.z*B.z |
fld [esi].fW
fmul [edi].fW
fld [esi].fX
fmul [edi].fX
fsub
fld [esi].fY
fmul [edi].fY
fsub
fld [esi].fZ
fmul [edi].fZ
fsub
fstp [ecx].fW

assume esi:nothing
assume edi:nothing
assume ecx:nothing
ret
QuaternionMultiply endp


Does everything here look right?
TIA, Homer.
Posted on 2005-02-16 02:50:39 by Homer
Two other formulas for QuaternionMultiply:

;this one is correct


Cx = Ax*Bw + Ay*Bz - Az*By + Aw*Bx
Cy = - Ax*Bz + Ay*Bw + Az*Bx + Aw*By
Cz = Ax*By - Ay*Bx + Az*Bw + Aw*Bz
Cw = - Ax*Bx - Ay*By - Az*Bz + Aw*Bw

;this one must work also, not tested though...


Cx = Ax*Bw + Aw*Bx + Az*By + Ay*Bz
Cy = Ay*Bw + Aw*By + Ax*Bz + Az*Bx
Cz = Az*Bw + Aw*Bz + Ay*Bx + Ax*By
Cw = Aw*Bw + Ax*Bx + Ay*By + Az*Bz
Posted on 2005-02-16 05:36:37 by Siekmanski
Afternoon, EvilHomer2k.

Code seems fine.

Are you sure the sourcecode example you found on Gamedev.net actually works as intended? Reading through the comments on the article shows at least one person had to "fix" the code to make it work correctly.

I've seen Quaternion code for rotating cameras which don't supply GimbalLock, even though Quaternions are supposed to do that easily.

Are you still keen on using a Quaternion method, or would this one be easier?
http://www.asmcommunity.net/board/viewtopic.php?t=16454

Cheers,
Scronty
Posted on 2005-02-16 05:54:11 by Scronty
Thanks for the replies.
The first source I tried was the GameDev one, which uses glLookAt to apply the camera perspective. It's a bit long-winded imho.
The second one I tried was from NeHe and applies perspective by concatenating the combined rotation matrix (from Quat) to the model matrix using glMatrixMultf. The code is much shorter, and the camera requires less data, ie we don't need stuff like "vStrafe" anymore.

I too had to "fix" the code to make it work.
Most of the errors were obvious, some were less so.
I made some small optimizations to the procs (when I translate C/C++ to asm literally, some optimizations are glaringly obvious, and I can't stand redundancy in clearcode blocks).
The main errors which were wigging me were both in the Quaternion math as I suspected. The functions to convert quat-->matrix and to multiply quats were both in error - the formulae were bad. Damn the disinformation out there, my code was fine :roll:

I found yet another alternative formula for the Multiply:

NeHe (WORKS)
Cx = Aw*Bx + Ax*Bw + Ay*Bz - Az*By;
Cy = Aw*By + Ay*Bw + Az*Bx - Ax*Bz;
Cz = Aw*Bz + Az*Bw + Ax*By - Ay*Bx;
Cw = Aw*Bw - Ax*Bx - Ay*By - Az*Bz;

Compared to these:

Siekmanski #1 (WORKS)
Cx = Ax*Bw + Ay*Bz - Az*By + Aw*Bx
Cy = - Ax*Bz + Ay*Bw + Az*Bx + Aw*By
Cz = Ax*By - Ay*Bx + Az*Bw + Aw*Bz
Cw = - Ax*Bx - Ay*By - Az*Bz + Aw*Bw

Siekmanski#2 (UNTESTED)
Cx = Ax*Bw + Aw*Bx + Az*By + Ay*Bz
Cy = Ay*Bw + Aw*By + Ax*Bz + Az*Bx
Cz = Az*Bw + Aw*Bz + Ay*Bx + Ax*By
Cw = Aw*Bw + Ax*Bx + Ay*By + Az*Bz

I also found an error in the formula for "CreateMatrix" (convert quat to matrix) , the alternative formula for this (NeHe) works.

My quat camera is working, I'll post the source in the gamecoding forum for suggestions to improve it. At the moment, time has no bearing on camera movement, which needs to be remedied (camera physics 101)

Scronty - I've discovered that quaternions, like matrices, are a tool.
Like matrices, are non commutative.
Like matrices, can perform various transformations.
Like matrices, offer shortcuts to those familiar with the structure.
Like matrices, can cause loads of headaches when applied incorrectly.

I can see how you could gimbal lock with quatrot.
You would however be applying quatrot incorrectly to achieve this.
Nonetheless, it took me three tries to get it right, even with source (in C) on offer.
Third time is a charm :)

Yes Scronty, there's a REALLY good reason why I wanted to get quatrot working, and it's only partly to do with camera.
In a nutshell, I want to implement QuatSlerp, and I intend to test my slerp code by slerping the camera between first and thirdperson views over a second or two of time.
It's possible to slerp between any two complex matrices if you convert them to quats, and the possibilities are interesting to say the least.
Posted on 2005-02-16 06:09:56 by Homer