General Quaternion Maths.
Note that it uses the Vector and Matrix4 structures from the previous two posts - I am duplicating them here for educational purposes.
Code:
; ################################################################### ;
;/
;/ Quaternion Maths
;/
; ################################################################### ;
Structure Vector
X.f
Y.f
Z.f
EndStructure
Structure Matrix4
W1.f
X1.f
Y1.f
Z1.f
W2.f
X2.f
Y2.f
Z2.f
W3.f
X3.f
Y3.f
Z3.f
W4.f
X4.f
Y4.f
Z4.f
EndStructure
Structure Quaternion
X.f
Y.f
Z.f
W.f
EndStructure
Procedure Quaternion_ToMatrix(*ReturnMatrix.Matrix4, *q.Quaternion)
*ReturnMatrix\W1 = 1.0 - 2.0 * ( *q\Y * *q\Y + *q\Z * *q\Z );
*ReturnMatrix\X1 = 2.0 * (*q\X * *q\Y + *q\Z * *q\w);
*ReturnMatrix\Y1 = 2.0 * (*q\X * *q\Z - *q\Y * *q\w);
*ReturnMatrix\Z1 = 0.0;
; Second row
*ReturnMatrix\W2 = 2.0 * ( *q\X * *q\Y - *q\Z * *q\w );
*ReturnMatrix\X2 = 1.0 - 2.0 * ( *q\X * *q\X + *q\Z * *q\Z );
*ReturnMatrix\Y2 = 2.0 * (*q\Z * *q\Y + *q\X * *q\w );
*ReturnMatrix\Z2 = 0.0;
; Third row
*ReturnMatrix\W3 = 2.0 * ( *q\X * *q\Z + *q\Y * *q\w );
*ReturnMatrix\X3 = 2.0 * ( *q\Y * *q\Z - *q\X * *q\w );
*ReturnMatrix\Y3 = 1.0 - 2.0 * ( *q\X * *q\X + *q\Y * *q\Y );
*ReturnMatrix\Z3 = 0.0;
; Fourth row
*ReturnMatrix\W4 = 0;
*ReturnMatrix\X4 = 0;
*ReturnMatrix\Y4 = 0;
*ReturnMatrix\Z4 = 1.0;
EndProcedure
Procedure Quaternion_ToEular(*ReturnEular.Vector, *q.Quaternion)
Protected test.f = *q\X * *q\Y + *q\Z * *q\w;
Protected.f heading, attitude, bank
If (test > 0.499) ; singularity at north pole
heading = 2 * ATan2(*q\X, *q\w);
attitude = #PI/2;
bank = 0;
ElseIf (test < -0.499) ; singularity at south pole
heading = -2 * ATan2(*q\X, *q\w);
attitude = - #PI/2;
bank = 0;
Else
Protected sqx.f = *q\X * *q\X;
Protected sqy.f = *q\Y * *q\Y;
Protected sqz.f = *q\Z * *q\Z;
heading = ATan2(2 * *q\Y * *q\w - 2 * *q\X * *q\Z, 1 - 2 * sqy - 2 * sqz);
attitude = ASin(2 * test);
bank = ATan2(2 * *q\X * *q\w - 2 * *q\Y * *q\Z, 1 - 2 * sqx - 2 * sqz)
EndIf
*ReturnEular\Z = heading
*ReturnEular\Y = bank - #PI/2
*ReturnEular\X = attitude
EndProcedure
Procedure.f Quaternion_Length(*TempQuat.Quaternion)
ProcedureReturn Sqr(*TempQuat\w * *TempQuat\w + *TempQuat\X * *TempQuat\X + *TempQuat\Y * *TempQuat\Y + *TempQuat\Z * *TempQuat\Z)
EndProcedure
Procedure Quaternion_Conjugate(*NewQuat.Quaternion, *Quat1.Quaternion)
*NewQuat\X = -1 * *Quat1\X
*NewQuat\Y = -1 * *Quat1\Y
*NewQuat\Z = -1 * *Quat1\Z
*NewQuat\w = *Quat1\w
EndProcedure
Procedure Quaternion_Multiply(*NewQuat.Quaternion, *Quat1.Quaternion, *Quat2.Quaternion)
*NewQuat\X = *Quat1\X * *Quat2\w + *Quat1\Y * *Quat2\Z - *Quat1\Z * *Quat2\Y + *Quat1\w * *Quat2\X
*NewQuat\Y = -*Quat1\X * *Quat2\Z + *Quat1\Y * *Quat2\W + *Quat1\Z * *Quat2\X + *Quat1\w * *Quat2\Y
*NewQuat\Z = *Quat1\X * *Quat2\Y - *Quat1\Y * *Quat2\X + *Quat1\Z * *Quat2\W + *Quat1\w * *Quat2\Z
*NewQuat\w = -*Quat1\X * *Quat2\X - *Quat1\Y * *Quat2\Y - *Quat1\Z * *Quat2\Z + *Quat1\w * *Quat2\w
EndProcedure
Procedure Quaternion_Normalize(*TempQuat.Quaternion)
TEMP_length.f = Quaternion_Length(*TempQuat)
*TempQuat\X = *TempQuat\X / TEMP_length
*TempQuat\Y = *TempQuat\Y / TEMP_length
*TempQuat\Z = *TempQuat\Z / TEMP_length
*TempQuat\w = *TempQuat\w / TEMP_length
EndProcedure
Procedure Quaternion_ApplyVector(*quat.Quaternion, *vecin.Vector, *vecout.Vector)
Protected vecQuat.Quaternion, qResult.Quaternion, qTemp.Quaternion
vecQuat\X = *vecin\X
vecQuat\Y = *vecin\Y
vecQuat\Z = *vecin\Z
vecQuat\w = 0
Quaternion_Conjugate(@qTemp, @vecQuat)
Quaternion_Multiply(@qResult, @vecQuat, @qTemp)
CopyStructure(@qResult, @qTemp, QUATERNION)
Quaternion_Multiply(@qResult, *Quat, @qTemp)
*vecout\X = qResult\X
*vecout\Y = qResult\Y
*vecout\Z = qResult\Z
EndProcedure
Procedure Quaternion_FromEuler(*TempQuat.Quaternion, Yaw.f, Pitch.f, Roll.f)
If Yaw = 0 And Pitch = 0 And Roll = 0
*TempQuat\X = 0
*TempQuat\Y = 0
*TempQuat\Z = 0
*TempQuat\w = 1
ProcedureReturn 0
EndIf
Roll = (Roll * 0.01745329) / 2
Pitch = (Pitch * 0.01745329) / 2
Yaw = (Yaw * 0.01745329) / 2
cosRoll.f = Cos(Roll)
cosPitch.f = Cos(Pitch)
cosYaw.f = Cos(Yaw)
sinRoll.f = Sin(Roll)
sinPitch.f = Sin(Pitch)
sinYaw.f = Sin(Yaw)
cosPitchYaw.f = cosPitch * cosYaw
sinPitchYaw.f = sinPitch * sinYaw
*TempQuat\X = sinRoll * cosPitchYaw - cosRoll * sinPitchYaw
*TempQuat\Y = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw
*TempQuat\Z = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw
*TempQuat\w = cosRoll * cosPitchYaw + sinRoll * sinPitchYaw
Quaternion_Normalize(*TempQuat)
EndProcedure
Procedure Quaternion_FromAngleAxis(*TempQuat.Quaternion, X.f, Y.f, Z.f, angleDegrees.f)
If X = 0 And Y = 0 And Z = 0
*TempQuat\X = 0
*TempQuat\Y = 0
*TempQuat\Z = 0
*TempQuat\w = 1
ProcedureReturn 0
EndIf
TEMP_angle.f = angleDegrees * 0.01745329
TEMP_angle = TEMP_angle / 2
TEMP_scale.f = Sin(TEMP_angle)
*TempQuat\X = X * TEMP_scale
*TempQuat\Y = Y * TEMP_scale
*TempQuat\Z = Z * TEMP_scale
*TempQuat\w = Cos(TEMP_angle)
Quaternion_Normalize(*TempQuat)
EndProcedure