SRB2/src/tables.c

543 lines
16 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2024 by Sonic Team Junior.
// Copyright (C) 2009 by Stephen McGranahan.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file tables.c
/// \brief Lookup tables
/// Do not try to look them up :-).
// In the order of appearance:
// fixed_t finetangent[4096] - Tangents LUT.
// Should work with BAM fairly well (12 of 16bit, effectively, by shifting).
// fixed_t finesine[10240] - Sine lookup.
// Guess what, serves as cosine, too.
// Remarkable thing is, how to use BAMs with this?
// fixed_t tantoangle[2049] - ArcTan LUT,
// Maps tan(angle) to angle fast. Gotta search.
#include "tables.h"
unsigned SlopeDiv(unsigned num, unsigned den)
{
unsigned ans;
num <<= (FINE_FRACBITS-FRACBITS);
den <<= (FINE_FRACBITS-FRACBITS);
if (den < 512)
return SLOPERANGE;
ans = (num<<3) / (den>>8);
return ans <= SLOPERANGE ? ans : SLOPERANGE;
}
UINT64 SlopeDivEx(unsigned int num, unsigned int den)
{
UINT64 ans;
if (den < 512)
return SLOPERANGE;
ans = ((UINT64)num<<3)/(den>>8);
return ans <= SLOPERANGE ? ans : SLOPERANGE;
}
fixed_t AngleFixed(angle_t af)
{
angle_t wa = ANGLE_180;
fixed_t wf = 180*FRACUNIT;
fixed_t rf = 0*FRACUNIT;
while (af)
{
while (af < wa)
{
wa /= 2;
wf /= 2;
}
rf += wf;
af -= wa;
}
return rf;
}
static FUNCMATH angle_t AngleAdj(const fixed_t fa, const fixed_t wf,
angle_t ra)
{
const angle_t adj = 0x77;
const boolean fan = fa < 0;
const fixed_t sl = FixedDiv(fa, wf*2);
const fixed_t lb = fa % (wf*2);
const fixed_t lo = (wf*2)-lb;
if (ra == 0)
{
if (lb == 0)
{
ra = FixedMul(FRACUNIT/512, sl);
if (ra > FRACUNIT/64)
return InvAngle(ra);
return ra;
}
else if (lb > 0)
return InvAngle(FixedMul(lo*FRACUNIT, adj));
else
return InvAngle(FixedMul(lo*FRACUNIT, adj));
}
if (fan)
return InvAngle(ra);
else
return ra;
}
angle_t FixedAngleC(fixed_t fa, fixed_t factor)
{
angle_t wa = ANGLE_180;
fixed_t wf = 180*FRACUNIT;
angle_t ra = 0;
const fixed_t cfa = fa;
fixed_t cwf = wf;
if (fa == 0)
return 0;
// -2,147,483,648 has no absolute value in a 32 bit signed integer
// so this code _would_ infinite loop if passed it
if (fa == INT32_MIN)
return 0;
if (factor == 0)
return FixedAngle(fa);
else if (factor > 0)
cwf = wf = FixedMul(wf, factor);
else if (factor < 0)
cwf = wf = FixedDiv(wf, -factor);
fa = abs(fa);
while (fa)
{
while (fa < wf)
{
wa /= 2;
wf /= 2;
}
ra = ra + wa;
fa = fa - wf;
}
return AngleAdj(cfa, cwf, ra);
}
angle_t FixedAngle(fixed_t fa)
{
angle_t wa = ANGLE_180;
fixed_t wf = 180*FRACUNIT;
angle_t ra = 0;
const fixed_t cfa = fa;
const fixed_t cwf = wf;
if (fa == 0)
return 0;
// -2,147,483,648 has no absolute value in a 32 bit signed integer
// so this code _would_ infinite loop if passed it
if (fa == INT32_MIN)
return 0;
fa = abs(fa);
while (fa)
{
while (fa < wf)
{
wa /= 2;
wf /= 2;
}
ra = ra + wa;
fa = fa - wf;
}
return AngleAdj(cfa, cwf, ra);
}
#include "t_ftan.c"
#include "t_fsin.c"
fixed_t *finecosine = &finesine[FINEANGLES/4];
#include "t_tan2a.c"
#include "t_facon.c"
FUNCMATH angle_t FixedAcos(fixed_t x)
{
if (-FRACUNIT > x || x >= FRACUNIT) return 0;
return fineacon[((x<<(FINE_FRACBITS-FRACBITS)))+FRACUNIT];
}
//
// AngleBetweenVectors
//
// This checks to see if a point is inside the ranges of a polygon
//
angle_t FV2_AngleBetweenVectors(const vector2_t *Vector1, const vector2_t *Vector2)
{
// Remember, above we said that the Dot Product of returns the cosine of the angle
// between 2 vectors? Well, that is assuming they are unit vectors (normalize vectors).
// So, if we don't have a unit vector, then instead of just saying arcCos(DotProduct(A, B))
// We need to divide the dot product by the magnitude of the 2 vectors multiplied by each other.
// Here is the equation: arc cosine of (V . W / || V || * || W || )
// the || V || means the magnitude of V. This then cancels out the magnitudes dot product magnitudes.
// But basically, if you have normalize vectors already, you can forget about the magnitude part.
// Get the dot product of the vectors
fixed_t dotProduct = FV2_Dot(Vector1, Vector2);
// Get the product of both of the vectors magnitudes
fixed_t vectorsMagnitude = FixedMul(FV2_Magnitude(Vector1), FV2_Magnitude(Vector2));
// Return the arc cosine of the (dotProduct / vectorsMagnitude) which is the angle in RADIANS.
return FixedAcos(FixedDiv(dotProduct, vectorsMagnitude));
}
angle_t FV3_AngleBetweenVectors(const vector3_t *Vector1, const vector3_t *Vector2)
{
// Remember, above we said that the Dot Product of returns the cosine of the angle
// between 2 vectors? Well, that is assuming they are unit vectors (normalize vectors).
// So, if we don't have a unit vector, then instead of just saying arcCos(DotProduct(A, B))
// We need to divide the dot product by the magnitude of the 2 vectors multiplied by each other.
// Here is the equation: arc cosine of (V . W / || V || * || W || )
// the || V || means the magnitude of V. This then cancels out the magnitudes dot product magnitudes.
// But basically, if you have normalize vectors already, you can forget about the magnitude part.
// Get the dot product of the vectors
fixed_t dotProduct = FV3_Dot(Vector1, Vector2);
// Get the product of both of the vectors magnitudes
fixed_t vectorsMagnitude = FixedMul(FV3_Magnitude(Vector1), FV3_Magnitude(Vector2));
// Return the arc cosine of the (dotProduct / vectorsMagnitude) which is the angle in RADIANS.
return FixedAcos(FixedDiv(dotProduct, vectorsMagnitude));
}
//
// InsidePolygon
//
// This checks to see if a point is inside the ranges of a polygon
//
boolean FV2_InsidePolygon(const vector2_t *vIntersection, const vector2_t *Poly, const INT32 vertexCount)
{
INT32 i;
UINT64 Angle = 0; // Initialize the angle
vector2_t vA, vB; // Create temp vectors
// Just because we intersected the plane, doesn't mean we were anywhere near the polygon.
// This functions checks our intersection point to make sure it is inside of the polygon.
// This is another tough function to grasp at first, but let me try and explain.
// It's a brilliant method really, what it does is create triangles within the polygon
// from the intersection point. It then adds up the inner angle of each of those triangles.
// If the angles together add up to 360 degrees (or 2 * PI in radians) then we are inside!
// If the angle is under that value, we must be outside of polygon. To further
// understand why this works, take a pencil and draw a perfect triangle. Draw a dot in
// the middle of the triangle. Now, from that dot, draw a line to each of the vertices.
// Now, we have 3 triangles within that triangle right? Now, we know that if we add up
// all of the angles in a triangle we get 360 right? Well, that is kinda what we are doing,
// but the inverse of that. Say your triangle is an isosceles triangle, so add up the angles
// and you will get 360 degree angles. 90 + 90 + 90 is 360.
for (i = 0; i < vertexCount; i++) // Go in a circle to each vertex and get the angle between
{
FV2_Point2Vec(&Poly[i], vIntersection, &vA); // Subtract the intersection point from the current vertex
// Subtract the point from the next vertex
FV2_Point2Vec(&Poly[(i + 1) % vertexCount], vIntersection, &vB);
Angle += FV2_AngleBetweenVectors(&vA, &vB); // Find the angle between the 2 vectors and add them all up as we go along
}
// Now that we have the total angles added up, we need to check if they add up to 360 degrees.
// Since we are using the dot product, we are working in radians, so we check if the angles
// equals 2*PI. We defined PI in 3DMath.h. You will notice that we use a MATCH_FACTOR
// in conjunction with our desired degree. This is because of the inaccuracy when working
// with floating point numbers. It usually won't always be perfectly 2 * PI, so we need
// to use a little twiddling. I use .9999, but you can change this to fit your own desired accuracy.
if(Angle >= ANGLE_MAX) // If the angle is greater than 2 PI, (360 degrees)
return 1; // The point is inside of the polygon
return 0; // If you get here, it obviously wasn't inside the polygon.
}
boolean FV3_InsidePolygon(const vector3_t *vIntersection, const vector3_t *Poly, const INT32 vertexCount)
{
INT32 i;
UINT64 Angle = 0; // Initialize the angle
vector3_t vA, vB; // Create temp vectors
// Just because we intersected the plane, doesn't mean we were anywhere near the polygon.
// This functions checks our intersection point to make sure it is inside of the polygon.
// This is another tough function to grasp at first, but let me try and explain.
// It's a brilliant method really, what it does is create triangles within the polygon
// from the intersection point. It then adds up the inner angle of each of those triangles.
// If the angles together add up to 360 degrees (or 2 * PI in radians) then we are inside!
// If the angle is under that value, we must be outside of polygon. To further
// understand why this works, take a pencil and draw a perfect triangle. Draw a dot in
// the middle of the triangle. Now, from that dot, draw a line to each of the vertices.
// Now, we have 3 triangles within that triangle right? Now, we know that if we add up
// all of the angles in a triangle we get 360 right? Well, that is kinda what we are doing,
// but the inverse of that. Say your triangle is an isosceles triangle, so add up the angles
// and you will get 360 degree angles. 90 + 90 + 90 is 360.
for (i = 0; i < vertexCount; i++) // Go in a circle to each vertex and get the angle between
{
FV3_Point2Vec(&Poly[i], vIntersection, &vA); // Subtract the intersection point from the current vertex
// Subtract the point from the next vertex
FV3_Point2Vec(&Poly[(i + 1) % vertexCount], vIntersection, &vB);
Angle += FV3_AngleBetweenVectors(&vA, &vB); // Find the angle between the 2 vectors and add them all up as we go along
}
// Now that we have the total angles added up, we need to check if they add up to 360 degrees.
// Since we are using the dot product, we are working in radians, so we check if the angles
// equals 2*PI. We defined PI in 3DMath.h. You will notice that we use a MATCH_FACTOR
// in conjunction with our desired degree. This is because of the inaccuracy when working
// with floating point numbers. It usually won't always be perfectly 2 * PI, so we need
// to use a little twiddling. I use .9999, but you can change this to fit your own desired accuracy.
if(Angle >= ANGLE_MAX) // If the angle is greater than 2 PI, (360 degrees)
return 1; // The point is inside of the polygon
return 0; // If you get here, it obviously wasn't inside the polygon.
}
//
// IntersectedPolygon
//
// This checks if a line is intersecting a polygon
//
boolean FV3_IntersectedPolygon(const vector3_t *vPoly, const vector3_t *vLine, const INT32 vertexCount, vector3_t *collisionPoint)
{
vector3_t vNormal, vIntersection;
fixed_t originDistance = 0*FRACUNIT;
// First we check to see if our line intersected the plane. If this isn't true
// there is no need to go on, so return false immediately.
// We pass in address of vNormal and originDistance so we only calculate it once
if(!FV3_IntersectedPlane(vPoly, vLine, &vNormal, &originDistance))
return false;
// Now that we have our normal and distance passed back from IntersectedPlane(),
// we can use it to calculate the intersection point. The intersection point
// is the point that actually is ON the plane. It is between the line. We need
// this point test next, if we are inside the polygon. To get the I-Point, we
// give our function the normal of the plane, the points of the line, and the originDistance.
FV3_IntersectionPoint(&vNormal, vLine, originDistance, &vIntersection);
// Now that we have the intersection point, we need to test if it's inside the polygon.
// To do this, we pass in :
// (our intersection point, the polygon, and the number of vertices our polygon has)
if(FV3_InsidePolygon(&vIntersection, vPoly, vertexCount))
{
if (collisionPoint != NULL) // Optional - load the collision point.
{
collisionPoint->x = vIntersection.x;
collisionPoint->y = vIntersection.y;
collisionPoint->z = vIntersection.z;
}
return true; // We collided!
}
// If we get here, we must have NOT collided
return false;
}
//
// RotateVector
//
// Rotates a vector around another vector
//
void FV3_Rotate(vector3_t *rotVec, const vector3_t *axisVec, const angle_t angle)
{
// Rotate the point (x,y,z) around the vector (u,v,w)
fixed_t ux = FixedMul(axisVec->x, rotVec->x);
fixed_t uy = FixedMul(axisVec->x, rotVec->y);
fixed_t uz = FixedMul(axisVec->x, rotVec->z);
fixed_t vx = FixedMul(axisVec->y, rotVec->x);
fixed_t vy = FixedMul(axisVec->y, rotVec->y);
fixed_t vz = FixedMul(axisVec->y, rotVec->z);
fixed_t wx = FixedMul(axisVec->z, rotVec->x);
fixed_t wy = FixedMul(axisVec->z, rotVec->y);
fixed_t wz = FixedMul(axisVec->z, rotVec->z);
fixed_t sa = FINESINE(angle);
fixed_t ca = FINECOSINE(angle);
fixed_t ua = ux+vy+wz;
fixed_t ax = FixedMul(axisVec->x,ua);
fixed_t ay = FixedMul(axisVec->y,ua);
fixed_t az = FixedMul(axisVec->z,ua);
fixed_t xs = FixedMul(axisVec->x,axisVec->x);
fixed_t ys = FixedMul(axisVec->y,axisVec->y);
fixed_t zs = FixedMul(axisVec->z,axisVec->z);
fixed_t bx = FixedMul(rotVec->x,ys+zs);
fixed_t by = FixedMul(rotVec->y,xs+zs);
fixed_t bz = FixedMul(rotVec->z,xs+ys);
fixed_t cx = FixedMul(axisVec->x,vy+wz);
fixed_t cy = FixedMul(axisVec->y,ux+wz);
fixed_t cz = FixedMul(axisVec->z,ux+vy);
fixed_t dx = FixedMul(bx-cx, ca);
fixed_t dy = FixedMul(by-cy, ca);
fixed_t dz = FixedMul(bz-cz, ca);
fixed_t ex = FixedMul(vz-wy, sa);
fixed_t ey = FixedMul(wx-uz, sa);
fixed_t ez = FixedMul(uy-vx, sa);
rotVec->x = ax+dx+ex;
rotVec->y = ay+dy+ey;
rotVec->z = az+dz+ez;
}
#define M(row,col) dest->m[row * 4 + col]
matrix_t *FM_Rotate(matrix_t *dest, angle_t angle, fixed_t x, fixed_t y, fixed_t z)
{
const fixed_t sinA = FINESINE(angle>>ANGLETOFINESHIFT);
const fixed_t cosA = FINECOSINE(angle>>ANGLETOFINESHIFT);
const fixed_t invCosA = FRACUNIT - cosA;
vector3_t nrm;
fixed_t xSq, ySq, zSq;
fixed_t sx, sy, sz;
fixed_t sxy, sxz, syz;
nrm.x = x;
nrm.y = y;
nrm.z = z;
FV3_Normalize(&nrm);
x = nrm.x;
y = nrm.y;
z = nrm.z;
xSq = FixedMul(x, FixedMul(invCosA,x));
ySq = FixedMul(y, FixedMul(invCosA,y));
zSq = FixedMul(z, FixedMul(invCosA,z));
sx = FixedMul(sinA, x);
sy = FixedMul(sinA, y);
sz = FixedMul(sinA, z);
sxy = FixedMul(x, FixedMul(invCosA,y));
sxz = FixedMul(x, FixedMul(invCosA,z));
syz = FixedMul(y, FixedMul(invCosA,z));
M(0, 0) = xSq + cosA;
M(1, 0) = sxy - sz;
M(2, 0) = sxz + sy;
M(3, 0) = 0;
M(0, 1) = sxy + sz;
M(1, 1) = ySq + cosA;
M(2, 1) = syz - sx;
M(3, 1) = 0;
M(0, 2) = sxz - sy;
M(1, 2) = syz + sx;
M(2, 2) = zSq + cosA;
M(3, 2) = 0;
M(0, 3) = 0;
M(1, 3) = 0;
M(2, 3) = 0;
M(3, 3) = FRACUNIT;
return dest;
}
matrix_t *FM_RotateX(matrix_t *dest, angle_t rad)
{
const angle_t fa = rad>>ANGLETOFINESHIFT;
const fixed_t cosrad = FINECOSINE(fa), sinrad = FINESINE(fa);
M(0, 0) = FRACUNIT;
M(0, 1) = 0;
M(0, 2) = 0;
M(0, 3) = 0;
M(1, 0) = 0;
M(1, 1) = cosrad;
M(1, 2) = sinrad;
M(1, 3) = 0;
M(2, 0) = 0;
M(2, 1) = -sinrad;
M(2, 2) = cosrad;
M(2, 3) = 0;
M(3, 0) = 0;
M(3, 1) = 0;
M(3, 2) = 0;
M(3, 3) = FRACUNIT;
return dest;
}
matrix_t *FM_RotateY(matrix_t *dest, angle_t rad)
{
const angle_t fa = rad>>ANGLETOFINESHIFT;
const fixed_t cosrad = FINECOSINE(fa), sinrad = FINESINE(fa);
M(0, 0) = cosrad;
M(0, 1) = 0;
M(0, 2) = -sinrad;
M(0, 3) = 0;
M(1, 0) = 0;
M(1, 1) = FRACUNIT;
M(1, 2) = 0;
M(1, 3) = 0;
M(2, 0) = sinrad;
M(2, 1) = 0;
M(2, 2) = cosrad;
M(2, 3) = 0;
M(3, 0) = 0;
M(3, 1) = 0;
M(3, 2) = 0;
M(3, 3) = FRACUNIT;
return dest;
}
matrix_t *FM_RotateZ(matrix_t *dest, angle_t rad)
{
const angle_t fa = rad>>ANGLETOFINESHIFT;
const fixed_t cosrad = FINECOSINE(fa), sinrad = FINESINE(fa);
M(0, 0) = cosrad;
M(0, 1) = sinrad;
M(0, 2) = 0;
M(0, 3) = 0;
M(1, 0) = -sinrad;
M(1, 1) = cosrad;
M(1, 2) = 0;
M(1, 3) = 0;
M(2, 0) = 0;
M(2, 1) = 0;
M(2, 2) = FRACUNIT;
M(2, 3) = 0;
M(3, 0) = 0;
M(3, 1) = 0;
M(3, 2) = 0;
M(3, 3) = FRACUNIT;
return dest;
}
#undef M