/****************************************************************************** @File PVRTQuaternionF.cpp @Title PVRTQuaternionF @Version @Copyright Copyright (c) Imagination Technologies Limited. @Platform ANSI compatible @Description Set of mathematical functions for quaternions. ******************************************************************************/ #include "PVRTGlobal.h" #include <math.h> #include <string.h> #include "PVRTFixedPoint.h" // Only needed for trig function float lookups #include "PVRTQuaternion.h" /**************************************************************************** ** Functions ****************************************************************************/ /*!*************************************************************************** @Function PVRTMatrixQuaternionIdentityF @Output qOut Identity quaternion @Description Sets the quaternion to (0, 0, 0, 1), the identity quaternion. *****************************************************************************/ void PVRTMatrixQuaternionIdentityF(PVRTQUATERNIONf &qOut) { qOut.x = 0; qOut.y = 0; qOut.z = 0; qOut.w = 1; } /*!*************************************************************************** @Function PVRTMatrixQuaternionRotationAxisF @Output qOut Rotation quaternion @Input vAxis Axis to rotate around @Input fAngle Angle to rotate @Description Create quaternion corresponding to a rotation of fAngle radians around submitted vector. *****************************************************************************/ void PVRTMatrixQuaternionRotationAxisF( PVRTQUATERNIONf &qOut, const PVRTVECTOR3f &vAxis, const float fAngle) { float fSin, fCos; fSin = (float)PVRTFSIN(fAngle * 0.5f); fCos = (float)PVRTFCOS(fAngle * 0.5f); /* Create quaternion */ qOut.x = vAxis.x * fSin; qOut.y = vAxis.y * fSin; qOut.z = vAxis.z * fSin; qOut.w = fCos; /* Normalise it */ PVRTMatrixQuaternionNormalizeF(qOut); } /*!*************************************************************************** @Function PVRTMatrixQuaternionToAxisAngleF @Input qIn Quaternion to transform @Output vAxis Axis of rotation @Output fAngle Angle of rotation @Description Convert a quaternion to an axis and angle. Expects a unit quaternion. *****************************************************************************/ void PVRTMatrixQuaternionToAxisAngleF( const PVRTQUATERNIONf &qIn, PVRTVECTOR3f &vAxis, float &fAngle) { float fCosAngle, fSinAngle; double temp; /* Compute some values */ fCosAngle = qIn.w; temp = 1.0f - fCosAngle*fCosAngle; fAngle = (float)PVRTFACOS(fCosAngle)*2.0f; fSinAngle = (float)sqrt(temp); /* This is to avoid a division by zero */ if ((float)fabs(fSinAngle)<0.0005f) fSinAngle = 1.0f; /* Get axis vector */ vAxis.x = qIn.x / fSinAngle; vAxis.y = qIn.y / fSinAngle; vAxis.z = qIn.z / fSinAngle; } /*!*************************************************************************** @Function PVRTMatrixQuaternionSlerpF @Output qOut Result of the interpolation @Input qA First quaternion to interpolate from @Input qB Second quaternion to interpolate from @Input t Coefficient of interpolation @Description Perform a Spherical Linear intERPolation between quaternion A and quaternion B at time t. t must be between 0.0f and 1.0f *****************************************************************************/ void PVRTMatrixQuaternionSlerpF( PVRTQUATERNIONf &qOut, const PVRTQUATERNIONf &qA, const PVRTQUATERNIONf &qB, const float t) { float fCosine, fAngle, A, B; /* Parameter checking */ if (t<0.0f || t>1.0f) { _RPT0(_CRT_WARN, "PVRTMatrixQuaternionSlerp : Bad parameters\n"); qOut.x = 0; qOut.y = 0; qOut.z = 0; qOut.w = 1; return; } /* Find sine of Angle between Quaternion A and B (dot product between quaternion A and B) */ fCosine = qA.w*qB.w + qA.x*qB.x + qA.y*qB.y + qA.z*qB.z; if (fCosine < 0) { PVRTQUATERNIONf qi; /* <http://www.magic-software.com/Documentation/Quaternions.pdf> "It is important to note that the quaternions q and -q represent the same rotation... while either quaternion will do, the interpolation methods require choosing one over the other. "Although q1 and -q1 represent the same rotation, the values of Slerp(t; q0, q1) and Slerp(t; q0,-q1) are not the same. It is customary to choose the sign... on q1 so that... the angle between q0 and q1 is acute. This choice avoids extra spinning caused by the interpolated rotations." */ qi.x = -qB.x; qi.y = -qB.y; qi.z = -qB.z; qi.w = -qB.w; PVRTMatrixQuaternionSlerpF(qOut, qA, qi, t); return; } fCosine = PVRT_MIN(fCosine, 1.0f); fAngle = (float)PVRTFACOS(fCosine); /* Avoid a division by zero */ if (fAngle==0.0f) { qOut = qA; return; } /* Precompute some values */ A = (float)(PVRTFSIN((1.0f-t)*fAngle) / PVRTFSIN(fAngle)); B = (float)(PVRTFSIN(t*fAngle) / PVRTFSIN(fAngle)); /* Compute resulting quaternion */ qOut.x = A * qA.x + B * qB.x; qOut.y = A * qA.y + B * qB.y; qOut.z = A * qA.z + B * qB.z; qOut.w = A * qA.w + B * qB.w; /* Normalise result */ PVRTMatrixQuaternionNormalizeF(qOut); } /*!*************************************************************************** @Function PVRTMatrixQuaternionNormalizeF @Modified quat Vector to normalize @Description Normalize quaternion. *****************************************************************************/ void PVRTMatrixQuaternionNormalizeF(PVRTQUATERNIONf &quat) { float fMagnitude; double temp; /* Compute quaternion magnitude */ temp = quat.w*quat.w + quat.x*quat.x + quat.y*quat.y + quat.z*quat.z; fMagnitude = (float)sqrt(temp); /* Divide each quaternion component by this magnitude */ if (fMagnitude!=0.0f) { fMagnitude = 1.0f / fMagnitude; quat.x *= fMagnitude; quat.y *= fMagnitude; quat.z *= fMagnitude; quat.w *= fMagnitude; } } /*!*************************************************************************** @Function PVRTMatrixRotationQuaternionF @Output mOut Resulting rotation matrix @Input quat Quaternion to transform @Description Create rotation matrix from submitted quaternion. Assuming the quaternion is of the form [X Y Z W]: | 2 2 | | 1 - 2Y - 2Z 2XY - 2ZW 2XZ + 2YW 0 | | | | 2 2 | M = | 2XY + 2ZW 1 - 2X - 2Z 2YZ - 2XW 0 | | | | 2 2 | | 2XZ - 2YW 2YZ + 2XW 1 - 2X - 2Y 0 | | | | 0 0 0 1 | *****************************************************************************/ void PVRTMatrixRotationQuaternionF( PVRTMATRIXf &mOut, const PVRTQUATERNIONf &quat) { const PVRTQUATERNIONf *pQ; #if defined(BUILD_DX11) PVRTQUATERNIONf qInv; qInv.x = -quat.x; qInv.y = -quat.y; qInv.z = -quat.z; qInv.w = quat.w; pQ = &qInv; #else pQ = &quat; #endif /* Fill matrix members */ mOut.f[0] = 1.0f - 2.0f*pQ->y*pQ->y - 2.0f*pQ->z*pQ->z; mOut.f[1] = 2.0f*pQ->x*pQ->y - 2.0f*pQ->z*pQ->w; mOut.f[2] = 2.0f*pQ->x*pQ->z + 2.0f*pQ->y*pQ->w; mOut.f[3] = 0.0f; mOut.f[4] = 2.0f*pQ->x*pQ->y + 2.0f*pQ->z*pQ->w; mOut.f[5] = 1.0f - 2.0f*pQ->x*pQ->x - 2.0f*pQ->z*pQ->z; mOut.f[6] = 2.0f*pQ->y*pQ->z - 2.0f*pQ->x*pQ->w; mOut.f[7] = 0.0f; mOut.f[8] = 2.0f*pQ->x*pQ->z - 2*pQ->y*pQ->w; mOut.f[9] = 2.0f*pQ->y*pQ->z + 2.0f*pQ->x*pQ->w; mOut.f[10] = 1.0f - 2.0f*pQ->x*pQ->x - 2*pQ->y*pQ->y; mOut.f[11] = 0.0f; mOut.f[12] = 0.0f; mOut.f[13] = 0.0f; mOut.f[14] = 0.0f; mOut.f[15] = 1.0f; } /*!*************************************************************************** @Function PVRTMatrixQuaternionMultiplyF @Output qOut Resulting quaternion @Input qA First quaternion to multiply @Input qB Second quaternion to multiply @Description Multiply quaternion A with quaternion B and return the result in qOut. *****************************************************************************/ void PVRTMatrixQuaternionMultiplyF( PVRTQUATERNIONf &qOut, const PVRTQUATERNIONf &qA, const PVRTQUATERNIONf &qB) { PVRTVECTOR3f CrossProduct; PVRTQUATERNIONf qRet; /* Compute scalar component */ qRet.w = (qA.w*qB.w) - (qA.x*qB.x + qA.y*qB.y + qA.z*qB.z); /* Compute cross product */ CrossProduct.x = qA.y*qB.z - qA.z*qB.y; CrossProduct.y = qA.z*qB.x - qA.x*qB.z; CrossProduct.z = qA.x*qB.y - qA.y*qB.x; /* Compute result vector */ qRet.x = (qA.w * qB.x) + (qB.w * qA.x) + CrossProduct.x; qRet.y = (qA.w * qB.y) + (qB.w * qA.y) + CrossProduct.y; qRet.z = (qA.w * qB.z) + (qB.w * qA.z) + CrossProduct.z; /* Normalize resulting quaternion */ PVRTMatrixQuaternionNormalizeF(qRet); /* Copy result to mOut */ qOut = qRet; } /***************************************************************************** End of file (PVRTQuaternionF.cpp) *****************************************************************************/