/*
* Copyright (c) 2005-2008 Hypertriton, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Quaternion operations.
*/
#include
#include "m.h"
#define SLERP_TO_LERP_THRESH 0.001
M_Quaternion
M_QuaternionMultIdentity(void)
{
M_Quaternion q;
q.w = 1.0;
q.x = 0.0;
q.y = 0.0;
q.z = 0.0;
return (q);
}
M_Quaternion
M_QuaternionAddIdentity(void)
{
M_Quaternion q;
q.w = 0.0;
q.x = 0.0;
q.y = 0.0;
q.z = 0.0;
return (q);
}
void
M_QuaternionToMatrix44(M_Matrix44 *A, const M_Quaternion *q)
{
M_Real x = q->x;
M_Real y = q->y;
M_Real z = q->z;
M_Real w = q->w;
A->m[0][0] = 1.0 - 2.0*y*y - 2.0*z*z;
A->m[0][1] = 2.0*x*y - 2.0*w*z;
A->m[0][2] = 2.0*x*z + 2.0*w*y;
A->m[0][3] = 0.0;
A->m[1][0] = 2.0*x*y + 2.0*w*z;
A->m[1][1] = 1.0 - 2.0*x*x - 2.0*z*z;
A->m[1][2] = 2.0*y*z - 2.0*w*x;
A->m[1][3] = 0.0;
A->m[2][0] = 2.0*x*z - 2.0*w*y;
A->m[2][1] = 2.0*y*z + 2.0*w*x;
A->m[2][2] = 1.0 - 2.0*x*x - 2.0*y*y;
A->m[2][3] = 0.0;
A->m[3][0] = 0.0;
A->m[3][1] = 0.0;
A->m[3][2] = 0.0;
A->m[3][3] = 1.0;
}
void
M_QuaternionpToAxisAngle(const M_Quaternion *q, M_Vector3 *v, M_Real *theta)
{
M_Real s;
s = Sqrt(q->x*q->x + q->y*q->y + q->z*q->z);
if (s != 0.0) {
*theta = 2.0*Acos(q->w);
v->x = q->x/s;
v->y = q->y/s;
v->z = q->z/s;
} else {
*theta = 0.0;
v->x = 1.0;
v->y = 0.0;
v->z = 0.0;
}
}
void
M_QuaternionpToAxisAngle3(const M_Quaternion *q, M_Real *theta, M_Real *x,
M_Real *y, M_Real *z)
{
M_Vector3 axis;
M_QuaternionpToAxisAngle(q, &axis, theta);
*x = axis.x;
*y = axis.y;
*z = axis.z;
}
M_Quaternion
M_QuaternionFromAxisAngle(M_Vector3 axis, M_Real theta)
{
M_Quaternion q;
M_Real s;
s = Sin(theta/2.0);
q.w = Cos(theta/2.0);
q.x = axis.x*s;
q.y = axis.y*s;
q.z = axis.z*s;
M_QuaternionNormv(&q);
return (q);
}
M_Quaternion
M_QuaternionFromAxisAngle3(M_Real theta, M_Real x, M_Real y, M_Real z)
{
M_Quaternion q;
M_Real s;
s = Sin(theta/2.0);
q.w = Cos(theta/2.0);
q.x = x*s;
q.y = y*s;
q.z = z*s;
M_QuaternionNormv(&q);
return (q);
}
void
M_QuaternionpFromAxisAngle(M_Quaternion *q, M_Vector3 axis, M_Real theta)
{
M_Real s;
s = Sin(theta/2.0);
q->w = Cos(theta/2.0);
q->x = axis.x*s;
q->y = axis.y*s;
q->z = axis.z*s;
M_QuaternionNormv(q);
}
void
M_QuaternionpFromAxisAngle3(M_Quaternion *q, M_Real theta, M_Real x, M_Real y,
M_Real z)
{
M_Real s, len;
s = Sin(theta/2.0);
q->w = Cos(theta/2.0);
q->x = x*s;
q->y = y*s;
q->z = z*s;
len = Sqrt(q->x*q->x + q->y*q->y + q->z*q->z + q->w*q->w);
q->w /= len;
q->x /= len;
q->y /= len;
q->z /= len;
}
void
M_QuaternionFromEulv(M_Quaternion *Qr, M_Real a, M_Real b, M_Real c)
{
M_Quaternion Qx, Qy;
Qx.w = Cos(a/2.0);
Qx.x = Sin(a/2.0);
Qx.y = 0.0;
Qx.z = 0.0;
Qy.w = Cos(b/2.0);
Qy.x = 0.0;
Qy.y = Sin(b/2.0);
Qy.z = 0.0;
Qy.w = Cos(c/2.0);
Qy.x = 0.0;
Qy.y = 0.0;
Qy.z = Sin(b/2.0);
*Qr = M_QuaternionMult3(Qx, Qy, Qx);
}
M_Quaternion
M_QuaternionFromEul(M_Real a, M_Real b, M_Real c)
{
M_Quaternion Qr;
M_QuaternionFromEulv(&Qr, a, b, c);
return (Qr);
}
M_Quaternion
M_QuaternionConj(M_Quaternion q)
{
return (M_QuaternionConjp(&q));
}
M_Quaternion
M_QuaternionConjp(const M_Quaternion *q)
{
M_Quaternion nq;
nq.x = -(q->x);
nq.y = -(q->y);
nq.z = -(q->z);
nq.w = q->w;
return (nq);
}
void
M_QuaternionConjv(M_Quaternion *q)
{
q->x = -(q->x);
q->y = -(q->y);
q->z = -(q->z);
}
M_Quaternion
M_QuaternionScale(M_Quaternion q, M_Real c)
{
return (M_QuaternionScalep(&q, c));
}
M_Quaternion
M_QuaternionScalep(const M_Quaternion *q, M_Real c)
{
M_Quaternion nq;
nq.x = q->x*c;
nq.y = q->y*c;
nq.z = q->z*c;
nq.w = q->w*c;
return (nq);
}
void
M_QuaternionScalev(M_Quaternion *q, M_Real c)
{
q->x *= c;
q->y *= c;
q->z *= c;
q->w *= c;
}
M_Quaternion
M_QuaternionMult(M_Quaternion q1, M_Quaternion q2)
{
return (M_QuaternionMultp(&q1, &q2));
}
M_Quaternion
M_QuaternionMultp(const M_Quaternion *A, const M_Quaternion *B)
{
M_Quaternion C;
C.w = A->w*B->w - A->x*B->x - A->y*B->y - A->z*B->z;
C.x = A->w*B->x + A->x*B->w + A->y*B->z - A->z*B->y;
C.y = A->w*B->y - A->x*B->z + A->y*B->w + A->z*B->x;
C.z = A->w*B->z + A->x*B->y - A->y*B->x + A->z*B->w;
return (C);
}
void
M_QuaternionMultv(M_Quaternion *C, const M_Quaternion *A, const M_Quaternion *B)
{
C->w = A->w*B->w - A->x*B->x - A->y*B->y - A->z*B->z;
C->x = A->w*B->x + A->x*B->w + A->y*B->z - A->z*B->y;
C->y = A->w*B->y - A->x*B->z + A->y*B->w + A->z*B->x;
C->z = A->w*B->z + A->x*B->y - A->y*B->x + A->z*B->w;
}
/* Alternative to M_QuaternionMult(). */
M_Quaternion
M_QuaternionConcat(const M_Quaternion *A, const M_Quaternion *B)
{
M_Vector3 v1, v2, cross;
M_Quaternion R;
M_Real angle;
v1.x = A->x;
v1.y = A->y;
v1.z = A->z;
v2.x = B->x;
v2.y = B->y;
v2.z = B->z;
angle = A->w*B->w - M_VecDot3p(&v1, &v2);
cross = M_VecCross3p(&v1, &v2);
v1.x *= B->w;
v1.y *= B->w;
v1.z *= B->w;
v2.x *= A->w;
v2.y *= A->w;
v2.z *= A->w;
R.w = angle;
R.x = v1.x + v2.x + cross.x;
R.y = v1.y + v2.y + cross.y;
R.z = v1.z + v2.z + cross.z;
return (R);
}
M_Quaternion
M_QuaternionNorm(M_Quaternion q)
{
return (M_QuaternionNormp(&q));
}
M_Quaternion
M_QuaternionNormp(const M_Quaternion *q)
{
M_Quaternion nq;
M_Real s;
s = Sqrt(q->x*q->x + q->y*q->y + q->z*q->z + q->w*q->w);
if (s == 0.0) {
nq = *q;
return (nq);
}
nq.x = q->x/s;
nq.y = q->y/s;
nq.z = q->z/s;
nq.w = q->w/s;
return (nq);
}
void
M_QuaternionNormv(M_Quaternion *q)
{
M_Real s;
s = Sqrt(q->x*q->x + q->y*q->y + q->z*q->z + q->w*q->w);
if (s == 0.0) {
return;
}
q->x /= s;
q->y /= s;
q->z /= s;
q->w /= s;
}
M_Quaternion
M_QuaternionInverse(M_Quaternion Q)
{
M_Quaternion Qc;
Qc = M_QuaternionConjp(&Q);
M_QuaternionNormv(&Qc);
return (Qc);
}
M_Quaternion
M_QuaternionInversep(const M_Quaternion *Q)
{
M_Quaternion Qc;
Qc = M_QuaternionConjp(Q);
M_QuaternionNormv(&Qc);
return (Qc);
}
void
M_QuaternionInversev(M_Quaternion *Q)
{
M_QuaternionConjv(Q);
M_QuaternionNormv(Q);
}
M_Quaternion
M_QuaternionSLERP(M_Quaternion q1, M_Quaternion q2, M_Real alpha)
{
return (M_QuaternionSLERPp(&q1, &q2, alpha));
}
M_Quaternion
M_QuaternionSLERPp(const M_Quaternion *q1, const M_Quaternion *q2,
M_Real alpha)
{
M_Real o, co, so, scale0, scale1;
M_Real qi[4];
M_Quaternion qr;
co = q1->x*q2->x + q1->y*q2->y + q1->z*q2->z + q1->w*q2->w;
if (co < 0.0) {
co = -co;
qi[0] = -q2->x;
qi[1] = -q2->y;
qi[2] = -q2->z;
qi[3] = -q2->w;
} else {
qi[0] = q2->x;
qi[1] = q2->y;
qi[2] = q2->z;
qi[3] = q2->w;
}
if ((1.0 - co) < SLERP_TO_LERP_THRESH) {
scale0 = 1.0-alpha;
scale1 = alpha;
} else {
o = Acos(co);
so = Sin(o);
scale0 = Sin((1.0-alpha)*o)/so;
scale1 = Sin(alpha*o)/so;
}
qr.w = scale0*q1->w + scale1*qi[3];
qr.x = scale0*q1->x + scale1*qi[0];
qr.y = scale0*q1->y + scale1*qi[1];
qr.z = scale0*q1->z + scale1*qi[2];
return (qr);
}
M_Quaternion
M_ReadQuaternion(AG_DataSource *buf)
{
M_Quaternion q;
q.w = M_ReadReal(buf);
q.x = M_ReadReal(buf);
q.y = M_ReadReal(buf);
q.z = M_ReadReal(buf);
return (q);
}
void
M_ReadQuaternionv(AG_DataSource *buf, M_Quaternion *q)
{
q->w = M_ReadReal(buf);
q->x = M_ReadReal(buf);
q->y = M_ReadReal(buf);
q->z = M_ReadReal(buf);
}
void
M_WriteQuaternion(AG_DataSource *buf, M_Quaternion *q)
{
M_WriteReal(buf, q->w);
M_WriteReal(buf, q->x);
M_WriteReal(buf, q->y);
M_WriteReal(buf, q->z);
}