/****************************************************************************** @File PVRTQuaternionX.cpp @Title PVRTQuaternionX @Version @Copyright Copyright (c) Imagination Technologies Limited. @Platform ANSI compatible @Description Set of mathematical functions for quaternions. ******************************************************************************/ #include "PVRTContext.h" #include <math.h> #include <string.h> #include "PVRTFixedPoint.h" #include "PVRTQuaternion.h" /**************************************************************************** ** Functions ****************************************************************************/ /*!*************************************************************************** @Function PVRTMatrixQuaternionIdentityX @Output qOut Identity quaternion @Description Sets the quaternion to (0, 0, 0, 1), the identity quaternion. *****************************************************************************/ void PVRTMatrixQuaternionIdentityX(PVRTQUATERNIONx &qOut) { qOut.x = PVRTF2X(0.0f); qOut.y = PVRTF2X(0.0f); qOut.z = PVRTF2X(0.0f); qOut.w = PVRTF2X(1.0f); } /*!*************************************************************************** @Function PVRTMatrixQuaternionRotationAxisX @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 PVRTMatrixQuaternionRotationAxisX( PVRTQUATERNIONx &qOut, const PVRTVECTOR3x &vAxis, const int fAngle) { int fSin, fCos; fSin = PVRTXSIN(fAngle>>1); fCos = PVRTXCOS(fAngle>>1); /* Create quaternion */ qOut.x = PVRTXMUL(vAxis.x, fSin); qOut.y = PVRTXMUL(vAxis.y, fSin); qOut.z = PVRTXMUL(vAxis.z, fSin); qOut.w = fCos; /* Normalise it */ PVRTMatrixQuaternionNormalizeX(qOut); } /*!*************************************************************************** @Function PVRTMatrixQuaternionToAxisAngleX @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 PVRTMatrixQuaternionToAxisAngleX( const PVRTQUATERNIONx &qIn, PVRTVECTOR3x &vAxis, int &fAngle) { int fCosAngle, fSinAngle; int temp; /* Compute some values */ fCosAngle = qIn.w; temp = PVRTF2X(1.0f) - PVRTXMUL(fCosAngle, fCosAngle); fAngle = PVRTXMUL(PVRTXACOS(fCosAngle), PVRTF2X(2.0f)); fSinAngle = PVRTF2X(((float)sqrt(PVRTX2F(temp)))); /* This is to avoid a division by zero */ if (PVRTABS(fSinAngle)<PVRTF2X(0.0005f)) { fSinAngle = PVRTF2X(1.0f); } /* Get axis vector */ vAxis.x = PVRTXDIV(qIn.x, fSinAngle); vAxis.y = PVRTXDIV(qIn.y, fSinAngle); vAxis.z = PVRTXDIV(qIn.z, fSinAngle); } /*!*************************************************************************** @Function PVRTMatrixQuaternionSlerpX @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 Requires input quaternions to be normalized *****************************************************************************/ void PVRTMatrixQuaternionSlerpX( PVRTQUATERNIONx &qOut, const PVRTQUATERNIONx &qA, const PVRTQUATERNIONx &qB, const int t) { int fCosine, fAngle, A, B; /* Parameter checking */ if (t<PVRTF2X(0.0f) || t>PVRTF2X(1.0f)) { _RPT0(_CRT_WARN, "PVRTMatrixQuaternionSlerp : Bad parameters\n"); qOut.x = PVRTF2X(0.0f); qOut.y = PVRTF2X(0.0f); qOut.z = PVRTF2X(0.0f); qOut.w = PVRTF2X(1.0f); return; } /* Find sine of Angle between Quaternion A and B (dot product between quaternion A and B) */ fCosine = PVRTXMUL(qA.w, qB.w) + PVRTXMUL(qA.x, qB.x) + PVRTXMUL(qA.y, qB.y) + PVRTXMUL(qA.z, qB.z); if(fCosine < PVRTF2X(0.0f)) { PVRTQUATERNIONx 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; PVRTMatrixQuaternionSlerpX(qOut, qA, qi, t); return; } fCosine = PVRT_MIN(fCosine, PVRTF2X(1.0f)); fAngle = PVRTXACOS(fCosine); /* Avoid a division by zero */ if (fAngle==PVRTF2X(0.0f)) { qOut = qA; return; } /* Precompute some values */ A = PVRTXDIV(PVRTXSIN(PVRTXMUL((PVRTF2X(1.0f)-t), fAngle)), PVRTXSIN(fAngle)); B = PVRTXDIV(PVRTXSIN(PVRTXMUL(t, fAngle)), PVRTXSIN(fAngle)); /* Compute resulting quaternion */ qOut.x = PVRTXMUL(A, qA.x) + PVRTXMUL(B, qB.x); qOut.y = PVRTXMUL(A, qA.y) + PVRTXMUL(B, qB.y); qOut.z = PVRTXMUL(A, qA.z) + PVRTXMUL(B, qB.z); qOut.w = PVRTXMUL(A, qA.w) + PVRTXMUL(B, qB.w); /* Normalise result */ PVRTMatrixQuaternionNormalizeX(qOut); } /*!*************************************************************************** @Function PVRTMatrixQuaternionNormalizeX @Modified quat Vector to normalize @Description Normalize quaternion. Original quaternion is scaled down prior to be normalized in order to avoid overflow issues. *****************************************************************************/ void PVRTMatrixQuaternionNormalizeX(PVRTQUATERNIONx &quat) { PVRTQUATERNIONx qTemp; int f, n; /* Scale vector by uniform value */ n = PVRTABS(quat.w) + PVRTABS(quat.x) + PVRTABS(quat.y) + PVRTABS(quat.z); qTemp.w = PVRTXDIV(quat.w, n); qTemp.x = PVRTXDIV(quat.x, n); qTemp.y = PVRTXDIV(quat.y, n); qTemp.z = PVRTXDIV(quat.z, n); /* Compute quaternion magnitude */ f = PVRTXMUL(qTemp.w, qTemp.w) + PVRTXMUL(qTemp.x, qTemp.x) + PVRTXMUL(qTemp.y, qTemp.y) + PVRTXMUL(qTemp.z, qTemp.z); f = PVRTXDIV(PVRTF2X(1.0f), PVRTF2X(sqrt(PVRTX2F(f)))); /* Multiply vector components by f */ quat.x = PVRTXMUL(qTemp.x, f); quat.y = PVRTXMUL(qTemp.y, f); quat.z = PVRTXMUL(qTemp.z, f); quat.w = PVRTXMUL(qTemp.w, f); } /*!*************************************************************************** @Function PVRTMatrixRotationQuaternionX @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 PVRTMatrixRotationQuaternionX( PVRTMATRIXx &mOut, const PVRTQUATERNIONx &quat) { const PVRTQUATERNIONx *pQ; #if defined(BUILD_DX11) PVRTQUATERNIONx 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] = PVRTF2X(1.0f) - (PVRTXMUL(pQ->y, pQ->y)<<1) - (PVRTXMUL(pQ->z, pQ->z)<<1); mOut.f[1] = (PVRTXMUL(pQ->x, pQ->y)<<1) - (PVRTXMUL(pQ->z, pQ->w)<<1); mOut.f[2] = (PVRTXMUL(pQ->x, pQ->z)<<1) + (PVRTXMUL(pQ->y, pQ->w)<<1); mOut.f[3] = PVRTF2X(0.0f); mOut.f[4] = (PVRTXMUL(pQ->x, pQ->y)<<1) + (PVRTXMUL(pQ->z, pQ->w)<<1); mOut.f[5] = PVRTF2X(1.0f) - (PVRTXMUL(pQ->x, pQ->x)<<1) - (PVRTXMUL(pQ->z, pQ->z)<<1); mOut.f[6] = (PVRTXMUL(pQ->y, pQ->z)<<1) - (PVRTXMUL(pQ->x, pQ->w)<<1); mOut.f[7] = PVRTF2X(0.0f); mOut.f[8] = (PVRTXMUL(pQ->x, pQ->z)<<1) - (PVRTXMUL(pQ->y, pQ->w)<<1); mOut.f[9] = (PVRTXMUL(pQ->y, pQ->z)<<1) + (PVRTXMUL(pQ->x, pQ->w)<<1); mOut.f[10] = PVRTF2X(1.0f) - (PVRTXMUL(pQ->x, pQ->x)<<1) - (PVRTXMUL(pQ->y, pQ->y)<<1); mOut.f[11] = PVRTF2X(0.0f); mOut.f[12] = PVRTF2X(0.0f); mOut.f[13] = PVRTF2X(0.0f); mOut.f[14] = PVRTF2X(0.0f); mOut.f[15] = PVRTF2X(1.0f); } /*!*************************************************************************** @Function PVRTMatrixQuaternionMultiplyX @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. Input quaternions must be normalized. *****************************************************************************/ void PVRTMatrixQuaternionMultiplyX( PVRTQUATERNIONx &qOut, const PVRTQUATERNIONx &qA, const PVRTQUATERNIONx &qB) { PVRTVECTOR3x CrossProduct; /* Compute scalar component */ qOut.w = PVRTXMUL(qA.w, qB.w) - (PVRTXMUL(qA.x, qB.x) + PVRTXMUL(qA.y, qB.y) + PVRTXMUL(qA.z, qB.z)); /* Compute cross product */ CrossProduct.x = PVRTXMUL(qA.y, qB.z) - PVRTXMUL(qA.z, qB.y); CrossProduct.y = PVRTXMUL(qA.z, qB.x) - PVRTXMUL(qA.x, qB.z); CrossProduct.z = PVRTXMUL(qA.x, qB.y) - PVRTXMUL(qA.y, qB.x); /* Compute result vector */ qOut.x = PVRTXMUL(qA.w, qB.x) + PVRTXMUL(qB.w, qA.x) + CrossProduct.x; qOut.y = PVRTXMUL(qA.w, qB.y) + PVRTXMUL(qB.w, qA.y) + CrossProduct.y; qOut.z = PVRTXMUL(qA.w, qB.z) + PVRTXMUL(qB.w, qA.z) + CrossProduct.z; /* Normalize resulting quaternion */ PVRTMatrixQuaternionNormalizeX(qOut); } /***************************************************************************** End of file (PVRTQuaternionX.cpp) *****************************************************************************/