mirror of
https://github.com/kevinbentley/Descent3.git
synced 2026-04-05 05:00:06 -04:00
1344 lines
32 KiB
C
1344 lines
32 KiB
C
#ifndef OSIRIS_VECTOR_H
|
|
#define OSIRIS_VECTOR_H
|
|
|
|
#include <math.h>
|
|
#include <time.h>
|
|
|
|
#include "../../Main/lib/vecmat_external.h"
|
|
|
|
const vector Zero_vector = {0.0f, 0.0f, 0.0f};
|
|
|
|
// Disable the "possible loss of data" warning
|
|
#pragma warning (disable:4244)
|
|
|
|
//Angles are unsigned shorts
|
|
typedef unsigned short angle;
|
|
|
|
//The basic fixed-point type
|
|
typedef long fix;
|
|
|
|
#define PI 3.141592654
|
|
|
|
//Constants for converted between fix and float
|
|
#define FLOAT_SCALER 65536.0
|
|
#define FIX_SHIFT 16
|
|
|
|
//1.0 in fixed-point
|
|
#define F1_0 (1 << FIX_SHIFT)
|
|
|
|
//Generate the data for the trig tables. Must be called before trig functions
|
|
void InitMathTables ();
|
|
|
|
//Returns the sine of the given angle. Linearly interpolates between two entries in a 256-entry table
|
|
float FixSin(angle a);
|
|
|
|
//Returns the cosine of the given angle. Linearly interpolates between two entries in a 256-entry table
|
|
float FixCos(angle a);
|
|
|
|
//Returns the sine of the given angle, but does no interpolation
|
|
float FixSinFast(angle a);
|
|
|
|
//Returns the cosine of the given angle, but does no interpolation
|
|
float FixCosFast(angle a);
|
|
|
|
#define Round(x) ((int) (x + 0.5))
|
|
|
|
fix FloatToFixFast(float num);
|
|
|
|
//Conversion macros
|
|
//??#define FloatToFix(num) Round((num) * FLOAT_SCALER)
|
|
#define FloatToFix(num) ((fix)((num)*FLOAT_SCALER))
|
|
#define IntToFix(num) ((num) << FIX_SHIFT)
|
|
#define ShortToFix(num) (((long) (num)) << FIX_SHIFT)
|
|
#define FixToFloat(num) (((float) (num)) / FLOAT_SCALER)
|
|
#define FixToInt(num) ((num) >> FIX_SHIFT)
|
|
#define FixToShort(num) ((short) ((num) >> FIX_SHIFT))
|
|
|
|
/* --NOT NEEDED -JEFF
|
|
//Fixed-point math functions in inline ASM form
|
|
#if defined(MACINTOSH)
|
|
#include "fixmac.h"
|
|
#elif defined(WIN32)
|
|
#include "..\..\main\win\fixwin32.h"
|
|
#endif
|
|
*/
|
|
|
|
//Tables for trig functions
|
|
float sincos_table[321]; //256 entries + 64 sin-only + 1 for interpolation
|
|
angle asin_table[257]; // 1 quadrants worth, +1 for interpolation
|
|
angle acos_table[257];
|
|
|
|
//Generate the data for the trig tables
|
|
void InitMathTables ()
|
|
{
|
|
int i;
|
|
float rad,s,c;
|
|
|
|
for (i=0;i<321;i++) {
|
|
rad = (float) ((double) i / 256.0 * 2 * PI);
|
|
sincos_table[i] = (float) sin(rad);
|
|
}
|
|
|
|
for (i=0;i<256;i++) {
|
|
|
|
s=asin((float)i/256.0);
|
|
c=acos((float)i/256.0);
|
|
|
|
s=(s/(PI*2));
|
|
c=(c/(PI*2));
|
|
|
|
asin_table[i] = FloatToFix(s);
|
|
acos_table[i] = FloatToFix(c);
|
|
}
|
|
|
|
asin_table[256]=asin_table[255];
|
|
acos_table[256]=acos_table[255];
|
|
|
|
// Initialize a random seed.
|
|
srand(time(NULL));
|
|
}
|
|
|
|
//Returns the sine of the given angle. Linearly interpolates between two entries in a 256-entry table
|
|
float FixSin(angle a)
|
|
{
|
|
int i,f;
|
|
float s0,s1;
|
|
|
|
i = (a>>8)&0xff;
|
|
f = a&0xff;
|
|
|
|
s0 = sincos_table[i];
|
|
s1 = sincos_table[i+1];
|
|
return (float) (s0 + ((s1 - s0) * (double) f / 256.0));
|
|
}
|
|
|
|
//Returns the cosine of the given angle. Linearly interpolates between two entries in a 256-entry table
|
|
float FixCos(angle a)
|
|
{
|
|
int i,f;
|
|
float c0,c1;
|
|
|
|
i = (a>>8)&0xff;
|
|
f = a&0xff;
|
|
|
|
c0 = sincos_table[i+64];
|
|
c1 = sincos_table[i+64+1];
|
|
return (float) (c0 + ((c1 - c0) * (double) f / 256.0));
|
|
}
|
|
|
|
//Returns the sine of the given angle, but does no interpolation
|
|
float FixSinFast(angle a)
|
|
{
|
|
int i;
|
|
|
|
i = ((a+0x80)>>8)&0xff;
|
|
|
|
return sincos_table[i];
|
|
}
|
|
|
|
//Returns the cosine of the given angle, but does no interpolation
|
|
float FixCosFast(angle a)
|
|
{
|
|
int i;
|
|
|
|
i = ((a+0x80)>>8)&0xff;
|
|
|
|
return sincos_table[i+64];
|
|
}
|
|
|
|
// use this instead of:
|
|
// for: (int)floor(x+0.5f) use FloatRound(x)
|
|
// (int)ceil(x-0.5f) use FloatRound(x)
|
|
// (int)floor(x-0.5f) use FloatRound(x-1.0f)
|
|
// (int)floor(x) use FloatRound(x-0.5f)
|
|
// for values in the range -2048 to 2048
|
|
|
|
// Set a vector to {0,0,0}
|
|
int FloatRound( float x )
|
|
{
|
|
float nf;
|
|
nf = x + 8390656.0f;
|
|
return ((*((int *)&nf)) & 0x7FFFFF)-2048;
|
|
}
|
|
|
|
// A fast way to convert floats to fix
|
|
fix FloatToFixFast( float x )
|
|
{
|
|
|
|
float nf;
|
|
nf = x*65536.0f + 8390656.0f;
|
|
return ((*((int *)&nf)) & 0x7FFFFF)-2048;
|
|
}
|
|
|
|
//Get rid of the "no return value" warnings in the next three functions
|
|
#pragma warning (disable:4035)
|
|
|
|
//compute inverse sine
|
|
angle FixAsin(float v)
|
|
{
|
|
fix vv;
|
|
int i,f,aa;
|
|
|
|
vv = FloatToFix(fabs(v));
|
|
|
|
if (vv >= F1_0) //check for out of range
|
|
return 0x4000;
|
|
|
|
i = (vv>>8)&0xff;
|
|
f = vv&0xff;
|
|
|
|
aa = asin_table[i];
|
|
aa = aa + (((asin_table[i+1] - aa) * f)>>8);
|
|
|
|
if (v < 0)
|
|
aa = F1_0-aa;
|
|
|
|
return aa;
|
|
}
|
|
|
|
//compute inverse cosine
|
|
angle FixAcos(float v)
|
|
{
|
|
fix vv;
|
|
int i,f,aa;
|
|
|
|
vv = FloatToFix(fabs(v));
|
|
|
|
if (vv >= F1_0) //check for out of range
|
|
return 0;
|
|
|
|
i = (vv>>8)&0xff;
|
|
f = vv&0xff;
|
|
|
|
aa = acos_table[i];
|
|
aa = aa + (((acos_table[i+1] - aa) * f)>>8);
|
|
|
|
if (v < 0)
|
|
aa = 0x8000 - aa;
|
|
|
|
return aa;
|
|
}
|
|
|
|
//given cos & sin of an angle, return that angle.
|
|
//parms need not be normalized, that is, the ratio of the parms cos/sin must
|
|
//equal the ratio of the actual cos & sin for the result angle, but the parms
|
|
//need not be the actual cos & sin.
|
|
//NOTE: this is different from the standard C atan2, since it is left-handed.
|
|
angle FixAtan2(float cos,float sin)
|
|
{
|
|
float q,m;
|
|
angle t;
|
|
|
|
//find smaller of two
|
|
|
|
|
|
q=(sin*sin)+(cos*cos);
|
|
|
|
m = sqrt(q);
|
|
|
|
if (m==0)
|
|
return 0;
|
|
|
|
if (fabs(sin) < fabs(cos))
|
|
{
|
|
//sin is smaller, use arcsin
|
|
t = FixAsin(sin/m);
|
|
if (cos<0)
|
|
t = 0x8000 - t;
|
|
|
|
return t;
|
|
}
|
|
else
|
|
{
|
|
t = FixAcos(cos/m);
|
|
if (sin<0)
|
|
t = F1_0-t;
|
|
|
|
return t;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Does a ceiling operation on a fixed number
|
|
fix FixCeil (fix num)
|
|
{
|
|
int int_num;
|
|
fix new_num;
|
|
|
|
int_num=FixToInt (num);
|
|
|
|
if (num & 0xFFFF)
|
|
{
|
|
new_num=IntToFix(int_num+1);
|
|
return new_num;
|
|
}
|
|
|
|
new_num=IntToFix(int_num);
|
|
return (new_num);
|
|
}
|
|
|
|
// Floors a fixed number
|
|
fix FixFloor (fix num)
|
|
{
|
|
int int_num=FixToInt (num);
|
|
|
|
return (IntToFix (int_num));
|
|
}
|
|
|
|
// use this instead of:
|
|
// for: (int)floor(x+0.5f) use FloatRound(x)
|
|
// (int)ceil(x-0.5f) use FloatRound(x)
|
|
// (int)floor(x-0.5f) use FloatRound(x-1.0f)
|
|
// (int)floor(x) use FloatRound(x-0.5f)
|
|
// for values in the range -2048 to 2048
|
|
int FloatRound( float x );
|
|
|
|
angle FixAtan2(float cos,float sin);
|
|
angle FixAsin(float v);
|
|
angle FixAcos(float v);
|
|
|
|
// Does a ceiling operation on a fixed number
|
|
fix FixCeil (fix num);
|
|
|
|
// Floors a fixed number
|
|
fix FixFloor (fix num);
|
|
|
|
// Used for debugging. It is used in printf's so we do not have to write out the structure 3 times
|
|
// to print all the coordinates.
|
|
#define XYZ(v) (v)->x,(v)->y,(v)->z
|
|
|
|
extern const matrix Identity_matrix;
|
|
|
|
// Given a matrix, makes it an identity matrix
|
|
extern void vm_MakeIdentity (matrix *);
|
|
|
|
// Set a vector to {0,0,0}
|
|
extern void vm_MakeZero(vector *v);
|
|
|
|
// Set an angvec to {0,0,0}
|
|
extern void vm_MakeZero(angvec *a);
|
|
|
|
// Rotates a vector thru a matrix
|
|
extern void vm_MatrixMulVector (vector *,vector *,matrix *);
|
|
|
|
//Multiply a vector times the transpose of a matrix
|
|
void vm_VectorMulTMatrix(vector *result,vector *v,matrix *m);
|
|
|
|
// Multiplies 2 3x3 matrixes, returning the result in first argument
|
|
extern void vm_MatrixMul (matrix *,matrix *,matrix *);
|
|
|
|
//Multiply a matrix times the transpose of a matrix
|
|
void vm_MatrixMulTMatrix(matrix *dest,matrix *src0,matrix *src1);
|
|
|
|
// Computes all math look up tables, must be called before any vector stuff is used
|
|
extern void vm_InitMathTables();
|
|
|
|
// Given a vector, returns the magnitude. Uses sqrt so it's slow
|
|
extern float vm_GetMagnitude (vector *);
|
|
|
|
// Given a vector, returns an approximation of the magnitude
|
|
extern float vm_GetMagnitudeFast (vector *);
|
|
|
|
// Returns the dot product of the two given vectors
|
|
extern float vm_DotProduct (vector *,vector *);
|
|
|
|
// Returns a perpendicular vector to the two given vectors
|
|
extern void vm_CrossProduct (vector *,vector *,vector *);
|
|
|
|
// Returns the difference between two vectors
|
|
extern void vm_SubVectors (vector *,const vector *,const vector *);
|
|
|
|
// Returns adds two vectors, returns result in first arg
|
|
extern void vm_AddVectors (vector *,vector *,vector *);
|
|
|
|
// Inits vector to 0,0,0
|
|
extern void vm_CenterVector (vector *);
|
|
|
|
// Given a vector, divides second arg by vector components
|
|
extern void vm_AverageVector (vector *,int);
|
|
|
|
// Normalizes a vector
|
|
// Returns the magnitude before normalization
|
|
extern float vm_VectorNormalize (vector *);
|
|
|
|
// Scales second arg vector by 3rd arg, placing result in first arg
|
|
extern void vm_ScaleVector (vector *,vector *,float);
|
|
|
|
// Scales all components of vector v by value s adds the result to p and stores result in vector d
|
|
extern void vm_ScaleAddVector (vector *d,vector *p,vector *v,float s);
|
|
|
|
// Divides second vector components by 3rd arg, placing result in first arg. Useful for parametric lines
|
|
extern void vm_DivVector (vector *,vector *,float);
|
|
|
|
// Same as VectorNormalize, but uses approximation
|
|
extern float vm_VectorNormalizeFast (vector *);
|
|
|
|
// Clears a matrix to zero
|
|
extern void vm_ClearMatrix (matrix *);
|
|
|
|
// Transposes a matrix in place
|
|
extern void vm_TransposeMatrix (matrix *);
|
|
|
|
// Given 3 angles (p,h,b), makes a rotation matrix out of them
|
|
extern void vm_AnglesToMatrix (matrix *,angle p,angle h,angle b);
|
|
|
|
//Ensure that a matrix is orthogonal
|
|
void vm_Orthogonalize(matrix *m);
|
|
|
|
//Compute a matrix from one or two vectors. At least one and at most two vectors must/can be specified.
|
|
//Parameters: m - filled in with the orienation matrix
|
|
// fvec,uvec,rvec - pointers to vectors that determine the matrix.
|
|
// One or two of these must be specified, with the other(s) set to NULL.
|
|
void vm_VectorToMatrix(matrix *m,vector *fvec,vector *uvec=NULL,vector *rvec=NULL);
|
|
|
|
//Computes a matrix from a vector and and angle of rotation around that vector
|
|
//Parameters: m - filled in with the computed matrix
|
|
// v - the forward vector of the new matrix
|
|
// a - the angle of rotation around the forward vector
|
|
void vm_VectorAngleToMatrix(matrix *m,vector *v,angle a);
|
|
|
|
// Given an angle, places sin in 2nd arg, cos in 3rd. Either can be null
|
|
extern void vm_SinCos (angle,float *,float *);
|
|
|
|
// Given x1,y1,x2,y2, returns the slope
|
|
extern float vm_GetSlope (float,float,float,float);
|
|
|
|
//Calculates the perpendicular vector given three points
|
|
//Parms: n - the computed perp vector (filled in)
|
|
// v0,v1,v2 - three clockwise vertices
|
|
void vm_GetPerp(vector *n,vector *a,vector *b,vector *c);
|
|
|
|
//Calculates the (normalized) surface normal give three points
|
|
//Parms: n - the computed surface normal (filled in)
|
|
// v0,v1,v2 - three clockwise vertices
|
|
//Returns the magnitude of the normal before it was normalized.
|
|
//The bigger this value, the better the normal.
|
|
float vm_GetNormal(vector *n,vector *v0,vector *v1,vector *v2);
|
|
|
|
#define vm_GetSurfaceNormal vm_GetNormal
|
|
|
|
// Gets the distances (magnitude) between two vectors. Slow.
|
|
extern float vm_VectorDistance (const vector *a, const vector *b);
|
|
|
|
// Gets the approx distances (magnitude) between two vectors. Faster.
|
|
extern float vm_VectorDistanceQuick (vector *a,vector *b);
|
|
|
|
//Computes a normalized direction vector between two points
|
|
//Parameters: dest - filled in with the normalized direction vector
|
|
// start,end - the start and end points used to calculate the vector
|
|
//Returns: the distance between the two input points
|
|
float vm_GetNormalizedDir (vector *dest,vector *end,vector *start);
|
|
|
|
// Returns a normalized direction vector between two points
|
|
// Uses sloppier magnitude, less precise
|
|
float vm_GetNormalizedDirFast (vector *dest,vector *end,vector *start);
|
|
|
|
//extract angles from a matrix
|
|
angvec *vm_ExtractAnglesFromMatrix(angvec *a,matrix *m);
|
|
|
|
// returns the angle between two vectors and a forward vector
|
|
angle vm_DeltaAngVec(vector *v0,vector *v1,vector *fvec);
|
|
|
|
// returns the angle between two normalized vectors and a forward vector
|
|
angle vm_DeltaAngVecNorm(vector *v0,vector *v1,vector *fvec);
|
|
|
|
// Computes the distance from a point to a plane.
|
|
// Parms: checkp - the point to check
|
|
// Parms: norm - the (normalized) surface normal of the plane
|
|
// planep - a point on the plane
|
|
// Returns: The signed distance from the plane; negative dist is on the back of the plane
|
|
float vm_DistToPlane(vector *checkp,vector *norm,vector *planep);
|
|
|
|
//returns the value of a determinant
|
|
float calc_det_value(matrix *det);
|
|
|
|
void vm_MakeInverseMatrix (matrix *dest);
|
|
void vm_SinCosToMatrix(matrix *m,float sinp,float cosp,float sinb,float cosb,float sinh,float cosh);
|
|
|
|
// Gets the real center of a polygon
|
|
float vm_GetCentroid (vector *centroid,vector *src,int nv);
|
|
|
|
// retrieves a random vector in values -RAND_MAX/2 to RAND_MAX/2
|
|
void vm_MakeRandomVector(vector *vec);
|
|
|
|
// Given a set of points, computes the minimum bounding sphere of those points
|
|
float vm_ComputeBoundingSphere(vector *center,vector *vecs,int num_verts);
|
|
|
|
// Gets the real center of a polygon, but uses fast magnitude calculation
|
|
// Returns the size of the passed in stuff
|
|
float vm_GetCentroidFast (vector *centroid,vector *src,int nv);
|
|
|
|
|
|
// Here are the C++ operator overloads -- they do as expected
|
|
extern matrix operator *(matrix src0, matrix src1);
|
|
extern matrix operator *=(matrix &src0, matrix src1);
|
|
|
|
|
|
void vm_AverageVector (vector *a,int num)
|
|
{
|
|
// Averages a vector. ie divides each component of vector a by num
|
|
//assert (num!=0);
|
|
|
|
a->x=a->x/(float)num;
|
|
a->y=a->y/(float)num;
|
|
a->z=a->z/(float)num;
|
|
}
|
|
|
|
void vm_AddVectors (vector *result,vector *a,vector *b)
|
|
{
|
|
// Adds two vectors. Either source can equal dest
|
|
|
|
result->x=a->x+b->x;
|
|
result->y=a->y+b->y;
|
|
result->z=a->z+b->z;
|
|
}
|
|
|
|
|
|
void vm_SubVectors (vector *result,const vector *a, const vector *b)
|
|
{
|
|
// Subtracts second vector from first. Either source can equal dest
|
|
|
|
result->x=a->x-b->x;
|
|
result->y=a->y-b->y;
|
|
result->z=a->z-b->z;
|
|
}
|
|
|
|
float vm_VectorDistance (const vector *a,const vector *b)
|
|
{
|
|
// Given two vectors, returns the distance between them
|
|
|
|
vector dest;
|
|
float dist;
|
|
|
|
vm_SubVectors (&dest,a,b);
|
|
dist=vm_GetMagnitude (&dest);
|
|
return dist;
|
|
}
|
|
float vm_VectorDistanceQuick (vector *a,vector *b)
|
|
{
|
|
// Given two vectors, returns the distance between them
|
|
|
|
vector dest;
|
|
float dist;
|
|
|
|
vm_SubVectors (&dest,a,b);
|
|
dist=vm_GetMagnitudeFast (&dest);
|
|
return dist;
|
|
}
|
|
|
|
//Calculates the perpendicular vector given three points
|
|
//Parms: n - the computed perp vector (filled in)
|
|
// v0,v1,v2 - three clockwise vertices
|
|
void vm_GetPerp(vector *n,vector *a,vector *b,vector *c)
|
|
{
|
|
// Given 3 vertices, return the surface normal in n
|
|
// IMPORTANT: B must be the 'corner' vertex
|
|
|
|
vector x,y;
|
|
|
|
vm_SubVectors (&x,b,a);
|
|
vm_SubVectors (&y,c,b);
|
|
|
|
vm_CrossProduct (n,&x,&y);
|
|
}
|
|
|
|
//Calculates the (normalized) surface normal give three points
|
|
//Parms: n - the computed surface normal (filled in)
|
|
// v0,v1,v2 - three clockwise vertices
|
|
//Returns the magnitude of the normal before it was normalized.
|
|
//The bigger this value, the better the normal.
|
|
float vm_GetNormal(vector *n,vector *v0,vector *v1,vector *v2)
|
|
{
|
|
vm_GetPerp(n,v0,v1,v2);
|
|
|
|
return vm_VectorNormalize(n);
|
|
}
|
|
|
|
|
|
// Does a simple dot product calculation
|
|
float vm_DotProduct (vector *u,vector *v)
|
|
{
|
|
return (u->x * v->x) + (u->y * v->y) + (u->z * v->z);
|
|
}
|
|
|
|
// Scales all components of vector v by value s and stores result in vector d
|
|
// dest can equal source
|
|
void vm_ScaleVector (vector *d,vector *v,float s)
|
|
{
|
|
d->x=(v->x * s);
|
|
d->y=(v->y * s);
|
|
d->z=(v->z * s);
|
|
}
|
|
|
|
|
|
void vm_ScaleAddVector (vector *d,vector *p,vector *v,float s)
|
|
{
|
|
// Scales all components of vector v by value s
|
|
// adds the result to p and stores result in vector d
|
|
// dest can equal source
|
|
|
|
d->x=p->x+(v->x * s);
|
|
d->y=p->y+(v->y * s);
|
|
d->z=p->z+(v->z * s);
|
|
}
|
|
|
|
void vm_DivVector (vector *dest,vector *src,float n)
|
|
{
|
|
// Divides a vector into n portions
|
|
// Dest can equal src
|
|
|
|
//assert (n!=0);
|
|
|
|
|
|
dest->x=src->x/n;
|
|
dest->y=src->y/n;
|
|
dest->z=src->z/n;
|
|
}
|
|
|
|
void vm_CrossProduct (vector *dest,vector *u,vector *v)
|
|
{
|
|
// Computes a cross product between u and v, returns the result
|
|
// in Normal. Dest cannot equal source.
|
|
|
|
dest->x = (u->y * v->z ) - (u->z * v->y);
|
|
dest->y = (u->z * v->x ) - (u->x * v->z);
|
|
dest->z = (u->x * v->y ) - (u->y * v->x);
|
|
}
|
|
|
|
//Normalize a vector.
|
|
//Returns: the magnitude before normalization
|
|
float vm_VectorNormalize (vector *a)
|
|
{
|
|
float mag;
|
|
|
|
mag=vm_GetMagnitude (a);
|
|
|
|
if (mag>0)
|
|
*a /= mag;
|
|
else
|
|
{
|
|
*a = Zero_vector;
|
|
a->x = 1.0;
|
|
mag = 0.0f;
|
|
}
|
|
|
|
return mag;
|
|
}
|
|
|
|
float vm_GetMagnitude (vector *a)
|
|
{
|
|
float f;
|
|
|
|
f=(a->x * a->x) + (a->y * a->y) + (a->z * a->z);
|
|
|
|
return (sqrt(f));
|
|
}
|
|
|
|
void vm_ClearMatrix (matrix *dest)
|
|
{
|
|
memset (dest,0,sizeof (matrix));
|
|
}
|
|
|
|
void vm_MakeIdentity (matrix *dest)
|
|
{
|
|
memset (dest,0,sizeof (matrix));
|
|
dest->rvec.x=dest->uvec.y=dest->fvec.z=1.0;
|
|
}
|
|
void vm_MakeInverseMatrix (matrix *dest)
|
|
{
|
|
memset ((void *)dest,0,sizeof (matrix));
|
|
dest->rvec.x=dest->uvec.y=dest->fvec.z=-1.0;
|
|
}
|
|
|
|
void vm_TransposeMatrix (matrix *m)
|
|
{
|
|
// Transposes a matrix in place
|
|
|
|
float t;
|
|
|
|
t = m->uvec.x; m->uvec.x = m->rvec.y; m->rvec.y = t;
|
|
t = m->fvec.x; m->fvec.x = m->rvec.z; m->rvec.z = t;
|
|
t = m->fvec.y; m->fvec.y = m->uvec.z; m->uvec.z = t;
|
|
|
|
}
|
|
|
|
void vm_MatrixMulVector (vector *result,vector *v,matrix *m)
|
|
{
|
|
// Rotates a vector thru a matrix
|
|
|
|
//assert(result != v);
|
|
|
|
result->x = *v * m->rvec;
|
|
result->y = *v * m->uvec;
|
|
result->z = *v * m->fvec;
|
|
}
|
|
|
|
//Multiply a vector times the transpose of a matrix
|
|
void vm_VectorMulTMatrix(vector *result,vector *v,matrix *m)
|
|
{
|
|
//assert(result != v);
|
|
|
|
result->x = vm_Dot3Vector(m->rvec.x,m->uvec.x,m->fvec.x,v);
|
|
result->y = vm_Dot3Vector(m->rvec.y,m->uvec.y,m->fvec.y,v);
|
|
result->z = vm_Dot3Vector(m->rvec.z,m->uvec.z,m->fvec.z,v);
|
|
}
|
|
|
|
void vm_MatrixMul (matrix *dest,matrix *src0,matrix *src1)
|
|
{
|
|
// For multiplying two 3x3 matrices together
|
|
|
|
//assert((dest != src0) && (dest != src1));
|
|
|
|
dest->rvec.x = vm_Dot3Vector(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->rvec);
|
|
dest->uvec.x = vm_Dot3Vector(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->uvec);
|
|
dest->fvec.x = vm_Dot3Vector(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->fvec);
|
|
|
|
dest->rvec.y = vm_Dot3Vector(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->rvec);
|
|
dest->uvec.y = vm_Dot3Vector(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->uvec);
|
|
dest->fvec.y = vm_Dot3Vector(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->fvec);
|
|
|
|
dest->rvec.z = vm_Dot3Vector(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->rvec);
|
|
dest->uvec.z = vm_Dot3Vector(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->uvec);
|
|
dest->fvec.z = vm_Dot3Vector(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->fvec);
|
|
}
|
|
|
|
//Multiply a matrix times the transpose of a matrix
|
|
void vm_MatrixMulTMatrix(matrix *dest,matrix *src0,matrix *src1)
|
|
{
|
|
// For multiplying two 3x3 matrices together
|
|
|
|
//assert((dest != src0) && (dest != src1));
|
|
|
|
dest->rvec.x = src0->rvec.x * src1->rvec.x + src0->uvec.x * src1->uvec.x + src0->fvec.x * src1->fvec.x;
|
|
dest->uvec.x = src0->rvec.x * src1->rvec.y + src0->uvec.x * src1->uvec.y + src0->fvec.x * src1->fvec.y;
|
|
dest->fvec.x = src0->rvec.x * src1->rvec.z + src0->uvec.x * src1->uvec.z + src0->fvec.x * src1->fvec.z;
|
|
|
|
dest->rvec.y = src0->rvec.y * src1->rvec.x + src0->uvec.y * src1->uvec.x + src0->fvec.y * src1->fvec.x;
|
|
dest->uvec.y = src0->rvec.y * src1->rvec.y + src0->uvec.y * src1->uvec.y + src0->fvec.y * src1->fvec.y;
|
|
dest->fvec.y = src0->rvec.y * src1->rvec.z + src0->uvec.y * src1->uvec.z + src0->fvec.y * src1->fvec.z;
|
|
|
|
dest->rvec.z = src0->rvec.z * src1->rvec.x + src0->uvec.z * src1->uvec.x + src0->fvec.z * src1->fvec.x;
|
|
dest->uvec.z = src0->rvec.z * src1->rvec.y + src0->uvec.z * src1->uvec.y + src0->fvec.z * src1->fvec.y;
|
|
dest->fvec.z = src0->rvec.z * src1->rvec.z + src0->uvec.z * src1->uvec.z + src0->fvec.z * src1->fvec.z;
|
|
}
|
|
|
|
matrix operator *(matrix src0, matrix src1)
|
|
{
|
|
// For multiplying two 3x3 matrices together
|
|
matrix dest;
|
|
|
|
dest.rvec.x = vm_Dot3Vector(src0.rvec.x,src0.uvec.x,src0.fvec.x, &src1.rvec);
|
|
dest.uvec.x = vm_Dot3Vector(src0.rvec.x,src0.uvec.x,src0.fvec.x, &src1.uvec);
|
|
dest.fvec.x = vm_Dot3Vector(src0.rvec.x,src0.uvec.x,src0.fvec.x, &src1.fvec);
|
|
|
|
dest.rvec.y = vm_Dot3Vector(src0.rvec.y,src0.uvec.y,src0.fvec.y, &src1.rvec);
|
|
dest.uvec.y = vm_Dot3Vector(src0.rvec.y,src0.uvec.y,src0.fvec.y, &src1.uvec);
|
|
dest.fvec.y = vm_Dot3Vector(src0.rvec.y,src0.uvec.y,src0.fvec.y, &src1.fvec);
|
|
|
|
dest.rvec.z = vm_Dot3Vector(src0.rvec.z,src0.uvec.z,src0.fvec.z, &src1.rvec);
|
|
dest.uvec.z = vm_Dot3Vector(src0.rvec.z,src0.uvec.z,src0.fvec.z, &src1.uvec);
|
|
dest.fvec.z = vm_Dot3Vector(src0.rvec.z,src0.uvec.z,src0.fvec.z, &src1.fvec);
|
|
|
|
return dest;
|
|
}
|
|
|
|
matrix operator *=(matrix &src0, matrix src1)
|
|
{
|
|
return (src0 = src0 * src1);
|
|
}
|
|
|
|
//Computes a normalized direction vector between two points
|
|
//Parameters: dest - filled in with the normalized direction vector
|
|
// start,end - the start and end points used to calculate the vector
|
|
//Returns: the distance between the two input points
|
|
float vm_GetNormalizedDir (vector *dest,vector *end,vector *start)
|
|
{
|
|
vm_SubVectors (dest,end,start);
|
|
return vm_VectorNormalize(dest);
|
|
}
|
|
|
|
//Returns a normalized direction vector between two points
|
|
//Just like vm_GetNormalizedDir(), but uses sloppier magnitude, less precise
|
|
//Parameters: dest - filled in with the normalized direction vector
|
|
// start,end - the start and end points used to calculate the vector
|
|
//Returns: the distance between the two input points
|
|
float vm_GetNormalizedDirFast (vector *dest,vector *end,vector *start)
|
|
{
|
|
vm_SubVectors (dest,end,start);
|
|
return vm_VectorNormalizeFast(dest);
|
|
}
|
|
|
|
float vm_GetMagnitudeFast(vector *v)
|
|
{
|
|
float a,b,c,bc;
|
|
|
|
a = fabs(v->x);
|
|
b = fabs(v->y);
|
|
c = fabs(v->z);
|
|
|
|
if (a < b) {
|
|
float t=a; a=b; b=t;
|
|
}
|
|
|
|
if (b < c) {
|
|
float t=b; b=c; c=t;
|
|
|
|
if (a < b) {
|
|
float t=a; a=b; b=t;
|
|
}
|
|
}
|
|
|
|
bc = (b/4) + (c/8);
|
|
|
|
return a + bc + (bc/2);
|
|
}
|
|
|
|
|
|
//Normalize a vector using an approximation of the magnitude
|
|
//Returns: the magnitude before normalization
|
|
float vm_VectorNormalizeFast (vector *a)
|
|
{
|
|
float mag;
|
|
|
|
mag=vm_GetMagnitudeFast (a);
|
|
|
|
if (mag==0.0)
|
|
{
|
|
a->x=a->y=a->z=0.0;
|
|
return 0;
|
|
}
|
|
|
|
a->x=(a->x/mag);
|
|
a->y=(a->y/mag);
|
|
a->z=(a->z/mag);
|
|
|
|
return mag;
|
|
}
|
|
|
|
// Computes the distance from a point to a plane.
|
|
// Parms: checkp - the point to check
|
|
// Parms: norm - the (normalized) surface normal of the plane
|
|
// planep - a point on the plane
|
|
// Returns: The signed distance from the plane; negative dist is on the back of the plane
|
|
float vm_DistToPlane(vector *checkp,vector *norm,vector *planep)
|
|
{
|
|
vector t;
|
|
|
|
t = *checkp - *planep;
|
|
|
|
return t * *norm;
|
|
|
|
}
|
|
|
|
float vm_GetSlope(float x1,float y1,float x2,float y2)
|
|
{
|
|
// returns the slope of a line
|
|
float r;
|
|
|
|
if (y2-y1==0)
|
|
return (0.0);
|
|
|
|
r=(x2-x1)/(y2-y1);
|
|
return (r);
|
|
|
|
}
|
|
|
|
void vm_SinCosToMatrix(matrix *m,float sinp,float cosp,float sinb,float cosb,float sinh,float cosh)
|
|
{
|
|
float sbsh,cbch,cbsh,sbch;
|
|
|
|
sbsh = (sinb*sinh);
|
|
cbch = (cosb*cosh);
|
|
cbsh = (cosb*sinh);
|
|
sbch = (sinb*cosh);
|
|
|
|
m->rvec.x = cbch + (sinp*sbsh); //m1
|
|
m->uvec.z = sbsh + (sinp*cbch); //m8
|
|
|
|
m->uvec.x = (sinp*cbsh) - sbch; //m2
|
|
m->rvec.z = (sinp*sbch) - cbsh; //m7
|
|
|
|
m->fvec.x = (sinh*cosp); //m3
|
|
m->rvec.y = (sinb*cosp); //m4
|
|
m->uvec.y = (cosb*cosp); //m5
|
|
m->fvec.z = (cosh*cosp); //m9
|
|
|
|
m->fvec.y = -sinp; //m6
|
|
}
|
|
|
|
void vm_AnglesToMatrix(matrix *m,angle p,angle h,angle b)
|
|
{
|
|
float sinp,cosp,sinb,cosb,sinh,cosh;
|
|
|
|
sinp = FixSin(p); cosp = FixCos(p);
|
|
sinb = FixSin(b); cosb = FixCos(b);
|
|
sinh = FixSin(h); cosh = FixCos(h);
|
|
|
|
vm_SinCosToMatrix(m,sinp,cosp,sinb,cosb,sinh,cosh);
|
|
}
|
|
|
|
//Computes a matrix from a vector and and angle of rotation around that vector
|
|
//Parameters: m - filled in with the computed matrix
|
|
// v - the forward vector of the new matrix
|
|
// a - the angle of rotation around the forward vector
|
|
void vm_VectorAngleToMatrix(matrix *m,vector *v,angle a)
|
|
{
|
|
float sinb,cosb,sinp,cosp,sinh,cosh;
|
|
|
|
sinb = FixSin(a); cosb = FixCos(a);
|
|
|
|
sinp = -v->y;
|
|
cosp = sqrt(1.0 - (sinp * sinp));
|
|
|
|
if (cosp != 0.0) {
|
|
sinh = v->x / cosp;
|
|
cosh = v->z / cosp;
|
|
}
|
|
else {
|
|
sinh = 0;
|
|
cosh = 1.0;
|
|
}
|
|
|
|
vm_SinCosToMatrix(m,sinp,cosp,sinb,cosb,sinh,cosh);
|
|
}
|
|
|
|
|
|
//Ensure that a matrix is orthogonal
|
|
void vm_Orthogonalize(matrix *m)
|
|
{
|
|
//Normalize forward vector
|
|
if (vm_VectorNormalize(&m->fvec) == 0) {
|
|
return;
|
|
}
|
|
|
|
//Generate right vector from forward and up vectors
|
|
m->rvec = m->uvec ^ m->fvec;
|
|
|
|
//Normaize new right vector
|
|
if (vm_VectorNormalize(&m->rvec) == 0) {
|
|
vm_VectorToMatrix(m,&m->fvec,NULL,NULL); //error, so generate from forward vector only
|
|
return;
|
|
}
|
|
|
|
//Recompute up vector, in case it wasn't entirely perpendiclar
|
|
m->uvec = m->fvec ^ m->rvec;
|
|
}
|
|
|
|
//do the math for vm_VectorToMatrix()
|
|
void DoVectorToMatrix(matrix *m,vector *fvec,vector *uvec,vector *rvec)
|
|
{
|
|
vector *xvec=&m->rvec,*yvec=&m->uvec,*zvec=&m->fvec;
|
|
|
|
//ASSERT(fvec != NULL);
|
|
|
|
*zvec = *fvec;
|
|
if (vm_VectorNormalize(zvec) == 0) {
|
|
return;
|
|
}
|
|
|
|
if (uvec == NULL) {
|
|
|
|
if (rvec == NULL) { //just forward vec
|
|
|
|
bad_vector2:
|
|
;
|
|
|
|
if (zvec->x==0 && zvec->z==0) { //forward vec is straight up or down
|
|
|
|
m->rvec.x = 1.0;
|
|
m->uvec.z = (zvec->y<0)?1.0:-1.0;
|
|
|
|
m->rvec.y = m->rvec.z = m->uvec.x = m->uvec.y = 0;
|
|
}
|
|
else { //not straight up or down
|
|
|
|
xvec->x = zvec->z;
|
|
xvec->y = 0;
|
|
xvec->z = -zvec->x;
|
|
|
|
vm_VectorNormalize(xvec);
|
|
|
|
*yvec = *zvec ^ *xvec;
|
|
|
|
}
|
|
|
|
}
|
|
else { //use right vec
|
|
|
|
*xvec = *rvec;
|
|
if (vm_VectorNormalize(xvec) == 0)
|
|
goto bad_vector2;
|
|
|
|
*yvec = *zvec ^ *xvec;
|
|
|
|
//normalize new perpendicular vector
|
|
if (vm_VectorNormalize(yvec) == 0)
|
|
goto bad_vector2;
|
|
|
|
//now recompute right vector, in case it wasn't entirely perpendiclar
|
|
*xvec = *yvec ^ *zvec;
|
|
}
|
|
}
|
|
else { //use up vec
|
|
|
|
*yvec = *uvec;
|
|
if (vm_VectorNormalize(yvec) == 0)
|
|
goto bad_vector2;
|
|
|
|
*xvec = *yvec ^ *zvec;
|
|
|
|
//normalize new perpendicular vector
|
|
if (vm_VectorNormalize(xvec) == 0)
|
|
goto bad_vector2;
|
|
|
|
//now recompute up vector, in case it wasn't entirely perpendiclar
|
|
*yvec = *zvec ^ *xvec;
|
|
|
|
}
|
|
}
|
|
|
|
//Compute a matrix from one or two vectors. At least one and at most two vectors must/can be specified.
|
|
//Parameters: m - filled in with the orienation matrix
|
|
// fvec,uvec,rvec - pointers to vectors that determine the matrix.
|
|
// One or two of these must be specified, with the other(s) set to NULL.
|
|
void vm_VectorToMatrix(matrix *m,vector *fvec,vector *uvec,vector *rvec)
|
|
{
|
|
if (! fvec) { //no forward vector. Use up and/or right vectors.
|
|
matrix tmatrix;
|
|
|
|
if (uvec) { //got up vector. use up and, if specified, right vectors.
|
|
DoVectorToMatrix(&tmatrix,uvec,NULL,rvec);
|
|
m->fvec = -tmatrix.uvec;
|
|
m->uvec = tmatrix.fvec;
|
|
m->rvec = tmatrix.rvec;
|
|
return;
|
|
}
|
|
else { //no up vector. Use right vector only.
|
|
//ASSERT(rvec);
|
|
DoVectorToMatrix(&tmatrix,rvec,NULL,NULL);
|
|
m->fvec = -tmatrix.rvec;
|
|
m->uvec = tmatrix.uvec;
|
|
m->rvec = tmatrix.fvec;
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
//ASSERT(! (uvec && rvec)); //can only have 1 or 2 vectors specified
|
|
DoVectorToMatrix(m,fvec,uvec,rvec);
|
|
}
|
|
}
|
|
|
|
void vm_SinCos(unsigned short a, float *s, float *c)
|
|
{
|
|
if(s)
|
|
*s = FixSin(a);
|
|
if(c)
|
|
*c = FixCos(a);
|
|
}
|
|
|
|
//extract angles from a matrix
|
|
angvec *vm_ExtractAnglesFromMatrix(angvec *a,matrix *m)
|
|
{
|
|
float sinh,cosh,cosp;
|
|
|
|
if (m->fvec.x==0 && m->fvec.z==0) //zero head
|
|
a->h = 0;
|
|
else
|
|
a->h = FixAtan2(m->fvec.z,m->fvec.x);
|
|
|
|
sinh=FixSin(a->h);
|
|
cosh=FixCos(a->h);
|
|
|
|
if (fabs(sinh) > fabs(cosh)) //sine is larger, so use it
|
|
cosp = (m->fvec.x/sinh);
|
|
else //cosine is larger, so use it
|
|
cosp = (m->fvec.z/cosh);
|
|
|
|
if (cosp==0 && m->fvec.y==0)
|
|
a->p = 0;
|
|
else
|
|
a->p = FixAtan2(cosp,-m->fvec.y);
|
|
|
|
|
|
if (cosp == 0) //the cosine of pitch is zero. we're pitched straight up. say no bank
|
|
|
|
a->b = 0;
|
|
|
|
else {
|
|
float sinb,cosb;
|
|
|
|
sinb = (m->rvec.y/cosp);
|
|
cosb = (m->uvec.y/cosp);
|
|
|
|
if (sinb==0 && cosb==0)
|
|
a->b = 0;
|
|
else
|
|
a->b = FixAtan2(cosb,sinb);
|
|
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
//returns the value of a determinant
|
|
float calc_det_value(matrix *det)
|
|
{
|
|
return det->rvec.x*det->uvec.y*det->fvec.z -
|
|
det->rvec.x*det->uvec.z*det->fvec.y -
|
|
det->rvec.y*det->uvec.x*det->fvec.z +
|
|
det->rvec.y*det->uvec.z*det->fvec.x +
|
|
det->rvec.z*det->uvec.x*det->fvec.y -
|
|
det->rvec.z*det->uvec.y*det->fvec.x;
|
|
}
|
|
|
|
|
|
|
|
//computes the delta angle between two vectors.
|
|
//vectors need not be normalized. if they are, call vm_vec_delta_ang_norm()
|
|
//the forward vector (third parameter) can be NULL, in which case the absolute
|
|
//value of the angle in returned. Otherwise the angle around that vector is
|
|
//returned.
|
|
|
|
angle vm_DeltaAngVec(vector *v0,vector *v1,vector *fvec)
|
|
{
|
|
vector t0,t1;
|
|
|
|
t0 = *v0;
|
|
t1 = *v1;
|
|
|
|
vm_VectorNormalize(&t0);
|
|
vm_VectorNormalize(&t1);
|
|
|
|
return vm_DeltaAngVecNorm(&t0,&t1,fvec);
|
|
}
|
|
|
|
//computes the delta angle between two normalized vectors.
|
|
angle vm_DeltaAngVecNorm(vector *v0,vector *v1,vector *fvec)
|
|
{
|
|
angle a;
|
|
|
|
a = FixAcos(vm_DotProduct(v0,v1));
|
|
|
|
if (fvec) {
|
|
vector t;
|
|
|
|
vm_CrossProduct(&t,v0,v1);
|
|
if (vm_DotProduct(&t,fvec) < 0)
|
|
a = -a;
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
// Gets the real center of a polygon
|
|
// Returns the size of the passed in stuff
|
|
float vm_GetCentroid (vector *centroid,vector *src,int nv)
|
|
{
|
|
//ASSERT (nv>2);
|
|
vector normal;
|
|
float area,total_area;
|
|
int i;
|
|
vector tmp_center;
|
|
|
|
vm_MakeZero (centroid);
|
|
|
|
// First figure out the total area of this polygon
|
|
vm_GetPerp (&normal,&src[0],&src[1],&src[2]);
|
|
total_area=(vm_GetMagnitude (&normal)/2);
|
|
|
|
for (i=2;i<nv-1;i++)
|
|
{
|
|
vm_GetPerp (&normal,&src[0],&src[i],&src[i+1]);
|
|
area=(vm_GetMagnitude (&normal)/2);
|
|
total_area+=area;
|
|
}
|
|
|
|
// Now figure out how much weight each triangle represents to the overall
|
|
// polygon
|
|
vm_GetPerp (&normal,&src[0],&src[1],&src[2]);
|
|
area=(vm_GetMagnitude (&normal)/2);
|
|
|
|
// Get the center of the first polygon
|
|
vm_MakeZero (&tmp_center);
|
|
for (i=0;i<3;i++)
|
|
{
|
|
tmp_center+=src[i];
|
|
}
|
|
tmp_center/=3;
|
|
|
|
*centroid+=(tmp_center*(area/total_area));
|
|
|
|
// Now do the same for the rest
|
|
for (i=2;i<nv-1;i++)
|
|
{
|
|
vm_GetPerp (&normal,&src[0],&src[i],&src[i+1]);
|
|
area=(vm_GetMagnitude (&normal)/2);
|
|
|
|
vm_MakeZero (&tmp_center);
|
|
|
|
tmp_center+=src[0];
|
|
tmp_center+=src[i];
|
|
tmp_center+=src[i+1];
|
|
|
|
tmp_center/=3;
|
|
|
|
*centroid+=(tmp_center*(area/total_area));
|
|
}
|
|
|
|
return total_area;
|
|
}
|
|
|
|
// Gets the real center of a polygon, but uses fast magnitude calculation
|
|
// Returns the size of the passed in stuff
|
|
float vm_GetCentroidFast (vector *centroid,vector *src,int nv)
|
|
{
|
|
//ASSERT (nv>2);
|
|
vector normal;
|
|
float area,total_area;
|
|
int i;
|
|
vector tmp_center;
|
|
|
|
vm_MakeZero (centroid);
|
|
|
|
// First figure out the total area of this polygon
|
|
vm_GetPerp (&normal,&src[0],&src[1],&src[2]);
|
|
total_area=(vm_GetMagnitudeFast (&normal)/2);
|
|
|
|
for (i=2;i<nv-1;i++)
|
|
{
|
|
vm_GetPerp (&normal,&src[0],&src[i],&src[i+1]);
|
|
area=(vm_GetMagnitudeFast (&normal)/2);
|
|
total_area+=area;
|
|
}
|
|
|
|
// Now figure out how much weight each triangle represents to the overall
|
|
// polygon
|
|
vm_GetPerp (&normal,&src[0],&src[1],&src[2]);
|
|
area=(vm_GetMagnitudeFast (&normal)/2);
|
|
|
|
// Get the center of the first polygon
|
|
vm_MakeZero (&tmp_center);
|
|
for (i=0;i<3;i++)
|
|
{
|
|
tmp_center+=src[i];
|
|
}
|
|
tmp_center/=3;
|
|
|
|
*centroid+=(tmp_center*(area/total_area));
|
|
|
|
// Now do the same for the rest
|
|
for (i=2;i<nv-1;i++)
|
|
{
|
|
vm_GetPerp (&normal,&src[0],&src[i],&src[i+1]);
|
|
area=(vm_GetMagnitudeFast (&normal)/2);
|
|
|
|
vm_MakeZero (&tmp_center);
|
|
|
|
tmp_center+=src[0];
|
|
tmp_center+=src[i];
|
|
tmp_center+=src[i+1];
|
|
|
|
tmp_center/=3;
|
|
|
|
*centroid+=(tmp_center*(area/total_area));
|
|
}
|
|
|
|
return total_area;
|
|
}
|
|
|
|
|
|
// creates a completely random, non-normalized vector with a range of values from -1023 to +1024 values)
|
|
void vm_MakeRandomVector(vector *vec)
|
|
{
|
|
vec->x = rand() - RAND_MAX/2;
|
|
vec->y = rand() - RAND_MAX/2;
|
|
vec->z = rand() - RAND_MAX/2;
|
|
}
|
|
|
|
|
|
// Given a set of points, computes the minimum bounding sphere of those points
|
|
float vm_ComputeBoundingSphere(vector *center,vector *vecs,int num_verts)
|
|
{
|
|
//This algorithm is from Graphics Gems I. There's a better algorithm in Graphics Gems III that
|
|
//we should probably implement sometime.
|
|
|
|
vector *min_x,*max_x,*min_y,*max_y,*min_z,*max_z,*vp;
|
|
float dx,dy,dz;
|
|
float rad,rad2;
|
|
int i;
|
|
|
|
//Initialize min, max vars
|
|
min_x = max_x = min_y = max_y = min_z = max_z = &vecs[0];
|
|
|
|
//First, find the points with the min & max x,y, & z coordinates
|
|
for (i=0,vp=vecs;i<num_verts;i++,vp++) {
|
|
|
|
if (vp->x < min_x->x)
|
|
min_x = vp;
|
|
|
|
if (vp->x > max_x->x)
|
|
max_x = vp;
|
|
|
|
if (vp->y < min_y->y)
|
|
min_y = vp;
|
|
|
|
if (vp->y > max_y->y)
|
|
max_y = vp;
|
|
|
|
if (vp->z < min_z->z)
|
|
min_z = vp;
|
|
|
|
if (vp->z > max_z->z)
|
|
max_z = vp;
|
|
}
|
|
|
|
//Calculate initial sphere
|
|
|
|
dx = vm_VectorDistance(min_x,max_x);
|
|
dy = vm_VectorDistance(min_y,max_y);
|
|
dz = vm_VectorDistance(min_z,max_z);
|
|
|
|
if (dx > dy)
|
|
if (dx > dz) {
|
|
*center = (*min_x + *max_x) / 2; rad = dx / 2;
|
|
}
|
|
else {
|
|
*center = (*min_z + *max_z) / 2; rad = dz / 2;
|
|
}
|
|
else
|
|
if (dy > dz) {
|
|
*center = (*min_y + *max_y) / 2; rad = dy / 2;
|
|
}
|
|
else {
|
|
*center = (*min_z + *max_z) / 2; rad = dz / 2;
|
|
}
|
|
|
|
|
|
//Go through all points and look for ones that don't fit
|
|
rad2 = rad * rad;
|
|
for (i=0,vp=vecs;i<num_verts;i++,vp++) {
|
|
vector delta;
|
|
float t2;
|
|
|
|
delta = *vp - *center;
|
|
t2 = delta.x * delta.x + delta.y * delta.y + delta.z * delta.z;
|
|
|
|
//If point outside, make the sphere bigger
|
|
if (t2 > rad2) {
|
|
float t;
|
|
|
|
t = sqrt(t2);
|
|
rad = (rad + t) / 2;
|
|
rad2 = rad * rad;
|
|
*center += delta * (t - rad) / t;
|
|
}
|
|
}
|
|
|
|
//We're done
|
|
return rad;
|
|
}
|
|
|
|
#endif
|
|
|