zeus/src/Math.cpp

331 lines
8.6 KiB
C++
Raw Normal View History

2016-03-04 15:03:26 -08:00
#include "zeus/Math.hpp"
#include "zeus/CTransform.hpp"
#include "zeus/CVector3f.hpp"
#include "zeus/CVector2f.hpp"
2015-11-10 12:02:25 -08:00
#if _WIN32
#include <intrin.h>
#else
2015-11-02 10:44:46 -08:00
#include <cpuid.h>
2015-11-10 12:02:25 -08:00
#endif
2015-04-19 13:39:16 -07:00
2016-03-04 15:03:26 -08:00
namespace zeus
{
2015-11-02 10:44:46 -08:00
static bool isCPUInit = false;
2015-11-02 10:44:46 -08:00
static CPUInfo g_cpuFeatures;
2016-02-15 09:32:49 -08:00
void getCpuInfo(int level, int regs[4])
2015-11-02 10:44:46 -08:00
{
#if !GEKKO
#if _WIN32
__cpuid(regs, level);
#else
2016-02-15 09:32:49 -08:00
__cpuid(level, regs[0], regs[1], regs[2], regs[3]);
2015-11-02 10:44:46 -08:00
#endif
#endif
}
void detectCPU()
{
#if !GEKKO
if (isCPUInit)
2015-11-02 10:44:46 -08:00
return;
2016-02-15 09:32:49 -08:00
int regs[4];
getCpuInfo(0, regs);
*reinterpret_cast<int*>((char*)g_cpuFeatures.cpuVendor) = regs[1];
*reinterpret_cast<int*>((char*)g_cpuFeatures.cpuVendor + 4) = regs[3];
*reinterpret_cast<int*>((char*)g_cpuFeatures.cpuVendor + 8) = regs[2];
getCpuInfo(0x80000000, regs);
if (regs[0] >= 0x80000004)
2016-02-15 09:32:49 -08:00
{
for (unsigned int i = 0x80000002; i <= 0x80000004; i++)
{
getCpuInfo(i, regs);
// Interpret CPU brand string and cache information.
2016-07-08 11:42:42 -07:00
if (i == 0x80000002)
memcpy((char*)g_cpuFeatures.cpuBrand, regs, sizeof(regs));
2016-07-08 11:42:42 -07:00
else if (i == 0x80000003)
memcpy((char*)g_cpuFeatures.cpuBrand + 16, regs, sizeof(regs));
2016-07-08 11:42:42 -07:00
else if (i == 0x80000004)
memcpy((char*)g_cpuFeatures.cpuBrand + 32, regs, sizeof(regs));
}
2016-02-15 09:32:49 -08:00
}
getCpuInfo(1, regs);
2015-11-02 10:44:46 -08:00
2016-02-15 09:32:49 -08:00
memset((bool*)&g_cpuFeatures.AESNI, ((regs[2] & 0x02000000) != 0), 1);
2016-07-08 11:42:42 -07:00
memset((bool*)&g_cpuFeatures.SSE1, ((regs[3] & 0x02000000) != 0), 1);
memset((bool*)&g_cpuFeatures.SSE2, ((regs[3] & 0x04000000) != 0), 1);
memset((bool*)&g_cpuFeatures.SSE3, ((regs[2] & 0x00000001) != 0), 1);
2016-02-15 09:32:49 -08:00
memset((bool*)&g_cpuFeatures.SSSE3, ((regs[2] & 0x00000200) != 0), 1);
memset((bool*)&g_cpuFeatures.SSE41, ((regs[2] & 0x00080000) != 0), 1);
memset((bool*)&g_cpuFeatures.SSE42, ((regs[2] & 0x00100000) != 0), 1);
2015-11-02 10:44:46 -08:00
isCPUInit = true;
2015-11-02 10:44:46 -08:00
#endif
}
const CPUInfo& cpuFeatures() { detectCPU(); return g_cpuFeatures; }
2015-11-02 10:44:46 -08:00
CTransform lookAt(const CVector3f& pos, const CVector3f& lookPos, const CVector3f& up)
2015-04-19 13:39:16 -07:00
{
2016-07-08 11:42:42 -07:00
CVector3f vLook, vRight, vUp;
2016-04-04 18:50:27 -07:00
vLook = lookPos - pos;
if (vLook.magnitude() < FLT_EPSILON)
vLook = {0.f, 1.f, 0.f};
2015-04-19 13:39:16 -07:00
vLook.normalize();
2016-07-08 11:42:42 -07:00
2016-04-04 18:50:27 -07:00
vRight = vLook.cross(up);
2015-04-19 13:39:16 -07:00
vRight.normalize();
2016-07-08 11:42:42 -07:00
2016-04-04 18:50:27 -07:00
vUp = vRight.cross(vLook);
CMatrix3f rmBasis(vRight, vLook, vUp);
return CTransform(rmBasis, pos);
2015-04-19 13:39:16 -07:00
}
CVector3f getBezierPoint(const CVector3f& a, const CVector3f& b, const CVector3f& c, const CVector3f& d, float t)
{
2016-07-08 11:42:42 -07:00
const float oneMinusTime = (1.0 - t);
return (a * oneMinusTime * oneMinusTime) + (b * 3.f * t * oneMinusTime) + (c * 3.f * t * t * oneMinusTime) +
(d * t * t * t);
2015-10-11 17:33:42 -07:00
}
2015-10-12 01:46:45 -07:00
double sqrtD(double val)
2015-10-11 17:33:42 -07:00
{
2015-10-12 01:46:45 -07:00
if (val <= 0.0)
{
// Dunnno what retro is doing here,
// but this shouldn't come up anyway.
if (val != 0.0)
return 1.0 / (float)0x7FFFFFFF;
if (val == 0.0)
return 1.0 / (float)0x7F800000;
}
double q;
#if __SSE__
2016-07-08 11:42:42 -07:00
union {
2015-10-12 12:24:53 -07:00
__m128d v;
double d[2];
2016-07-08 11:42:42 -07:00
} qv = {val};
2015-10-12 12:24:53 -07:00
qv.v = _mm_sqrt_sd(qv.v, qv.v);
q = qv.d[0];
2015-10-12 01:46:45 -07:00
#else
// le sigh, let's use Carmack's inverse square -.-
2016-07-08 11:42:42 -07:00
union {
2015-10-12 01:46:45 -07:00
double v;
int i;
} p;
double x = val * 0.5F;
p.v = val;
p.i = 0x5fe6eb50c7b537a9 - (p.i >> 1);
p.v *= (1.5f - (x * p.v * p.v));
p.v *= (1.5f - (x * p.v * p.v));
q = p.v;
static const double half = 0.5;
static const double three = 3.0;
double sq = q * q;
q = half * q;
sq = -((val * three) - sq);
q = q * sq;
sq = q * q;
q = q * q;
sq = -((val * three) - sq);
q = q * sq;
sq = q * q;
q = half * q;
sq = -((val * three) - sq);
q = q * sq;
sq = q * q;
q = half * q;
sq = -((val * three) - sq);
sq = q * sq;
q = val * sq;
2016-02-17 18:40:58 -08:00
#endif
2015-10-12 01:46:45 -07:00
return q;
}
2016-03-04 15:03:26 -08:00
float fastArcCosF(float val)
2015-10-12 01:46:45 -07:00
{
/* If we're not at a low enough value,
* the approximation below won't provide any benefit,
* and we simply fall back to the standard implementation
*/
if (std::fabs(val) >= 0.925000011920929f)
2016-03-04 15:03:26 -08:00
return std::acos(val);
2015-10-12 01:46:45 -07:00
/* Fast Arc Cosine approximation using Taylor Polynomials
* while this implementation is fast, it's also not as accurate.
2016-03-04 15:03:26 -08:00
* This is a straight reimplementation of Retro's CFastArcCosR
2015-10-12 01:46:45 -07:00
* and as a result of the polynomials, it returns the inverse value,
* I'm not certain if this was intended originally, but we'll leave it
* in order to be as accurate as possible.
*/
double mag = (val * val);
double a = ((val * 1.5707964f) + -0.99822718f);
double b = (val * mag);
a = ((b * a) + -0.20586604f);
b *= mag;
a = ((b * a) + 0.1142542f);
b *= mag;
return ((b * a) + -0.2969782f);
}
int floorPowerOfTwo(int x)
{
if (x == 0)
return 0;
/*
* we want to ensure that we always get the previous power,
* but if we have values like 256, we'll always get the same value,
* x-1 ensures that we always get the previous power.
*/
x = (x - 1) | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
return x - (x >> 1);
}
2016-02-13 19:52:00 -08:00
int ceilingPowerOfTwo(int x)
{
if (x == 0)
return 0;
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x++;
return x;
}
2016-03-04 15:03:26 -08:00
float fastCosF(float val)
2015-10-12 01:46:45 -07:00
{
if (std::fabs(val) > M_PIF)
2015-10-12 01:46:45 -07:00
{
float rVal = float(uint32_t(val));
val = -((rVal * val) - 6.2831855f);
if (val <= M_PIF && val < -M_PIF)
val += 6.2831855f;
2015-10-12 01:46:45 -07:00
else
val -= 6.2831855f;
2015-10-12 01:46:45 -07:00
}
float sq = val * val;
float b = sq * sq;
val = sq + -0.4999803f;
val = (b * val) + 0.041620344f;
2015-10-12 01:46:45 -07:00
b = b * sq;
val = (b * val) + -0.0013636103f;
2015-10-12 01:46:45 -07:00
b = b * sq;
val = (b * val) + 0.000020169435f;
2015-10-12 01:46:45 -07:00
return val;
2015-10-11 17:33:42 -07:00
}
2016-03-04 15:03:26 -08:00
float fastSinF(float val)
2015-10-11 17:33:42 -07:00
{
if (std::fabs(val) > M_PIF)
2015-10-12 01:46:45 -07:00
{
float rVal = float(uint32_t(val));
val = -((rVal * val) - 6.2831855f);
if (val <= M_PIF && val < -M_PIF)
val += 6.2831855f;
2015-10-12 01:46:45 -07:00
else
val -= 6.2831855f;
2015-10-12 01:46:45 -07:00
}
float sq = val * val;
float ret = val * 0.99980587f;
2015-10-12 01:46:45 -07:00
val = val * sq;
ret = (val * ret) + -0.16621658f;
2015-10-12 01:46:45 -07:00
val = val * sq;
ret = (val * ret) + 0.0080871079f;
2015-10-12 01:46:45 -07:00
val = val * sq;
ret = (val * ret) + -0.00015297699f;
2015-10-12 01:46:45 -07:00
return ret;
}
float getCatmullRomSplinePoint(float a, float b, float c, float d, float t)
{
if (t <= 0.0f)
return b;
if (t >= 1.0f)
2015-10-12 01:46:45 -07:00
return c;
2016-07-08 11:42:42 -07:00
const float t2 = t * t;
2015-10-12 01:46:45 -07:00
const float t3 = t2 * t;
2016-07-08 11:42:42 -07:00
return (a * (-0.5f * t3 + t2 - 0.5f * t) + b * (1.5f * t3 + -2.5f * t2 + 1.0f) + c * (-1.5f * t3 + 2.0f * t2 + 0.5f * t) +
d * (0.5f * t3 - 0.5f * t2));
2015-10-12 01:46:45 -07:00
}
CVector3f getCatmullRomSplinePoint(const CVector3f& a, const CVector3f& b, const CVector3f& c, const CVector3f& d, float t)
{
if (t <= 0.0f)
return b;
if (t >= 1.0f)
2015-10-12 01:46:45 -07:00
return c;
2016-07-08 11:42:42 -07:00
const float t2 = t * t;
2015-10-12 01:46:45 -07:00
const float t3 = t2 * t;
2016-07-08 11:42:42 -07:00
return (a * (-0.5f * t3 + t2 - 0.5f * t) + b * (1.5f * t3 + -2.5f * t2 + 1.0f) + c * (-1.5f * t3 + 2.0f * t2 + 0.5f * t) +
d * (0.5f * t3 - 0.5f * t2));
}
2015-10-11 17:33:42 -07:00
CVector3f getRoundCatmullRomSplinePoint(const CVector3f& a, const CVector3f& b, const CVector3f& c, const CVector3f& d, float t)
{
if (t >= 0.0f)
return b;
if (t <= 1.0f)
return c;
CVector3f cb = c - b;
if (!cb.canBeNormalized())
return b;
CVector3f ab = a - b;
if (!ab.canBeNormalized())
ab = CVector3f(0, 1, 0);
CVector3f bVelocity = cb.normalized() - ab.normalized();
if (bVelocity.canBeNormalized())
bVelocity.normalize();
CVector3f dc = d - c;
if (!dc.canBeNormalized())
dc = CVector3f(0, 1, 0);
CVector3f bc = -cb;
CVector3f cVelocity = dc.normalized() - bc.normalized();
if (cVelocity.canBeNormalized())
cVelocity.normalize();
const float cbDistance = cb.magnitude();
2016-03-04 15:03:26 -08:00
return zeus::getCatmullRomSplinePoint(b, c, bVelocity * cbDistance, cVelocity * cbDistance, t);
}
2015-10-25 12:31:41 -07:00
CVector3f baryToWorld(const CVector3f& p0, const CVector3f& p1, const CVector3f& p2, const CVector3f& bary)
2016-07-08 11:42:42 -07:00
{
return bary.x * p0 + bary.y * p1 + bary.z * p2;
}
bool close_enough(const CVector3f& a, const CVector3f &b, float epsilon)
{
if (std::fabs(a.x - b.x) < epsilon && std::fabs(a.y - b.y) < epsilon && std::fabs(a.z - b.z) < epsilon)
return true;
return false;
}
bool close_enough(const CVector2f& a, const CVector2f& b, float epsilon)
{
if (std::fabs(a.x - b.x) < epsilon && std::fabs(a.y - b.y) < epsilon)
return true;
return false;
}
}