| /****************************************************************************** |
| |
| @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) |
| *****************************************************************************/ |
| |