Serious-Engine/Sources/Engine/Math/Functions.h

616 lines
13 KiB
C++
Executable File

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#ifndef SE_INCL_FUNCTIONS_H
#define SE_INCL_FUNCTIONS_H
#ifdef PRAGMA_ONCE
#pragma once
#endif
// asm shortcuts
#define O offset
#define Q qword ptr
#define D dword ptr
#define W word ptr
#define B byte ptr
/*
* template implementations
*/
template<class Type>
inline Type Abs( const Type x)
{
return ( x>=Type(0) ? x : -x );
}
template<class Type>
inline Type Max( const Type a, const Type b)
{
return ( a<b ? b : a );
}
template<class Type>
inline Type Min( const Type a, const Type b)
{
return ( a>b ? b : a );
}
// linear interpolation
template<class Type>
inline Type Lerp( const Type x0, const Type x1, const FLOAT fRatio)
{
if( fRatio==0) return x0;
else if( fRatio==1) return x1;
else return ((Type) (x0+(x1-x0)*fRatio));
}
template<class Type>
inline Type Sgn( const Type x)
{
return (x)>Type(0) ? Type(1):( x<0 ? Type(-1):Type(0) );
}
template<class Type>
inline Type SgnNZ( const Type x)
{
return (x)>=Type(0) ? Type(1):Type(-1);
}
template<class Type>
inline void Swap( Type &a, Type &b)
{
Type t=a; a=b; b=t;
}
template<class Type>
inline Type ClampUp( const Type x, const Type uplimit)
{
return ( x<=uplimit ? x : uplimit );
}
template<class Type>
inline Type ClampDn( const Type x, const Type dnlimit)
{
return ( x>=dnlimit ? x : dnlimit );
}
template<class Type>
inline Type Clamp( const Type x, const Type dnlimit, const Type uplimit)
{
return ( x>=dnlimit ? (x<=uplimit ? x : uplimit): dnlimit );
}
/*
* fast implementations
*/
inline DOUBLE Abs( const DOUBLE f) { return fabs(f); }
inline FLOAT Abs( const FLOAT f) { return (FLOAT)fabs(f); }
inline SLONG Abs( const SLONG sl) { return labs(sl); }
/*
inline FLOAT Min( const FLOAT fA, const FLOAT fB)
{
FLOAT fRet;
__asm {
fld D [fA]
fld D [fB]
fucomi st(0),st(1)
fcmovnb st(0),st(1)
ffree st(1)
fstp D [fRet]
}
return fRet;
}
inline FLOAT Max( const FLOAT fA, const FLOAT fB)
{
FLOAT fRet;
__asm {
fld D [fA]
fld D [fB]
fucomi st(0),st(1)
fcmovb st(0),st(1)
ffree st(1)
fstp D [fRet]
}
return fRet;
}
inline SLONG Min( const SLONG slA, const SLONG slB)
{
SLONG slRet;
__asm {
mov eax,D [slA]
cmp eax,D [slB]
cmovg eax,D [slB]
mov D [slRet],eax
}
return slRet;
}
inline ULONG Min( const ULONG slA, const ULONG slB)
{
ULONG ulRet;
__asm {
mov eax,D [slA]
cmp eax,D [slB]
cmova eax,D [slB]
mov D [ulRet],eax
}
return ulRet;
}
inline SLONG Max( const SLONG slA, const SLONG slB)
{
SLONG slRet;
__asm {
mov eax,D [slA]
cmp eax,D [slB]
cmovl eax,D [slB]
mov D [slRet],eax
}
return slRet;
}
inline ULONG Max( const ULONG slA, const ULONG slB)
{
ULONG ulRet;
__asm {
mov eax,D [slA]
cmp eax,D [slB]
cmovb eax,D [slB]
mov D [ulRet],eax
}
return ulRet;
}
inline FLOAT ClampUp( const FLOAT f, const FLOAT fuplimit)
{
FLOAT fRet;
__asm {
fld D [fuplimit]
fld D [f]
fucomi st(0),st(1)
fcmovnb st(0),st(1)
fstp D [fRet]
fstp st(0)
}
return fRet;
}
inline FLOAT ClampDn( const FLOAT f, const FLOAT fdnlimit)
{
FLOAT fRet;
__asm {
fld D [fdnlimit]
fld D [f]
fucomi st(0),st(1)
fcmovb st(0),st(1)
fstp D [fRet]
fstp st(0)
}
return fRet;
}
inline FLOAT Clamp( const FLOAT f, const FLOAT fdnlimit, const FLOAT fuplimit)
{
FLOAT fRet;
__asm {
fld D [fdnlimit]
fld D [fuplimit]
fld D [f]
fucomi st(0),st(2)
fcmovb st(0),st(2)
fucomi st(0),st(1)
fcmovnb st(0),st(1)
fstp D [fRet]
fcompp
}
return fRet;
}
inline SLONG ClampDn( const SLONG sl, const SLONG sldnlimit)
{
SLONG slRet;
__asm {
mov eax,D [sl]
cmp eax,D [sldnlimit]
cmovl eax,D [sldnlimit]
mov D [slRet],eax
}
return slRet;
}
inline SLONG ClampUp( const SLONG sl, const SLONG sluplimit)
{
SLONG slRet;
__asm {
mov eax,D [sl]
cmp eax,D [sluplimit]
cmovg eax,D [sluplimit]
mov D [slRet],eax
}
return slRet;
}
inline SLONG Clamp( const SLONG sl, const SLONG sldnlimit, const SLONG sluplimit)
{
SLONG slRet;
__asm {
mov eax,D [sl]
cmp eax,D [sldnlimit]
cmovl eax,D [sldnlimit]
cmp eax,D [sluplimit]
cmovg eax,D [sluplimit]
mov D [slRet],eax
}
return slRet;
}
*/
/*
* fast functions
*/
#define FP_ONE_BITS 0x3F800000
// fast reciprocal value
inline FLOAT FastRcp( const FLOAT f)
{
INDEX i = 2*FP_ONE_BITS - *(INDEX*)&(f);
FLOAT r = *(FLOAT*)&i;
return( r * (2.0f - f*r));
}
// convert float from 0.0f to 1.0f -> ulong form 0 to 255
inline ULONG NormFloatToByte( const FLOAT f)
{
/* rcg10042001 !!! FIXME: Move this elsewhere. */
#ifdef __MSVC_INLINE__
const FLOAT f255 = 255.0f;
ULONG ulRet;
__asm {
fld D [f]
fmul D [f255]
fistp D [ulRet]
}
return ulRet;
#else
ASSERT((f >= 0.0f) && (f <= 1.0f));
return( (ULONG) (f * 255.0f) );
#endif
}
// convert ulong from 0 to 255 -> float form 0.0f to 255.0f
inline FLOAT NormByteToFloat( const ULONG ul)
{
return (FLOAT)ul * (1.0f/255.0f);
}
// fast float to int conversion
inline SLONG FloatToInt( FLOAT f)
{
#if (defined __MSVC_INLINE__)
SLONG slRet;
__asm {
fld D [f]
fistp D [slRet]
}
return slRet;
#elif (defined __GNU_INLINE_X86_32__)
SLONG slRet;
__asm__ __volatile__ (
"flds (%%eax) \n\t"
"fistpl (%%esi) \n\t"
:
: "a" (&f), "S" (&slRet)
: "memory"
);
return(slRet);
#else
// round to nearest by adding/subtracting 0.5 (depending on f pos/neg) before converting to SLONG
float addToRound = copysignf(0.5f, f); // copy f's signbit to 0.5 => if f<0 then addToRound = -0.5, else 0.5
return((SLONG) (f + addToRound));
#endif
}
// log base 2 of any float numero
inline FLOAT Log2( FLOAT f) {
#if (defined __MSVC_INLINE__)
FLOAT fRet;
_asm {
fld1
fld D [f]
fyl2x
fstp D [fRet]
}
return fRet;
#elif (defined __GNU_INLINE_X86_32__)
FLOAT fRet;
__asm__ __volatile__ (
"fld1 \n\t"
"flds (%%eax) \n\t"
"fyl2x \n\t"
"fstps (%%esi) \n\t"
:
: "a" (&f), "S" (&fRet)
: "memory"
);
return(fRet);
#else
return log2f(f);
#endif
}
// returns accurate values only for integers that are power of 2
inline SLONG FastLog2( SLONG x)
{
#if (defined __MSVC_INLINE__)
SLONG slRet;
__asm {
bsr eax,D [x]
mov D [slRet],eax
}
return slRet;
#elif (defined __GNU_INLINE_X86_32__)
SLONG slRet;
__asm__ __volatile__ (
"bsrl %%ecx, %%eax \n\t"
: "=a" (slRet)
: "c" (x)
: "memory"
);
return(slRet);
#elif (defined __GNUC__)
if(x == 0) return 0; // __builtin_clz() is undefined for 0
int numLeadingZeros = __builtin_clz(x);
return 31 - numLeadingZeros;
#else
register SLONG val = x;
register SLONG retval = 31;
while (retval > 0)
{
if (val & (1 << retval))
return retval;
retval--;
}
return 0;
#endif
}
/* DG: function is unused => doesn't matter that portable implementation is not optimal :)
// returns log2 of first larger value that is a power of 2
inline SLONG FastMaxLog2( SLONG x)
{
#if (defined __MSVC_INLINE__)
SLONG slRet;
__asm {
bsr eax,D [x]
bsf edx,D [x]
cmp edx,eax
adc eax,0
mov D [slRet],eax
}
return slRet;
#elif (defined __GNU_INLINE_X86_32__)
SLONG slRet;
__asm__ __volatile__ (
"bsrl %%ecx, %%eax \n\t"
"bsfl %%ecx, %%edx \n\t"
"cmpl %%eax, %%edx \n\t"
"adcl $0, %%eax \n\t"
: "=a" (slRet)
: "c" (x)
: "memory"
);
return(slRet);
#else
printf("CHECK THIS: %s:%d\n", __FILE__, __LINE__);
return((SLONG) log2((double) x));
#endif
}
*/
// square root (works with negative numbers)
#ifdef __arm__
inline FLOAT Sqrt( FLOAT x) { return sqrtf( ClampDn( x, 0.0f)); }
#else
inline FLOAT Sqrt( FLOAT x) { return (FLOAT)sqrt( ClampDn( x, 0.0f)); }
#endif
/*
* Trigonometrical functions
*/
//#define ANGLE_MASK 0x3fff
#define ANGLE_SNAP (0.25f) //0x0010
// Wrap angle to be between 0 and 360 degrees
inline ANGLE WrapAngle(ANGLE a) {
return (ANGLE) fmod( fmod(a,360.0) + 360.0, 360.0); // 0..360
}
// Normalize angle to be between -180 and +180 degrees
inline ANGLE NormalizeAngle(ANGLE a) {
return WrapAngle(a+ANGLE_180)-ANGLE_180;
}
// math constants
static const FLOAT PI = FLOAT(3.14159265359);
// convert degrees into angle
inline ANGLE AngleDeg(FLOAT fDegrees) {
//return ANGLE (fDegrees*ANGLE_180/FLOAT(180.0));
return fDegrees;
}
// convert radians into angle
inline ANGLE AngleRad(FLOAT fRadians) {
return ANGLE (fRadians*ANGLE_180/PI);
}
// convert radians into angle
inline ANGLE AngleRad(DOUBLE dRadians) {
return ANGLE (dRadians*ANGLE_180/PI);
}
// convert angle into degrees
inline FLOAT DegAngle(ANGLE aAngle) {
//return FLOAT (WrapAngle(aAngle)*FLOAT(180.0)/ANGLE_180);
return WrapAngle(aAngle);
}
// convert angle into radians
inline FLOAT RadAngle(ANGLE aAngle) {
return FLOAT (WrapAngle(aAngle)*PI/ANGLE_180);
}
#ifdef __arm__
inline ENGINE_API FLOAT Sin(ANGLE a) { return sinf(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT Cos(ANGLE a) { return cosf(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT Tan(ANGLE a) { return tanf(a*(PI/ANGLE_180)); };
#else
inline ENGINE_API FLOAT Sin(ANGLE a) { return sin(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT Cos(ANGLE a) { return cos(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT Tan(ANGLE a) { return tan(a*(PI/ANGLE_180)); };
#endif
#ifdef __arm__
inline ENGINE_API FLOAT SinFast(ANGLE a) { return sinf(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT CosFast(ANGLE a) { return cosf(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT TanFast(ANGLE a) { return tanf(a*(PI/ANGLE_180)); };
inline ANGLE ASin(FLOAT y) {
return AngleRad (asinf(Clamp(y, -1.0f, 1.0f)));
}
inline ANGLE ACos(FLOAT x) {
return AngleRad (acosf(Clamp(x, -1.0f, 1.0f)));
}
inline ANGLE ATan(FLOAT z) {
return AngleRad (atanf(z));
}
inline ANGLE ATan2(FLOAT y, FLOAT x) {
return AngleRad (atan2f(y, x));
}
#else
inline ENGINE_API FLOAT SinFast(ANGLE a) { return (FLOAT)sin(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT CosFast(ANGLE a) { return (FLOAT)cos(a*(PI/ANGLE_180)); };
inline ENGINE_API FLOAT TanFast(ANGLE a) { return (FLOAT)tan(a*(PI/ANGLE_180)); };
inline ANGLE ASin(FLOAT y) {
return AngleRad (asin(Clamp(y, -1.0f, 1.0f)));
}
inline ANGLE ACos(FLOAT x) {
return AngleRad (acos(Clamp(x, -1.0f, 1.0f)));
}
inline ANGLE ATan(FLOAT z) {
return AngleRad (atan(z));
}
inline ANGLE ATan2(FLOAT y, FLOAT x) {
return AngleRad (atan2(y, x));
}
#endif
inline ANGLE ASin(DOUBLE y) {
return AngleRad (asin(Clamp(y, -1.0, 1.0)));
}
inline ANGLE ACos(DOUBLE x) {
return AngleRad (acos(Clamp(x, -1.0, 1.0)));
}
inline ANGLE ATan(DOUBLE z) {
return AngleRad (atan(z));
}
inline ANGLE ATan2(DOUBLE y, DOUBLE x) {
return AngleRad (atan2(y, x));
}
// does "snap to grid" for given coordinate
ENGINE_API void Snap( FLOAT &fDest, FLOAT fStep);
ENGINE_API void Snap( DOUBLE &fDest, DOUBLE fStep);
// does "snap to grid" for given angle
//ENGINE_API void Snap( ANGLE &angDest, ANGLE angStep);
/*
* linear interpolation, special functions for floats and angles
*/
inline FLOAT LerpFLOAT(FLOAT f0, FLOAT f1, FLOAT fFactor)
{
return f0+(f1-f0)*fFactor;
}
inline ANGLE LerpANGLE(ANGLE a0, ANGLE a1, FLOAT fFactor)
{
// calculate delta
ANGLE aDelta = WrapAngle(a1)-WrapAngle(a0);
// adjust delta not to wrap around 360
if (aDelta>ANGLE_180) {
aDelta-=ANGLE(ANGLE_360);
} else if (aDelta<-ANGLE_180) {
aDelta+=ANGLE(ANGLE_360);
}
// interpolate the delta
return a0+ANGLE(fFactor*aDelta);
}
// Calculates ratio function /~~\ where 0<x<1, taking in consideration fade in and fade out percentages
// (ie. 0.2f means 20% fade in, 0.1f stands for 10% fade out)
inline FLOAT CalculateRatio(FLOAT fCurr, FLOAT fMin, FLOAT fMax, FLOAT fFadeInRatio, FLOAT fFadeOutRatio)
{
if(fCurr<=fMin || fCurr>=fMax)
{
return 0.0f;
}
FLOAT fDelta = fMax-fMin;
FLOAT fRatio=(fCurr-fMin)/fDelta;
if(fRatio<fFadeInRatio) {
fRatio = Clamp( fRatio/fFadeInRatio, 0.0f, 1.0f);
} else if(fRatio>(1-fFadeOutRatio)) {
fRatio = Clamp( (1.0f-fRatio)/fFadeOutRatio, 0.0f, 1.0f);
} else {
fRatio = 1.0f;
}
return fRatio;
}
#undef O
#undef Q
#undef D
#undef W
#undef B
#endif /* include-once check. */