//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: spherical math routines // //=====================================================================================// #include #include // Needed for FLT_EPSILON #include "basetypes.h" #include #include "tier0/dbg.h" #include "mathlib/mathlib.h" #include "mathlib/vector.h" #include "mathlib/spherical_geometry.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" float s_flFactorials[]={ 1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880., 3628800., 39916800., 479001600., 6227020800., 87178291200., 1307674368000., 20922789888000., 355687428096000., 6402373705728000., 121645100408832000., 2432902008176640000., 51090942171709440000., 1124000727777607680000., 25852016738884976640000., 620448401733239439360000., 15511210043330985984000000., 403291461126605635584000000., 10888869450418352160768000000., 304888344611713860501504000000., 8841761993739701954543616000000., 265252859812191058636308480000000., 8222838654177922817725562880000000., 263130836933693530167218012160000000., 8683317618811886495518194401280000000. }; float AssociatedLegendrePolynomial( int nL, int nM, float flX ) { // evaluate associated legendre polynomial at flX, using recurrence relation float flPmm = 1.; if ( nM > 0 ) { float flSomX2 = sqrt( ( 1 - flX ) * ( 1 + flX ) ); float flFact = 1.; for( int i = 0 ; i < nM; i++ ) { flPmm *= -flFact * flSomX2; flFact += 2.0; } } if ( nL == nM ) return flPmm; float flPmmp1 = flX * ( 2.0 * nM + 1.0 ) * flPmm; if ( nL == nM + 1 ) return flPmmp1; float flPll = 0.; for( int nLL = nM + 2 ; nLL <= nL; nLL++ ) { flPll = ( ( 2.0 * nLL - 1.0 ) * flX * flPmmp1 - ( nLL + nM - 1.0 ) * flPmm ) * ( 1.0 / ( nLL - nM ) ); flPmm = flPmmp1; flPmmp1 = flPll; } return flPll; } static float SHNormalizationFactor( int nL, int nM ) { double flTemp = ( ( 2. * nL + 1.0 ) * s_flFactorials[ nL - nM ] )/ ( 4. * M_PI * s_flFactorials[ nL + nM ] ); return sqrt( flTemp ); } #define SQRT_2 1.414213562373095 FORCEINLINE float SphericalHarmonic( int nL, int nM, float flTheta, float flPhi, float flCosTheta ) { if ( nM == 0 ) return SHNormalizationFactor( nL, 0 ) * AssociatedLegendrePolynomial( nL, nM, flCosTheta ); if ( nM > 0 ) return SQRT_2 * SHNormalizationFactor( nL, nM ) * cos ( nM * flPhi ) * AssociatedLegendrePolynomial( nL, nM, flCosTheta ); return SQRT_2 * SHNormalizationFactor( nL, -nM ) * sin( -nM * flPhi ) * AssociatedLegendrePolynomial( nL, -nM, flCosTheta ); } float SphericalHarmonic( int nL, int nM, float flTheta, float flPhi ) { return SphericalHarmonic( nL, nM, flTheta, flPhi, cos( flTheta ) ); } float SphericalHarmonic( int nL, int nM, Vector const &vecDirection ) { Assert( fabs( VectorLength( vecDirection ) - 1.0 ) < 0.0001 ); float flPhi = acos( vecDirection.z ); float flTheta = 0; float S = Square( vecDirection.x ) + Square( vecDirection.y ); if ( S > 0 ) { flTheta = atan2( vecDirection.y, vecDirection.x ); } return SphericalHarmonic( nL, nM, flTheta, flPhi, cos( flTheta ) ); }