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