thug/Code/Core/Math/quat.inl

816 lines
23 KiB
Plaintext
Raw Normal View History

2016-02-13 21:39:12 +00:00
/*****************************************************************************
** **
** Neversoft Entertainment **
** **
** Copyright (C) 1999 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Core Library **
** **
** Module: Math (Mth) **
** **
** File name: core/math/Quat.inl **
** **
** Created: 11/23/99 - mjb **
** **
** Description: Quaternion Math Class **
** **
*****************************************************************************/
#ifndef __CORE_MATH_QUAT_INL
#define __CORE_MATH_QUAT_INL
namespace Mth
{
/******************************************************************/
/* */
/* */
/******************************************************************/
inline const float& Quat::operator[] ( sint i ) const
{
return quat[i];
}
/*****************************************************************************
** Private Inline Functions **
*****************************************************************************/
inline bool quat_equal( const Quat& q1, const Quat& q2, const float tol )
{
return ( Equal( q1.quat, q2.quat, tol ));
}
/*****************************************************************************
** Public Inline Functions **
*****************************************************************************/
inline Quat::Quat( float cx, float cy, float cz, float cw )
: quat( cx, cy, cz, cw )
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat::Quat( const Vector& axis, float angle )
{
float mod = axis.Length();
float ang = angle / 2.0f;
mod = ( mod > 0.0f ) ? ( 1.0f / mod ) : 0.0f;
mod *= sinf( ang );
SetVector( mod * axis[X], mod * axis[Y], mod * axis[Z] );
SetScalar( cosf ( ang ));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline float& Quat::operator[] ( sint i )
{
return quat[i];
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat::Quat( const Matrix& m )
{
float t = m[X][X] + m[Y][Y] + m[Z][Z];
float trace;
if ( t > 0.0f )
{
trace = sqrtf( t + 1.0f );
SetScalar( trace * 0.5f );
trace = 0.5f / trace;
SetVector(( m[Z][Y] - m[Y][Z] ) * trace,
( m[X][Z] - m[Z][X] ) * trace,
( m[Y][X] - m[X][Y] ) * trace );
}
else
{
// find greatest element in Matrix diagonal
sint i = X;
if ( m[Y][Y] > m[X][X] ) i = Y;
if ( m[Z][Z] > m[i][i] ) i = Z;
sint j = ( i + 1 ) % W;
sint k = ( j + 1 ) % W;
trace = sqrtf(( m[i][i] - (m[j][j] + m[k][k] )) + 1.0f );
quat[i] = ( trace * 0.5f );
trace = 0.5f / trace;
quat[j] = ( m[j][i] + m[i][j] ) * trace;
quat[k] = ( m[k][i] + m[i][k] ) * trace;
quat[W] = ( m[k][j] - m[j][k] ) * trace;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::Invert( void ) // this = Invert ( this )
{
quat.Negate();
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::Invert( const Quat& src ) // this = Invert ( src )
{
quat.Negate( src.quat );
quat[W] = src.quat[W];
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline float Quat::Modulus( void )
{
return sqrtf( ModulusSqr () );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline float Quat::ModulusSqr ( void ) const
{
return (( quat[X] * quat[X] ) +
( quat[Y] * quat[Y] ) +
( quat[Z] * quat[Z] ) +
( quat[W] * quat[W] ));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::Normalize( float len )
{
float mod = Modulus();
if ( mod > 0.0f )
{
mod = len / mod;
quat *= mod;
}
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Vector Quat::Rotate( const Vector& vec ) const
{
Quat inv;
// Quat pt( vec[X], vec[Y], vec[Z], vec[W] );
Quat pt( vec[X], vec[Y], vec[Z], 1.0f ); // Mick: Setting W to sensible value, otherwise can cause overflow
Quat res = *this;
inv.Invert( *this );
res *= pt;
res *= inv;
return Vector( res[X], res[Y], res[Z], res[W] );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline void Quat::SetScalar( const float w )
{
quat[W] = w;
};
/******************************************************************/
/* */
/* */
/******************************************************************/
inline void Quat::SetVector( const float x, const float y, const float z )
{
quat[X] = x;
quat[Y] = y;
quat[Z] = z;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline void Quat::SetVector( const Vector& v )
{
quat[X] = v[X];
quat[Y] = v[Y];
quat[Z] = v[Z];
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::operator= ( const Quat& q )
{
quat = q.quat;
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::operator+= ( const Quat& q )
{
quat += q.quat;
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::operator-= ( const Quat& q )
{
quat -= q.quat;
return *this;
}
inline const float& Quat::GetScalar ( void ) const
{
return quat[W];
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::operator*= ( const Quat& q )
{
float s1 = GetScalar();
float s2 = q.GetScalar();
Vector v1 = GetVector();
Vector v2 = q.GetVector();
SetVector (( v2 * s1 ) + ( v1 * s2 ) + CrossProduct ( v1, v2 ));
SetScalar (( s1 * s2 ) - DotProduct ( v1, v2 ));
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::operator*= ( float s )
{
quat *= s;
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat& Quat::operator/= ( float s )
{
quat /= s;
return *this;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline void Quat::GetMatrix ( Matrix& mat ) const
{
float xs, ys, zs,
wx, wy, wz,
xx, xy, xz,
yy, yz, zz, ss;
#if 0
// our original version. Seems essentially broken, as LengthSqr ignores W
ss = 2.0f / quat.LengthSqr();
#else
// version suggested by Andre at Left Field
// uses proper Modulus, and clamps 2.0/0.0f to zero
ss = ModulusSqr();
ss = ( ss>0.0f ? 2.0f/ss : 0.0f);
#endif
xs = quat[X] * ss;
ys = quat[Y] * ss;
zs = quat[Z] * ss;
wx = quat[W] * xs;
wy = quat[W] * ys;
wz = quat[W] * zs;
xx = quat[X] * xs;
xy = quat[X] * ys;
xz = quat[X] * zs;
yy = quat[Y] * ys;
yz = quat[Y] * zs;
zz = quat[Z] * zs;
mat[X][X] = 1.0f - (yy + zz);
mat[Y][X] = xy + wz;
mat[Z][X] = xz - wy;
mat[X][Y] = xy - wz;
mat[Y][Y] = 1.0f - (xx + zz);
mat[Z][Y] = yz + wx;
mat[X][Z] = xz + wy;
mat[Y][Z] = yz - wx;
mat[Z][Z] = 1.0f - (xx + yy );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat operator+ ( const Quat& q1, const Quat& q2 )
{
Quat sum = q1;
sum += q2;
return sum;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat operator- ( const Quat& q1, const Quat& q2 )
{
Quat diff = q1;
diff -= q2;
return diff;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat operator* ( const Quat& q1, const Quat& q2 )
{
Quat prod = q1;
prod *= q2;
return prod;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat operator* ( const Quat& q, const float s )
{
Quat prod = q;
prod *= s;
return prod;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat operator* ( const float s, const Quat& q )
{
return q * s;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat operator- ( const Quat& q )
{
return Quat ( -q[X], -q[Y], -q[Z], -q[W] );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline float DotProduct ( const Quat& v1, const Quat& v2 )
{
return ( v1[X] * v2[X] ) + ( v1[Y] * v2[Y] ) + ( v1[Z] * v2[Z] ) + ( v1[W] * v2[W] );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Quat Slerp ( const Quat& q1, const Quat& q3, float t )
{
float sclp, sclq;
// GJ: Need to possibly negate the second quaternion,
// to avoid spinning in the wrong direction.
Quat q2 = q3;
if ( DotProduct( q1, q3 ) < 0.0f )
{
q2 = -q2;
}
float coso = q1.GetScalar() * q2.GetScalar() +
DotProduct ( q1.GetVector(), q2.GetVector());
Quat q;
if (( 1.0f + coso + 1.0f ) > EPSILON )
{
if (( 1.0f - coso ) > EPSILON ) // slerp
{
float omega = acosf ( coso );
float sino = sinf ( omega );
sclp = sinf (( 1.0f - t ) * omega ) / sino;
sclq = sinf ( t * omega ) / sino;
}
else // lerp ( angle tends to 0 )
{
sclp = 1.0f - t;
sclq = t;
}
q = ( sclp * q1 ) + ( sclq * q2 );
}
else // angle tends to 2*PI
{
q.SetVector ( -q1[Y], q1[X], -q1[W] );
q.SetScalar ( q1[Z] );
sclp = sinf (( 1.0f - t ) * PI / 2.0f );
sclq = sinf ( t * PI / 2.0f );
q.SetVector (( sclp * q1.GetVector()) + ( sclq * q.GetVector()));
}
return q;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline bool Equal ( const Quat& q1, const Quat& q2, const float tol )
{
if (( quat_equal ( q1, q2, tol )) ||
( quat_equal ( -q1, q2, tol )))
{
return true;
}
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline ostream& operator<< ( ostream& os, const Quat& q )
{
return os << "(( " << q[X] << ", " << q[Y] << ", " << q[Z] << ") , " << q[W] << " )";
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline void QuatVecToMatrix( Mth::Quat* pQ, Mth::Vector* pT, Mth::Matrix* pMatrix )
{
Dbg_Assert( pQ );
Dbg_Assert( pT );
Dbg_Assert( pMatrix );
pQ->Invert();
Mth::Vector square;
Mth::Vector cross;
Mth::Vector wimag;
square[X] = (*pQ)[X] * (*pQ)[X];
square[Y] = (*pQ)[Y] * (*pQ)[Y];
square[Z] = (*pQ)[Z] * (*pQ)[Z];
cross[X] = (*pQ)[Y] * (*pQ)[Z];
cross[Y] = (*pQ)[Z] * (*pQ)[X];
cross[Z] = (*pQ)[X] * (*pQ)[Y];
wimag[X] = (*pQ)[W] * (*pQ)[X];
wimag[Y] = (*pQ)[W] * (*pQ)[Y];
wimag[Z] = (*pQ)[W] * (*pQ)[Z];
(*pMatrix)[Mth::RIGHT][X] = 1 - 2 * (square[Y] + square[Z]);
(*pMatrix)[Mth::RIGHT][Y] = 2 * (cross[Z] + wimag[Z]);
(*pMatrix)[Mth::RIGHT][Z] = 2 * (cross[Y] - wimag[Y]);
(*pMatrix)[Mth::RIGHT][W] = 0.0f;
(*pMatrix)[Mth::UP][X] = 2 * (cross[Z] - wimag[Z]);
(*pMatrix)[Mth::UP][Y] = 1 - 2 * (square[X] + square[Z]);
(*pMatrix)[Mth::UP][Z] = 2 * (cross[X] + wimag[X]);
(*pMatrix)[Mth::UP][W] = 0.0f;
(*pMatrix)[Mth::AT][X] = 2 * (cross[Y] + wimag[Y]);
(*pMatrix)[Mth::AT][Y] = 2 * (cross[X] - wimag[X]);
(*pMatrix)[Mth::AT][Z] = 1 - 2 * (square[X] + square[Y]);
(*pMatrix)[Mth::AT][W] = 0.0f;
(*pMatrix)[Mth::POS] = *pT;
(*pMatrix)[Mth::POS][W] = 1.0f;
}
inline void SCacheQuatVecToMatrix( Mth::Quat* pQ, Mth::Vector* pT, Mth::Matrix* pMatrix )
{
Dbg_Assert( pQ );
Dbg_Assert( pT );
Dbg_Assert( pMatrix );
pQ->Invert();
Mth::Vector *p_square = ((Mth::Vector*)0x7000000)+0;
Mth::Vector *p_cross = ((Mth::Vector*)0x7000000)+1;
Mth::Vector *p_wimag = ((Mth::Vector*)0x7000000)+2;
(*p_square)[X] = (*pQ)[X] * (*pQ)[X];
(*p_square)[Y] = (*pQ)[Y] * (*pQ)[Y];
(*p_square)[Z] = (*pQ)[Z] * (*pQ)[Z];
(*p_cross)[X] = (*pQ)[Y] * (*pQ)[Z];
(*p_cross)[Y] = (*pQ)[Z] * (*pQ)[X];
(*p_cross)[Z] = (*pQ)[X] * (*pQ)[Y];
(*p_wimag)[X] = (*pQ)[W] * (*pQ)[X];
(*p_wimag)[Y] = (*pQ)[W] * (*pQ)[Y];
(*p_wimag)[Z] = (*pQ)[W] * (*pQ)[Z];
(*pMatrix)[Mth::RIGHT][X] = 1 - 2 * ((*p_square)[Y] + (*p_square)[Z]);
(*pMatrix)[Mth::RIGHT][Y] = 2 * ((*p_cross)[Z] + (*p_wimag)[Z]);
(*pMatrix)[Mth::RIGHT][Z] = 2 * ((*p_cross)[Y] - (*p_wimag)[Y]);
(*pMatrix)[Mth::RIGHT][W] = 0.0f;
(*pMatrix)[Mth::UP][X] = 2 * ((*p_cross)[Z] - (*p_wimag)[Z]);
(*pMatrix)[Mth::UP][Y] = 1 - 2 * ((*p_square)[X] + (*p_square)[Z]);
(*pMatrix)[Mth::UP][Z] = 2 * ((*p_cross)[X] + (*p_wimag)[X]);
(*pMatrix)[Mth::UP][W] = 0.0f;
(*pMatrix)[Mth::AT][X] = 2 * ((*p_cross)[Y] + (*p_wimag)[Y]);
(*pMatrix)[Mth::AT][Y] = 2 * ((*p_cross)[X] - (*p_wimag)[X]);
(*pMatrix)[Mth::AT][Z] = 1 - 2 * ((*p_square)[X] + (*p_square)[Y]);
(*pMatrix)[Mth::AT][W] = 0.0f;
(*pMatrix)[Mth::POS] = *pT;
(*pMatrix)[Mth::POS][W] = 1.0f;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline void Slerp ( Matrix& result, const Matrix& s1, const Matrix& s2, float t )
{
Slerp ( Quat(s1), Quat(s2), t).GetMatrix ( result );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline float counter_warp(float t, float cos_alpha)
{
const float ATTENUATION = 0.82279687f;
const float WORST_CASE_SLOPE = 0.58549219f;
float factor = 1.0f - ATTENUATION * cos_alpha;
factor *= factor;
float k = WORST_CASE_SLOPE * factor;
return t*(k*t*(2.0f*t - 3.0f) + 1.0f + k);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Mth::Quat FastSlerp( Mth::Quat& qIn1, Mth::Quat& qIn2, const float t )
{
if ( Mth::DotProduct( qIn1, qIn2 ) < 0.0f )
{
qIn2 = -qIn2;
}
# if 0
float cos_a = v1[X]*v2[X] + v1[Y]*v2[Y] + v1[Z]*v2[Z] + v1[W]*v2[W];
if (t <= 0.5f)
{
t = counter_warp(t, cos_a);
}
else
{
t = 1.0f - counter_warp(1.0f - t, cos_a);
}
# endif
float xxx_x = qIn1[X] + ( qIn2[X] - qIn1[X] ) * t;
float xxx_y = qIn1[Y] + ( qIn2[Y] - qIn1[Y] ) * t;
float xxx_z = qIn1[Z] + ( qIn2[Z] - qIn1[Z] ) * t;
float xxx_w = qIn1[W] + ( qIn2[W] - qIn1[W] ) * t;
float len = 1.0f / sqrtf( xxx_x * xxx_x + xxx_y * xxx_y + xxx_z * xxx_z + xxx_w * xxx_w );
qIn2.SetVector( xxx_x * len, xxx_y * len, xxx_z * len );
qIn2.SetScalar( xxx_w * len );
return qIn2;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline Mth::Quat EulerToQuat( const Mth::Vector& euler )
{
// This code is a modified version of Left Field's
// MakeQuatFromEuler() function.
Mth::Quat q;
float roll = euler[X];
float pitch = euler[Y];
float yaw = euler[Z];
float cyaw, cpitch, croll, syaw, spitch, sroll;
float cyawcpitch, syawspitch, cyawspitch, syawcpitch;
cyaw = cosf(0.5f * yaw);
cpitch = cosf(0.5f * pitch);
croll = cosf(0.5f * roll);
syaw = sinf(0.5f * yaw);
spitch = sinf(0.5f * pitch);
sroll = sinf(0.5f * roll);
cyawcpitch = cyaw * cpitch;
syawspitch = syaw * spitch;
cyawspitch = cyaw * spitch;
syawcpitch = syaw * cpitch;
q[W] = cyawcpitch * croll + syawspitch * sroll;
q[X] = cyawcpitch * sroll - syawspitch * croll;
q[Y] = cyawspitch * croll + syawcpitch * sroll;
q[Z] = syawcpitch * croll - cyawspitch * sroll;
return q;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
} // namespace Mth
#endif // __CORE_MATH_QUAT_INL