//
// Asura_Maths.h
//
// Created by Kevin Lea - 12/06/2001 10:59:42
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//
// Modified by Mike Healey - 17.4.02 
//	- all function bodies moved to Asura_PlatformMaths.h, except for
//	empty constructors and array accessors for compound types.

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_MATHS_H__
#define __ASURA_MATHS_H__

#include "Debugging/Asura_Assert.h"

// Kevin Lea - 27/09/2001 13:03:42 - just discovered that the MAX headers have defines with exactly the same names,
// so even having our separate namespace doesn't stop the compiler from being very confused
#ifdef DegToRad
#undef DegToRad
#endif
#ifdef RadToDeg
#undef RadToDeg
#endif

#ifdef ASURA_VECTOR_4_IN_PLATFORM_CODE
#error ASURA_VECTOR_4_IN_PLATFORM_CODE is obsolete - use ASURA_USE_PLATFORM_VECTORS instead
#endif

typedef float Asura_Angle;

class Asura_Vector_2;
class Asura_Vector_3;

class Asura_Matrix_3x3;
class Asura_Euler_3;
class Asura_Bounding_Box;
class Asura_Quat;
class Asura_SphericalHarmonics_Order3;
class Asura_IntVector_2;

class Asura_AVector3;
class Asura_AMatrix3x3;

class Asura_AVector4;
class Asura_AMatrix4x4;

namespace Asura_Maths
{
    static inline float Sine(Asura_Angle angle);
    static inline float Cosine(Asura_Angle angle);
    static inline void SinCos(Asura_Angle fAngle, float& fOutputSine, float& fOutputCos);
    static inline void SinCos2(Asura_Angle fAngle1, Asura_Angle fAngle2, float& fOutputSine1,
                               float& fOutputCos1, float& fOutputSine2, float& fOutputCos2);
    static inline float Tangent(Asura_Angle angle);
    static inline Asura_Angle ArcSine(float sine);
    static inline Asura_Angle ArcCosine(float cosine);
	static inline Asura_Angle ArcTangent(float x, float y);

	// return x raised to the y'th power
	static inline float Power(float x, float y);

	template< typename T >
	static inline float Square(const T xValue)
	{
		return(xValue * xValue);
	}

    static inline float Sqrt(float f);
    static inline float InvSqrt(float f);

    extern float RayIntersectsTri( const Asura_Vector_3& alpha, const Asura_Vector_3& beta, const Asura_Vector_3& v0, const Asura_Vector_3& v1, const Asura_Vector_3& v2, bool& facingToward ) ASURA_CODE_SECTION("module_los");
    extern float RayIntersectsTri( const Asura_AVector3& alpha, const Asura_AVector3& beta, const Asura_AVector3& v0, const Asura_AVector3& v1, const Asura_AVector3& v2, bool& facingToward );
    extern bool  RayIntersectsSphere( const Asura_Vector_3& alpha, const Asura_Vector_3& beta, const Asura_Vector_3& centre, float radius, float *pfFirstIntersectDist = 0, bool bTreatAsHollow = false );
    extern bool  RayIntersectsBoundingBox( const Asura_Vector_3&     xAlpha,
                                           const Asura_Vector_3&     xBeta,
                                           const Asura_Bounding_Box& xBox,
                                           float*          pfDistance    = 0,
                                           Asura_Vector_3* pxHitPosition = 0,
                                           bool*           pbInside      = 0,
                                           Asura_Vector_3* pxNormal      = 0 );
    // Version of the above that gives full information if the ray starts
    // inside, rather than a distance of 0. This is of course slower.
    extern bool RayIntersectsBoundingBox_AllowInside( const Asura_Vector_3& xOrigin,
                                                      const Asura_Vector_3& xDir,
                                                      const Asura_Bounding_Box& xBB,
                                                      float* pfDistance = NULL,
                                                      Asura_Vector_3* pxHitPosition = NULL,
                                                      bool* pbInside = NULL,
                                                      Asura_Vector_3* pxNormal = NULL );
	// piHitFace will be given one of the following values if RayIntersectsCylinder returns true:
	// -1 : hit start end of cylinder (as given by xCylinderStart)
	//  0 : hit side of cylinder
	//  1 : hit other end of cylinder
	extern bool	 RayIntersectsCylinder(const Asura_Vector_3& xAlpha,
									   const Asura_Vector_3& xBeta,
									   const Asura_Vector_3& xCylinderStart,
									   const Asura_Vector_3& xCylinderVector,
									   const float fCylinderRadius,
									   float* pfDistance = NULL,
									   int* piHitFace = NULL);


    extern float GetMinDistSqdBetweenLineSegmentAndRay( const Asura_Vector_3& xRayOrigin,
                                                        const Asura_Vector_3& xRayDirection,
                                                        const Asura_Vector_3& xLineSegmentOrigin,
                                                        const Asura_Vector_3& xLineSegmentVector );
	extern void GetLeastOffsetBetweenLineSegmentAndRay(	const Asura_Vector_3& xRayOrigin,
														const Asura_Vector_3& xRayDirection,
														const Asura_Vector_3& xLineSegmentOrigin,
														const Asura_Vector_3& xLineSegmentVector,
														Asura_Vector_3& xOffset,
														Asura_Vector_3* pxPositionOnLineSegment = NULL,
														Asura_Vector_3* pxPositionOnRay = NULL );

    // Calculates xNearestPoint, the nearest point on the line xLineStart -> xLineEnd to xPoint.
    extern void GetNearestPointOnLineSegment( const Asura_AVector3& xLineStart, const Asura_AVector3& xLineEnd, const Asura_AVector3& xPoint, Asura_AVector3& xNearestPoint );
    extern void GetNearestPointOnLineSegment( const Asura_Vector_3& xLineStart, const Asura_Vector_3& xLineEnd, const Asura_Vector_3& xPoint, Asura_Vector_3& xNearestPoint );

    const float LargeFloat = 1.0e30f;

    const Asura_Angle Pi     = 3.141592654f;
    const Asura_Angle Deg90  = Pi * 0.5f;
    const Asura_Angle Deg180 = Pi;
    const Asura_Angle Deg270 = Pi * 1.5f;
    const Asura_Angle Deg360 = Pi * 2.0f;

    const Asura_Angle DegToRadConstant = (Pi/180.0f);
    const Asura_Angle RadToDegConstant = (180.0f/Pi);

    static inline Asura_Angle DegToRad(float deg);
    static inline float       RadToDeg(Asura_Angle rad);

    /// Kevin Lea - 22/07/2002 15:34:18 - the original FloatToInt was written because
    /// simply casting a float to an int is very slow on the PC, and there is a 'standard'
    /// way of overcoming this with a few lines of inline assembler. A side-effect of this
    /// is that FloatToInt on the PC rounds to the nearest integer, whereas a simple cast
    /// by prefixing (int) will usually round down. This means some platforms of Asura will
    /// have different behaviours - if the need arises I will add a FloatToInt_RoundUp()
    /// and FloatToInt_RoundDown().
    static inline int FloatToInt(float f);

    // (MP - 22.08.2002) These functions behave like the corresponding ANSI
    // floating point rounding modes.
    static inline int FloatToInt_Chop(float f);
    static inline int FloatToInt_Near(float f);
	static inline int FloatToInt_Ceil(float f); 

    // (MH - 18.04.2002) - since vararg functions implicitly convert floats to doubles,
    // (as well as chars to ints) centralise the conversion here. This prevents a warning
    // every time vararg functions are used in the code.
    static inline double FtoD(float f);

    extern void OrientationFromDirection( Asura_Matrix_3x3& xOrientation, const Asura_Vector_3& xDirection );
    extern void OrientationFromNormal( Asura_Matrix_3x3& xOrientation, const Asura_Vector_3& xNormal );

    // Kevin Lea - 10/09/2001 10:38:13 - max & min inlines
	template< typename T > static inline T Max( const T& tA, const T& tB );
	template< typename T > static inline T Min( const T& tA, const T& tB );
    
	template< typename T > static inline T ClampToRange( const T& tValue, const T& tMin, const T& tMax );

	// returns 0 if tValue < tMin, 1 if tValue > tMax or a smooth 0-1 value if imbetween
	// if 
	template< typename T > static inline T SmoothStep( const T& tValue, const T& tMin, const T& tMax );


	// Sorts arguments such that tA <= tB
	template< typename T > static inline void Sort( T& tA, T& tB ); 
	
	//Swaps the contents of tA and tB
	template< typename T > static inline void Swap( T& tA, T& tB ); 


    static inline float CalculateTriArea( const Asura_Vector_3& xV0, const Asura_Vector_3& xV1, const Asura_Vector_3& xV2 );

    /// Kevin Lea - 04/02/2002 14:14:32 - abs and sign methods
    /// optimisations can be made knowing more about formats
    /// e.g. float IEEE standard, size of int
    union Asura_IntOrFloat
    {
        int I;
        float F;
    };

    static inline float Abs(float f);
    static inline int Abs(int a);
    static inline float Sign(float a);
    static inline int Sign(int a);

    // Valid for u in range [1, UINT_MAX].
    static inline u_int GetFloorLog2( u_int u );
    static inline u_int GetCeilLog2( u_int u );

	// DLH - 17 July 2006 14:29:20 - Returns rounded up or down nearest power of two
	static inline u_int GetFloorPow2( u_int uValueOld );
	static inline u_int GetCeilPow2( u_int uValueOld );

	// KOR - 28/11/2005 - floating point modulo (remainder of fX/fY); should be fmod() on most platforms
	static inline float FloatMod(float fX, float fY);

	// KOR - 28/11/2005 - Returns f where f is in the range [fMin, fMax) and f = fAngle mod (fMax - fMin)
	static inline float AngleToRange(float fAngle, float fMin, float fMax);

	// KOR - 28/11/2005 - common angle to range wrappers
	static inline float RadiansToZeroCentredRange(float fRadians); // -Pi to +Pi
	static inline float RadiansToZeroMinimumRange(float fRadians); // 0 to 2 * Pi

	// ART - 21/09/2006 - Linear interpolation...
	static inline float Lerp(const float fFrom, const float fTo, const float fWeight);
	static inline int Lerp(const int iFrom, const int iTo, const float fWeight);

	static inline bool IsNaN ( const float fNum );
}
//end of namespace Asura_Maths

// ART 15/09/06 - Should be platform specific, we'll sort this later...

// Linear interpolation functions...
inline float Asura_Maths::Lerp (const float fFrom,
								const float fTo,
								const float fWeight)
{
	if( fFrom == fTo )
	{
		return fFrom;
	}
	if(fWeight <= 0.0f)
	{
		Asura_Assert((fWeight == 0.0f), "Invalid weight specified for Lerp.");
		return(fFrom);
	}
	else if(fWeight >= 1.0f)
	{
		Asura_Assert((fWeight == 1.0f), "Invalid weight specified for Lerp.");
		return(fTo);
	}
	else
	{
		const float	fFromWeight	= 1.0f - fWeight;

		// Interpolate...
		return((fFrom * fFromWeight) + (fTo * fWeight));
	}
}

inline int Asura_Maths::Lerp (	const int iFrom,
								const int iTo,
								const float fWeight)
{
	// TY 07.11.08 -	Currently this just prevents the caller from having to do a load of casting when
	//					lerping ints, could be changed to do an optimised integer lerp though
	return static_cast<int>( Lerp( static_cast<float>( iFrom ), static_cast<float>( iTo ), fWeight ) );
}

// ...should be platform specific, we'll sort this later - ART 15/09/06

class Asura_Ranrot
{

	public:

        // M E T H O D S /////////////////////////////////////////////////////
		Asura_Ranrot()
		{
			Initialise(0);
		}

		void Initialise( int iSeed ) 
		{
			m_iLow  =  iSeed;
			m_iHigh = ~iSeed;
		}

		int GetInt( void )
		{
		   m_iHigh = (m_iHigh<<16) + (m_iHigh>>16);
		   m_iHigh += m_iLow;
		   m_iLow += m_iHigh;

		   return m_iHigh;
		}

//		u_int UpTo(u_int a)
//		{
//			return ((GetInt()&65535)*a)>>16;
//		}

		// Code for saving and restoring the state of the RNG
		void GetState(int& iLow, int& iHigh)
		{
			iLow = m_iLow;
			iHigh = m_iHigh;
		}
		void SetState(int iLow, int iHigh)
		{
			m_iLow = iLow;
			m_iHigh = iHigh;
		}
	
	private:

        // S T A T E /////////////////////////////////////////////////////////

		int m_iHigh;
		int m_iLow;

};
// End class Asura_Ranrot.

class Asura_RandomGenerator
{
	public :
		Asura_RandomGenerator()	{m_xGenerator.Initialise(0);}
		Asura_RandomGenerator(int iSeed) {m_xGenerator.Initialise(iSeed);}

		void SetSeed(int iSeed) {m_xGenerator.Initialise(iSeed);}

        //
        // Uniform distribution integers.
        //
        
		inline int GetInt( void );                             // [INT_MIN, INT_MAX].
        inline u_int GetIntU( void );                          // [0, INT_MAX]. (Note: not UINT_MAX!)
        inline int GetInt( int iMin, int iMax );               // Range specified is inclusive.
        inline u_int GetIntU( u_int uMin, u_int uMax );        //               "

        //
        // Uniform distribution floats.
        //

        inline float GetFloat( void );                         // [0, 1].
        inline float GetFloat( float fMin, float fMax );       // Range specified is inclusive.

        //
        // Normal distribution floats.
        //

        float GetNormalFloat( void );                          // Mean 0 and standard deviation 1.

		// Functions for saving and restoring the state of the RNGs
		// Slightly nastily, this is assuming we're using the Asura_Ranrot class as it is today (04/07/05), so if the system changes these functions need to be adjusted accordingly.
		void GetCurrentState(int& iLow, int& iHigh){m_xGenerator.GetState(iLow,iHigh);}
		void SetCurrentState(int iLow, int iHigh){m_xGenerator.SetState(iLow,iHigh);}

	private :
        int GetIntInternal( void ) {return m_xGenerator.GetInt();}

        // Ensure output is in [0, INT_MAX] range specified above. Also,
        // preserve higher bits, since they're typically more random.
        static u_int I2U( int i ) { return( static_cast<u_int>(i) >> 1u ); }


		Asura_Ranrot m_xGenerator;
};

class Asura_Random
{

    public:

        // C L A S S   M E T H O D S /////////////////////////////////////////

        static void Initialise( void );
        static void Shutdown( void );

        //
        // Uniform distribution integers.
        //
												   
        static inline int GetInt( void )    	                      // [INT_MIN, INT_MAX].
			{return s_xSharedGenerator.GetInt();}

        static inline u_int GetIntU( void )                           // [0, INT_MAX]. (Note: not UINT_MAX!)
			{return s_xSharedGenerator.GetIntU();}

        static inline int GetInt( int iMin, int iMax )                // Range specified is inclusive.
			{return s_xSharedGenerator.GetInt(iMin,iMax);}

        static inline u_int GetIntU( u_int uMin, u_int uMax )         //               "
			{return s_xSharedGenerator.GetIntU(uMin,uMax);}



        static void SetSeed( int iSeed ) 
			{s_xSharedGenerator.SetSeed(iSeed);}

        // legacy seeded functions
		static inline int GetSeededInt( void )                        // [INT_MIN, INT_MAX].
			{return GetInt();}

        static inline u_int GetSeededIntU( void )                     // [0, INT_MAX]. (Note: not UINT_MAX!)
			{return GetIntU();}

        static inline int GetSeededInt( int iMin, int iMax )          // Range specified is inclusive.
			{return GetInt(iMin,iMax);}

        static inline u_int GetSeededIntU( u_int uMin, u_int uMax )   //               "
			{return GetIntU(uMin,uMax);}

        //
        // Uniform distribution floats.
        //

        static inline float GetFloat( void )                          // [0, 1].
			{return s_xSharedGenerator.GetFloat();}

        static inline float GetFloat( float fMin, float fMax )        // Range specified is inclusive.
			{return s_xSharedGenerator.GetFloat(fMin,fMax);}

        // legacy seeded functions
		static inline float GetSeededFloat( void )                    // [0, 1].
			{return GetFloat();}

        static inline float GetSeededFloat( float fMin, float fMax )  // Range specified is inclusive.
			{return GetFloat(fMin,fMax);}


        //
        // Normal distribution floats.
        //

        static float GetNormalFloat( void )                           // Mean 0 and standard deviation 1.
			{return s_xSharedGenerator.GetNormalFloat();}


		// Functions for saving and restoring the state of the RNG
		// Slightly nastily, this is assuming we're using the Asura_Ranrot class as it is today (04/07/05), so if the system changes these functions need to be adjusted accordingly.
		static void GetCurrentState(int& iLow, int& iHigh)
		{s_xSharedGenerator.GetCurrentState(iLow,iHigh);}

		static void SetCurrentState(int iLow, int iHigh)
		{s_xSharedGenerator.SetCurrentState(iLow,iHigh);}

		// GetSharedGenerator() exists so that functions can take a Asura_RandomGenerator* as an argument
		// and either use a private generator, or grab this one and use it.
		static Asura_RandomGenerator* GetSharedGenerator(){return &s_xSharedGenerator;}

	private:

		// static class data
		static Asura_RandomGenerator s_xSharedGenerator;
};

inline int   Asura_RandomGenerator::GetInt( void )        { return GetIntInternal();            }
inline u_int Asura_RandomGenerator::GetIntU( void )       { return I2U(GetIntInternal());       }

class Asura_Vector_2
{
	public:	
		union
		{

			struct
			{
				float x, y;
			};
			float Element[2];
		};

        inline float& operator[]( u_int u ) { return Element[u]; }
        inline float operator[]( u_int u ) const { return Element[u]; }

		// constructors
		Asura_Vector_2() {}
		
		Asura_Vector_2(const Asura_Vector_2& v);
	    Asura_Vector_2(float x0, float y0);

		// binary ops
		inline Asura_Vector_2 operator + ( const Asura_Vector_2& v) const;
		inline void operator += ( const Asura_Vector_2& v);
		inline Asura_Vector_2 operator - ( const Asura_Vector_2& v) const;
		inline void operator -= ( const Asura_Vector_2& v);
	    inline Asura_Vector_2 operator * ( float fScale ) const;
		inline void operator *= ( float fScale );
	    inline void operator /= ( float f);
		inline bool operator == ( const Asura_Vector_2& v) const;
		inline bool operator != ( const Asura_Vector_2& v) const;
		//dot product
		inline float  operator * ( const Asura_Vector_2& v) const;

		//random vectors using shared RNG
		inline Asura_Vector_2& RandomDirection() {return RandomDirection(Asura_Random::GetSharedGenerator());}
		inline Asura_Vector_2& RandomVector() {return RandomVector(Asura_Random::GetSharedGenerator());}

		Asura_Vector_2 ChooseRandomDirectionInSegment(const float fMaxTheta, const float fMinTheta=0.f) const ;
		Asura_Vector_2 ChooseRandomVectorInSegment(const float fMaxTheta, const float fMinTheta=0.f, const float fMaxLength=1.f, const float fMinLength=0.f) const;

		//random vectors using seeded RNG (legacy functions)
		inline Asura_Vector_2& RandomDirectionSeeded() {return RandomDirection();}
		inline Asura_Vector_2& RandomVectorSeeded() {return RandomVector();}

		Asura_Vector_2 ChooseRandomDirectionInSegmentSeeded(const float fMaxTheta, const float fMinTheta=0.f) const ;
		Asura_Vector_2 ChooseRandomVectorInSegmentSeeded(const float fMaxTheta, const float fMinTheta=0.f, const float fMaxLength=1.f, const float fMinLength=0.f) const;

		//random vectors using specified RNG
		inline Asura_Vector_2& RandomDirection(Asura_RandomGenerator* pxRNG);
		inline Asura_Vector_2& RandomVector(Asura_RandomGenerator* pxRNG);
		
		inline float Normalise(void);
		inline float Magnitude(void) const;
		inline float MagnitudeSquared(void) const;

		inline void Zero(void);
};



class Asura_Vector_3
{
	public:

		// constructors
	    inline Asura_Vector_3() {}
	    
	    Asura_Vector_3(const float * fPtr);
		Asura_Vector_3(const Asura_Vector_3& v);	    
	    Asura_Vector_3(float x0, float y0, float z0);

		inline void operator = ( const Asura_AVector3& xV4);
		// to explicitly convert Asura_AVector3 to Asura_Vector_3 (for legacy code etc.)
		explicit Asura_Vector_3(const Asura_AVector3& v);	    

	    // assignment operators
		inline void operator = ( const Asura_Vector_3& v);
	    inline void operator += ( const Asura_Vector_3& v);
	    inline void operator -= ( const Asura_Vector_3& v);
		inline void operator *= ( float f);
	    inline void operator /= ( float f);

	    // unary operators
	    inline Asura_Vector_3 operator + () const;
		inline Asura_Vector_3 operator - () const;

	    // binary operators
	    inline Asura_Vector_3 operator + ( const Asura_Vector_3& v) const;
	    inline Asura_Vector_3 operator - ( const Asura_Vector_3& v) const;
	    inline Asura_Vector_3 operator * ( float f) const;
	    inline Asura_Vector_3 operator / ( float f) const;
		inline Asura_Vector_3 operator ^ ( const Asura_Vector_3& v) const;
		inline float  operator * ( const Asura_Vector_3& v) const;
		inline Asura_Vector_3 operator * ( const Asura_Matrix_3x3& m) const;

	    inline bool operator == ( const Asura_Vector_3& v) const;
		inline bool ApproximatelyEquals(const Asura_Vector_3& v, float epsilon = 1.0e-6f) const;
	    inline bool operator != ( const Asura_Vector_3& v) const;

		inline Asura_Vector_3 Vector_Multiply( const Asura_Vector_3& v1) const;

		// useful operations
		//random vectors using shared RNG
		inline Asura_Vector_3& RandomDirection() {return RandomDirection(Asura_Random::GetSharedGenerator());}
		inline Asura_Vector_3& RandomVector() {return RandomVector(Asura_Random::GetSharedGenerator());}

		Asura_Vector_3 ChooseRandomDirectionInCone(const float fMaxTheta, const float fMinTheta=0.f) const;
		Asura_Vector_3 ChooseRandomVectorInCone(const float fMaxTheta, const float fMinTheta=0.f, const float fMaxLength=1.f, const float fMinLength=0.f) const;

		//random vectors using seeded RNG (legacy)
		inline Asura_Vector_3& RandomDirectionSeeded() {return RandomDirection();}
		inline Asura_Vector_3& RandomVectorSeeded() {return RandomVector();}

		Asura_Vector_3 ChooseRandomDirectionInConeSeeded(const float fMaxTheta, const float fMinTheta=0.f) const;
		Asura_Vector_3 ChooseRandomVectorInConeSeeded(const float fMaxTheta, const float fMinTheta=0.f, const float fMaxLength=1.f, const float fMinLength=0.f) const;

		//random vectors using specified RNG
		inline Asura_Vector_3& RandomDirection(Asura_RandomGenerator* pxRNG);
		inline Asura_Vector_3& RandomVector(Asura_RandomGenerator* pxRNG);
		
		inline float Normalise(void);
		inline float Magnitude(void) const;
		inline float MagnitudeSquared(void) const;
		inline void Lerp(const Asura_Vector_3& xTo, const float fWeight);
		inline void Lerp(const Asura_Vector_3& xFrom, const Asura_Vector_3& xTo, const float fWeight);
		inline void Zero(void);

        inline float& operator[]( u_int u ) { return *((&x)+ u); }
        inline float operator[]( u_int u ) const { return *((&x)+ u); }
		inline float GetElement( u_int u ) const { return *((&x)+ u); }
		inline void SetElement( u_int u, float f ) { *((&x)+ u) = f; }
		
		inline float GetX(void) const { return x; }
		inline float GetY(void) const { return y; }
		inline float GetZ(void) const { return z; }
		inline void SetX(float fX) { x = fX; }
		inline void SetY(float fY) { y = fY; }
		inline void SetZ(float fZ) { z = fZ; }

		inline bool AnyComponentIsNaN () const	{ return	Asura_Maths::IsNaN( x ) 
														||	Asura_Maths::IsNaN( y )
														||	Asura_Maths::IsNaN( z ); }

	public:
		// MH 6.12.05 - removed union of x,y,z with m_fX, m_fY, m_fZ and Element[3] to help
		// the SN compiler (and others) generate better code with fewer loads and stores.
		float x, y, z;
};

// ART 15/09/06 - Should be platform specific, we'll sort this later...

// Linear interpolation functions...
inline void Asura_Vector_3::Lerp (const Asura_Vector_3& xTo,
								  const float fWeight)
{
	const float	fFromWeight	= 1.0f - fWeight;

	// Interpolate...
	x	= (x * fFromWeight) + (xTo.x * fWeight);
	y	= (y * fFromWeight) + (xTo.y * fWeight);
	z	= (z * fFromWeight) + (xTo.z * fWeight);
}

inline void Asura_Vector_3::Lerp (const Asura_Vector_3& xFrom,
								  const Asura_Vector_3& xTo,
								  const float fWeight)
{
	const float	fFromWeight	= 1.0f - fWeight;

	// Interpolate...
	x	= (xFrom.x * fFromWeight) + (xTo.x * fWeight);
	y	= (xFrom.y * fFromWeight) + (xTo.y * fWeight);
	z	= (xFrom.z * fFromWeight) + (xTo.z * fWeight);
}

// ...should be platform specific, we'll sort this later - ART 15/09/06

class Asura_Matrix_3x3
{
	public:

        union
		{
			struct
			{
				float e11;
				float e12;
				float e13;

				float e21;
				float e22;
				float e23;

				float e31;
				float e32;
				float e33;
			};
			float Element[3][3];
		};

        inline Asura_Matrix_3x3( void ) { }

        Asura_Matrix_3x3( float new_e11, float new_e12, float new_e13,
                          float new_e21, float new_e22, float new_e23,
                          float new_e31, float new_e32, float new_e33 )
        {
            this->e11 = new_e11;
            this->e12 = new_e12;
            this->e13 = new_e13;
            this->e21 = new_e21;
            this->e22 = new_e22;
            this->e23 = new_e23;
            this->e31 = new_e31;
            this->e32 = new_e32;
            this->e33 = new_e33;
        }
        
		inline void operator = ( const Asura_Matrix_3x3& xOtherMat )
		{
			e11 = xOtherMat.e11;
			e12 = xOtherMat.e12;
			e13 = xOtherMat.e13;
			e21 = xOtherMat.e21;
			e22 = xOtherMat.e22;
			e23 = xOtherMat.e23;
			e31 = xOtherMat.e31;
			e32 = xOtherMat.e32;
			e33 = xOtherMat.e33;
		}

		Asura_Matrix_3x3( const Asura_Matrix_3x3& xOtherMat )
		{
			// use operator = (for inlining, make sure it's *defined* above and not in platform header)
			*this = xOtherMat;
		}

  		inline float* operator []( int iIdx )
  		{
  			return &Element[iIdx][0];
  		}
  		inline const float* operator []( int iIdx ) const
  		{
  			return &Element[iIdx][0];
  		}

		inline void Transpose(void);
        inline Asura_Vector_3 ApplyTranspose( const Asura_Vector_3& xVec ) const;
		inline void Identity(void);
		inline Asura_Matrix_3x3 operator * ( const Asura_Matrix_3x3& m) const;
		inline Asura_Matrix_3x3 operator * ( const float s) const;
		
		inline void Zero(void)
		{
			e11 = e12 = e13 = e21 = e22 = e23 = e31 = e32 = e33 = 0.0f;
		}

		bool ApproximatelyEquals(const Asura_Matrix_3x3& xMat, float epsilon = 1.0e-6f) const;
		inline bool operator == ( const Asura_Matrix_3x3& xMat) const
		{
			return	(e11 == xMat.e11) && (e12 == xMat.e12) && (e13 == xMat.e13) &&
					(e21 == xMat.e21) && (e22 == xMat.e22) && (e23 == xMat.e23) &&
					(e31 == xMat.e31) && (e32 == xMat.e32) && (e33 == xMat.e33);
		}
		inline bool operator != ( const Asura_Matrix_3x3& xMat) const
		{
			return ! (*this==xMat);
		}

		inline float Heading(void) const;

		#ifdef ASURA_TOOLS
		//Gets matrix snapped to the nearest set of axes. 
		//Not sure its likely to be needed outside of the tools,
		//hence the #ifdef. RAR 20/04/04
		Asura_Matrix_3x3 GetAxiallyAlignedMatrix() const;
		#endif
};


class Asura_Euler_3
{
	public:
		union
		{
			struct
			{
				Asura_Angle x;
				Asura_Angle y;
				Asura_Angle z;
			};

			Asura_Angle Element[3];
		};
		
		Asura_Euler_3(){}

        Asura_Euler_3( const Asura_Euler_3& xRhs )
        : x( xRhs.x ), y( xRhs.y ), z( xRhs.z )
        {}

	    Asura_Euler_3(float x0, float y0, float z0);

		// assignment operators
		inline void operator += ( const Asura_Euler_3& xEuler);
	    inline void operator -= ( const Asura_Euler_3& xEuler);
		inline void operator *= ( float f);
	    inline void operator /= ( float f);

	    // unary operators
		inline Asura_Euler_3 operator - () const;

	    // binary operators
	    inline Asura_Euler_3 operator + ( const Asura_Euler_3& xEuler) const;
	    inline Asura_Euler_3 operator - ( const Asura_Euler_3& xEuler) const;
	    inline Asura_Euler_3 operator * ( float f) const;
	    inline Asura_Euler_3 operator / ( float f) const;

		// array-access operators
        inline float& operator[]( u_int u ) { return Element[u]; }
        inline float operator[]( u_int u ) const { return Element[u]; }

		inline void ToMatrix(Asura_Matrix_3x3& m) const;
	
		// KJL - 18-May-01 16:22:35 - possibly the z angle
		// should be chosen as zero for the non-unique case
		inline void FromMatrix(const Asura_Matrix_3x3& m);

		inline float MagnitudeSquared() const { return x*x+y*y+z*z; }

		inline void Zero(void);
};

class Asura_Quat
{
	public:
		float x,y,z,w;

			Asura_Quat(){}

		Asura_Quat(float x0, float y0, float z0, float w0);

        explicit Asura_Quat(const Asura_Euler_3& e) {FromEuler(e);}
        explicit Asura_Quat(const Asura_Matrix_3x3& m) {FromMatrix(m);}
        
        inline void Identity( void );
        inline void Invert( void );
		inline Asura_Quat operator * ( float f) const;
        inline Asura_Vector_3 RotateVector(const Asura_Vector_3& p) const;
        inline Asura_AVector3 RotateVector(const Asura_AVector3& p) const;
        inline Asura_Quat operator *(const Asura_Quat& q) const;
		inline Asura_Quat operator - () const;
		inline Asura_Quat operator + ( const Asura_Quat& q) const;
		inline bool ApproximatelyEquals(const Asura_Quat& q, float epsilon = 1.0e-6f) const;
		inline bool operator == ( const Asura_Quat& q) const;
		inline bool operator != ( const Asura_Quat& q) const	{ return(!(*this == q)); }
		inline float MagnitudeSquared(void) const;

		inline void ToMatrix(Asura_Matrix_3x3& m) const;
		inline void ToMatrix(Asura_AMatrix3x3& m) const;
		inline void ToEuler(Asura_Euler_3& e) const;
		inline void FromEuler(const Asura_Euler_3& e); 
		inline void FromMatrix(const Asura_Matrix_3x3& m);
		inline void FromMatrix(const Asura_AMatrix3x3& m);
        inline void FromRotation(Asura_Angle angle, const Asura_Vector_3& axis);
        inline void FromRotation(Asura_Angle angle, const Asura_AVector3& axis);
		Asura_Angle ToRotation(Asura_Vector_3& xAxis) const;
		Asura_Angle ToRotation(Asura_AVector3& xAxis) const;
		void RotationFromAToB(const Asura_Vector_3& xA, const Asura_Vector_3& xB);
		void RotationFromAToB(const Asura_AVector3& xA, const Asura_AVector3& xB);

		inline void Normalise(void);

		inline float Dot(const Asura_Quat& q2) const;
		inline void Slerp(const Asura_Quat& xInput2, float fDist);
		inline void Slerp(const Asura_Quat& input1, const Asura_Quat& input2, float l);
		inline void NLerp(const Asura_Quat& xInput1, const Asura_Quat& xInput2, float fDist);

		// The following is equivalent to NLerping from the identity to the specified quaternion...
		inline Asura_Quat ScaleRotation(float fScale) const;
};

inline void Asura_Quat::Slerp(const Asura_Quat& xInput2, float fDist)
{
	const Asura_Quat	xTempQuat(*this);

	// ART - 21/09/06 - This simply copies "this" before the slerp, this is necessary
	// since the slerp operation keeps using the source parameter so we cannot
	// fill in the result until the end of the slerp.
	Slerp(xTempQuat, xInput2, fDist);
}

class Asura_Comp_Quat
{
	public:
		Asura_Comp_Quat() {}
		// Compress from quat constructor
	Asura_Comp_Quat( const Asura_Quat& xQuat ) { FromQuat( xQuat ); }
	
	inline void ToQuat( Asura_Quat& xQuat ) const;
	inline void FromQuat( const Asura_Quat& xQuat );

	unsigned int GetBits() const { return m_uBits; }
	void SetBits( unsigned int uBits ) { m_uBits = uBits; }
		
private:
	unsigned int m_uBits;
};

class Asura_Comp_Quat_Fast
{
	public:
		Asura_Comp_Quat_Fast() {}
		// Compress from quat constructor
	Asura_Comp_Quat_Fast( const Asura_Quat& xQuat ) { FromQuat( xQuat ); }
	
	inline void ToQuat( Asura_Quat& xQuat ) const;
	inline void FromQuat( const Asura_Quat& xQuat );

	unsigned int GetBits() const { return m_uBits; }
	void SetBits( unsigned int uBits ) { m_uBits = uBits; }
		
private:
	unsigned int m_uBits;
};

class Asura_Bounding_Rectangle
{
	public:
		float MinX;
		float MinY;
		float MaxX;
		float MaxY;

		Asura_Bounding_Rectangle()	{;}
		Asura_Bounding_Rectangle( const float fX0, const float fY0,
			const float fX1, const float fY1 )	{ MinX = fX0; MinY = fY0; MaxX = fX1; MaxY = fY1; }

		inline void Reset(void);
		inline void Set( const Asura_Vector_2& v );
		inline void Set( const float fMinX,const float fMinY,const float fMaxX,const float fMaxY )
		{
			MinX = fMinX;
			MinY = fMinY;
			MaxX = fMaxX;
			MaxY = fMaxY;		
		}
	
		inline bool Intersects(const Asura_Bounding_Rectangle& xRect) const;
		
		inline bool Contains( const Asura_Vector_2& point ) const;
		inline Asura_Vector_2 CalculateCentre(void) const;

		inline float GetWidth(void) const  { return Asura_Maths::Abs(MaxX - MinX); }
		inline float GetHeight(void) const { return Asura_Maths::Abs(MaxY - MinY); }
		inline void  Displace( const Asura_Vector_2& xDisp );

		inline bool operator == ( const Asura_Bounding_Rectangle& xRect) const
		{
			return (MinX == xRect.MinX) && (MinY == xRect.MinY) &&  
				(MaxX == xRect.MaxX) && (MaxY == xRect.MaxY);
		}
		inline bool operator != ( const Asura_Bounding_Rectangle& xRect) const
		{
			return ! (*this==xRect);
		}

		inline const Asura_Vector_2 GetTopLeft() const
		{
			return Asura_Vector_2( MinX,MinY );
		}

		inline void SetTopLeft( const Asura_Vector_2& xTopLeft )
		{
			MinX = xTopLeft.x;
			MinY = xTopLeft.y;
		}

		inline void SetTopLeft( const float fMinX,const float fMinY )
		{
			MinX = fMinX;
			MinY = fMinY;
		}

		inline const Asura_Vector_2 GetBottomRight() const
		{
			return Asura_Vector_2( MaxX,MaxY );
		}

		inline void SetBottomRight( const Asura_Vector_2& xBottomRight )
		{
			MaxX = xBottomRight.x;
			MaxY = xBottomRight.y;
		}
		
		inline void SetBottomRight( const float fMaxX,const float fMaxY )
		{
			MaxX = fMaxX;
			MaxY = fMaxY;
		}

		// returns the size of the rectange in x and y
		inline const Asura_Vector_2 GetDimension() const
		{
			return Asura_Vector_2( GetWidth(),GetHeight() );
		}

};
 
class Asura_Bounding_Box
{
	public:
		union
		{
			struct
			{
				float MinX;
				float MaxX;
				float MinY;
				float MaxY;
				float MinZ;
				float MaxZ;
			};

			float m_fElement[6];
		};

		//Asura_Vector_3 Centre;

		void Reset(void);
		void Set(const class Asura_Vector_3& v);
		void Set(const class Asura_Bounding_Box& bb);
		void SetToPoint(const class Asura_Vector_3& xPoint)
		{
			MinX = MaxX = xPoint.x;
			MinY = MaxY = xPoint.y;
			MinZ = MaxZ = xPoint.z;
		}

		// Increase the size of the BB by adding an amount to the extents
		// (amount is subtracted from minima and added to maxima).
		void Grow( const float fIncrease )
		{
			// KJFL - 18 November 2005 - We need to increase by a +ve amount
			// otherwise the bounding box may get "inside out". If we want
			// a fn to shrink the BB we'll have to check for points where
			// we shrink to zero length along each axis.
			Asura_Assert( ( fIncrease >= 0.0f ), "Asura_Bounding_Box::Grow - must grow by a +ve amount." );

            MinX -= fIncrease;
			MinY -= fIncrease;
			MinZ -= fIncrease;

			MaxX += fIncrease;
			MaxY += fIncrease;
			MaxZ += fIncrease;
		}

		// ART - 08/03/06 - Scale functions for the bounding-box...
		void Scale(const float fScale)
		{
			Asura_Assert((fScale > 0.0f), "Must scale by a +ve amount");

			MinX *= fScale;
			MinY *= fScale;
			MinZ *= fScale;

			MaxX *= fScale;
			MaxY *= fScale;
			MaxZ *= fScale;
		}

		void Scale(const Asura_Vector_3& xScale)
		{
			Asura_Assert(((xScale[0] > 0.0f) && (xScale[1] > 0.0f) && (xScale[2] > 0.0f)), "Must scale by a +ve amount");

			MinX *= xScale[0];
			MinY *= xScale[1];
			MinZ *= xScale[2];

			MaxX *= xScale[0];
			MaxY *= xScale[1];
			MaxZ *= xScale[2];
		}
		
		void Zero(){ MinX = MaxX = MinY = MaxY = MinZ = MaxZ = 0.f; }
        
		Asura_Vector_3 CalculateCentre(void) const;
        Asura_Vector_3 CalculateExtents( void ) const;
		float GetVolume(void) const;

		float GetSurfaceArea( void ) const
		{
			const float fLength = MaxX - MinX;
			const float fWidth = MaxZ - MinZ;
			const float fHeight = MaxY - MinY;

			return (fLength*fWidth + fHeight*fWidth + fLength*fHeight) * 2.0f;
		}

		inline bool Intersects(const Asura_Bounding_Box& b) const;
		inline bool Intersects(const Asura_Bounding_Box& b, const Asura_Vector_3 & offset) const;
		inline bool IntersectsSphere(const Asura_Vector_3& centre, const float radius) const;

		inline bool Contains(const Asura_Vector_3& point) const ;
		inline bool Contains(const Asura_Bounding_Box& b) const;
        
		inline void Displace(const Asura_Vector_3& displacement);
        
        float GetSqdMinDistFromBB( const Asura_Bounding_Box& xOtherBB ) const;
        float GetSqdMinDistFromPoint( const Asura_Vector_3& xPoint ) const;
        void  CalcMinVectorIntoBox( const Asura_Vector_3& xPoint, Asura_Vector_3& xVector ) const;

		inline bool operator == ( const Asura_Bounding_Box& xBB) const
		{
			return (MinX == xBB.MinX) && (MinY == xBB.MinY) && (MinZ == xBB.MinZ) && 
				(MaxX == xBB.MaxX) && (MaxY == xBB.MaxY) && (MaxZ == xBB.MaxZ);
		}
		inline bool operator != ( const Asura_Bounding_Box& xBB) const
		{
			return ! (*this==xBB);
		}

        // Rotates an axis aligned bounding box and generates a new AABB that encompasses it.
		// By default the rotation is around (0,0,0) , which may result in the bounding box being displaced
		// if it is a world space bounding box.
		// If bRotateAboutMidpoint is true , then the box will be rotated about the centre instead (as taken from CalculateCentre())
		// RAR 18/01/2007
        void FromOBB( const Asura_Bounding_Box& xOBB, const Asura_Matrix_3x3& xOri, bool bRotateAboutMidpoint = false );

		inline Asura_Bounding_Box Intersection(const Asura_Bounding_Box& b)
		{
			Asura_Bounding_Box result = b;
			if ( MaxX < result.MaxX )	result.MaxX = MaxX;
			if ( MaxY < result.MaxY )	result.MaxY = MaxY;
			if ( MaxZ < result.MaxZ )	result.MaxZ = MaxZ;

			if ( MinX > result.MinX )	result.MinX = MinX;
			if ( MinY > result.MinY )	result.MinY = MinY;
			if ( MinZ > result.MinZ )	result.MinZ = MinZ;

			// wipeout in case we've gone negative
			if ( ( result.MaxX < result.MinX) ||
				 ( result.MaxY < result.MinY) ||
				 ( result.MaxZ < result.MinZ) )
			{
				result.MaxX = result.MinX = MinX;
				result.MaxY = result.MinY = MinY;
				result.MaxZ = result.MinZ = MinZ;
			}
			return result;
		}

		inline float CalculateRadius(void) const
		{
			return( CalculateExtents().Magnitude() * 0.5f );
		}
		
		inline Asura_Vector_3 GetMax() const { return( Asura_Vector_3( MaxX, MaxY, MaxZ));}
		inline Asura_Vector_3 GetMin() const { return( Asura_Vector_3( MinX, MinY, MinZ));}

		// VGP 14Mar2006 Get nearest/further AABB point along vector
		inline Asura_Vector_3 GetNear(const Asura_Vector_3& v)
		{
			return Asura_Vector_3(	(v.x > 0) ? MinX : MaxX,
									(v.y > 0) ? MinY : MaxY,
									(v.z > 0) ? MinZ : MaxZ );
		}

		inline Asura_Vector_3 GetFar(const Asura_Vector_3& v)
		{
			return Asura_Vector_3(	(v.x < 0) ? MinX : MaxX,
									(v.y < 0) ? MinY : MaxY,
									(v.z < 0) ? MinZ : MaxZ );
		}
};

class Asura_HermiteSpline
{

public:

	void Recalculate(const Asura_Vector_3& xPos0, const Asura_Vector_3& xPos1, const Asura_Vector_3& xVel0, const Asura_Vector_3& xVel1)
	{
		// I got this derivation from Game Programming Gems 4 p.173. It corresponds
		// to the version in Nick Caldwell's article "Defeating Lag With Cubic Splines"
		// http://www.gamedev.net/reference/articles/article914.asp which uses 4 points
		// instead of using 2 points and 2 velocities.
		m_xA = (xPos0 * 2.0f) + (xPos1 * -2.0f) + xVel0 + xVel1;
		m_xB = (xPos0 * -3.0f) + (xPos1 * 3.0f) + (xVel0 * -2.0f) - xVel1;
		m_xC = xVel0;
		m_xD = xPos0;
	}

	// For time value between 0 and 1, returns interpolated position. At time = 0 
	// it returns the initial pos at time = 1 it returns the end position.
	void GetPosition(float fTime, Asura_Vector_3& xOutput) const
	{
		Asura_Assert(fTime >= 0.0f && fTime <= 1.0f, "cubic spline parameter out of range");
		float fTime2 = fTime * fTime;
		float fTime3 = fTime2 * fTime;
		xOutput = m_xA * fTime3 + m_xB * fTime2 + m_xC * fTime + m_xD;
	}

	void GetVelocity(float fTime, Asura_Vector_3& xVelOutput) const
	{
		Asura_Assert(fTime >= 0.0f && fTime <= 1.0f, "cubic spline parameter out of range");
		// derived from figures 2.4.2 and 2.4.14 in the Gems article.
		// Essentially this formula is just the first derivative of the formula for 
		// position wrt time.
		float fTime2 = fTime * fTime;
		xVelOutput = m_xA * (fTime2 * 3.0f) + m_xB * (fTime * 2.0f) + m_xC;
	}

private:
	
	Asura_Vector_3 m_xA, m_xB, m_xC, m_xD;
};




class Asura_Complex
{

    public:

        // C O N S T R U C T O R S   /   D E S T R U C T O R S ///////////////

        Asura_Complex( void ) { }
        Asura_Complex( const Asura_Complex& xOther ) : m_fR( xOther.m_fR ), m_fI( xOther.m_fI ) { }
        Asura_Complex( float fR, float fI ) : m_fR( fR ), m_fI( fI ) { }

        // O P E R A T O R S /////////////////////////////////////////////////

        Asura_Complex& operator=( const Asura_Complex& xOther )
        {
            m_fR = xOther.m_fR;
            m_fI = xOther.m_fI;
            return *this;
        }

        // Allows you to treat an instance as an array of {real,imaginary}.
        float operator[]( u_int u ) const { return m_afE[u]; }
        float& operator[]( u_int u ) { return m_afE[u]; }

        Asura_Complex& operator*=( const Asura_Complex& xOther );
        Asura_Complex& operator+=( const Asura_Complex& xOther );
        Asura_Complex& operator-=( const Asura_Complex& xOther );

        Asura_Complex operator*( const Asura_Complex& xOther ) const;
        Asura_Complex operator+( const Asura_Complex& xOther ) const;
        Asura_Complex operator-( const Asura_Complex& xOther ) const;

        // M E T H O D S /////////////////////////////////////////////////////

        float GetR( void ) const { return m_fR; }
        float GetI( void ) const { return m_fI; }

        void Set( float fR, float fI ) { m_fR = fR; m_fI = fI; }
        void SetR( float fR ) { m_fR = fR; }
        void SetI( float fI ) { m_fI = fI; }

        Asura_Complex& Conjugate( void ) { m_fI = -m_fI; return *this; }
        Asura_Complex GetConjugate( void ) const { return Asura_Complex( m_fR, -m_fI ); }

        // S T A T E /////////////////////////////////////////////////////////

        union
        {
            float m_afE[2];

            struct
            {
                float m_fR;
                float m_fI;
            };
        };

};


//! KJFL - 2004-04-22 - class to contain the coefficients for 3rd order Spherical Harmonics basis functions.
//! May benefit from moving code to Platform_Maths when and if it gets used by in-game code. It's more likely that
//! a specialised 3-vector version (i.e. RGB handled simultaneously) gets made to cope with making SHs of incoming
//! lights to use on objects/characters in game, and this version will only get used by the Tools.
//! Anyway, easier to keep it all here whilst it's in development.
class Asura_SphericalHarmonics_Order3
{
	public:

		enum { NUMBER_OF_TERMS = 9 };

		void Zero( void )
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afCoefficient[u] = 0.0f;
			}
		}

		bool IsZero() const
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				if(m_afCoefficient[u]) return false;
			}
			return true;
		}
		
		inline bool operator == ( const Asura_SphericalHarmonics_Order3& xOther ) const
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				if (m_afCoefficient[u] != xOther.m_afCoefficient[u]) return false;
			}
			return true;
		}
		
		inline bool operator != ( const Asura_SphericalHarmonics_Order3& xOther ) const
		{
			return ! (*this==xOther);
		}

		//! get the signal for a given direction
		float EvaluateInDirection( const class Asura_Vector_3& xNormal ) const 
		{
			float f;
	
			f  = m_afCoefficient[0];  
			f += m_afCoefficient[1] * xNormal.x;
			f += m_afCoefficient[2] * xNormal.y;
			f += m_afCoefficient[3] * xNormal.z;
			f += m_afCoefficient[4] * xNormal.x * xNormal.y;
			f += m_afCoefficient[5] * xNormal.y * xNormal.z;
			f += m_afCoefficient[6] * xNormal.x * xNormal.z;
			f += m_afCoefficient[7] * (3.0f * xNormal.z*xNormal.z - 1.0f);
			f += m_afCoefficient[8] * (xNormal.x*xNormal.x - xNormal.y*xNormal.y);

			return f;
		}
		
		float GetZerothOrderOcclusionTerm( void ) const
		{
			return m_afCoefficient[0];
		}


		//! integrate this SH with another one (effectively dot-product as it happens)
		float Integrate( const Asura_SphericalHarmonics_Order3& xSH ) const
		{
			float fIntegral = 0.0f;

			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				fIntegral += xSH.m_afCoefficient[u] * m_afCoefficient[u];
			}

			return fIntegral;
		}
	
		//! add a sample for a given direction
		void AddSampleForDirection( const float fSample, const class Asura_Vector_3& xNormal )
		{
			m_afCoefficient[0] += fSample;
			m_afCoefficient[1] += fSample * xNormal.x;
			m_afCoefficient[2] += fSample * xNormal.y;
			m_afCoefficient[3] += fSample * xNormal.z;
			m_afCoefficient[4] += fSample * xNormal.x * xNormal.y;
			m_afCoefficient[5] += fSample * xNormal.y * xNormal.z;
			m_afCoefficient[6] += fSample * xNormal.x * xNormal.z;
			m_afCoefficient[7] += fSample * (3.0f * xNormal.z*xNormal.z - 1.0f);
			m_afCoefficient[8] += fSample * (xNormal.x*xNormal.x - xNormal.y*xNormal.y);
			
		}
		
		//! if you add N stratified samples then you'll want to normalise the results at the end
		void ScaleCoefficientsForNSamples( const float fNumberOfSamples )
		{
			float fScale =  /*4.0f * Asura_Maths::Pi*/ 1.0f / fNumberOfSamples;

			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afCoefficient[u] *= fScale;
			}
		}

		//! scale coefficients prior to using them for irradiance sampling
		void ScaleCoefficientsForIrradiance( void )
		{
			// KJFL - 2004-04-22 
			//
			// This is going to be far too technical to explain in a simple comment - you need a fairly good grounding
			// in the maths behind basis functions and Real Spherical Harmonics. At least this should serve as a reminder to myself. ;)
			//
			// Basically the cartesian terms of the first few real SH functions which we'll be using are
			//
			//				m=-2					m=-1			 	 m=0					m=+1						m=+2
			//
			// l=0													  sqrt(1/4PI)
			//
			// l=1								sqrt(3/4PI)y   		  sqrt(3/4PI)z			sqrt(3/4PI)x
			//
			// l=2		sqrt(15/4PI)yx		   sqrt(15/4PI)yz	  sqrt(5/16PI)(3z*z-1)     sqrt(15/4PI)zx			sqrt(15/16PI)(xx-yy)
			//
			// where xx+yy+zz = 1 (x, y and z are pretty exchangeable when defining things - you'll be fine so long as you stick to the same axis
			// ordering throughout; other derivations have x/y/z swapped and minus signs etc.)
			//
			// One of the standard resources for SH with games, "Spherical Harmonic Lighting: The Gritty Details" by Robin Green (Sony Research)
			// gets one of the above constants wrong (the xx-yy term), so expect to see incorrect terms in lots of papers (and engines).
			//
			// The constant coefficients in these basis function should in theory be used when constructing the SH and when evaluating the SH
			// for a certain direction etc. However, we can save time by ignoring the constants until we're ready to use the SH - and then simply
			// multiply our basis function coefficients by the square of the values. To further complicate matters we want to a) multiply our results
			// by a normalisation factor of (16*PI)/17 to get them in the 0->1.0f range, and b) convolve the signal in the normal direction so we're 
			// effectively calculating the integral of the lighting environment against a cosine term oriented in that direction, rather than 
			// doing a simple point evaluation.
			//
			// The upshot of all this is that the constants we need to multiply our basis function coefficients by lose the PI terms and any square
			// roots since they all cancel out. Such is the beauty of mathematics. ;)
			//
			// The changes in the constants to convolve over the cosine term etc. are taken from work by Peter-Pike Sloan at Microsoft Research - 
			// they're the third bracketed terms in the constants below; I confess I haven't derived the maths for these and just took them as 
			// gospel since they produced good results (which is what we're here for at the end of the day).

			const float fConst1 = (1.0f  /  4.0f) * (16.0f/17.0f) * (1.0f);
			const float fConst2 = (3.0f  /  4.0f) * (16.0f/17.0f) * (2.0f/3.0f);
			const float fConst3 = (15.0f /  4.0f) * (16.0f/17.0f) * (1.0f/4.0f);
			const float fConst4 = (5.0f  / 16.0f) * (16.0f/17.0f) * (1.0f/4.0f);
			const float fConst5 = (15.0f / 16.0f) * (16.0f/17.0f) * (1.0f/4.0f);

			m_afCoefficient[0] *= fConst1;
			m_afCoefficient[1] *= fConst2;
			m_afCoefficient[2] *= fConst2;
			m_afCoefficient[3] *= fConst2;
			m_afCoefficient[4] *= fConst3;
			m_afCoefficient[5] *= fConst3;
			m_afCoefficient[6] *= fConst3;
			m_afCoefficient[7] *= fConst4;
			m_afCoefficient[8] *= fConst5; 
		}

		
		inline Asura_SphericalHarmonics_Order3& operator += ( const Asura_SphericalHarmonics_Order3& xOther)
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afCoefficient[u] += xOther.m_afCoefficient[u];
			}
			return *this;
		}

		inline Asura_SphericalHarmonics_Order3& operator *= ( const Asura_SphericalHarmonics_Order3& xOther)
		{
			// approximation just returning 1st order
			
			float fCoeff0 = m_afCoefficient[0] * xOther.m_afCoefficient[0];
			float fCoeff1 = xOther.m_afCoefficient[0] * m_afCoefficient[1] + m_afCoefficient[0] * xOther.m_afCoefficient[1];
			float fCoeff2 = xOther.m_afCoefficient[0] * m_afCoefficient[2] + m_afCoefficient[0] * xOther.m_afCoefficient[2];
			float fCoeff3 = xOther.m_afCoefficient[0] * m_afCoefficient[3] + m_afCoefficient[0] * xOther.m_afCoefficient[3];
	
			m_afCoefficient[0] = fCoeff0;
			m_afCoefficient[1] = fCoeff1;
			m_afCoefficient[2] = fCoeff2;
			m_afCoefficient[3] = fCoeff3;

			
			return *this;
		}

		inline void Scale( float f) 
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afCoefficient[u] *= f;
			}
		}

		inline float operator[]( u_int u ) const 
		{
			return m_afCoefficient[u];
		}

		inline float& operator[]( u_int u)
		{
			return m_afCoefficient[u];
		}
	private:

		float m_afCoefficient[NUMBER_OF_TERMS];
};

class Asura_IntVector_2
{
	public:	
		union
		{

			struct
			{
				int iX, iY;
			};
			int iElement[2];
		};

        inline int& operator[]( u_int u ) { return iElement[u]; }
        inline int operator[]( u_int u ) const { return iElement[u]; }

		// constructors
		Asura_IntVector_2() {}
		
		Asura_IntVector_2(const Asura_IntVector_2& v)
		{
			iX = v.iX;
			iY = v.iY;
		}

	    Asura_IntVector_2(int x0, int y0)
		{
			iX = x0;
			iY = y0;
		}

		// binary ops
		inline Asura_IntVector_2 operator * ( int iScale ) const
		{
			return Asura_IntVector_2( this->iX * iScale, this->iY * iScale );
		}

		inline Asura_IntVector_2 operator + ( const Asura_IntVector_2& v) const
		{
			return Asura_IntVector_2(iX + v.iX, iY + v.iY);
		}
		inline void operator += ( const Asura_IntVector_2& v)
		{
			iX += v.iX;
			iY += v.iY;
		}

		inline Asura_IntVector_2 operator - ( const Asura_IntVector_2& v) const
		{
			return Asura_IntVector_2(iX - v.iX, iY - v.iY);
		}
		inline void operator -= ( const Asura_IntVector_2& v)
		{
			iX -= v.iX;
			iY -= v.iY;
		}

		inline void operator *= ( int i )
		{
			iX *= i;
			iY *= i;
		}
		inline void operator /= ( int i)
		{
			iX /= i;
			iY /= i;
		}

		inline bool operator == ( const Asura_IntVector_2& v) const
		{
			return (iX == v.iX) && (iY == v.iY);
		}

		inline bool operator != ( const Asura_IntVector_2& v) const
		{
			return (iX != v.iX) || (iY != v.iY);
		}

		inline void Zero(void)
		{
			iX = 0;
			iY = 0;
		}
};

#include "Core/Asura_PlatformMaths.h"
// stuff that uses the contents of platform maths goes here.

// RDM - 17/01/08 - Moved include of reference vector maths here as it makes use of Min/Max which
// utilises specialised templates instantiated in PlatformMaths
#include "Core/Asura_Reference_VectorMaths.h"

// (AAM - 22.09.2008) Moved these after the #include so they follow the definition of the aligned
// vector classes. LTCG configs throw warnings about alignment redefinitions otherwise
namespace Asura_Maths
{
	extern const Asura_Vector_2		ZeroVector2;
	extern const Asura_Vector_3		ZeroVector3;
	extern const Asura_AVector3		ZeroAVector3;
	extern const Asura_AVector4		ZeroAVector4W0;
	extern const Asura_AVector4		ZeroAVector4W1;
	extern const Asura_Matrix_3x3	IdentityMatrix3x3;
	extern const Asura_AMatrix3x3	IdentityAMatrix3x3;
	extern const Asura_AMatrix4x4	IdentityAMatrix4x4;
	extern const Asura_Euler_3		ZeroEuler3;
	extern const Asura_Quat			IdentityQuat;
	extern const Asura_IntVector_2	ZeroIntVector2;
}

// (AAM - 22.09.2008) Had to move Asura_Quat::ScaleRotation() here after the above change, since it
// uses IdentityQuat. Can't keep it with class defn, since Asura_PlatformMaths.h might need it
inline Asura_Quat Asura_Quat::ScaleRotation(float fScale) const
{
	Asura_Quat xResult;

	xResult.NLerp(Asura_Maths::IdentityQuat, *this, fScale);
	return(xResult);
}

// ClampToRange & Sort need to be instantiated here as they use Min/Max
template< typename T >
static inline T Asura_Maths::ClampToRange( const T& tValue, const T& tMin, const T& tMax )
{
	Asura_Assert(tMin <= tMax, "Invalid parameters passed to ClampToRange. Behaviour is undefined");
	return Asura_Maths::Min( Asura_Maths::Max( tValue, tMin ), tMax );
}

template< typename T >
static inline T Asura_Maths::SmoothStep( const T& tValue, const T& tMin, const T& tMax )
{
	return (Asura_Maths::ClampToRange(tValue, tMin, tMax) - tMin) / (tMax - tMin);
}


template< typename T > 
static inline void Asura_Maths::Sort( T& tA, T& tB )
{
	const T tTemp = tA;
	tA = Asura_Maths::Min( tA, tB );
	tB = Asura_Maths::Max( tTemp, tB );
}

template< typename T > 
static inline void Asura_Maths::Swap( T& tA, T& tB )
{
	const T tTemp = tA;
	tA = tB;
	tB = tTemp;
}


class Asura_SphericalHarmonics_Order2_RGB
{
	public:

		enum { NUMBER_OF_TERMS = 4, NUMBER_OF_COEFFICIENTS = NUMBER_OF_TERMS*3 };

		inline void Set( const Asura_SphericalHarmonics_Order3& xR, const Asura_SphericalHarmonics_Order3& xG, const Asura_SphericalHarmonics_Order3& xB )
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afRedCoefficients[u] = xR[u];
				m_afGreenCoefficients[u] = xG[u];
				m_afBlueCoefficients[u] = xB[u];
			}
		}

		inline void ScaledAdd( const Asura_SphericalHarmonics_Order2_RGB& xAdd, const float fScale )
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afRedCoefficients[u]		+= xAdd.m_afRedCoefficients[u] * fScale;
				m_afGreenCoefficients[u]	+= xAdd.m_afGreenCoefficients[u] * fScale;
				m_afBlueCoefficients[u]		+= xAdd.m_afBlueCoefficients[u] * fScale;
			}
		}

		inline void Scale( const float fScale )
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afRedCoefficients[u]		*= fScale;
				m_afGreenCoefficients[u]	*= fScale;
				m_afBlueCoefficients[u]		*= fScale;
			}
		}

		// scale the zeroth and first order coefficients by different amounts
		inline void ScaleCoefficients( float fZerothOrderScale, float fFirstOrderScale )
		{
			m_afRedCoefficients[0]		*= fZerothOrderScale;
			m_afGreenCoefficients[0]	*= fZerothOrderScale;
			m_afBlueCoefficients[0]		*= fZerothOrderScale;

			for (u_int u=1; u<NUMBER_OF_TERMS; u++)
			{
				m_afRedCoefficients[u]		*= fFirstOrderScale;
				m_afGreenCoefficients[u]	*= fFirstOrderScale;
				m_afBlueCoefficients[u]		*= fFirstOrderScale;
			}
		}

		inline void Zero( void )
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afRedCoefficients[u] = 0.0f;
				m_afGreenCoefficients[u] = 0.0f;
				m_afBlueCoefficients[u] = 0.0f;
			}
		}
		
		inline Asura_SphericalHarmonics_Order2_RGB& operator += ( const Asura_SphericalHarmonics_Order2_RGB& xOther)
		{
			for (u_int u=0; u<NUMBER_OF_TERMS; u++)
			{
				m_afRedCoefficients[u]		+= xOther.m_afRedCoefficients[u];
				m_afGreenCoefficients[u]	+= xOther.m_afGreenCoefficients[u];
				m_afBlueCoefficients[u]		+= xOther.m_afBlueCoefficients[u];
			}
			return *this;
		}

			//! add a sample for a given direction
		void AddSampleForDirection( const float fR, const float fG, const float fB, const class Asura_Vector_3& xNormal )
		{
			m_afRedCoefficients[0] += fR;
			m_afRedCoefficients[1] += fR * xNormal.x;
			m_afRedCoefficients[2] += fR * xNormal.y;
			m_afRedCoefficients[3] += fR * xNormal.z;

			m_afGreenCoefficients[0] += fG;
			m_afGreenCoefficients[1] += fG * xNormal.x;
			m_afGreenCoefficients[2] += fG * xNormal.y;
			m_afGreenCoefficients[3] += fG * xNormal.z;

			m_afBlueCoefficients[0] += fB;
			m_afBlueCoefficients[1] += fB * xNormal.x;
			m_afBlueCoefficients[2] += fB * xNormal.y;
			m_afBlueCoefficients[3] += fB * xNormal.z;

		}
	
		//! scale coefficients prior to using them for irradiance sampling
		void ScaleCoefficientsForIrradiance( void )
		{
			// for explanation of constants, see Asura_SphericalHarmonics_Order3
			const float fConst1 = (1.0f  /  4.0f) * (16.0f/17.0f) * (1.0f);
			const float fConst2 = (3.0f  /  4.0f) * (16.0f/17.0f) * (2.0f/3.0f);
		
			m_afRedCoefficients[0] *= fConst1;
			m_afRedCoefficients[1] *= fConst2;
			m_afRedCoefficients[2] *= fConst2;
			m_afRedCoefficients[3] *= fConst2;

			m_afGreenCoefficients[0] *= fConst1;
			m_afGreenCoefficients[1] *= fConst2;
			m_afGreenCoefficients[2] *= fConst2;
			m_afGreenCoefficients[3] *= fConst2;
		
			m_afBlueCoefficients[0] *= fConst1;
			m_afBlueCoefficients[1] *= fConst2;
			m_afBlueCoefficients[2] *= fConst2;
			m_afBlueCoefficients[3] *= fConst2;
		}

		inline float& operator[]( u_int u)
		{
			return m_afCoefficient[u];
		}

		inline float operator[]( u_int u ) const 
		{
			return m_afCoefficient[u];
		}

        // Given an object with the specified orientation, these rotates the
        // current instance into that object's modelspace.
        void RotateToModelspace( const Asura_Matrix_3x3& xOri )
        {
            float fX, fY, fZ;

            #define ROTATE( afCoefficients ) \
                fX = xOri.e11 * afCoefficients[1] + xOri.e21 * afCoefficients[2] + xOri.e31 * afCoefficients[3]; \
                fY = xOri.e12 * afCoefficients[1] + xOri.e22 * afCoefficients[2] + xOri.e32 * afCoefficients[3]; \
                fZ = xOri.e13 * afCoefficients[1] + xOri.e23 * afCoefficients[2] + xOri.e33 * afCoefficients[3]; \
                \
                afCoefficients[1] = fX; \
                afCoefficients[2] = fY; \
                afCoefficients[3] = fZ;

            ROTATE( m_afRedCoefficients );
            ROTATE( m_afGreenCoefficients );
            ROTATE( m_afBlueCoefficients );

            #undef ROTATE
        }

        //! get the signal for a given direction
        void EvaluateInDirection( const class Asura_Vector_3& xNormal, class Asura_RGBA& xValue ) const;

		// KOR - 25/10/2004 - this gives the approximate direction on the unit sphere at which the harmonic will return the brightest colour
		// This can be used as a decent direction for a specular highlight.
		// For the moment, brightness is simply treated as r + g + b.
		// We may want to change it to use a more accurate brightness metric (e.g. more biased to green and away from blue)
		void GetBrightestDirection(Asura_Vector_3& xDirection) const
		{
			xDirection.x = m_afRedCoefficients[1] + m_afGreenCoefficients[1] + m_afBlueCoefficients[1];
			xDirection.y = m_afRedCoefficients[2] + m_afGreenCoefficients[2] + m_afBlueCoefficients[2];
			xDirection.z = m_afRedCoefficients[3] + m_afGreenCoefficients[3] + m_afBlueCoefficients[3];
			xDirection.Normalise();
		}

		// Colourise the SH towards a RGB colour. A lerp value of 0.0f leaves the SH unchanged, and a value of 1.0f gives
		// you a new SH which is equivalent to a monochromatic version of the original filtered by the given colour.
		// This means it is *not* a simple filter, e.g. if you have a red SH and colourise it to blue you will end 
		// up with a blue SH.
		void Colourise( const float fR, const float fG, const float fB, float fLerp )
		{
			fLerp = Asura_Maths::ClampToRange( fLerp, 0.0f, 1.0f );

			const float fOneMinusLerp = 1.0f - fLerp;

			for ( u_int u=0; u<NUMBER_OF_TERMS; ++u )
			{
				float fMonochromaticCoefficient = 0.30f * m_afRedCoefficients[u] + 0.59f * m_afGreenCoefficients[u] + 0.11f * m_afBlueCoefficients[u];
			
				fMonochromaticCoefficient *= fLerp;

				m_afRedCoefficients[u]		= m_afRedCoefficients[u]	* fOneMinusLerp + fMonochromaticCoefficient * fR;
				m_afGreenCoefficients[u]	= m_afGreenCoefficients[u]	* fOneMinusLerp + fMonochromaticCoefficient * fG;
				m_afBlueCoefficients[u]		= m_afBlueCoefficients[u]	* fOneMinusLerp + fMonochromaticCoefficient * fB;
			}
			
		}


		union
		{
			struct
			{
				float m_afRedCoefficients[NUMBER_OF_TERMS];
				float m_afGreenCoefficients[NUMBER_OF_TERMS];
				float m_afBlueCoefficients[NUMBER_OF_TERMS];
			};

			struct
			{
				float m_afCoefficient[NUMBER_OF_COEFFICIENTS];
			};
		};
};

inline float Asura_Matrix_3x3::Heading(void) const 
{
	if (e13 == 0 && e33 == 0) return 0;

	return Asura_Maths::ArcTangent(e13,e33);
}


#if 0
// new version of BB using aligned vectors. Since eventually all code should probably
// use aligned vectors using Asura_BoundingBox was decided to be OK. The class using 
// 6 floats is called Asura_Bounding_Box.
class Asura_BoundingBox
{
	private:
		Asura_AVector3 m_xMin;
		Asura_AVector3 m_xMax;

	public:
		// constructors
		Asura_BoundingBox() {}
		Asura_BoundingBox(const Asura_BoundingBox& xOther)
		{
			m_xMin = xOther.m_xMin;
			m_xMax = xOther.m_xMax;
		}
		Asura_BoundingBox(const Asura_AVector3& xMin, const Asura_AVector3& xMax) 
		{
			m_xMin = xMin;
			m_xMax = xMax;
		}
		// conversion ctor
		explicit Asura_BoundingBox( const Asura_Bounding_Box& xOldBB )
		{
			m_xMin = Asura_AVector3(xOldBB.MinX, xOldBB.MinY, xOldBB.MinZ); 
			m_xMax = Asura_AVector3(xOldBB.MaxX, xOldBB.MaxY, xOldBB.MaxZ);
		}
		// operator=
		inline Asura_BoundingBox& operator = ( const Asura_BoundingBox& xOther ) 
		{
			m_xMin = xOther.m_xMin;
			m_xMax = xOther.m_xMax;
			return *this;
		}


		const Asura_AVector3& GetMin() const { return m_xMin; }
		const Asura_AVector3& GetMax() const { return m_xMax; }
		void SetMin(const Asura_AVector3& xMin) { m_xMin = xMin; }
		void SetMax(const Asura_AVector3& xMax) { m_xMax = xMax; }

		inline void Reset(void)
		{
			m_xMin.SetX(Asura_Maths::LargeFloat);
			m_xMin.SetY(Asura_Maths::LargeFloat);
			m_xMin.SetZ(Asura_Maths::LargeFloat);
			m_xMax = -m_xMin;
		}
		inline void Set(const Asura_AVector3& xVec)
		{
			m_xMin.SelectLE(m_xMin, xVec, m_xMin, xVec);
			m_xMax.SelectGE(m_xMax, xVec, m_xMax, xVec);
		}
		inline void Set(const Asura_BoundingBox& xBB)
		{
			m_xMin.SelectLE(m_xMin, xBB.m_xMin, m_xMin, xBB.m_xMin);
			m_xMax.SelectGE(m_xMax, xBB.m_xMax, m_xMax, xBB.m_xMax);
		}
		inline void SetToPoint(const Asura_AVector3& xPoint)
		{
			m_xMin = xPoint;
			m_xMax = xPoint;
		}

		inline void Zero()
		{ 
			m_xMin.Zero();
			m_xMax.Zero();
		}
		
		inline Asura_AVector3 CalculateCentre(void) const
		{
			return (m_xMin + m_xMax) * 0.5f;
		}

		inline Asura_AVector3 CalculateExtents( void ) const
		{
			return m_xMax - m_xMin;
		}

// (MH - 07.12.2005) TODO if required
//		inline float GetVolume(void) const 
//		{
//			float x = MaxX-MinX;
//			float y = MaxY-MinY;
//			float z = MaxZ-MinZ;
//			return x*y*z;
//		}

		inline bool Intersects(const Asura_BoundingBox& xBB) const
		{
			return (m_xMax >= xBB.m_xMin) && (m_xMin <= xBB.m_xMax);
		}


		inline bool Intersects(const Asura_BoundingBox& xBB, const Asura_AVector3& xOffset) const
		{
			return (m_xMax >= (xBB.m_xMin + xOffset)) && (m_xMin <= (xBB.m_xMax + xOffset));
		}

// (MH - 07.12.2005) TODO if required
//		inline bool IntersectsSphere(const Asura_Vector_3& centre, const float radius) const
//		{
//			if ( ((MaxX>=centre.x-radius) && (MinX <= centre.x+radius))
//			&& ((MaxY>=centre.y-radius) && (MinY <= centre.y+radius))
//			&& ((MaxZ>=centre.z-radius) && (MinZ <= centre.z+radius)) )
//			{
//				return true;
//			}
//			else return false;
//		}

		inline bool Contains(const Asura_AVector3& xPoint) const 
		{
			return (xPoint >= m_xMin) && xPoint <= m_xMax;
		}

		inline bool Contains(const Asura_BoundingBox& xBB) const
		{
			return (xBB.m_xMin >= m_xMin) && (xBB.m_xMax <= m_xMax);
		}

		inline void Displace(const Asura_AVector3& xDisplacement)
		{
			m_xMax += xDisplacement;
			m_xMin += xDisplacement;
		}

		inline bool operator == ( const Asura_BoundingBox& xBB) const
		{
			return (m_xMin == xBB.m_xMin) && (m_xMax == xBB.m_xMax);
		}
		inline bool operator != ( const Asura_BoundingBox& xBB) const
		{
			return (m_xMin != xBB.m_xMin) || (m_xMax != xBB.m_xMax);
		}

        // (MH - 07.12.2005) TODO if required
		//void FromOBB( const Asura_Bounding_Box& xOBB, const Asura_Matrix_3x3& xOri );

		inline Asura_BoundingBox Intersection(const Asura_BoundingBox& xBB)
		{
			Asura_BoundingBox xResult;

			xResult.m_xMax.SelectLT( m_xMax, xBB.m_xMax, m_xMax, xBB.m_xMax );
			xResult.m_xMin.SelectGT( m_xMin, xBB.m_xMin, m_xMin, xBB.m_xMin );

			// wipeout in case we've gone negative
			Asura_Assert( xResult.m_xMin <= xResult.m_xMax, "invalid bb result" );
			return xResult;
		}

		inline float CalculateRadius(void) const
		{
			return( CalculateExtents().Magnitude() * 0.5f );
		}
        // (MH - 07.12.2005) TODO - if required, currently scalar addition/subtraction not implemented.
		// Increase the size of the BB by adding an amount to the extents
		// (amount is subtracted from minima and added to maxima).
//		void Grow( const float fIncrease )
//		{
//			// KJFL - 18 November 2005 - We need to increase by a +ve amount
//			// otherwise the bounding box may get "inside out". If we want
//			// a fn to shrink the BB we'll have to check for points where
//			// we shrink to zero length along each axis.
//          Asura_Assert( fIncrease>0.0f, "Must grow by a +ve amount" );
//
//          m_xMin -= fIncrease;
//			m_xMax += fIncrease;
//		}
		
        // (MH - 07.12.2005) TODO if required.
//		float GetSurfaceArea( void ) const
//		{
//			const float fLength = MaxX - MinX;
//			const float fWidth = MaxZ - MinZ;
//			const float fHeight = MaxY - MinY;
//
//			return (fLength*fWidth + fHeight*fWidth + fLength*fHeight) * 2.0f;
//		}
};
#endif

// KJFL - 20 July 2006 - Orientated bounding box class, using Aligned Vectors and Matrix. Internal
// structure (half-extents) is designed to be friendly to SAT-based algorithms.
class Asura_OBB
{
	public:

		// construct the OBB from its natural constituents
		Asura_OBB(void)
		{
		}

		Asura_OBB( const Asura_AVector3& xHalfExtents, const Asura_AVector3& xCentre, const Asura_AMatrix3x3& xOrientation )
		{
			SetHalfExtents( xHalfExtents );
			SetCentre( xCentre );
			SetOrientation( xOrientation );
		}

		// construct the OBB from a sphere
		explicit Asura_OBB( float fSphereRadius )
		{
			m_xHalfExtents = Asura_AVector3( fSphereRadius, fSphereRadius, fSphereRadius );
			m_xCentre.Zero();
			m_xOrientation.Identity();
		}

		// construct the OBB from an AABB - flagged as explicit to stop people using it accidentally
		explicit Asura_OBB( const Asura_Bounding_Box& xBB )
		{
			SetHalfExtents( xBB );
			m_xCentre = Asura_AVector3( xBB.CalculateCentre() );
			m_xOrientation.Identity();
		}

		// construct the OBB from an AABB with a position offset
		Asura_OBB( const Asura_Bounding_Box& xBB, const Asura_AVector3& xCentre )
		{
			SetHalfExtents( xBB );
			m_xCentre = Asura_AVector3( xBB.CalculateCentre() );
			m_xCentre += xCentre;
			m_xOrientation.Identity();
		}

        // construct the OBB from an AABB with an orientation
        Asura_OBB( const Asura_Bounding_Box& xBB, const Asura_AMatrix3x3& xOrientation )
        {
            SetHalfExtents( xBB );
            m_xCentre = Asura_AVector3( xBB.CalculateCentre() ) * xOrientation;
            SetOrientation( xOrientation );
        }

		// construct the OBB from an AABB with a position offset, and an orientation
		Asura_OBB( const Asura_Bounding_Box& xBB, const Asura_AVector3& xCentre, const Asura_AMatrix3x3& xOrientation )
		{
			SetHalfExtents( xBB );
			m_xCentre = Asura_AVector3( xBB.CalculateCentre() ) * xOrientation;
			m_xCentre += xCentre;
			SetOrientation( xOrientation );
		}
	
		void SetHalfExtents( const Asura_Bounding_Box& xBB )
		{
			m_xHalfExtents = Asura_AVector3( (xBB.MaxX - xBB.MinX), (xBB.MaxY - xBB.MinY), (xBB.MaxZ - xBB.MinZ) );
			m_xHalfExtents *= 0.5f;
		}

		void SetHalfExtents( const Asura_AVector3& xHalfExtents )
		{
			m_xHalfExtents = xHalfExtents;
		}

		void SetCentre( const Asura_AVector3& xCentre )
		{
			m_xCentre = xCentre;
		}

		void SetOrientation( const Asura_AMatrix3x3& xOrientation )
		{
			m_xOrientation = xOrientation;
		}

		const Asura_AVector3& GetHalfExtents( void ) const
		{
			return m_xHalfExtents;
		}

		float GetHalfExtentX( void ) const
		{
			return m_xHalfExtents.GetX();
		}

		float GetHalfExtentY( void ) const
		{
			return m_xHalfExtents.GetY();
		}

		float GetHalfExtentZ( void ) const
		{
			return m_xHalfExtents.GetZ();
		}

		// return the minimum 'radius' of the cuboid, i.e. the smallest half-extent
		float GetMinimumHalfExtent( void ) const
		{
			float fMinRadius = GetHalfExtentX();
			fMinRadius = Asura_Maths::Min( fMinRadius, GetHalfExtentY() );
			fMinRadius = Asura_Maths::Min( fMinRadius, GetHalfExtentZ() );

			return fMinRadius;
		}

		// return the maximum 'radius' of the cuboid, i.e. the largest half-extent
		float GetMaximumHalfExtent( void ) const
		{
			float fMaxRadius = GetHalfExtentX();
			fMaxRadius = Asura_Maths::Max( fMaxRadius, GetHalfExtentY() );
			fMaxRadius = Asura_Maths::Max( fMaxRadius, GetHalfExtentZ() );

			return fMaxRadius;
		}

		inline const Asura_AVector3& GetCentre( void ) const
		{
			return m_xCentre;
		}

		const Asura_AMatrix3x3& GetOrientation( void ) const
		{
			return m_xOrientation;
		}

		const Asura_AVector3 GetMinPosition( void ) const
		{
			return Asura_AVector3( m_xCentre - m_xHalfExtents );
		}

		const Asura_AVector3 GetMaxPosition( void ) const
		{
			return Asura_AVector3( m_xCentre + m_xHalfExtents );
		}

		// project the OBB onto the axis given by xAxis and find the centre and half-extent along that axis
		inline void GetCentreAndHalfExtentAlongAxis( const Asura_AVector3& xAxis, float& fCentre, float& fHalfExtent ) const
		{
			// KJFL - 02 October 2006 - vector version of the maths below actually slower that float version! 
			// Probably due to memory & scalar->vector register conversion overheads. Vector constructor
			// doesn't seem that fast basically.
			#if 0
			// first project the OBB axes onto the axis
			const Asura_AVector3 xProjectedAxes
			(
				Asura_Maths::Abs( xAxis * GetOrientation().GetColumn0() ),
				Asura_Maths::Abs( xAxis * GetOrientation().GetColumn1() ),
				Asura_Maths::Abs( xAxis * GetOrientation().GetColumn2() )
			);

			// then scale them by the OBB extents
			fHalfExtent = xProjectedAxes * GetHalfExtents();
			#else
			fHalfExtent = 	  Asura_Maths::Abs( xAxis * GetOrientation().GetColumn0() ) * GetHalfExtentX()
						  	+ Asura_Maths::Abs( xAxis * GetOrientation().GetColumn1() ) * GetHalfExtentY()
						  	+ Asura_Maths::Abs( xAxis * GetOrientation().GetColumn2() ) * GetHalfExtentZ();
			#endif

			// then project the centre onto the axis
			fCentre = GetCentre() * xAxis;
		}

		// project the OBB onto the axis given by xAxis and find the min and max extents along that axis
		void GetExtentsAlongAxis( const Asura_AVector3& xAxis, float& fMin, float& fMax ) const
		{
			float fProjectedCentre;
			float fHalfExtent;

			GetCentreAndHalfExtentAlongAxis( xAxis, fProjectedCentre, fHalfExtent );

			// and thus find the min and max along the axis
			fMin = fProjectedCentre - fHalfExtent;
			fMax = fProjectedCentre + fHalfExtent;
		}

		// Get the support points for the given OBB...
		u_int GetSupportFeature(Asura_AVector3* pxSupportPoints, const Asura_AVector3& xAxis) const;

		// Return the axis of the largest extent, and the largest radius around that
		// of the other two axes. Axis vector is intentionally not normalised.
		// Method used by the Physics for creating sphere stacks - also handy for 
		// cylinders.
		void GetPrimaryAxisAndItsRadius( Asura_AVector3& xAxis, float& fRadius ) const
		{
			if ( GetHalfExtentX() > GetHalfExtentY() )
			{
				if ( GetHalfExtentX() > GetHalfExtentZ() )
				{
					// it's X
					xAxis = Asura_AVector3( GetHalfExtentX(), 0.0f, 0.0f );
					fRadius = Asura_Maths::Max( GetHalfExtentY(), GetHalfExtentZ() );
				}
				else // it's Z
				{
					xAxis = Asura_AVector3( 0.0f, 0.0f, GetHalfExtentZ()  );
					fRadius = Asura_Maths::Max( GetHalfExtentX(), GetHalfExtentY() );
				}
			}
			else if ( GetHalfExtentY() > GetHalfExtentZ() )
			{
				// it's Y
				xAxis = Asura_AVector3( 0.0f, GetHalfExtentY(), 0.0f  );
				fRadius = Asura_Maths::Max( GetHalfExtentX(), GetHalfExtentZ() );
			}
			else // it's Z
			{
				xAxis = Asura_AVector3( 0.0f, 0.0f, GetHalfExtentZ()  );
				fRadius = Asura_Maths::Max( GetHalfExtentX(), GetHalfExtentY() );
			}

			xAxis = xAxis * GetOrientation();
		}
	
		// returns true if 'this' OBB is intersecting with the OBB passed in.
		bool Intersects( const Asura_OBB& xOtherOBB ) const
		{
			const Asura_AMatrix3x3& xObjectAOrient = this->GetOrientation();
			const Asura_AMatrix3x3& xObjectBOrient = xOtherOBB.GetOrientation();

			// The potential separating axes are formed from the edges of the
			// OBBs, and the vectors perpendicular to the edge pairings. This
			// makes 15 axes to test

			const unsigned int uNumberOfSeparatingAxes = 15;

			// KJFL - 20 July 2006 - TODO - could save some time by calculating these as and when needed?
			Asura_AVector3 axAxes[uNumberOfSeparatingAxes] = 
			{
				xObjectAOrient.GetColumn0(),
				xObjectAOrient.GetColumn1(),
				xObjectAOrient.GetColumn2(),

				xObjectBOrient.GetColumn0(),
				xObjectBOrient.GetColumn1(),
				xObjectBOrient.GetColumn2(),

				xObjectAOrient.GetColumn0() ^ xObjectBOrient.GetColumn0(),
				xObjectAOrient.GetColumn0() ^ xObjectBOrient.GetColumn1(),
				xObjectAOrient.GetColumn0() ^ xObjectBOrient.GetColumn2(),

				xObjectAOrient.GetColumn1() ^ xObjectBOrient.GetColumn0(),
				xObjectAOrient.GetColumn1() ^ xObjectBOrient.GetColumn1(),
				xObjectAOrient.GetColumn1() ^ xObjectBOrient.GetColumn2(),

				xObjectAOrient.GetColumn2() ^ xObjectBOrient.GetColumn0(),
				xObjectAOrient.GetColumn2() ^ xObjectBOrient.GetColumn1(),
				xObjectAOrient.GetColumn2() ^ xObjectBOrient.GetColumn2(),
			};

			for ( u_int u = 0; u<uNumberOfSeparatingAxes; ++u )
			{
				float fMinA = 0.0f;
				float fMaxA = 0.0f;
				float fMinB = 0.0f;
				float fMaxB = 0.0f;

				// Skip invalid axes (e.g. check for cases of parallel edges etc).
				// Non-normal axes shouldn't matter particularly (e.g. if the matrices
				// aren't quite orthonormal) since we're comparing extents in the 
				// same projected space each time.
				if ( axAxes[u].MagnitudeSquared() < 0.01f ) continue;

				this->GetExtentsAlongAxis( axAxes[u], fMinA, fMaxA );
				xOtherOBB.GetExtentsAlongAxis( axAxes[u], fMinB, fMaxB );

				// test for a no overlap case
				if ( fMaxA < fMinB || fMinA > fMaxB )
				{
					// these objects definitely don't overlap
					return false;
				}
			}

			// looks like they overlap
			return true;
		}

		void CalculateEnclosingAABB( Asura_Bounding_Box& xAABB ) const
		{
			// KJFL - 20 July 2006 - Hmm, can't quite see how I can vectorise this nicely since I need to do those
			// abs on each bit. Possibly we need some sort of AbsDot method... who knows, some hardware may do it
			// natively.

			float fWorldSpaceExtentX = Asura_Maths::Abs( m_xHalfExtents.GetX() * m_xOrientation.GetColumn0().GetX() )
									 + Asura_Maths::Abs( m_xHalfExtents.GetY() * m_xOrientation.GetColumn1().GetX() )
									 + Asura_Maths::Abs( m_xHalfExtents.GetZ() * m_xOrientation.GetColumn2().GetX() );

			float fWorldSpaceExtentY = Asura_Maths::Abs( m_xHalfExtents.GetX() * m_xOrientation.GetColumn0().GetY() )
									 + Asura_Maths::Abs( m_xHalfExtents.GetY() * m_xOrientation.GetColumn1().GetY() )
									 + Asura_Maths::Abs( m_xHalfExtents.GetZ() * m_xOrientation.GetColumn2().GetY() );

			float fWorldSpaceExtentZ = Asura_Maths::Abs( m_xHalfExtents.GetX() * m_xOrientation.GetColumn0().GetZ() )
									 + Asura_Maths::Abs( m_xHalfExtents.GetY() * m_xOrientation.GetColumn1().GetZ() )
									 + Asura_Maths::Abs( m_xHalfExtents.GetZ() * m_xOrientation.GetColumn2().GetZ() );

			xAABB.MinX = m_xCentre.GetX() - fWorldSpaceExtentX;
			xAABB.MaxX = m_xCentre.GetX() + fWorldSpaceExtentX;

			xAABB.MinY = m_xCentre.GetY() - fWorldSpaceExtentY;
			xAABB.MaxY = m_xCentre.GetY() + fWorldSpaceExtentY;

			xAABB.MinZ = m_xCentre.GetZ() - fWorldSpaceExtentZ;
			xAABB.MaxZ = m_xCentre.GetZ() + fWorldSpaceExtentZ;
		}


	private:

		Asura_AVector3		m_xHalfExtents;
		Asura_AVector3		m_xCentre;
		Asura_AMatrix3x3	m_xOrientation;

};

#endif//__ASURA_MATHS_H__
