st/code/renderer/tr_x42-math.c

189 lines
3.5 KiB
C

/*
===========================================================================
Copyright (C) 2006 Hermitworks Entertainment Corp
This file is part of Space Trader source code.
Space Trader source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Space Trader source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Space Trader source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
#include "tr_x42-local.h"
#ifdef _MSC_VER
#define DO_INLINE __forceinline
#else
#define DO_INLINE ID_INLINE
#endif
NOGLOBALALIAS static DO_INLINE float rsqrt( float x )
{
long i;
float y, r;
y = x * 0.5F;
i = *(long*)&x;
i = 0x5F375A86 - (i >> 1);
r = *(float*)&i;
r = r * (1.5F - r * r * y);
r = r * (1.5F - r * r * y);
return r;
}
NOGLOBALALIAS static DO_INLINE float sin_0_HalfPi( float a )
{
float s, t;
s = a * a;
t = -2.39e-08F;
t *= s;
t += 2.7526e-06F;
t *= s;
t += -1.98409e-04F;
t *= s;
t += 8.3333315e-03F;
t *= s;
t += -1.666666664e-01F;
t *= s;
t += 1.0f;
t *= a;
return t;
}
NOGLOBALALIAS static DO_INLINE float atan2_pos( float y, float x )
{
float a, d, s, t;
if( y > x )
{
a = -x / y;
d = ((float)M_PI / 2.0F);
}
else
{
a = y / x;
d = 0.0F;
}
s = a * a;
t = 0.0028662257F;
t *= s;
t += -0.0161657367F;
t *= s;
t += 0.0429096138F;
t *= s;
t += -0.0752896400F;
t *= s;
t += 0.1065626393F;
t *= s;
t += -0.1420889944F;
t *= s;
t += 0.1999355085F;
t *= s;
t += -0.3333314528F;
t *= s;
t += 1.0F;
t *= a;
t += d;
return t;
}
NOGLOBALALIAS void quat_interp( float * RESTRICT o, const float * RESTRICT a, const float * RESTRICT b, float t )
{
float cosTheta;
float ta, tb;
bool neg, norm;
cosTheta = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
if( cosTheta < 0.0F )
{
cosTheta = -cosTheta;
neg = true;
}
else
neg = false;
if( cosTheta >= 1.0F - 1e-5F )
{
//quats are very close, just lerp to avoid the division by zero
ta = 1.0F - t;
tb = t;
norm = false;
}
else if( cosTheta >= 0.2F )
{
//quats are somewhat close - normalized lerp will do
ta = 1.0F - t;
tb = t;
norm = true;
}
else
{
//do full slerp - quickly
float sinThetaSqr = 1.0F - (cosTheta * cosTheta);
float sinThetaInv = rsqrt( sinThetaSqr );
float theta = atan2_pos( sinThetaSqr * sinThetaInv, cosTheta );
//we're good to SLERP
ta = sin_0_HalfPi( (1.0F - t) * theta ) * sinThetaInv;
tb = sin_0_HalfPi( t * theta ) * sinThetaInv;
norm = false;
}
if( neg )
tb = -tb;
o[0] = a[0] * ta + b[0] * tb;
o[1] = a[1] * ta + b[1] * tb;
o[2] = a[2] * ta + b[2] * tb;
o[3] = a[3] * ta + b[3] * tb;
if( norm )
{
float l = o[0] * o[0] + o[1] * o[1] + o[2] * o[2] + o[3] * o[3];
l = rsqrt( l );
o[0] *= l;
o[1] *= l;
o[2] *= l;
o[3] *= l;
}
}