mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2025-09-19 12:06:07 +08:00
1671 lines
51 KiB
C++
1671 lines
51 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#ifndef MATH_BASE_H
|
|
#define MATH_BASE_H
|
|
|
|
#include <math.h>
|
|
#include "basetypes.h"
|
|
#include "vector.h"
|
|
#include "vector2d.h"
|
|
#include "tier0/dbg.h"
|
|
|
|
#ifdef _WIN32
|
|
#define FORCEINLINE_MATH FORCEINLINE
|
|
#else
|
|
#define FORCEINLINE_MATH __inline__ FORCEINLINE
|
|
#endif
|
|
|
|
// plane_t structure
|
|
// !!! if this is changed, it must be changed in asm code too !!!
|
|
// FIXME: does the asm code even exist anymore?
|
|
// FIXME: this should move to a different file
|
|
struct cplane_t
|
|
{
|
|
Vector normal;
|
|
float dist;
|
|
byte type; // for fast side tests
|
|
byte signbits; // signx + (signy<<1) + (signz<<1)
|
|
byte pad[2];
|
|
|
|
#ifdef VECTOR_NO_SLOW_OPERATIONS
|
|
cplane_t() {}
|
|
|
|
private:
|
|
// No copy constructors allowed if we're in optimal mode
|
|
cplane_t(const cplane_t& vOther);
|
|
#endif
|
|
};
|
|
|
|
// structure offset for asm code
|
|
#define CPLANE_NORMAL_X 0
|
|
#define CPLANE_NORMAL_Y 4
|
|
#define CPLANE_NORMAL_Z 8
|
|
#define CPLANE_DIST 12
|
|
#define CPLANE_TYPE 16
|
|
#define CPLANE_SIGNBITS 17
|
|
#define CPLANE_PAD0 18
|
|
#define CPLANE_PAD1 19
|
|
|
|
// 0-2 are axial planes
|
|
#define PLANE_X 0
|
|
#define PLANE_Y 1
|
|
#define PLANE_Z 2
|
|
|
|
// 3-5 are non-axial planes snapped to the nearest
|
|
#define PLANE_ANYX 3
|
|
#define PLANE_ANYY 4
|
|
#define PLANE_ANYZ 5
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Frustum plane indices.
|
|
// WARNING: there is code that depends on these values
|
|
//-----------------------------------------------------------------------------
|
|
|
|
enum
|
|
{
|
|
FRUSTUM_RIGHT = 0,
|
|
FRUSTUM_LEFT = 1,
|
|
FRUSTUM_TOP = 2,
|
|
FRUSTUM_BOTTOM = 3,
|
|
FRUSTUM_NEARZ = 4,
|
|
FRUSTUM_FARZ = 5,
|
|
FRUSTUM_NUMPLANES = 6
|
|
};
|
|
|
|
extern int SignbitsForPlane( cplane_t *out );
|
|
|
|
class Frustum_t
|
|
{
|
|
public:
|
|
void SetPlane( int i, int nType, const Vector &vecNormal, float dist )
|
|
{
|
|
m_Plane[i].normal = vecNormal;
|
|
m_Plane[i].dist = dist;
|
|
m_Plane[i].type = nType;
|
|
m_Plane[i].signbits = SignbitsForPlane( &m_Plane[i] );
|
|
m_AbsNormal[i].Init( fabs(vecNormal.x), fabs(vecNormal.y), fabs(vecNormal.z) );
|
|
}
|
|
|
|
inline const cplane_t *GetPlane( int i ) const { return &m_Plane[i]; }
|
|
inline const Vector &GetAbsNormal( int i ) const { return m_AbsNormal[i]; }
|
|
|
|
private:
|
|
cplane_t m_Plane[FRUSTUM_NUMPLANES];
|
|
Vector m_AbsNormal[FRUSTUM_NUMPLANES];
|
|
};
|
|
|
|
// Cull the world-space bounding box to the specified frustum.
|
|
extern bool R_CullBox( const Vector& mins, const Vector& maxs, const Frustum_t &frustum );
|
|
extern bool R_CullBoxSkipNear( const Vector& mins, const Vector& maxs, const Frustum_t &frustum );
|
|
|
|
struct matrix3x4_t
|
|
{
|
|
matrix3x4_t() {}
|
|
matrix3x4_t(
|
|
float m00, float m01, float m02, float m03,
|
|
float m10, float m11, float m12, float m13,
|
|
float m20, float m21, float m22, float m23 )
|
|
{
|
|
m_flMatVal[0][0] = m00; m_flMatVal[0][1] = m01; m_flMatVal[0][2] = m02; m_flMatVal[0][3] = m03;
|
|
m_flMatVal[1][0] = m10; m_flMatVal[1][1] = m11; m_flMatVal[1][2] = m12; m_flMatVal[1][3] = m13;
|
|
m_flMatVal[2][0] = m20; m_flMatVal[2][1] = m21; m_flMatVal[2][2] = m22; m_flMatVal[2][3] = m23;
|
|
}
|
|
|
|
float *operator[]( int i ) { Assert(( i >= 0 ) && ( i < 3 )); return m_flMatVal[i]; }
|
|
const float *operator[]( int i ) const { Assert(( i >= 0 ) && ( i < 3 )); return m_flMatVal[i]; }
|
|
float *Base() { return &m_flMatVal[0][0]; }
|
|
const float *Base() const { return &m_flMatVal[0][0]; }
|
|
|
|
float m_flMatVal[3][4];
|
|
};
|
|
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h
|
|
#endif
|
|
|
|
#define M_PI_F ((float)(M_PI)) // Shouldn't collide with anything.
|
|
|
|
// NJS: Inlined to prevent floats from being autopromoted to doubles, as with the old system.
|
|
#ifndef RAD2DEG
|
|
#define RAD2DEG( x ) ( (float)(x) * (float)(180.f / M_PI_F) )
|
|
#endif
|
|
|
|
#ifndef DEG2RAD
|
|
#define DEG2RAD( x ) ( (float)(x) * (float)(M_PI_F / 180.f) )
|
|
#endif
|
|
|
|
// Used to represent sides of things like planes.
|
|
#define SIDE_FRONT 0
|
|
#define SIDE_BACK 1
|
|
#define SIDE_ON 2
|
|
#define SIDE_CROSS -2 // necessary for polylib.c
|
|
|
|
#define ON_VIS_EPSILON 0.01 // necessary for vvis (flow.c) -- again look into moving later!
|
|
#define EQUAL_EPSILON 0.001 // necessary for vbsp (faces.c) -- should look into moving it there?
|
|
|
|
|
|
extern const Vector vec3_origin;
|
|
extern const QAngle vec3_angle;
|
|
extern const Vector vec3_invalid;
|
|
extern const int nanmask;
|
|
|
|
#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
|
|
|
|
FORCEINLINE_MATH vec_t DotProduct(const vec_t *v1, const vec_t *v2)
|
|
{
|
|
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
|
|
}
|
|
|
|
FORCEINLINE_MATH void VectorSubtract(const vec_t *a, const vec_t *b, vec_t *c)
|
|
{
|
|
c[0]=a[0]-b[0];
|
|
c[1]=a[1]-b[1];
|
|
c[2]=a[2]-b[2];
|
|
}
|
|
|
|
FORCEINLINE_MATH void VectorAdd(const vec_t *a, const vec_t *b, vec_t *c)
|
|
{
|
|
c[0]=a[0]+b[0];
|
|
c[1]=a[1]+b[1];
|
|
c[2]=a[2]+b[2];
|
|
}
|
|
|
|
FORCEINLINE_MATH void VectorCopy(const vec_t *a, vec_t *b)
|
|
{
|
|
b[0]=a[0];
|
|
b[1]=a[1];
|
|
b[2]=a[2];
|
|
}
|
|
|
|
FORCEINLINE_MATH void VectorClear(vec_t *a)
|
|
{
|
|
a[0]=a[1]=a[2]=0;
|
|
}
|
|
|
|
FORCEINLINE_MATH float VectorMaximum(const vec_t *v)
|
|
{
|
|
return MAX( v[0], MAX( v[1], v[2] ) );
|
|
}
|
|
|
|
FORCEINLINE_MATH float VectorMaximum(const Vector& v)
|
|
{
|
|
return MAX( v.x, MAX( v.y, v.z ) );
|
|
}
|
|
|
|
FORCEINLINE_MATH void VectorScale (const float* in, vec_t scale, float* out)
|
|
{
|
|
out[0] = in[0]*scale;
|
|
out[1] = in[1]*scale;
|
|
out[2] = in[2]*scale;
|
|
}
|
|
|
|
|
|
// Cannot be forceinline as they have overloads:
|
|
inline void VectorFill(vec_t *a, float b)
|
|
{
|
|
a[0]=a[1]=a[2]=b;
|
|
}
|
|
|
|
inline void VectorNegate(vec_t *a)
|
|
{
|
|
a[0]=-a[0];
|
|
a[1]=-a[1];
|
|
a[2]=-a[2];
|
|
}
|
|
|
|
|
|
//#define VectorMaximum(a) ( MAX( (a)[0], MAX( (a)[1], (a)[2] ) ) )
|
|
#define Vector2Clear(x) {(x)[0]=(x)[1]=0;}
|
|
#define Vector2Negate(x) {(x)[0]=-((x)[0]);(x)[1]=-((x)[1]);}
|
|
#define Vector2Copy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];}
|
|
#define Vector2Subtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];}
|
|
#define Vector2Add(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];}
|
|
#define Vector2Scale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];}
|
|
|
|
// NJS: Some functions in VBSP still need to use these for dealing with mixing vec4's and shorts with vec_t's.
|
|
// remove when no longer needed.
|
|
#define VECTOR_COPY( A, B ) do { (B)[0] = (A)[0]; (B)[1] = (A)[1]; (B)[2]=(A)[2]; } while(0)
|
|
#define DOT_PRODUCT( A, B ) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] + (A)[2]*(B)[2] )
|
|
|
|
#ifndef PFN_VECTORMA
|
|
void VectorMA( const float *start, float scale, const float *direction, float *dest );
|
|
#endif
|
|
|
|
FORCEINLINE_MATH void VectorMAInline( const float* start, float scale, const float* direction, float* dest )
|
|
{
|
|
dest[0]=start[0]+direction[0]*scale;
|
|
dest[1]=start[1]+direction[1]*scale;
|
|
dest[2]=start[2]+direction[2]*scale;
|
|
}
|
|
|
|
FORCEINLINE_MATH void VectorMAInline( const Vector& start, float scale, const Vector& direction, Vector& dest )
|
|
{
|
|
dest.x=start.x+direction.x*scale;
|
|
dest.y=start.y+direction.y*scale;
|
|
dest.z=start.z+direction.z*scale;
|
|
}
|
|
|
|
|
|
vec_t _DotProduct (const float *v1, const float *v2);
|
|
|
|
int VectorCompare (const float *v1, const float *v2);
|
|
|
|
inline float VectorLength(const float *v)
|
|
{
|
|
return FastSqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + FLT_EPSILON );
|
|
}
|
|
|
|
void CrossProduct (const float *v1, const float *v2, float *cross);
|
|
|
|
qboolean VectorsEqual( const float *v1, const float *v2 );
|
|
|
|
inline vec_t RoundInt (vec_t in)
|
|
{
|
|
return floor(in + 0.5f);
|
|
}
|
|
|
|
int Q_log2(int val);
|
|
|
|
// Math routines done in optimized assembly math package routines
|
|
void inline SinCos( float radians, float *sine, float *cosine )
|
|
{
|
|
#ifdef _WIN32
|
|
_asm
|
|
{
|
|
fld DWORD PTR [radians]
|
|
fsincos
|
|
|
|
mov edx, DWORD PTR [cosine]
|
|
mov eax, DWORD PTR [sine]
|
|
|
|
fstp DWORD PTR [edx]
|
|
fstp DWORD PTR [eax]
|
|
}
|
|
#elif _LINUX
|
|
register double __cosr, __sinr;
|
|
__asm __volatile__
|
|
("fsincos"
|
|
: "=t" (__cosr), "=u" (__sinr) : "0" (radians));
|
|
|
|
*sine = __sinr;
|
|
*cosine = __cosr;
|
|
#endif
|
|
}
|
|
|
|
#define SIN_TABLE_SIZE 256
|
|
#define FTOIBIAS 12582912.f
|
|
extern float SinCosTable[SIN_TABLE_SIZE];
|
|
|
|
|
|
inline float TableCos( float theta )
|
|
{
|
|
union
|
|
{
|
|
int i;
|
|
float f;
|
|
} ftmp;
|
|
|
|
// ideally, the following should compile down to: theta * constant + constant, changing any of these constants from defines sometimes fubars this.
|
|
ftmp.f = theta * ( float )( SIN_TABLE_SIZE / ( 2.0f * M_PI ) ) + ( FTOIBIAS + ( SIN_TABLE_SIZE / 4 ) );
|
|
return SinCosTable[ ftmp.i & ( SIN_TABLE_SIZE - 1 ) ];
|
|
}
|
|
|
|
inline float TableSin( float theta )
|
|
{
|
|
union
|
|
{
|
|
int i;
|
|
float f;
|
|
} ftmp;
|
|
|
|
// ideally, the following should compile down to: theta * constant + constant
|
|
ftmp.f = theta * ( float )( SIN_TABLE_SIZE / ( 2.0f * M_PI ) ) + FTOIBIAS;
|
|
return SinCosTable[ ftmp.i & ( SIN_TABLE_SIZE - 1 ) ];
|
|
}
|
|
|
|
|
|
// These globals are initialized in GL_RMISC.C: R_SSE(), R_3DNow(), R_MMX()
|
|
extern float (*pfInvRSquared)(const float *v);
|
|
extern float (*pfSqrt)(float x);
|
|
extern float (*pfRSqrt)(float x);
|
|
extern float (*pfRSqrtFast)(float x);
|
|
extern void (*pfFastSinCos)(float x, float* s, float* c);
|
|
extern float (*pfFastCos)(float x);
|
|
|
|
// The following are not declared as macros because they are often used in limiting situations,
|
|
// and sometimes the compiler simply refuses to inline them for soem reason
|
|
#define FastSqrt(x) (*pfSqrt)(x)
|
|
#define FastRSqrt(x) (*pfRSqrt)(x)
|
|
#define FastRSqrtFast(x) (*pfRSqrtFast)(x)
|
|
#define FastSinCos(x,s,c) (*pfFastSinCos)(x,s,c)
|
|
#define FastCos(x) (*pfFastCos)(x)
|
|
|
|
FORCEINLINE_MATH vec_t InvRSquared(float const* v)
|
|
{
|
|
return (*pfInvRSquared)(v);
|
|
}
|
|
|
|
FORCEINLINE_MATH vec_t InvRSquared( const Vector& v )
|
|
{
|
|
return InvRSquared(&v.x);
|
|
}
|
|
|
|
|
|
#ifdef PFN_VECTORMA
|
|
extern void (__cdecl *pfVectorMA)( const float *start, float scale, const float *direction, float *dest );
|
|
#define VectorMA(v1,s,v2,d) (*pfVectorMA)(v1,s,v2,d)
|
|
#endif
|
|
|
|
template<class T>
|
|
FORCEINLINE_TEMPLATE T Square( T const &a )
|
|
{
|
|
return a * a;
|
|
}
|
|
|
|
|
|
// retunrn the largest power of two less than or = x. will return 0 if passed 0.
|
|
FORCEINLINE_MATH int LargestPowerOfTwoLessThanOrEqual(int x)
|
|
{
|
|
int ret=1;
|
|
for(;;)
|
|
{
|
|
if (ret==x)
|
|
return ret;
|
|
if (ret>x)
|
|
return ret>>1;
|
|
ret<<=1;
|
|
}
|
|
}
|
|
|
|
|
|
// Math routines for optimizing division
|
|
void FloorDivMod (double numer, double denom, int *quotient,
|
|
int *rem);
|
|
int GreatestCommonDivisor (int i1, int i2);
|
|
|
|
// Test for FPU denormal mode
|
|
bool IsDenormal( const float &val );
|
|
|
|
// MOVEMENT INFO
|
|
enum
|
|
{
|
|
PITCH = 0, // up / down
|
|
YAW, // left / right
|
|
ROLL // fall over
|
|
};
|
|
|
|
// Intersect the (infinite) line defined by point vLinePt and direction vLineDir
|
|
// with the sphere defined by vSphereCenter and fSphereRadius.
|
|
// Returns 0 if the line doesn't intersect the sphere.
|
|
// Returns 1 and sets fIntersection1 and fIntersection2 if the line does intersect the sphere.
|
|
// (Intersection points will be at vLinePt+vLineDir*fIntersection1 and vLinePt+vLineDir*fIntersection2.
|
|
int LineSphereIntersection(
|
|
const Vector &vSphereCenter,
|
|
const float fSphereRadius,
|
|
const Vector &vLinePt,
|
|
const Vector &vLineDir,
|
|
float *fIntersection1,
|
|
float *fIntersection2);
|
|
|
|
void MatrixAngles( const matrix3x4_t & matrix, float *angles ); // !!!!
|
|
void VectorTransform (const float *in1, const matrix3x4_t & in2, float *out);
|
|
void VectorITransform (const float *in1, const matrix3x4_t & in2, float *out);
|
|
void VectorRotate( const float *in1, const matrix3x4_t & in2, float *out);
|
|
void VectorRotate( const Vector &in1, const QAngle &in2, Vector &out );
|
|
void VectorIRotate( const float *in1, const matrix3x4_t & in2, float *out);
|
|
|
|
#ifndef VECTOR_NO_SLOW_OPERATIONS
|
|
|
|
QAngle TransformAnglesToLocalSpace( const QAngle &angles, const matrix3x4_t &parentMatrix );
|
|
QAngle TransformAnglesToWorldSpace( const QAngle &angles, const matrix3x4_t &parentMatrix );
|
|
|
|
#endif
|
|
|
|
void MatrixInitialize( matrix3x4_t &mat, const Vector &vecOrigin, const Vector &vecXAxis, const Vector &vecYAxis, const Vector &vecZAxis );
|
|
void MatrixCopy( const matrix3x4_t &in, matrix3x4_t &out );
|
|
void MatrixInvert( const matrix3x4_t &in, matrix3x4_t &out );
|
|
|
|
void MatrixGetColumn( const matrix3x4_t &in, int column, Vector &out );
|
|
void MatrixSetColumn( const Vector &in, int column, matrix3x4_t &out );
|
|
|
|
//void DecomposeRotation( const matrix3x4_t &mat, float *out );
|
|
void ConcatRotations (const matrix3x4_t &in1, const matrix3x4_t &in2, matrix3x4_t &out);
|
|
void ConcatTransforms (const matrix3x4_t &in1, const matrix3x4_t &in2, matrix3x4_t &out);
|
|
|
|
void QuaternionSlerp( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
|
void QuaternionSlerpNoAlign( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
|
void QuaternionBlend( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
|
void QuaternionBlendNoAlign( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
|
void QuaternionIdentityBlend( const Quaternion &p, float t, Quaternion &qt );
|
|
float QuaternionAngleDiff( const Quaternion &p, const Quaternion &q );
|
|
void QuaternionScale( const Quaternion &p, float t, Quaternion &q );
|
|
void QuaternionAlign( const Quaternion &p, const Quaternion &q, Quaternion &qt );
|
|
float QuaternionDotProduct( const Quaternion &p, const Quaternion &q );
|
|
void QuaternionConjugate( const Quaternion &p, Quaternion &q );
|
|
void QuaternionInvert( const Quaternion &p, Quaternion &q );
|
|
float QuaternionNormalize( Quaternion &q );
|
|
void QuaternionAdd( const Quaternion &p, const Quaternion &q, Quaternion &qt );
|
|
void QuaternionMult( const Quaternion &p, const Quaternion &q, Quaternion &qt );
|
|
void QuaternionMatrix( const Quaternion &q, matrix3x4_t &matrix );
|
|
void QuaternionMatrix( const Quaternion &q, const Vector &pos, matrix3x4_t &matrix );
|
|
void QuaternionAngles( const Quaternion &q, QAngle &angles );
|
|
void AngleQuaternion( const QAngle& angles, Quaternion &qt );
|
|
void QuaternionAngles( const Quaternion &q, RadianEuler &angles );
|
|
void AngleQuaternion( RadianEuler const &angles, Quaternion &qt );
|
|
void QuaternionAxisAngle( const Quaternion &q, Vector &axis, float &angle );
|
|
void AxisAngleQuaternion( const Vector &axis, float angle, Quaternion &q );
|
|
void BasisToQuaternion( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, Quaternion &q );
|
|
|
|
// A couple methods to find the dot product of a vector with a matrix row or column...
|
|
inline float MatrixRowDotProduct( const matrix3x4_t &in1, int row, const Vector& in2 )
|
|
{
|
|
Assert( (row >= 0) && (row < 3) );
|
|
return DotProduct( in1[row], in2.Base() );
|
|
}
|
|
|
|
inline float MatrixColumnDotProduct( const matrix3x4_t &in1, int col, const Vector& in2 )
|
|
{
|
|
Assert( (col >= 0) && (col < 4) );
|
|
return in1[0][col] * in2[0] + in1[1][col] * in2[1] + in1[2][col] * in2[2];
|
|
}
|
|
|
|
int __cdecl BoxOnPlaneSide (const float *emins, const float *emaxs, const cplane_t *plane);
|
|
|
|
inline float anglemod(float a)
|
|
{
|
|
a = (360.f/65536) * ((int)(a*(65536.f/360.0f)) & 65535);
|
|
return a;
|
|
}
|
|
|
|
// Remap a value in the range [A,B] to [C,D].
|
|
inline float RemapVal( float val, float A, float B, float C, float D)
|
|
{
|
|
return C + (D - C) * (val - A) / (B - A);
|
|
}
|
|
|
|
inline float RemapValClamped( float val, float A, float B, float C, float D)
|
|
{
|
|
float cVal = (val - A) / (B - A);
|
|
cVal = clamp( cVal, 0.0f, 1.0f );
|
|
|
|
return C + (D - C) * cVal;
|
|
}
|
|
|
|
// Returns A + (B-A)*flPercent.
|
|
// float Lerp( float flPercent, float A, float B );
|
|
template <class T>
|
|
FORCEINLINE_MATH T Lerp( float flPercent, T const &A, T const &B )
|
|
{
|
|
return static_cast<T>(A + (B - A) * flPercent);
|
|
}
|
|
|
|
// 5-argument floating point linear interpolation.
|
|
// FLerp(f1,f2,i1,i2,x)=
|
|
// f1 at x=i1
|
|
// f2 at x=i2
|
|
// smooth lerp between f1 and f2 at x>i1 and x<i2
|
|
// extrapolation for x<i1 or x>i2
|
|
//
|
|
// If you know a function f(x)'s value (f1) at position i1, and its value (f2) at position i2,
|
|
// the function can be linearly interpolated with FLerp(f1,f2,i1,i2,x)
|
|
// i2=i1 will cause a divide by zero.
|
|
static inline float FLerp(float f1, float f2, float i1, float i2, float x)
|
|
{
|
|
return f1+(f2-f1)*(x-i1)/(i2-i1);
|
|
}
|
|
|
|
|
|
#ifndef VECTOR_NO_SLOW_OPERATIONS
|
|
|
|
// YWB: Specialization for interpolating euler angles via quaternions...
|
|
template<> FORCEINLINE_MATH QAngle Lerp<QAngle>( float flPercent, const QAngle& q1, const QAngle& q2 )
|
|
{
|
|
// Avoid precision errors
|
|
if ( q1 == q2 )
|
|
return q1;
|
|
|
|
Quaternion src, dest;
|
|
|
|
// Convert to quaternions
|
|
AngleQuaternion( q1, src );
|
|
AngleQuaternion( q2, dest );
|
|
|
|
Quaternion result;
|
|
|
|
// Slerp
|
|
QuaternionSlerp( src, dest, flPercent, result );
|
|
|
|
// Convert to euler
|
|
QAngle output;
|
|
QuaternionAngles( result, output );
|
|
return output;
|
|
}
|
|
|
|
#else
|
|
|
|
#pragma error
|
|
|
|
// NOTE NOTE: I haven't tested this!! It may not work! Check out interpolatedvar.cpp in the client dll to try it
|
|
template<> FORCEINLINE_TEMPLATE QAngleByValue Lerp<QAngleByValue>( float flPercent, const QAngleByValue& q1, const QAngleByValue& q2 )
|
|
{
|
|
// Avoid precision errors
|
|
if ( q1 == q2 )
|
|
return q1;
|
|
|
|
Quaternion src, dest;
|
|
|
|
// Convert to quaternions
|
|
AngleQuaternion( q1, src );
|
|
AngleQuaternion( q2, dest );
|
|
|
|
Quaternion result;
|
|
|
|
// Slerp
|
|
QuaternionSlerp( src, dest, flPercent, result );
|
|
|
|
// Convert to euler
|
|
QAngleByValue output;
|
|
QuaternionAngles( result, output );
|
|
return output;
|
|
}
|
|
|
|
#endif // VECTOR_NO_SLOW_OPERATIONS
|
|
|
|
|
|
// Swap two of anything.
|
|
template <class T>
|
|
FORCEINLINE_TEMPLATE void V_swap( T& x, T& y )
|
|
{
|
|
T temp = x;
|
|
x = y;
|
|
y = temp;
|
|
}
|
|
|
|
template <class T> FORCEINLINE_TEMPLATE T AVG(T a, T b)
|
|
{
|
|
return (a+b)/2;
|
|
}
|
|
|
|
// number of elements in an array of static size
|
|
#define NELEMS(x) ((sizeof(x))/sizeof(x[0]))
|
|
|
|
// XYZ macro, for printf type functions - ex printf("%f %f %f",XYZ(myvector));
|
|
#define XYZ(v) (v).x,(v).y,(v).z
|
|
|
|
//
|
|
// Returns a clamped value in the range [min, max].
|
|
//
|
|
#define V_clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val)))
|
|
|
|
inline float Sign( float x )
|
|
{
|
|
return (x <0.0f) ? -1.0f : 1.0f;
|
|
}
|
|
|
|
//
|
|
// Clamps the input integer to the given array bounds.
|
|
// Equivilent to the following, but without using any branches:
|
|
//
|
|
// if( n < 0 ) return 0;
|
|
// else if ( n > maxindex ) return maxindex;
|
|
// else return n;
|
|
//
|
|
// This is not always a clear performance win, but when you have situations where a clamped
|
|
// value is thrashing against a boundary this is a big win. (ie, valid, invalid, valid, invalid, ...)
|
|
//
|
|
// Note: This code has been run against all possible integers.
|
|
//
|
|
inline int ClampArrayBounds( int n, unsigned maxindex )
|
|
{
|
|
// mask is 0 if less than 4096, 0xFFFFFFFF if greater than
|
|
unsigned int inrangemask = 0xFFFFFFFF + (((unsigned) n) > maxindex );
|
|
unsigned int lessthan0mask = 0xFFFFFFFF + ( n >= 0 );
|
|
|
|
// If the result was valid, set the result, (otherwise sets zero)
|
|
int result = (inrangemask & n);
|
|
|
|
// if the result was out of range or zero.
|
|
result |= ((~inrangemask) & (~lessthan0mask)) & maxindex;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \
|
|
(((p)->type < 3)? \
|
|
( \
|
|
((p)->dist <= (emins)[(p)->type])? \
|
|
1 \
|
|
: \
|
|
( \
|
|
((p)->dist >= (emaxs)[(p)->type])?\
|
|
2 \
|
|
: \
|
|
3 \
|
|
) \
|
|
) \
|
|
: \
|
|
BoxOnPlaneSide( (emins), (emaxs), (p)))
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// FIXME: Vector versions.... the float versions will go away hopefully soon!
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool SphereToAABBIntersection( const Vector& sphCenter, float sphRadius,
|
|
const Vector& boxMin, const Vector& boxMax );
|
|
|
|
void AngleVectors (const QAngle& angles, Vector *forward);
|
|
void AngleVectors (const QAngle& angles, Vector *forward, Vector *right, Vector *up);
|
|
void AngleVectorsTranspose (const QAngle& angles, Vector *forward, Vector *right, Vector *up);
|
|
void AngleMatrix (const QAngle &angles, matrix3x4_t &mat );
|
|
void AngleMatrix( const QAngle &angles, const Vector &position, matrix3x4_t &mat );
|
|
void AngleMatrix (const RadianEuler &angles, matrix3x4_t &mat );
|
|
void AngleMatrix( RadianEuler const &angles, const Vector &position, matrix3x4_t &mat );
|
|
void AngleIMatrix (const QAngle &angles, matrix3x4_t &mat );
|
|
void AngleIMatrix (const QAngle &angles, const Vector &position, matrix3x4_t &mat );
|
|
void AngleIMatrix (const RadianEuler &angles, matrix3x4_t &mat );
|
|
void VectorAngles( const Vector &forward, QAngle &angles );
|
|
void VectorAngles( const Vector &forward, const Vector &pseudoup, QAngle &angles );
|
|
void VectorMatrix( const Vector &forward, matrix3x4_t &mat );
|
|
void VectorVectors( const Vector &forward, Vector &right, Vector &up );
|
|
void SetIdentityMatrix( matrix3x4_t &mat );
|
|
|
|
inline void PositionMatrix( const Vector &position, matrix3x4_t &mat )
|
|
{
|
|
MatrixSetColumn( position, 3, mat );
|
|
}
|
|
|
|
inline void MatrixPosition( const matrix3x4_t &matrix, Vector &position )
|
|
{
|
|
MatrixGetColumn( matrix, 3, position );
|
|
}
|
|
|
|
inline void VectorRotate( const Vector& in1, const matrix3x4_t &in2, Vector &out)
|
|
{
|
|
VectorRotate( &in1.x, in2, &out.x );
|
|
}
|
|
|
|
inline void VectorIRotate( const Vector& in1, const matrix3x4_t &in2, Vector &out)
|
|
{
|
|
VectorIRotate( &in1.x, in2, &out.x );
|
|
}
|
|
|
|
inline void MatrixAngles( const matrix3x4_t &matrix, QAngle &angles )
|
|
{
|
|
MatrixAngles( matrix, &angles.x );
|
|
}
|
|
|
|
inline void MatrixAngles( const matrix3x4_t &matrix, QAngle &angles, Vector &position )
|
|
{
|
|
MatrixAngles( matrix, angles );
|
|
MatrixPosition( matrix, position );
|
|
}
|
|
|
|
inline void MatrixAngles( const matrix3x4_t &matrix, RadianEuler &angles )
|
|
{
|
|
MatrixAngles( matrix, &angles.x );
|
|
|
|
angles.Init( DEG2RAD( angles.z ), DEG2RAD( angles.x ), DEG2RAD( angles.y ) );
|
|
}
|
|
|
|
void MatrixAngles( const matrix3x4_t &mat, RadianEuler &angles, Vector &position );
|
|
|
|
void MatrixAngles( const matrix3x4_t &mat, Quaternion &q, Vector &position );
|
|
|
|
inline int VectorCompare (const Vector& v1, const Vector& v2)
|
|
{
|
|
return v1 == v2;
|
|
}
|
|
|
|
inline void VectorTransform (const Vector& in1, const matrix3x4_t &in2, Vector &out)
|
|
{
|
|
VectorTransform( &in1.x, in2, &out.x );
|
|
}
|
|
|
|
inline void VectorITransform (const Vector& in1, const matrix3x4_t &in2, Vector &out)
|
|
{
|
|
VectorITransform( &in1.x, in2, &out.x );
|
|
}
|
|
|
|
/*
|
|
inline void DecomposeRotation( const matrix3x4_t &mat, Vector &out )
|
|
{
|
|
DecomposeRotation( mat, &out.x );
|
|
}
|
|
*/
|
|
|
|
inline int BoxOnPlaneSide (const Vector& emins, const Vector& emaxs, const cplane_t *plane )
|
|
{
|
|
return BoxOnPlaneSide( &emins.x, &emaxs.x, plane );
|
|
}
|
|
|
|
inline void VectorFill(Vector& a, float b)
|
|
{
|
|
a[0]=a[1]=a[2]=b;
|
|
}
|
|
|
|
inline void VectorNegate(Vector& a)
|
|
{
|
|
a[0] = -a[0];
|
|
a[1] = -a[1];
|
|
a[2] = -a[2];
|
|
}
|
|
|
|
inline vec_t VectorAvg(Vector& a)
|
|
{
|
|
return ( a[0] + a[1] + a[2] ) / 3;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Box/plane test (slow version)
|
|
//-----------------------------------------------------------------------------
|
|
inline int FASTCALL BoxOnPlaneSide2 (const Vector& emins, const Vector& emaxs, const cplane_t *p, float tolerance = 0.f )
|
|
{
|
|
Vector corners[2];
|
|
|
|
if (p->normal[0] < 0)
|
|
{
|
|
corners[0][0] = emins[0];
|
|
corners[1][0] = emaxs[0];
|
|
}
|
|
else
|
|
{
|
|
corners[1][0] = emins[0];
|
|
corners[0][0] = emaxs[0];
|
|
}
|
|
|
|
if (p->normal[1] < 0)
|
|
{
|
|
corners[0][1] = emins[1];
|
|
corners[1][1] = emaxs[1];
|
|
}
|
|
else
|
|
{
|
|
corners[1][1] = emins[1];
|
|
corners[0][1] = emaxs[1];
|
|
}
|
|
|
|
if (p->normal[2] < 0)
|
|
{
|
|
corners[0][2] = emins[2];
|
|
corners[1][2] = emaxs[2];
|
|
}
|
|
else
|
|
{
|
|
corners[1][2] = emins[2];
|
|
corners[0][2] = emaxs[2];
|
|
}
|
|
|
|
int sides = 0;
|
|
|
|
float dist1 = DotProduct (p->normal, corners[0]) - p->dist;
|
|
if (dist1 >= tolerance)
|
|
sides = 1;
|
|
|
|
float dist2 = DotProduct (p->normal, corners[1]) - p->dist;
|
|
if (dist2 < -tolerance)
|
|
sides |= 2;
|
|
|
|
return sides;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helpers for bounding box construction
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void ClearBounds (Vector& mins, Vector& maxs);
|
|
void AddPointToBounds (const Vector& v, Vector& mins, Vector& maxs);
|
|
|
|
//
|
|
// COLORSPACE/GAMMA CONVERSION STUFF
|
|
//
|
|
void BuildGammaTable( float gamma, float texGamma, float brightness, int overbright );
|
|
void BuildExponentTable( );
|
|
|
|
// convert texture to linear 0..1 value
|
|
inline float TexLightToLinear( int c, int exponent )
|
|
{
|
|
extern float power2_n[256];
|
|
Assert( exponent >= -128 && exponent <= 127 );
|
|
return ( float )c * power2_n[exponent+128];
|
|
}
|
|
|
|
|
|
// convert texture to linear 0..1 value
|
|
int LinearToTexture( float f );
|
|
// converts 0..1 linear value to screen gamma (0..255)
|
|
int LinearToScreenGamma( float f );
|
|
float TextureToLinear( int c );
|
|
|
|
// compressed color format
|
|
struct ColorRGBExp32
|
|
{
|
|
byte r, g, b;
|
|
signed char exponent;
|
|
};
|
|
|
|
void ColorRGBExp32ToVector( const ColorRGBExp32& in, Vector& out );
|
|
void VectorToColorRGBExp32( const Vector& v, ColorRGBExp32 &c );
|
|
|
|
// solve for "x" where "a x^2 + b x + c = 0", return true if solution exists
|
|
bool SolveQuadratic( float a, float b, float c, float &root1, float &root2 );
|
|
|
|
// solves for "a, b, c" where "a x^2 + b x + c = y", return true if solution exists
|
|
bool SolveInverseQuadratic( float x1, float y1, float x2, float y2, float x3, float y3, float &a, float &b, float &c );
|
|
|
|
// solves for a,b,c specified as above, except that it always creates a monotonically increasing or
|
|
// decreasing curve if the data is monotonically increasing or decreasing. In order to enforce the
|
|
// monoticity condition, it is possible that the resulting quadratic will only approximate the data
|
|
// instead of interpolating it. This code is not especially fast.
|
|
bool SolveInverseQuadraticMonotonic( float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float &a, float &b, float &c );
|
|
|
|
|
|
|
|
|
|
// solves for "a, b, c" where "1/(a x^2 + b x + c ) = y", return true if solution exists
|
|
bool SolveInverseReciprocalQuadratic( float x1, float y1, float x2, float y2, float x3, float y3, float &a, float &b, float &c );
|
|
|
|
// rotate a vector around the Z axis (YAW)
|
|
void VectorYawRotate( const Vector& in, float flYaw, Vector &out);
|
|
|
|
|
|
// Bias takes an X value between 0 and 1 and returns another value between 0 and 1
|
|
// The curve is biased towards 0 or 1 based on biasAmt, which is between 0 and 1.
|
|
// Lower values of biasAmt bias the curve towards 0 and higher values bias it towards 1.
|
|
//
|
|
// For example, with biasAmt = 0.2, the curve looks like this:
|
|
//
|
|
// 1
|
|
// | *
|
|
// | *
|
|
// | *
|
|
// | **
|
|
// | **
|
|
// | ****
|
|
// |*********
|
|
// |___________________
|
|
// 0 1
|
|
//
|
|
//
|
|
// With biasAmt = 0.8, the curve looks like this:
|
|
//
|
|
// 1
|
|
// | **************
|
|
// | **
|
|
// | *
|
|
// | *
|
|
// |*
|
|
// |*
|
|
// |*
|
|
// |___________________
|
|
// 0 1
|
|
//
|
|
// With a biasAmt of 0.5, Bias returns X.
|
|
float Bias( float x, float biasAmt );
|
|
|
|
|
|
// Gain is similar to Bias, but biasAmt biases towards or away from 0.5.
|
|
// Lower bias values bias towards 0.5 and higher bias values bias away from it.
|
|
//
|
|
// For example, with biasAmt = 0.2, the curve looks like this:
|
|
//
|
|
// 1
|
|
// | *
|
|
// | *
|
|
// | **
|
|
// | ***************
|
|
// | **
|
|
// | *
|
|
// |*
|
|
// |___________________
|
|
// 0 1
|
|
//
|
|
//
|
|
// With biasAmt = 0.8, the curve looks like this:
|
|
//
|
|
// 1
|
|
// | *****
|
|
// | ***
|
|
// | *
|
|
// | *
|
|
// | *
|
|
// | ***
|
|
// |*****
|
|
// |___________________
|
|
// 0 1
|
|
float Gain( float x, float biasAmt );
|
|
|
|
|
|
// SmoothCurve maps a 0-1 value into another 0-1 value based on a cosine wave
|
|
// where the derivatives of the function at 0 and 1 (and 0.5) are 0. This is useful for
|
|
// any fadein/fadeout effect where it should start and end smoothly.
|
|
//
|
|
// The curve looks like this:
|
|
//
|
|
// 1
|
|
// | **
|
|
// | * *
|
|
// | * *
|
|
// | * *
|
|
// | * *
|
|
// | ** **
|
|
// |*** ***
|
|
// |___________________
|
|
// 0 1
|
|
//
|
|
float SmoothCurve( float x );
|
|
|
|
|
|
// This works like SmoothCurve, with two changes:
|
|
//
|
|
// 1. Instead of the curve peaking at 0.5, it will peak at flPeakPos.
|
|
// (So if you specify flPeakPos=0.2, then the peak will slide to the left).
|
|
//
|
|
// 2. flPeakSharpness is a 0-1 value controlling the sharpness of the peak.
|
|
// Low values blunt the peak and high values sharpen the peak.
|
|
float SmoothCurve_Tweak( float x, float flPeakPos=0.5, float flPeakSharpness=0.5 );
|
|
|
|
|
|
//float ExponentialDecay( float halflife, float dt );
|
|
//float ExponentialDecay( float decayTo, float decayTime, float dt );
|
|
|
|
// halflife is time for value to reach 50%
|
|
inline float ExponentialDecay( float halflife, float dt )
|
|
{
|
|
// log(0.5) == -0.69314718055994530941723212145818
|
|
return expf( -0.69314718f / halflife * dt);
|
|
}
|
|
|
|
// decayTo is factor the value should decay to in decayTime
|
|
inline float ExponentialDecay( float decayTo, float decayTime, float dt )
|
|
{
|
|
return expf( logf( decayTo ) / decayTime * dt);
|
|
}
|
|
|
|
|
|
// hermite basis function for smooth interpolation
|
|
// Similar to Gain() above, but very cheap to call
|
|
// value should be between 0 & 1 inclusive
|
|
inline float SimpleSpline( float value )
|
|
{
|
|
float valueSquared = value * value;
|
|
|
|
// Nice little ease-in, ease-out spline-like curve
|
|
return (3 * valueSquared - 2 * valueSquared * value);
|
|
}
|
|
|
|
// remaps a value in [startInterval, startInterval+rangeInterval] from linear to
|
|
// spline using SimpleSpline
|
|
inline float SimpleSplineRemapVal( float val, float A, float B, float C, float D)
|
|
{
|
|
return C + (D - C) * SimpleSpline( (val - A) / (B - A) );
|
|
}
|
|
|
|
inline int RoundFloatToInt(float f)
|
|
{
|
|
int nResult;
|
|
#ifdef _WIN32
|
|
__asm
|
|
{
|
|
fld f
|
|
fistp nResult
|
|
}
|
|
#elif _LINUX
|
|
__asm __volatile__ (
|
|
"fistpl %0;": "=m" (nResult): "t" (f) : "st"
|
|
);
|
|
#endif
|
|
return nResult;
|
|
}
|
|
|
|
inline unsigned char RoundFloatToByte(float f)
|
|
{
|
|
int nResult;
|
|
|
|
#ifdef _WIN32
|
|
__asm
|
|
{
|
|
fld f
|
|
fistp nResult
|
|
}
|
|
#elif _LINUX
|
|
__asm __volatile__ (
|
|
"fistpl %0;": "=m" (nResult): "t" (f) : "st"
|
|
);
|
|
#endif
|
|
|
|
#ifdef Assert
|
|
Assert( nResult >= 0 && nResult <= 255 );
|
|
#endif
|
|
return nResult;
|
|
}
|
|
|
|
inline unsigned long RoundFloatToUnsignedLong(float f)
|
|
{
|
|
unsigned char nResult[8];
|
|
|
|
#ifdef _WIN32
|
|
__asm
|
|
{
|
|
fld f
|
|
fistp qword ptr nResult
|
|
}
|
|
#elif _LINUX
|
|
__asm __volatile__ (
|
|
"fistpl %0;": "=m" (nResult): "t" (f) : "st"
|
|
);
|
|
#endif
|
|
|
|
return *((unsigned long*)nResult);
|
|
}
|
|
|
|
// Fast, accurate ftol:
|
|
inline int Float2Int( float a )
|
|
{
|
|
int RetVal;
|
|
|
|
#ifdef _WIN32
|
|
int CtrlwdHolder;
|
|
int CtrlwdSetter;
|
|
|
|
__asm
|
|
{
|
|
fld a // push 'a' onto the FP stack
|
|
fnstcw CtrlwdHolder // store FPU control word
|
|
movzx eax, CtrlwdHolder // move and zero extend word into eax
|
|
and eax, 0xFFFFF3FF // set all bits except rounding bits to 1
|
|
or eax, 0x00000C00 // set rounding mode bits to round down
|
|
mov CtrlwdSetter, eax // Prepare to set the rounding mode -- prepare to enter plaid!
|
|
fldcw CtrlwdSetter // Entering plaid!
|
|
fistp RetVal // Store and converted (to int) result
|
|
fldcw CtrlwdHolder // Restore control word
|
|
}
|
|
#elif _LINUX
|
|
RetVal = static_cast<int>( a );
|
|
#endif
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
// Over 15x faster than: (int)floor(value)
|
|
inline int Floor2Int( float a )
|
|
{
|
|
int RetVal;
|
|
|
|
#ifdef _WIN32
|
|
int CtrlwdHolder;
|
|
int CtrlwdSetter;
|
|
|
|
__asm
|
|
{
|
|
fld a // push 'a' onto the FP stack
|
|
fnstcw CtrlwdHolder // store FPU control word
|
|
movzx eax, CtrlwdHolder // move and zero extend word into eax
|
|
and eax, 0xFFFFF3FF // set all bits except rounding bits to 1
|
|
or eax, 0x00000400 // set rounding mode bits to round down
|
|
mov CtrlwdSetter, eax // Prepare to set the rounding mode -- prepare to enter plaid!
|
|
fldcw CtrlwdSetter // Entering plaid!
|
|
fistp RetVal // Store floored and converted (to int) result
|
|
fldcw CtrlwdHolder // Restore control word
|
|
}
|
|
#elif _LINUX
|
|
RetVal = static_cast<int>( floor(a) );
|
|
#endif
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Fast color conversion from float to unsigned char
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE_MATH unsigned char FastFToC( float c )
|
|
{
|
|
volatile float dc;
|
|
dc = c * 255.0f + ( float )( 1 << 23 );
|
|
return *(unsigned char*)&dc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Bound input float to .001 (millisecond) boundary
|
|
// Input : in -
|
|
// Output : inline float
|
|
//-----------------------------------------------------------------------------
|
|
inline float ClampToMsec( float in )
|
|
{
|
|
int msec = Floor2Int( in * 1000.0f + 0.5f );
|
|
return msec / 1000.0f;
|
|
}
|
|
|
|
// Over 15x faster than: (int)ceil(value)
|
|
inline int Ceil2Int( float a )
|
|
{
|
|
int RetVal;
|
|
|
|
#ifdef _WIN32
|
|
int CtrlwdHolder;
|
|
int CtrlwdSetter;
|
|
|
|
__asm
|
|
{
|
|
fld a // push 'a' onto the FP stack
|
|
fnstcw CtrlwdHolder // store FPU control word
|
|
movzx eax, CtrlwdHolder // move and zero extend word into eax
|
|
and eax, 0xFFFFF3FF // set all bits except rounding bits to 1
|
|
or eax, 0x00000800 // set rounding mode bits to round down
|
|
mov CtrlwdSetter, eax // Prepare to set the rounding mode -- prepare to enter plaid!
|
|
fldcw CtrlwdSetter // Entering plaid!
|
|
fistp RetVal // Store floored and converted (to int) result
|
|
fldcw CtrlwdHolder // Restore control word
|
|
}
|
|
#elif _LINUX
|
|
RetVal = static_cast<int>( ceil(a) );
|
|
#endif
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
|
|
// Regular signed area of triangle
|
|
#define TriArea2D( A, B, C ) \
|
|
( 0.5f * ( ( B.x - A.x ) * ( C.y - A.y ) - ( B.y - A.y ) * ( C.x - A.x ) ) )
|
|
|
|
// This version doesn't premultiply by 0.5f, so it's the area of the rectangle instead
|
|
#define TriArea2DTimesTwo( A, B, C ) \
|
|
( ( ( B.x - A.x ) * ( C.y - A.y ) - ( B.y - A.y ) * ( C.x - A.x ) ) )
|
|
|
|
|
|
// Get the barycentric coordinates of "pt" in triangle [A,B,C].
|
|
inline void GetBarycentricCoords2D(
|
|
Vector2D const &A,
|
|
Vector2D const &B,
|
|
Vector2D const &C,
|
|
Vector2D const &pt,
|
|
float bcCoords[3] )
|
|
{
|
|
// Note, because to top and bottom are both x2, the issue washes out in the composite
|
|
float invTriArea = 1.0f / TriArea2DTimesTwo( A, B, C );
|
|
|
|
// NOTE: We assume here that the lightmap coordinate vertices go counterclockwise.
|
|
// If not, TriArea2D() is negated so this works out right.
|
|
bcCoords[0] = TriArea2DTimesTwo( B, C, pt ) * invTriArea;
|
|
bcCoords[1] = TriArea2DTimesTwo( C, A, pt ) * invTriArea;
|
|
bcCoords[2] = TriArea2DTimesTwo( A, B, pt ) * invTriArea;
|
|
}
|
|
|
|
|
|
// Return true of the sphere might touch the box (the sphere is actually treated
|
|
// like a box itself, so this may return true if the sphere's bounding box touches
|
|
// a corner of the box but the sphere itself doesn't).
|
|
inline bool QuickBoxSphereTest(
|
|
const Vector& vOrigin,
|
|
float flRadius,
|
|
const Vector& bbMin,
|
|
const Vector& bbMax )
|
|
{
|
|
return vOrigin.x - flRadius < bbMax.x && vOrigin.x + flRadius > bbMin.x &&
|
|
vOrigin.y - flRadius < bbMax.y && vOrigin.y + flRadius > bbMin.y &&
|
|
vOrigin.z - flRadius < bbMax.z && vOrigin.z + flRadius > bbMin.z;
|
|
}
|
|
|
|
|
|
// Return true of the boxes intersect (but not if they just touch).
|
|
inline bool QuickBoxIntersectTest(
|
|
const Vector& vBox1Min,
|
|
const Vector& vBox1Max,
|
|
const Vector& vBox2Min,
|
|
const Vector& vBox2Max )
|
|
{
|
|
return
|
|
vBox1Min.x < vBox2Max.x && vBox1Max.x > vBox2Min.x &&
|
|
vBox1Min.y < vBox2Max.y && vBox1Max.y > vBox2Min.y &&
|
|
vBox1Min.z < vBox2Max.z && vBox1Max.z > vBox2Min.z;
|
|
}
|
|
|
|
|
|
extern float GammaToLinear( float gamma );
|
|
extern float LinearToGamma( float linear );
|
|
extern float GammaToLinearFullRange( float gamma );
|
|
extern float LinearToGammaFullRange( float linear );
|
|
|
|
// linear (0..4) to screen corrected vertex space (0..1?)
|
|
FORCEINLINE_MATH float LinearToVertexLight( float f )
|
|
{
|
|
extern float lineartovertex[4096];
|
|
|
|
// Gotta clamp before the multiply; could overflow...
|
|
int i = RoundFloatToInt(f * 1024.f); // assume 0..4 range
|
|
|
|
// Presumably the comman case will be not to clamp, so check that first:
|
|
if( (unsigned)i > 4095 )
|
|
{
|
|
if( i < 0 ) i = 0; // Compare to zero instead of 4095 to save 4 bytes in the instruction stream
|
|
else i = 4095;
|
|
}
|
|
|
|
return lineartovertex[i];
|
|
}
|
|
|
|
|
|
FORCEINLINE_MATH unsigned char LinearToLightmap( float f )
|
|
{
|
|
extern unsigned char lineartolightmap[4096];
|
|
|
|
// Gotta clamp before the multiply; could overflow...
|
|
int i = RoundFloatToInt( f * 1024.f ); // assume 0..4 range
|
|
|
|
// Presumably the comman case will be not to clamp, so check that first:
|
|
if( (unsigned)i > 4095 )
|
|
{
|
|
if( i < 0 ) i = 0; // Compare to zero instead of 4095 to save 4 bytes in the instruction stream
|
|
else i = 4095;
|
|
}
|
|
|
|
return lineartolightmap[i];
|
|
}
|
|
|
|
FORCEINLINE_MATH void ColorClamp( Vector& color )
|
|
{
|
|
float max = VectorMaximum( color );
|
|
if( max > 1.0f )
|
|
{
|
|
float ooMax = 1.0f / max;
|
|
color.x *= ooMax;
|
|
color.y *= ooMax;
|
|
color.z *= ooMax;
|
|
}
|
|
|
|
#if 1
|
|
if( color[0] < 0.f ) color[0] = 0.f;
|
|
if( color[1] < 0.f ) color[1] = 0.f;
|
|
if( color[2] < 0.f ) color[2] = 0.f;
|
|
#else
|
|
#ifdef Assert
|
|
Assert( color[0] >= 0.0f && color[0] <= 1.0f );
|
|
Assert( color[1] >= 0.0f && color[1] <= 1.0f );
|
|
Assert( color[2] >= 0.0f && color[2] <= 1.0f );
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
inline void ColorClampTruncate( Vector& color )
|
|
{
|
|
if (color[0] > 1.0f) color[0] = 1.0f; else if (color[0] < 0.0f) color[0] = 0.0f;
|
|
if (color[1] > 1.0f) color[1] = 1.0f; else if (color[1] < 0.0f) color[1] = 0.0f;
|
|
if (color[2] > 1.0f) color[2] = 1.0f; else if (color[2] < 0.0f) color[2] = 0.0f;
|
|
}
|
|
|
|
// Interpolate a Catmull-Rom spline.
|
|
// t is a [0,1] value and interpolates a curve between p2 and p3.
|
|
void Catmull_Rom_Spline(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector &output );
|
|
|
|
// Interpolate a Catmull-Rom spline.
|
|
// Returns the tangent of the point at t of the spline
|
|
void Catmull_Rom_Spline_Tangent(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector &output );
|
|
|
|
// Interpolate a Catmull-Rom spline.
|
|
// Normalize p2->p1 and p3->p4 to be the same length as p2->p3
|
|
void Catmull_Rom_Spline_Normalize(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector &output );
|
|
|
|
// Interpolate a Catmull-Rom spline.
|
|
// Normalize p2.x->p1.x and p3.x->p4.x to be the same length as p2.x->p3.x
|
|
void Catmull_Rom_Spline_NormalizeX(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector &output );
|
|
|
|
|
|
// Interpolate a Hermite spline.
|
|
// t is a [0,1] value and interpolates a curve between p1 and p2 with the deltas d1 and d2.
|
|
void Hermite_Spline(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &d1,
|
|
const Vector &d2,
|
|
float t,
|
|
Vector& output );
|
|
|
|
float Hermite_Spline(
|
|
float p1,
|
|
float p2,
|
|
float d1,
|
|
float d2,
|
|
float t );
|
|
|
|
// t is a [0,1] value and interpolates a curve between p1 and p2 with the slopes p0->p1 and p1->p2
|
|
void Hermite_Spline(
|
|
const Vector &p0,
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
float t,
|
|
Vector& output );
|
|
|
|
float Hermite_Spline(
|
|
float p0,
|
|
float p1,
|
|
float p2,
|
|
float t );
|
|
|
|
|
|
void Hermite_SplineBasis( float t, float basis[] );
|
|
|
|
void Hermite_Spline(
|
|
const Quaternion &q0,
|
|
const Quaternion &q1,
|
|
const Quaternion &q2,
|
|
float t,
|
|
Quaternion &output );
|
|
|
|
|
|
// See http://en.wikipedia.org/wiki/Kochanek-Bartels_curves
|
|
//
|
|
// Tension: -1 = Round -> 1 = Tight
|
|
// Bias: -1 = Pre-shoot (bias left) -> 1 = Post-shoot (bias right)
|
|
// Continuity: -1 = Box corners -> 1 = Inverted corners
|
|
//
|
|
// If T=B=C=0 it's the same matrix as Catmull-Rom.
|
|
// If T=1 & B=C=0 it's the same as Cubic.
|
|
// If T=B=0 & C=-1 it's just linear interpolation
|
|
//
|
|
// See http://news.povray.org/povray.binaries.tutorials/attachment/%3CXns91B880592482seed7@povray.org%3E/Splines.bas.txt
|
|
// for example code and descriptions of various spline types...
|
|
//
|
|
void Kochanek_Bartels_Spline(
|
|
float tension,
|
|
float bias,
|
|
float continuity,
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
void Kochanek_Bartels_Spline_NormalizeX(
|
|
float tension,
|
|
float bias,
|
|
float continuity,
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
// See link at Kochanek_Bartels_Spline for info on the basis matrix used
|
|
void Cubic_Spline(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
void Cubic_Spline_NormalizeX(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
// See link at Kochanek_Bartels_Spline for info on the basis matrix used
|
|
void BSpline(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
void BSpline_NormalizeX(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
// See link at Kochanek_Bartels_Spline for info on the basis matrix used
|
|
void Parabolic_Spline(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
void Parabolic_Spline_NormalizeX(
|
|
const Vector &p1,
|
|
const Vector &p2,
|
|
const Vector &p3,
|
|
const Vector &p4,
|
|
float t,
|
|
Vector& output );
|
|
|
|
float RangeCompressor( float flValue, float flMin, float flMax, float flBase );
|
|
|
|
// Get the minimum distance from vOrigin to the bounding box defined by [mins,maxs]
|
|
// using voronoi regions.
|
|
// 0 is returned if the origin is inside the box.
|
|
float CalcSqrDistanceToAABB( const Vector &mins, const Vector &maxs, const Vector &point );
|
|
void CalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point, Vector &closestOut );
|
|
|
|
inline float CalcDistanceToAABB( const Vector &mins, const Vector &maxs, const Vector &point )
|
|
{
|
|
float flDistSqr = CalcSqrDistanceToAABB( mins, maxs, point );
|
|
return sqrt(flDistSqr);
|
|
}
|
|
|
|
// Get the closest point from P to the (infinite) line through vLineA and vLineB and
|
|
// calculate the shortest distance from P to the line.
|
|
// If you pass in a value for t, it will tell you the t for (A + (B-A)t) to get the closest point.
|
|
// If the closest point lies on the segment between A and B, then 0 <= t <= 1.
|
|
void CalcClosestPointOnLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, Vector &vClosest, float *t=0 );
|
|
float CalcDistanceToLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 );
|
|
float CalcDistanceSqrToLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 );
|
|
|
|
// The same three functions as above, except now the line is closed between A and B.
|
|
void CalcClosestPointOnLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, Vector &vClosest, float *t=0 );
|
|
float CalcDistanceToLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 );
|
|
float CalcDistanceSqrToLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 );
|
|
|
|
// A function to compute the closes line segment connnection two lines (or false if the lines are parallel, etc.)
|
|
bool CalcLineToLineIntersectionSegment(
|
|
const Vector& p1,const Vector& p2,const Vector& p3,const Vector& p4,Vector *s1,Vector *s2,
|
|
float *t1, float *t2 );
|
|
|
|
// The above functions in 2D
|
|
void CalcClosestPointOnLine2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, Vector2D &vClosest, float *t=0 );
|
|
float CalcDistanceToLine2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 );
|
|
float CalcDistanceSqrToLine2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 );
|
|
void CalcClosestPointOnLineSegment2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, Vector2D &vClosest, float *t=0 );
|
|
float CalcDistanceToLineSegment2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 );
|
|
float CalcDistanceSqrToLineSegment2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 );
|
|
|
|
// Init the mathlib
|
|
void MathLib_Init( float gamma, float texGamma, float brightness, int overbright, bool bAllow3DNow = true, bool bAllowSSE = true, bool bAllowSSE2 = true, bool bAllowMMX = true );
|
|
bool MathLib_3DNowEnabled( void );
|
|
bool MathLib_MMXEnabled( void );
|
|
bool MathLib_SSEEnabled( void );
|
|
bool MathLib_SSE2Enabled( void );
|
|
|
|
float Approach( float target, float value, float speed );
|
|
float ApproachAngle( float target, float value, float speed );
|
|
float AngleDiff( float destAngle, float srcAngle );
|
|
float AngleDistance( float next, float cur );
|
|
float AngleNormalize( float angle );
|
|
|
|
// ensure that 0 <= angle <= 360
|
|
float AngleNormalizePositive( float angle );
|
|
|
|
bool AnglesAreEqual( float a, float b, float tolerance = 0.0f );
|
|
|
|
|
|
void RotationDeltaAxisAngle( const QAngle &srcAngles, const QAngle &destAngles, Vector &deltaAxis, float &deltaAngle );
|
|
void RotationDelta( const QAngle &srcAngles, const QAngle &destAngles, QAngle *out );
|
|
|
|
void ComputeTrianglePlane( const Vector& v1, const Vector& v2, const Vector& v3, Vector& normal, float& intercept );
|
|
int PolyFromPlane( Vector *outVerts, const Vector& normal, float dist, float fHalfScale = 9000.0f );
|
|
int ClipPolyToPlane( Vector *inVerts, int vertCount, Vector *outVerts, const Vector& normal, float dist, float fOnPlaneEpsilon = 0.1f );
|
|
int ClipPolyToPlane_Precise( double *inVerts, int vertCount, double *outVerts, const double *normal, double dist, double fOnPlaneEpsilon = 0.1 );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Transforms a AABB into another space; which will inherently grow the box.
|
|
//-----------------------------------------------------------------------------
|
|
void TransformAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Uses the inverse transform of in1
|
|
//-----------------------------------------------------------------------------
|
|
void ITransformAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Rotates a AABB into another space; which will inherently grow the box.
|
|
// (same as TransformAABB, but doesn't take the translation into account)
|
|
//-----------------------------------------------------------------------------
|
|
void RotateAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Uses the inverse transform of in1
|
|
//-----------------------------------------------------------------------------
|
|
void IRotateAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Transform a plane
|
|
//-----------------------------------------------------------------------------
|
|
inline void MatrixTransformPlane( const matrix3x4_t &src, const cplane_t &inPlane, cplane_t &outPlane )
|
|
{
|
|
// What we want to do is the following:
|
|
// 1) transform the normal into the new space.
|
|
// 2) Determine a point on the old plane given by plane dist * plane normal
|
|
// 3) Transform that point into the new space
|
|
// 4) Plane dist = DotProduct( new normal, new point )
|
|
|
|
// An optimized version, which works if the plane is orthogonal.
|
|
// 1) Transform the normal into the new space
|
|
// 2) Realize that transforming the old plane point into the new space
|
|
// is given by [ d * n'x + Tx, d * n'y + Ty, d * n'z + Tz ]
|
|
// where d = old plane dist, n' = transformed normal, Tn = translational component of transform
|
|
// 3) Compute the new plane dist using the dot product of the normal result of #2
|
|
|
|
// For a correct result, this should be an inverse-transpose matrix
|
|
// but that only matters if there are nonuniform scale or skew factors in this matrix.
|
|
VectorRotate( inPlane.normal, src, outPlane.normal );
|
|
outPlane.dist = inPlane.dist * DotProduct( outPlane.normal, outPlane.normal );
|
|
outPlane.dist += outPlane.normal.x * src[0][3] + outPlane.normal.y * src[1][3] + outPlane.normal.z * src[2][3];
|
|
}
|
|
|
|
inline void MatrixITransformPlane( const matrix3x4_t &src, const cplane_t &inPlane, cplane_t &outPlane )
|
|
{
|
|
// The trick here is that Tn = translational component of transform,
|
|
// but for an inverse transform, Tn = - R^-1 * T
|
|
Vector vecTranslation;
|
|
MatrixGetColumn( src, 3, vecTranslation );
|
|
|
|
Vector vecInvTranslation;
|
|
VectorIRotate( vecTranslation, src, vecInvTranslation );
|
|
|
|
VectorIRotate( inPlane.normal, src, outPlane.normal );
|
|
outPlane.dist = inPlane.dist * DotProduct( outPlane.normal, outPlane.normal );
|
|
outPlane.dist -= outPlane.normal.x * vecInvTranslation[0] + outPlane.normal.y * vecInvTranslation[1] + outPlane.normal.z * vecInvTranslation[2];
|
|
}
|
|
|
|
int CeilPow2( int in );
|
|
int FloorPow2( int in );
|
|
|
|
inline float *UnpackNormal( unsigned int *pPackedNormal, float *pNormal )
|
|
{
|
|
int temp[3];
|
|
temp[0] = ((*pPackedNormal >> 0L) & 0x7ff);
|
|
if ( temp[0] & 0x400 )
|
|
{
|
|
temp[0] = 2048 - temp[0];
|
|
}
|
|
temp[1] = ((*pPackedNormal >> 11L) & 0x7ff);
|
|
if ( temp[1] & 0x400 )
|
|
{
|
|
temp[1] = 2048 - temp[1];
|
|
}
|
|
temp[2] = ((*pPackedNormal >> 22L) & 0x3ff);
|
|
if ( temp[2] & 0x200 )
|
|
{
|
|
temp[2] = 1024 - temp[2];
|
|
}
|
|
pNormal[0] = (float)temp[0] * 1.0f/1023.0f;
|
|
pNormal[1] = (float)temp[1] * 1.0f/1023.0f;
|
|
pNormal[2] = (float)temp[2] * 1.0f/511.0f;
|
|
return pNormal;
|
|
}
|
|
|
|
FORCEINLINE_MATH unsigned int *PackNormal( float *pNormal, unsigned int *pPackedNormal )
|
|
{
|
|
int temp[3];
|
|
|
|
temp[0] = Float2Int( pNormal[0] * 1023.0f );
|
|
temp[1] = Float2Int( pNormal[1] * 1023.0f );
|
|
temp[2] = Float2Int( pNormal[2] * 511.0f );
|
|
|
|
// the normal is out of bounds, determine the source and fix
|
|
// clamping would be even more of a slowdown here
|
|
Assert( temp[0] >= -1023 && temp[0] <= 1023 );
|
|
Assert( temp[1] >= -1023 && temp[1] <= 1023 );
|
|
Assert( temp[2] >= -511 && temp[2] <= 511 );
|
|
|
|
*pPackedNormal = ( ( temp[2] & 0x3ff ) << 22L ) |
|
|
( ( temp[1] & 0x7ff ) << 11L ) |
|
|
( ( temp[0] & 0x7ff ) << 0L );
|
|
return pPackedNormal;
|
|
}
|
|
|
|
FORCEINLINE_MATH unsigned int *PackNormal( float nx, float ny, float nz, unsigned int *pPackedNormal )
|
|
{
|
|
int temp[3];
|
|
|
|
temp[0] = Float2Int( nx * 1023.0f );
|
|
temp[1] = Float2Int( ny * 1023.0f );
|
|
temp[2] = Float2Int( nz * 511.0f );
|
|
|
|
// the normal is out of bounds, determine the source and fix
|
|
// clamping would be even more of a slowdown here
|
|
Assert( temp[0] >= -1023 && temp[0] <= 1023 );
|
|
Assert( temp[1] >= -1023 && temp[1] <= 1023 );
|
|
Assert( temp[2] >= -511 && temp[2] <= 511 );
|
|
|
|
*pPackedNormal = ( ( temp[2] & 0x3ff ) << 22L ) |
|
|
( ( temp[1] & 0x7ff ) << 11L ) |
|
|
( ( temp[0] & 0x7ff ) << 0L );
|
|
return pPackedNormal;
|
|
}
|
|
|
|
#endif // MATH_BASE_H
|
|
|