mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
e00a871824
I found it rather confusing that the matrices were all backwards, and the existing comments about being "horizontal" didn't really help all that much. After spending some time with maxima, I was able to verify that the comments were indeed correct, just transposed (horizontal), with the final composition reversed to reflect that transposition.
1495 lines
33 KiB
C
1495 lines
33 KiB
C
/*
|
|
mathlib.c
|
|
|
|
math primitives
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include "qfalloca.h"
|
|
|
|
#include <math.h>
|
|
|
|
#define IMPLEMENT_R_Cull
|
|
#define IMPLEMENT_VectorNormalize
|
|
|
|
#include "QF/mathlib.h"
|
|
#include "QF/qtypes.h"
|
|
#include "QF/set.h"
|
|
#include "QF/sys.h"
|
|
|
|
static vec3_t _vec3_origin = { 0, 0, 0 };
|
|
VISIBLE const vec_t * const vec3_origin = _vec3_origin;
|
|
static quat_t _quat_origin = { 0, 0, 0, 0 };
|
|
VISIBLE const vec_t * const quat_origin = _quat_origin;
|
|
|
|
#define DEG2RAD(a) (a * (M_PI / 180.0))
|
|
|
|
#define FMANTBITS 23
|
|
#define FMANTMASK ((1 << FMANTBITS) - 1)
|
|
#define FEXPBITS 8
|
|
#define FEXPMASK ((1 << FEXPBITS) - 1)
|
|
#define FBIAS (1 << (FEXPBITS - 1))
|
|
#define FEXPMAX ((1 << FEXPBITS) - 1)
|
|
|
|
#define HMANTBITS 10
|
|
#define HMANTMASK ((1 << HMANTBITS) - 1)
|
|
#define HEXPBITS 5
|
|
#define HEXPMASK ((1 << HEXPBITS) - 1)
|
|
#define HBIAS (1 << (HEXPBITS - 1))
|
|
#define HEXPMAX ((1 << HEXPBITS) - 1)
|
|
|
|
int16_t
|
|
FloatToHalf (float x)
|
|
{
|
|
union {
|
|
float f;
|
|
uint32_t u;
|
|
} uf;
|
|
unsigned sign;
|
|
int exp;
|
|
unsigned mant;
|
|
int16_t half;
|
|
|
|
uf.f = x;
|
|
sign = (uf.u >> (FEXPBITS + FMANTBITS)) & 1;
|
|
exp = ((uf.u >> FMANTBITS) & FEXPMASK) - FBIAS + HBIAS;
|
|
mant = (uf.u & FMANTMASK) >> (FMANTBITS - HMANTBITS);
|
|
if (exp <= 0) {
|
|
mant |= 1 << HMANTBITS;
|
|
mant >>= min (1 - exp, HMANTBITS + 1);
|
|
exp = 0;
|
|
} else if (exp >= HEXPMAX) {
|
|
mant = 0;
|
|
exp = HEXPMAX;
|
|
}
|
|
half = (sign << (HEXPBITS + HMANTBITS)) | (exp << HMANTBITS) | mant;
|
|
return half;
|
|
}
|
|
|
|
float
|
|
HalfToFloat (int16_t x)
|
|
{
|
|
union {
|
|
float f;
|
|
uint32_t u;
|
|
} uf;
|
|
unsigned sign;
|
|
int exp;
|
|
unsigned mant;
|
|
|
|
sign = (x >> (HEXPBITS + HMANTBITS)) & 1;
|
|
exp = ((x >> HMANTBITS) & HEXPMASK);
|
|
mant = (x & HMANTMASK) << (FMANTBITS - HMANTBITS);
|
|
|
|
if (exp == 0) {
|
|
if (mant) {
|
|
while (mant < (1 << FMANTBITS)) {
|
|
mant <<= 1;
|
|
exp--;
|
|
}
|
|
mant &= (1 << FMANTBITS) - 1;
|
|
exp += FBIAS - HBIAS + 1;
|
|
}
|
|
} else if (exp == HEXPMAX) {
|
|
exp = FEXPMAX;
|
|
} else {
|
|
exp += FBIAS - HBIAS;
|
|
}
|
|
uf.u = (sign << (FEXPBITS + FMANTBITS)) | (exp << FMANTBITS) | mant;
|
|
return uf.f;
|
|
}
|
|
|
|
static void
|
|
ProjectPointOnPlane (vec3_t dst, const vec3_t p, const vec3_t normal)
|
|
{
|
|
float inv_denom, d;
|
|
vec3_t n;
|
|
|
|
inv_denom = 1.0F / DotProduct (normal, normal);
|
|
|
|
d = DotProduct (normal, p) * inv_denom;
|
|
|
|
VectorScale (normal, inv_denom * d, n);
|
|
|
|
VectorSubtract (p, n, dst);
|
|
}
|
|
|
|
// assumes "src" is normalized
|
|
static void
|
|
PerpendicularVector (vec3_t dst, const vec3_t src)
|
|
{
|
|
int pos, i;
|
|
float minelem = 1.0F;
|
|
vec3_t tempvec;
|
|
|
|
/* find the smallest magnitude axially aligned vector */
|
|
for (pos = 0, i = 0; i < 3; i++) {
|
|
if (fabs (src[i]) < minelem) {
|
|
pos = i;
|
|
minelem = fabs (src[i]);
|
|
}
|
|
}
|
|
VectorZero (tempvec);
|
|
tempvec[pos] = 1.0F;
|
|
|
|
/* project the point onto the plane defined by src */
|
|
ProjectPointOnPlane (dst, tempvec, src);
|
|
|
|
/* normalize the result */
|
|
VectorNormalize (dst);
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__GNUC__)
|
|
# pragma optimize( "", off )
|
|
#endif
|
|
|
|
VISIBLE void
|
|
VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
|
|
{
|
|
float d;
|
|
|
|
right[0] = forward[2];
|
|
right[1] = -forward[0];
|
|
right[2] = forward[1];
|
|
|
|
d = DotProduct(forward, right);
|
|
VectorMultSub (right, d, forward, right);
|
|
VectorNormalize (right);
|
|
CrossProduct(right, forward, up);
|
|
}
|
|
|
|
VISIBLE void
|
|
RotatePointAroundVector (vec3_t dst, const vec3_t axis, const vec3_t point,
|
|
float degrees)
|
|
{
|
|
float m[3][3];
|
|
float im[3][3];
|
|
float zrot[3][3];
|
|
float tmpmat[3][3];
|
|
float rot[3][3];
|
|
int i;
|
|
vec3_t vr, vup, vf;
|
|
|
|
VectorCopy (axis, vf);
|
|
|
|
PerpendicularVector (vr, axis);
|
|
CrossProduct (vr, vf, vup);
|
|
|
|
m[0][0] = vr[0];
|
|
m[1][0] = vr[1];
|
|
m[2][0] = vr[2];
|
|
|
|
m[0][1] = vup[0];
|
|
m[1][1] = vup[1];
|
|
m[2][1] = vup[2];
|
|
|
|
m[0][2] = vf[0];
|
|
m[1][2] = vf[1];
|
|
m[2][2] = vf[2];
|
|
|
|
memcpy (im, m, sizeof (im));
|
|
|
|
im[0][1] = m[1][0];
|
|
im[0][2] = m[2][0];
|
|
im[1][0] = m[0][1];
|
|
im[1][2] = m[2][1];
|
|
im[2][0] = m[0][2];
|
|
im[2][1] = m[1][2];
|
|
|
|
memset (zrot, 0, sizeof (zrot));
|
|
zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;
|
|
|
|
zrot[0][0] = cos (DEG2RAD (degrees));
|
|
zrot[0][1] = sin (DEG2RAD (degrees));
|
|
zrot[1][0] = -sin (DEG2RAD (degrees));
|
|
zrot[1][1] = cos (DEG2RAD (degrees));
|
|
|
|
R_ConcatRotations (m, zrot, tmpmat);
|
|
R_ConcatRotations (tmpmat, im, rot);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
dst[i] = DotProduct (rot[i], point);
|
|
}
|
|
}
|
|
|
|
VISIBLE void
|
|
QuatMult (const quat_t q1, const quat_t q2, quat_t out)
|
|
{
|
|
vec_t s;
|
|
vec3_t v;
|
|
|
|
s = q1[3] * q2[3] - DotProduct (q1, q2);
|
|
CrossProduct (q1, q2, v);
|
|
VectorMultAdd (v, q1[3], q2, v);
|
|
VectorMultAdd (v, q2[3], q1, out);
|
|
out[3] = s;
|
|
}
|
|
|
|
VISIBLE void
|
|
QuatMultVec (const quat_t q, const vec3_t v, vec3_t out)
|
|
{
|
|
vec3_t tv;
|
|
vec_t dqv, dqq;
|
|
vec_t s;
|
|
|
|
s = q[3];
|
|
CrossProduct (q, v, tv);
|
|
dqv = DotProduct (q, v);
|
|
dqq = DotProduct (q, q);
|
|
VectorScale (tv, s, tv);
|
|
VectorMultAdd (tv, dqv, q, tv);
|
|
VectorScale (tv, 2, tv);
|
|
VectorMultAdd (tv, s * s - dqq, v, out);
|
|
}
|
|
|
|
VISIBLE void
|
|
QuatRotation(const vec3_t a, const vec3_t b, quat_t out)
|
|
{
|
|
vec_t ma, mb;
|
|
vec_t den, mba_mab;
|
|
vec3_t t;
|
|
|
|
ma = VectorLength(a);
|
|
mb = VectorLength(b);
|
|
den = 2 * ma * mb;
|
|
VectorScale (a, mb, t);
|
|
VectorMultAdd(t, ma, b, t);
|
|
mba_mab = VectorLength(t);
|
|
CrossProduct (a, b, t);
|
|
VectorScale(t, 1 / mba_mab, out);
|
|
out[3] = mba_mab / den;
|
|
}
|
|
|
|
VISIBLE void
|
|
QuatInverse (const quat_t in, quat_t out)
|
|
{
|
|
quat_t q;
|
|
vec_t m;
|
|
|
|
m = QDotProduct (in, in); // in * in*
|
|
QuatConj (in, q);
|
|
QuatScale (q, 1 / m, out);
|
|
}
|
|
|
|
VISIBLE void
|
|
QuatExp (const quat_t a, quat_t b)
|
|
{
|
|
vec3_t n;
|
|
vec_t th;
|
|
vec_t r;
|
|
vec_t c, s;
|
|
|
|
VectorCopy (a, n);
|
|
th = VectorNormalize (n);
|
|
r = expf (a[3]);
|
|
c = cosf (th);
|
|
s = sinf (th);
|
|
VectorScale (n, r * s, b);
|
|
b[3] = r * c;
|
|
}
|
|
|
|
VISIBLE void
|
|
QuatToMatrix (const quat_t q, vec_t *m, int homogenous, int vertical)
|
|
{
|
|
vec_t xx, xy, xz, xw, yy, yz, yw, zz, zw;
|
|
vec_t *_m[4] = {
|
|
m + (homogenous ? 0 : 0),
|
|
m + (homogenous ? 4 : 3),
|
|
m + (homogenous ? 8 : 6),
|
|
m + (homogenous ? 12 : 9),
|
|
};
|
|
|
|
xx = 2 * q[0] * q[0];
|
|
xy = 2 * q[0] * q[1];
|
|
xz = 2 * q[0] * q[2];
|
|
xw = 2 * q[0] * q[3];
|
|
|
|
yy = 2 * q[1] * q[1];
|
|
yz = 2 * q[1] * q[2];
|
|
yw = 2 * q[1] * q[3];
|
|
|
|
zz = 2 * q[2] * q[2];
|
|
zw = 2 * q[2] * q[3];
|
|
|
|
if (vertical) {
|
|
VectorSet (1.0f - yy - zz, xy + zw, xz - yw, _m[0]);
|
|
VectorSet (xy - zw, 1.0f - xx - zz, yz + xw, _m[1]);
|
|
VectorSet (xz + yw, yz - xw, 1.0f - xx - yy, _m[2]);
|
|
} else {
|
|
VectorSet (1.0f - yy - zz, xy - zw, xz + yw, _m[0]);
|
|
VectorSet (xy + zw, 1.0f - xx - zz, yz - xw, _m[1]);
|
|
VectorSet (xz - yw, yz + xw, 1.0f - xx - yy, _m[2]);
|
|
}
|
|
if (homogenous) {
|
|
_m[0][3] = 0;
|
|
_m[1][3] = 0;
|
|
_m[2][3] = 0;
|
|
VectorZero (_m[3]);
|
|
_m[3][3] = 1;
|
|
}
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__GNUC__)
|
|
# pragma optimize( "", on )
|
|
#endif
|
|
|
|
VISIBLE float
|
|
anglemod (float a)
|
|
{
|
|
a = (360.0 / 65536) * ((int) (a * (65536 / 360.0)) & 65535);
|
|
return a;
|
|
}
|
|
|
|
/*
|
|
BOPS_Error
|
|
|
|
Split out like this for ASM to call.
|
|
*/
|
|
void __attribute__ ((noreturn)) BOPS_Error (void);
|
|
VISIBLE void __attribute__ ((noreturn))
|
|
BOPS_Error (void)
|
|
{
|
|
Sys_Error ("BoxOnPlaneSide: Bad signbits");
|
|
}
|
|
|
|
#ifndef USE_INTEL_ASM
|
|
|
|
/*
|
|
BoxOnPlaneSide
|
|
|
|
Returns 1, 2, or 1 + 2
|
|
*/
|
|
VISIBLE int
|
|
BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const plane_t *p)
|
|
{
|
|
float dist1, dist2;
|
|
int sides;
|
|
|
|
#if 0
|
|
// this is done by the BOX_ON_PLANE_SIDE macro before
|
|
// calling this function
|
|
|
|
// fast axial cases
|
|
if (p->type < 3) {
|
|
if (p->dist <= emins[p->type])
|
|
return 1;
|
|
if (p->dist >= emaxs[p->type])
|
|
return 2;
|
|
return 3;
|
|
}
|
|
#endif
|
|
|
|
// general case
|
|
switch (p->signbits) {
|
|
case 0:
|
|
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emaxs[2];
|
|
dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emins[2];
|
|
break;
|
|
case 1:
|
|
dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emaxs[2];
|
|
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emins[2];
|
|
break;
|
|
case 2:
|
|
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emaxs[2];
|
|
dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emins[2];
|
|
break;
|
|
case 3:
|
|
dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emaxs[2];
|
|
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emins[2];
|
|
break;
|
|
case 4:
|
|
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emins[2];
|
|
dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emaxs[2];
|
|
break;
|
|
case 5:
|
|
dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emins[2];
|
|
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emaxs[2];
|
|
break;
|
|
case 6:
|
|
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emins[2];
|
|
dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emaxs[2];
|
|
break;
|
|
case 7:
|
|
dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
|
|
p->normal[2] * emins[2];
|
|
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
|
|
p->normal[2] * emaxs[2];
|
|
break;
|
|
default:
|
|
BOPS_Error ();
|
|
}
|
|
|
|
#if 0
|
|
int i;
|
|
vec3_t corners[2];
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (plane->normal[i] < 0) {
|
|
corners[0][i] = emins[i];
|
|
corners[1][i] = emaxs[i];
|
|
} else {
|
|
corners[1][i] = emins[i];
|
|
corners[0][i] = emaxs[i];
|
|
}
|
|
}
|
|
dist = DotProduct (plane->normal, corners[0]) - plane->dist;
|
|
dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
|
|
sides = 0;
|
|
if (dist1 >= 0)
|
|
sides = 1;
|
|
if (dist2 < 0)
|
|
sides |= 2;
|
|
#endif
|
|
|
|
sides = 0;
|
|
if (dist1 >= -p->dist)
|
|
sides = 1;
|
|
if (dist2 < -p->dist)
|
|
sides |= 2;
|
|
|
|
#ifdef PARANOID
|
|
if (sides == 0)
|
|
Sys_Error ("BoxOnPlaneSide: sides==0");
|
|
#endif
|
|
|
|
return sides;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
angles is a left handed system: 'pitch yaw roll' with x (pitch) axis to
|
|
the right, y (yaw) axis up and z (roll) axis forward. However, the
|
|
rotations themselves are right-handed in that they follow the right-hand
|
|
rule for the world axes: pitch around +y, yaw around +z, and roll around
|
|
+x.
|
|
|
|
This results in the entity frame having forward pointed along the world +x
|
|
axis, right along the world -y axis, and up along the world +z axis.
|
|
Whether this means the entity frame is left-handed depends on whether
|
|
forward is local X and right is local Y (left handed), or forward is local
|
|
Y and right is local X (right handed).
|
|
|
|
NOTE: the matrices in this coment are transposed so the vector names
|
|
can be written naturally.
|
|
|
|
pitch =
|
|
cp 0 -sp
|
|
0 1 0
|
|
sp 0 cp
|
|
|
|
yaw =
|
|
cy sy 0
|
|
-sy cy 0
|
|
0 0 1
|
|
|
|
roll =
|
|
1 0 0
|
|
0 cr sr
|
|
0 -sr cr
|
|
|
|
// NOTE: as these matrices are transposed, this is the reverse of the
|
|
// actual operation
|
|
final = roll * (pitch * yaw)
|
|
final =
|
|
[forward]
|
|
[-right] -ve due to left handed to right handed conversion
|
|
[up]
|
|
*/
|
|
VISIBLE void
|
|
AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
|
|
{
|
|
float angle, sr, sp, sy, cr, cp, cy;
|
|
|
|
angle = angles[YAW] * (M_PI * 2 / 360);
|
|
sy = sin (angle);
|
|
cy = cos (angle);
|
|
angle = angles[PITCH] * (M_PI * 2 / 360);
|
|
sp = sin (angle);
|
|
cp = cos (angle);
|
|
angle = angles[ROLL] * (M_PI * 2 / 360);
|
|
sr = sin (angle);
|
|
cr = cos (angle);
|
|
|
|
forward[0] = cp * cy;
|
|
forward[1] = cp * sy;
|
|
forward[2] = -sp;
|
|
// need to flip right because the trig produces +Y but right is -Y
|
|
right[0] = -(sr * sp * cy + cr * -sy);
|
|
right[1] = -(sr * sp * sy + cr * cy);
|
|
right[2] = -(sr * cp);
|
|
up[0] = (cr * sp * cy + -sr * -sy);
|
|
up[1] = (cr * sp * sy + -sr * cy);
|
|
up[2] = cr * cp;
|
|
}
|
|
|
|
VISIBLE void
|
|
AngleQuat (const vec3_t angles, quat_t q)
|
|
{
|
|
float alpha, sr, sp, sy, cr, cp, cy;
|
|
|
|
// alpha is half the angle
|
|
alpha = angles[YAW] * (M_PI / 360);
|
|
sy = sin (alpha);
|
|
cy = cos (alpha);
|
|
alpha = angles[PITCH] * (M_PI / 360);
|
|
sp = sin (alpha);
|
|
cp = cos (alpha);
|
|
alpha = angles[ROLL] * (M_PI / 360);
|
|
sr = sin (alpha);
|
|
cr = cos (alpha);
|
|
|
|
QuatSet (cy * cp * sr - sy * sp * cr, // x
|
|
cy * sp * cr + sy * cp * sr, // y
|
|
sy * cp * cr - cy * sp * sr, // z
|
|
cy * cp * cr + sy * sp * sr, // w
|
|
q);
|
|
}
|
|
|
|
VISIBLE int
|
|
_VectorCompare (const vec3_t v1, const vec3_t v2)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
if (fabs (v1[i] - v2[i]) > EQUAL_EPSILON)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
VISIBLE void
|
|
_VectorMA (const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc)
|
|
{
|
|
vecc[0] = veca[0] + scale * vecb[0];
|
|
vecc[1] = veca[1] + scale * vecb[1];
|
|
vecc[2] = veca[2] + scale * vecb[2];
|
|
}
|
|
|
|
VISIBLE vec_t
|
|
_DotProduct (const vec3_t v1, const vec3_t v2)
|
|
{
|
|
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
|
|
}
|
|
|
|
VISIBLE void
|
|
_VectorSubtract (const vec3_t veca, const vec3_t vecb, vec3_t out)
|
|
{
|
|
out[0] = veca[0] - vecb[0];
|
|
out[1] = veca[1] - vecb[1];
|
|
out[2] = veca[2] - vecb[2];
|
|
}
|
|
|
|
VISIBLE void
|
|
_VectorAdd (const vec3_t veca, const vec3_t vecb, vec3_t out)
|
|
{
|
|
out[0] = veca[0] + vecb[0];
|
|
out[1] = veca[1] + vecb[1];
|
|
out[2] = veca[2] + vecb[2];
|
|
}
|
|
|
|
VISIBLE void
|
|
_VectorCopy (const vec3_t in, vec3_t out)
|
|
{
|
|
out[0] = in[0];
|
|
out[1] = in[1];
|
|
out[2] = in[2];
|
|
}
|
|
|
|
VISIBLE void
|
|
CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross)
|
|
{
|
|
float v10 = v1[0];
|
|
float v11 = v1[1];
|
|
float v12 = v1[2];
|
|
float v20 = v2[0];
|
|
float v21 = v2[1];
|
|
float v22 = v2[2];
|
|
|
|
cross[0] = v11 * v22 - v12 * v21;
|
|
cross[1] = v12 * v20 - v10 * v22;
|
|
cross[2] = v10 * v21 - v11 * v20;
|
|
}
|
|
|
|
VISIBLE vec_t
|
|
_VectorLength (const vec3_t v)
|
|
{
|
|
float length;
|
|
|
|
length = sqrt (DotProduct (v, v));
|
|
return length;
|
|
}
|
|
|
|
VISIBLE vec_t
|
|
_VectorNormalize (vec3_t v)
|
|
{
|
|
int i;
|
|
double length;
|
|
|
|
length = 0;
|
|
for (i = 0; i < 3; i++)
|
|
length += v[i] * v[i];
|
|
length = sqrt (length);
|
|
if (length == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
v[i] /= length;
|
|
|
|
return length;
|
|
}
|
|
|
|
VISIBLE void
|
|
_VectorScale (const vec3_t in, vec_t scale, vec3_t out)
|
|
{
|
|
out[0] = in[0] * scale;
|
|
out[1] = in[1] * scale;
|
|
out[2] = in[2] * scale;
|
|
}
|
|
|
|
VISIBLE int
|
|
Q_log2 (int val)
|
|
{
|
|
int answer = 0;
|
|
|
|
while ((val >>= 1) != 0)
|
|
answer++;
|
|
return answer;
|
|
}
|
|
|
|
VISIBLE void
|
|
R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
|
|
{
|
|
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
|
|
in1[0][2] * in2[2][0];
|
|
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
|
|
in1[0][2] * in2[2][1];
|
|
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
|
|
in1[0][2] * in2[2][2];
|
|
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
|
|
in1[1][2] * in2[2][0];
|
|
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
|
|
in1[1][2] * in2[2][1];
|
|
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
|
|
in1[1][2] * in2[2][2];
|
|
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
|
|
in1[2][2] * in2[2][0];
|
|
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
|
|
in1[2][2] * in2[2][1];
|
|
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
|
|
in1[2][2] * in2[2][2];
|
|
}
|
|
|
|
VISIBLE void
|
|
R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
|
|
{
|
|
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
|
|
in1[0][2] * in2[2][0];
|
|
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
|
|
in1[0][2] * in2[2][1];
|
|
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
|
|
in1[0][2] * in2[2][2];
|
|
out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
|
|
in1[0][2] * in2[2][3] + in1[0][3];
|
|
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
|
|
in1[1][2] * in2[2][0];
|
|
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
|
|
in1[1][2] * in2[2][1];
|
|
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
|
|
in1[1][2] * in2[2][2];
|
|
out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
|
|
in1[1][2] * in2[2][3] + in1[1][3];
|
|
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
|
|
in1[2][2] * in2[2][0];
|
|
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
|
|
in1[2][2] * in2[2][1];
|
|
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
|
|
in1[2][2] * in2[2][2];
|
|
out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
|
|
in1[2][2] * in2[2][3] + in1[2][3];
|
|
}
|
|
|
|
/*
|
|
FloorDivMod
|
|
|
|
Returns mathematically correct (floor-based) quotient and remainder for
|
|
numer and denom, both of which should contain no fractional part. The
|
|
quotient must fit in 32 bits.
|
|
*/
|
|
VISIBLE void
|
|
FloorDivMod (double numer, double denom, int *quotient, int *rem)
|
|
{
|
|
double x;
|
|
int q, r;
|
|
|
|
#ifndef PARANOID
|
|
if (denom <= 0.0)
|
|
Sys_Error ("FloorDivMod: bad denominator %f", denom);
|
|
|
|
// if ((floor(numer) != numer) || (floor(denom) != denom))
|
|
// Sys_Error ("FloorDivMod: non-integer numer or denom %f %f",
|
|
// numer, denom);
|
|
#endif
|
|
|
|
if (numer >= 0.0) {
|
|
x = floor (numer / denom);
|
|
q = (int) x;
|
|
r = (int) floor (numer - (x * denom));
|
|
} else {
|
|
// perform operations with positive values, and fix mod to make
|
|
// floor-based
|
|
x = floor (-numer / denom);
|
|
q = -(int) x;
|
|
r = (int) floor (-numer - (x * denom));
|
|
if (r != 0) {
|
|
q--;
|
|
r = (int) denom - r;
|
|
}
|
|
}
|
|
|
|
*quotient = q;
|
|
*rem = r;
|
|
}
|
|
|
|
VISIBLE int
|
|
GreatestCommonDivisor (int i1, int i2)
|
|
{
|
|
if (i1 > i2) {
|
|
if (i2 == 0)
|
|
return (i1);
|
|
return GreatestCommonDivisor (i2, i1 % i2);
|
|
} else {
|
|
if (i1 == 0)
|
|
return (i2);
|
|
return GreatestCommonDivisor (i1, i2 % i1);
|
|
}
|
|
}
|
|
|
|
#ifndef USE_INTEL_ASM
|
|
/*
|
|
Invert24To16
|
|
|
|
Inverts an 8.24 value to a 16.16 value
|
|
*/
|
|
VISIBLE fixed16_t
|
|
Invert24To16 (fixed16_t val)
|
|
{
|
|
if (val < 256)
|
|
return (0xFFFFFFFF);
|
|
|
|
return (fixed16_t)
|
|
(((double) 0x10000 * (double) 0x1000000 / (double) val) + 0.5);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
Mat3Init (const quat_t rot, const vec3_t scale, mat3_t mat)
|
|
{
|
|
QuatToMatrix (rot, mat, 0, 1);
|
|
VectorScale (mat + 0, scale[0], mat + 0);
|
|
VectorScale (mat + 3, scale[1], mat + 3);
|
|
VectorScale (mat + 6, scale[2], mat + 6);
|
|
}
|
|
|
|
void
|
|
Mat3Transpose (const mat3_t a, mat3_t b)
|
|
{
|
|
vec_t t;
|
|
int i, j;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
b[i * 3 + i] = a[i * 3 + i]; // in case b != a
|
|
for (j = i + 1; j < 3; j++) {
|
|
t = a[i * 3 + j]; // in case b == a
|
|
b[i * 3 + j] = a[j * 3 + i];
|
|
b[j * 3 + i] = t;
|
|
}
|
|
}
|
|
b[i * 3 + i] = a[i * 3 + i]; // in case b != a
|
|
}
|
|
|
|
vec_t
|
|
Mat3Determinant (const mat3_t m)
|
|
{
|
|
vec3_t t;
|
|
CrossProduct (m + 3, m + 6, t);
|
|
return DotProduct (m + 0, t);
|
|
}
|
|
|
|
typedef vec_t mat2_t[2 * 2];
|
|
|
|
static void
|
|
Mat3Sub2 (const mat3_t m3, mat2_t m2, int i, int j)
|
|
{
|
|
int si, sj, di, dj;
|
|
|
|
for (di = 0; di < 2; di++) {
|
|
for (dj = 0; dj < 2; dj++) {
|
|
si = di + ((di >= i) ? 1 : 0);
|
|
sj = dj + ((dj >= j) ? 1 : 0);
|
|
m2[di * 2 + dj] = m3[si * 3 + sj];
|
|
}
|
|
}
|
|
}
|
|
|
|
static vec_t
|
|
Mat2Det (const mat2_t m)
|
|
{
|
|
return m[0] * m[3] - m[1] * m[2];
|
|
}
|
|
|
|
int
|
|
Mat3Inverse (const mat3_t a, mat3_t b)
|
|
{
|
|
mat3_t tb;
|
|
mat2_t m2;
|
|
vec_t *m = b;
|
|
int i, j;
|
|
vec_t det;
|
|
vec_t sign[2] = { 1, -1};
|
|
|
|
det = Mat3Determinant (a);
|
|
if (det * det < 1e-6) {
|
|
Mat3Identity (b);
|
|
return 0;
|
|
}
|
|
if (b == a)
|
|
m = tb;
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
Mat3Sub2 (a, m2, i, j);
|
|
m[j * 3 + i] = sign[(i + j) & 1] * Mat2Det (m2) / det;
|
|
}
|
|
}
|
|
if (m != b)
|
|
Mat3Copy (m, b);
|
|
return 1;
|
|
}
|
|
|
|
void Mat3Mult (const mat3_t a, const mat3_t b, mat3_t c)
|
|
{
|
|
mat3_t ta, tb; // in case c == b or c == a
|
|
int i, j, k;
|
|
|
|
Mat3Transpose (a, ta); // transpose so we can use dot
|
|
Mat3Copy (b, tb);
|
|
|
|
k = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
c[k++] = DotProduct (ta + 3 * j, tb + 3 * i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mat3MultVec (const mat3_t a, const vec3_t b, vec3_t c)
|
|
{
|
|
int i;
|
|
vec3_t tb;
|
|
|
|
VectorCopy (b, tb);
|
|
for (i = 0; i < 3; i++)
|
|
c[i] = a[i + 0] * tb[0] + a[i + 3] * b[1] + a[i + 6] * b[2];
|
|
}
|
|
|
|
#define sqr(x) ((x) * (x))
|
|
void Mat3SymEigen (const mat3_t m, vec3_t e)
|
|
{
|
|
vec_t p, q, r;
|
|
vec_t phi;
|
|
mat3_t B;
|
|
|
|
p = sqr (m[1]) + sqr (m[2]) + sqr (m[5]);
|
|
if (p < 1e-6) {
|
|
e[0] = m[0];
|
|
e[1] = m[4];
|
|
e[2] = m[8];
|
|
return;
|
|
}
|
|
q = Mat3Trace (m) / 3;
|
|
p = sqr (m[0] - q) + sqr (m[4] - q) + sqr (m[8] - q) + 2 * p;
|
|
p = sqrt (p);
|
|
Mat3Zero (B);
|
|
B[0] = B[4] = B[8] = q;
|
|
Mat3Subtract (m, B, B);
|
|
Mat3Scale (B, 1.0 / p, B);
|
|
r = Mat3Determinant (B) / 2;
|
|
if (r >= 1)
|
|
phi = 0;
|
|
else if (r <= -1)
|
|
phi = M_PI / 3;
|
|
else
|
|
phi = acos (r) / 3;
|
|
|
|
e[0] = q + 2 * p * cos (phi);
|
|
e[2] = q + 2 * p * cos (phi + M_PI * 2 / 3);
|
|
e[1] = 3 * q - e[0] - e[2];
|
|
}
|
|
|
|
void
|
|
Mat4Init (const quat_t rot, const vec3_t scale, const vec3_t trans, mat4_t mat)
|
|
{
|
|
QuatToMatrix (rot, mat, 1, 1);
|
|
VectorScale (mat + 0, scale[0], mat + 0);
|
|
VectorScale (mat + 4, scale[1], mat + 4);
|
|
VectorScale (mat + 8, scale[2], mat + 8);
|
|
VectorCopy (trans, mat + 12);
|
|
}
|
|
|
|
void
|
|
Mat4Transpose (const mat4_t a, mat4_t b)
|
|
{
|
|
vec_t t;
|
|
int i, j;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
b[i * 4 + i] = a[i * 4 + i]; // in case b != a
|
|
for (j = i + 1; j < 4; j++) {
|
|
t = a[i * 4 + j]; // in case b == a
|
|
b[i * 4 + j] = a[j * 4 + i];
|
|
b[j * 4 + i] = t;
|
|
}
|
|
}
|
|
b[i * 4 + i] = a[i * 4 + i]; // in case b != a
|
|
}
|
|
|
|
static void
|
|
Mat4Sub3 (const mat4_t m4, mat3_t m3, int i, int j)
|
|
{
|
|
int si, sj, di, dj;
|
|
|
|
for (di = 0; di < 3; di++) {
|
|
for (dj = 0; dj < 3; dj++) {
|
|
si = di + ((di >= i) ? 1 : 0);
|
|
sj = dj + ((dj >= j) ? 1 : 0);
|
|
m3[di * 3 + dj] = m4[si * 4 + sj];
|
|
}
|
|
}
|
|
}
|
|
|
|
static vec_t
|
|
Mat4Det (const mat4_t m)
|
|
{
|
|
mat3_t t;
|
|
int i;
|
|
vec_t res = 0, det, s = 1;
|
|
|
|
for (i = 0; i < 4; i++, s = -s) {
|
|
Mat4Sub3 (m, t, 0, i);
|
|
det = Mat3Determinant (t);
|
|
res += m[i] * det * s;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int
|
|
Mat4Inverse (const mat4_t a, mat4_t b)
|
|
{
|
|
mat4_t tb;
|
|
mat3_t m3;
|
|
vec_t *m = b;
|
|
int i, j;
|
|
vec_t det;
|
|
vec_t sign[2] = { 1, -1};
|
|
|
|
det = Mat4Det (a);
|
|
if (det * det < 1e-6) {
|
|
Mat4Identity (b);
|
|
return 0;
|
|
}
|
|
if (b == a)
|
|
m = tb;
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
Mat4Sub3 (a, m3, i, j);
|
|
m[j * 4 + i] = sign[(i + j) & 1] * Mat3Determinant (m3) / det;
|
|
}
|
|
}
|
|
if (m != b)
|
|
Mat4Copy (m, b);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
Mat4Mult (const mat4_t a, const mat4_t b, mat4_t c)
|
|
{
|
|
mat4_t ta, tb; // in case c == b or c == a
|
|
int i, j, k;
|
|
|
|
Mat4Transpose (a, ta); // transpose so we can use dot
|
|
Mat4Copy (b, tb);
|
|
|
|
k = 0;
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
c[k++] = QDotProduct (ta + 4 * j, tb + 4 * i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Mat4MultVec (const mat4_t a, const vec3_t b, vec3_t c)
|
|
{
|
|
int i;
|
|
vec3_t tb;
|
|
|
|
VectorCopy (b, tb);
|
|
for (i = 0; i < 3; i++)
|
|
c[i] = a[i + 0] * tb[0] + a[i + 4] * b[1] + a[i + 8] * b[2] + a[i +12];
|
|
}
|
|
|
|
void
|
|
Mat4as3MultVec (const mat4_t a, const vec3_t b, vec3_t c)
|
|
{
|
|
int i;
|
|
vec3_t tb;
|
|
|
|
VectorCopy (b, tb);
|
|
for (i = 0; i < 3; i++)
|
|
c[i] = a[i + 0] * tb[0] + a[i + 4] * b[1] + a[i + 8] * b[2];
|
|
}
|
|
|
|
int
|
|
Mat3Decompose (const mat3_t mat, quat_t rot, vec3_t shear, vec3_t scale)
|
|
{
|
|
vec3_t row[3], shr, scl;
|
|
vec_t l, t;
|
|
int i, j;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
for (j = 0; j < 3; j++)
|
|
row[j][i] = mat[i * 3 + j];
|
|
|
|
l = DotProduct (row[0], row[0]);
|
|
if (l < 1e-5)
|
|
return 0;
|
|
scl[0] = sqrt (l);
|
|
VectorScale (row[0], 1/scl[0], row[0]);
|
|
shr[0] = DotProduct (row[0], row[1]);
|
|
|
|
VectorMultSub (row[1], shr[0], row[0], row[1]);
|
|
l = DotProduct (row[1], row[1]);
|
|
if (l < 1e-5)
|
|
return 0;
|
|
scl[1] = sqrt (l);
|
|
shr[0] /= scl[1];
|
|
VectorScale (row[1], 1/scl[1], row[1]);
|
|
shr[1] = DotProduct (row[0], row[2]);
|
|
|
|
VectorMultSub (row[2], shr[1], row[0], row[2]);
|
|
shr[2] = DotProduct (row[1], row[2]);
|
|
VectorMultSub (row[2], shr[2], row[1], row[2]);
|
|
l = DotProduct (row[2], row[2]);
|
|
if (l < 1e-5)
|
|
return 0;
|
|
scl[2] = sqrt (l);
|
|
shr[1] /= scl[2];
|
|
shr[2] /= scl[2];
|
|
VectorScale (row[2], 1/scl[2], row[2]);
|
|
if (scale)
|
|
VectorCopy (scl, scale);
|
|
if (shear)
|
|
VectorCopy (shr, shear);
|
|
if (!rot)
|
|
return 1;
|
|
|
|
t = 1 + row[0][0] + row[1][1] + row[2][2];
|
|
if (t >= 1e-5) {
|
|
vec_t s = sqrt (t) * 2;
|
|
rot[0] = (row[2][1] - row[1][2]) / s;
|
|
rot[1] = (row[0][2] - row[2][0]) / s;
|
|
rot[2] = (row[1][0] - row[0][1]) / s;
|
|
rot[3] = s / 4;
|
|
} else {
|
|
if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
|
|
vec_t s = sqrt (1 + row[0][0] - row[1][1] - row[2][2]) * 2;
|
|
rot[0] = s / 4;
|
|
rot[1] = (row[1][0] + row[0][1]) / s;
|
|
rot[2] = (row[0][2] + row[2][0]) / s;
|
|
rot[3] = (row[2][1] - row[1][2]) / s;
|
|
} else if (row[1][1] > row[2][2]) {
|
|
vec_t s = sqrt (1 + row[1][1] - row[0][0] - row[2][2]) * 2;
|
|
rot[0] = (row[1][0] + row[0][1]) / s;
|
|
rot[1] = s / 4;
|
|
rot[2] = (row[2][1] + row[1][2]) / s;
|
|
rot[3] = (row[0][2] - row[2][0]) / s;
|
|
} else {
|
|
vec_t s = sqrt (1 + row[2][2] - row[0][0] - row[1][1]) * 2;
|
|
rot[0] = (row[0][2] + row[2][0]) / s;
|
|
rot[1] = (row[2][1] + row[1][2]) / s;
|
|
rot[2] = s / 4;
|
|
rot[3] = (row[1][0] - row[0][1]) / s;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
Mat4Decompose (const mat4_t mat, quat_t rot, vec3_t shear, vec3_t scale,
|
|
vec3_t trans)
|
|
{
|
|
mat3_t m3;
|
|
|
|
if (trans)
|
|
VectorCopy (mat + 12, trans);
|
|
Mat4toMat3 (mat, m3);
|
|
return Mat3Decompose (m3, rot, shear, scale);
|
|
}
|
|
|
|
void
|
|
BarycentricCoords (const vec_t **points, int num_points, const vec3_t p,
|
|
vec_t *lambda)
|
|
{
|
|
vec3_t a, b, c, x, ab, bc, ca, n;
|
|
vec_t div;
|
|
if (num_points > 4)
|
|
Sys_Error ("Don't know how to compute the barycentric coordinates "
|
|
"for %d points", num_points);
|
|
switch (num_points) {
|
|
case 1:
|
|
lambda[0] = 1;
|
|
return;
|
|
case 2:
|
|
VectorSubtract (p, points[0], x);
|
|
VectorSubtract (points[1], points[0], a);
|
|
lambda[1] = DotProduct (x, a) / DotProduct (a, a);
|
|
lambda[0] = 1 - lambda[1];
|
|
return;
|
|
case 3:
|
|
VectorSubtract (p, points[0], x);
|
|
VectorSubtract (points[1], points[0], a);
|
|
VectorSubtract (points[2], points[0], b);
|
|
CrossProduct (a, b, ab);
|
|
div = DotProduct (ab, ab);
|
|
CrossProduct (x, b, n);
|
|
lambda[1] = DotProduct (n, ab);
|
|
CrossProduct (a, x, n);
|
|
lambda[2] = DotProduct (n, ab);
|
|
lambda[0] = div - lambda[1] - lambda[2];
|
|
VectorScale (lambda, 1 / div, lambda);
|
|
return;
|
|
case 4:
|
|
VectorSubtract (p, points[0], x);
|
|
VectorSubtract (points[1], points[0], a);
|
|
VectorSubtract (points[2], points[0], b);
|
|
VectorSubtract (points[3], points[0], c);
|
|
CrossProduct (a, b, ab);
|
|
CrossProduct (b, c, bc);
|
|
CrossProduct (c, a, ca);
|
|
div = DotProduct (a, bc);
|
|
lambda[1] = DotProduct (x, bc) / div;
|
|
lambda[2] = DotProduct (x, ca) / div;
|
|
lambda[3] = DotProduct (x, ab) / div;
|
|
lambda[0] = 1 - lambda[1] - lambda[2] - lambda[3];
|
|
return;
|
|
}
|
|
Sys_Error ("Not enough points to project or enclose the point");
|
|
}
|
|
|
|
static int
|
|
circum_circle (const vec_t **points, int num_points, sphere_t *sphere)
|
|
{
|
|
vec3_t a, c, b;
|
|
vec3_t bc, ca, ab;
|
|
vec_t aa, bb, cc;
|
|
vec_t div;
|
|
vec_t alpha, beta, gamma;
|
|
|
|
switch (num_points) {
|
|
case 1:
|
|
VectorCopy (points[0], sphere->center);
|
|
return 1;
|
|
case 2:
|
|
VectorBlend (points[0], points[1], 0.5, sphere->center);
|
|
return 1;
|
|
case 3:
|
|
VectorSubtract (points[0], points[1], a);
|
|
VectorSubtract (points[0], points[2], b);
|
|
VectorSubtract (points[1], points[2], c);
|
|
aa = DotProduct (a, a);
|
|
bb = DotProduct (b, b);
|
|
cc = DotProduct (c, c);
|
|
div = DotProduct (a, c);
|
|
div = 2 * (aa * cc - div * div);
|
|
if (fabs (div) < EQUAL_EPSILON) {
|
|
// degenerate
|
|
return 0;
|
|
}
|
|
alpha = cc * DotProduct (a, b) / div;
|
|
beta = -bb * DotProduct (a, c) / div;
|
|
gamma = aa * DotProduct (b, c) / div;
|
|
VectorScale (points[0], alpha, sphere->center);
|
|
VectorMultAdd (sphere->center, beta, points[1], sphere->center);
|
|
VectorMultAdd (sphere->center, gamma, points[2], sphere->center);
|
|
return 1;
|
|
case 4:
|
|
VectorSubtract (points[1], points[0], a);
|
|
VectorSubtract (points[2], points[0], b);
|
|
VectorSubtract (points[3], points[0], c);
|
|
CrossProduct (b, c, bc);
|
|
CrossProduct (c, a, ca);
|
|
CrossProduct (a, b, ab);
|
|
div = 2 * DotProduct (a, bc);
|
|
if (fabs (div) < EQUAL_EPSILON) {
|
|
// degenerate
|
|
return 0;
|
|
}
|
|
aa = DotProduct (a, a) / div;
|
|
bb = DotProduct (b, b) / div;
|
|
cc = DotProduct (c, c) / div;
|
|
VectorScale (bc, aa, sphere->center);
|
|
VectorMultAdd (sphere->center, bb, ca, sphere->center);
|
|
VectorMultAdd (sphere->center, cc, ab, sphere->center);
|
|
VectorAdd (sphere->center, points[0], sphere->center);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
CircumSphere (const vec3_t points[], int num_points, sphere_t *sphere)
|
|
{
|
|
const vec_t *p[] = {points[0], points[1], points[2], points[3]};
|
|
|
|
if (num_points > 4)
|
|
return 0;
|
|
sphere->radius = 0;
|
|
if (num_points) {
|
|
if (circum_circle (p, num_points, sphere)) {
|
|
if (num_points > 1)
|
|
sphere->radius = VectorDistance (sphere->center, points[0]);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
VectorZero (sphere->center);
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
closest_affine_point (const vec_t **points, int num_points, const vec3_t x,
|
|
vec3_t closest)
|
|
{
|
|
vec3_t a, b, n, d;
|
|
vec_t l;
|
|
|
|
switch (num_points) {
|
|
default:
|
|
case 1:
|
|
VectorCopy (points[0], closest);
|
|
break;
|
|
case 2:
|
|
VectorSubtract (points[1], points[0], n);
|
|
VectorSubtract (x, points[0], d);
|
|
l = DotProduct (d, n) / DotProduct (n, n);
|
|
VectorMultAdd (points[0], l, n, closest);
|
|
break;
|
|
case 3:
|
|
VectorSubtract (points[1], points[0], a);
|
|
VectorSubtract (points[2], points[0], b);
|
|
CrossProduct (a, b, n);
|
|
VectorSubtract (points[0], x, d);
|
|
l = DotProduct (d, n) / DotProduct (n, n);
|
|
VectorMultAdd (x, l, n, closest);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
test_support_points(const vec_t **points, int *num_points, const vec3_t center)
|
|
{
|
|
int in_affine = 0;
|
|
int in_convex = 0;
|
|
vec3_t v, d, n, a, b;
|
|
vec_t nn, dd, vv, dn;
|
|
|
|
switch (*num_points) {
|
|
case 1:
|
|
in_affine = VectorCompare (points[0], center);
|
|
// the convex hull and affine hull for a single point are the same
|
|
in_convex = in_affine;
|
|
break;
|
|
case 2:
|
|
VectorSubtract (points[1], points[0], v);
|
|
VectorAdd (points[0], points[1], d);
|
|
VectorScale (d, 0.5, d);
|
|
VectorSubtract (center, d, d);
|
|
CrossProduct (v, d, n);
|
|
nn = DotProduct (n, n);
|
|
vv = DotProduct (v, v);
|
|
in_affine = nn < 1e-5 * vv * vv;
|
|
break;
|
|
case 3:
|
|
VectorSubtract (points[1], points[0], a);
|
|
VectorSubtract (points[2], points[0], b);
|
|
VectorSubtract (center, points[0], d);
|
|
CrossProduct (a, b, n);
|
|
dn = DotProduct (d, n);
|
|
dd = DotProduct (d, d);
|
|
nn = DotProduct (n, n);
|
|
in_affine = dn * dn < 1e-5 * dd * nn;
|
|
break;
|
|
case 4:
|
|
in_affine = 1;
|
|
break;
|
|
default:
|
|
Sys_Error ("Invalid number of points (%d) in test_support_points",
|
|
*num_points);
|
|
}
|
|
|
|
// if in_convex is not true while in_affine is, then need to test as
|
|
// there is more than one dimension for the affine hull (a single support
|
|
// point is never dropped as it cannot be redundant)
|
|
if (in_affine && !in_convex) {
|
|
vec_t lambda[4];
|
|
int dropped = 0;
|
|
int count = *num_points;
|
|
|
|
BarycentricCoords (points, count, center, lambda);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
points[i - dropped] = points[i];
|
|
if (lambda[i] < -1e-4) {
|
|
dropped++;
|
|
(*num_points)--;
|
|
}
|
|
}
|
|
in_convex = !dropped;
|
|
if (dropped) {
|
|
for (int i = count - dropped; i < count; i++) {
|
|
points[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
return in_convex;
|
|
}
|
|
|
|
sphere_t
|
|
SmallestEnclosingBall (const vec3_t points[], int num_points)
|
|
{
|
|
set_t was_support = SET_STATIC_INIT (num_points, alloca);
|
|
sphere_t sphere;
|
|
const vec_t *best;
|
|
const vec_t *support[4];
|
|
int num_support;
|
|
vec_t dist, best_dist;
|
|
int i;
|
|
int best_i = 0;
|
|
int iters = 0;
|
|
|
|
if (num_points < 1) {
|
|
VectorZero (sphere.center);
|
|
sphere.radius = 0;
|
|
return sphere;
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
support[i] = 0;
|
|
set_empty (&was_support);
|
|
|
|
VectorCopy (points[0], sphere.center);
|
|
best_dist = dist = 0;
|
|
best = points[0];
|
|
for (i = 1; i < num_points; i++) {
|
|
dist = VectorDistance_fast (points[i], sphere.center);
|
|
if (dist > best_dist) {
|
|
best_dist = dist;
|
|
best_i = i;
|
|
best = points[i];
|
|
}
|
|
}
|
|
num_support = 1;
|
|
support[0] = best;
|
|
sphere.radius = best_dist; // note: radius squared until the end
|
|
set_add (&was_support, best_i);
|
|
|
|
while (!test_support_points (support, &num_support, sphere.center)) {
|
|
vec3_t affine, v, p, r;
|
|
vec_t x, best_x = 0, rr, pv, pp;
|
|
int i;
|
|
|
|
if (iters++ > 2 * num_points)
|
|
Sys_Error ("stuck SEB");
|
|
|
|
closest_affine_point (support, num_support, sphere.center, affine);
|
|
VectorSubtract (support[0], affine, r);
|
|
rr = DotProduct (r, r);
|
|
VectorSubtract (sphere.center, affine, v);
|
|
|
|
best = 0;
|
|
for (i = 0; i < num_points; i++) {
|
|
if (SET_TEST_MEMBER (&was_support, i)) {
|
|
continue;
|
|
}
|
|
VectorSubtract (points[i], affine, p);
|
|
pp = DotProduct (p, p);
|
|
pv = DotProduct (p, v);
|
|
if (pp <= rr || pv <= 0 || pv * pv < 1e-6 * rr) {
|
|
continue;
|
|
}
|
|
x = (pp - rr) / (2 * pv);
|
|
if (x > best_x) {
|
|
best = points[i];
|
|
best_i = i;
|
|
best_x = x;
|
|
}
|
|
}
|
|
VectorMultAdd (affine, best_x, v, sphere.center);
|
|
sphere.radius = VectorDistance_fast (sphere.center, support[0]);
|
|
if (best) {
|
|
support[num_support++] = best;
|
|
set_add (&was_support, best_i);
|
|
}
|
|
}
|
|
best_dist = 0;
|
|
for (i = 0; i < num_points; i++) {
|
|
dist = VectorDistance_fast (sphere.center, points[i]);
|
|
if (dist > best_dist)
|
|
best_dist = dist;
|
|
}
|
|
sphere.radius = sqrt (best_dist);
|
|
return sphere;
|
|
}
|