//
// 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__

// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!
// THIS IS THE BRANCHED VERSION!

#define ASURA_MATHS_DEFINE_M3x3_COPY_CTOR (0)
#define ASURA_MATHS_DEFINE_M3x3_OP_EQ     (0)
#define ASURA_MATHS_DEFINE_V3_OP_EQ       (0)

#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

typedef float Asura_Angle;

class Asura_Vector_2;
class Asura_Vector_3;

#if defined ( ASURA_USE_HACKED_VECTOR_4 )
typedef Asura_Vector_3 Asura_Vector_4;
#else
class Asura_Vector_4;
#endif

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

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);
    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");

	#if !defined ( ASURA_USE_HACKED_VECTOR_4 )
    extern float RayIntersectsTri(const Asura_Vector_4& alpha, const Asura_Vector_4& beta, const Asura_Vector_4& v0, const Asura_Vector_4& v1, const Asura_Vector_4& v2, bool& facingToward);
	#endif

    extern bool RayIntersectsSphere(const Asura_Vector_3& alpha, const Asura_Vector_3& beta, const Asura_Vector_3& centre, float radius, float *pfFirstIntersectDist=0);

    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);
    extern float GetMinDistSqdBetweenLineSegmentAndRay(const Asura_Vector_3 &xRayOrigin,
                                                        const Asura_Vector_3 &xRayDirection,
                                                        const Asura_Vector_3 &xLineSegmentOrigin,
                                                        const Asura_Vector_3 &xLineSegmentVector);

    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);

    // (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 &Orientation, const Asura_Vector_3 &d);

    // Kevin Lea - 10/09/2001 10:38:13 - max & min inlines
    static inline float Max(float a, float b);
    static inline int Max(int a, int b);
    static inline unsigned Max( unsigned u1, unsigned u2 );
    static inline float Min(float a, float b);
    static inline int Min(int a, int b);
    static inline unsigned Min( unsigned u1, unsigned u2 );

    template< typename T >
    static inline T ClampToRange( const T& xValue, const T& xMin, const T& xMax )
    {
        if( xValue < xMin )
        {
            return xMin;
        }
        else if( xValue > xMax )
        {
            return xMax;
        }
        else
        {
            return xValue;
        }
    }

    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 );

    extern const Asura_Vector_2    ZeroVector2;
    extern const Asura_Vector_3    ZeroVector3;
	extern const Asura_Vector_4	   ZeroVector4;
    extern const Asura_Matrix_3x3  IdentityMatrix3x3;
    extern const Asura_Euler_3     ZeroEuler3;
    extern const Asura_Quat        IdentityQuat;
    extern const Asura_IntVector_2 ZeroIntVector2;
}
//end of namespace Asura_Maths

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 Asura_Vector_2& operator += ( const Asura_Vector_2& v);
		inline Asura_Vector_2 operator - ( const Asura_Vector_2& v) const;
		inline Asura_Vector_2& operator -= ( const Asura_Vector_2& v);
	    inline Asura_Vector_2 operator * ( float fScale ) const;
		inline Asura_Vector_2& operator *= ( float fScale );
	    inline Asura_Vector_2& 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;

		inline Asura_Vector_2& RandomDirection();
		inline Asura_Vector_2& RandomVector();

		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;

		inline Asura_Vector_2& RandomDirectionSeeded();
		inline Asura_Vector_2& RandomVectorSeeded();

		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;

		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);


		#if !defined ( ASURA_USE_HACKED_VECTOR_4 )
		inline Asura_Vector_3& operator = ( const Asura_Vector_4& xV4);
		// to explicitly convert Asura_Vector_4 to Asura_Vector_3 (for legacy code etc.)
		explicit Asura_Vector_3(const Asura_Vector_4& v);	    
		#endif

		#if 0
	    // casting
	    inline operator float* ()
		{
		    return (float *) &x;
		}		
	    inline operator const float* () const
		{
		    return (const float *) &x;
		}		
		inline operator Asura_Vector_3* ()
		{
		    return (Asura_Vector_3 *) &x;
		}
	    inline operator const Asura_Vector_3* () const
		{
		    return (const Asura_Vector_3 *) &x
		}
	    inline operator Asura_Vector_3& ()
		{
		    return *((Asura_Vector_3 *) &x)
		}
	    inline operator const Asura_Vector_3& () const
		{
		    return *((const Asura_Vector_3 *) &x);
		}
		#endif
	    // assignment operators
#if ASURA_MATHS_DEFINE_V3_OP_EQ
		inline Asura_Vector_3& operator = ( const Asura_Vector_3& v)
		{
			x = v.x;
			y = v.y;
			z = v.z;
			return *this;
		}
#endif
	    inline Asura_Vector_3& operator += ( const Asura_Vector_3& v);
	    inline Asura_Vector_3& operator -= ( const Asura_Vector_3& v);
		inline Asura_Vector_3& operator *= ( float f);
	    inline Asura_Vector_3& 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 friend Asura_Vector_3 operator * ( float, const struct Asura_Vector_3& );

	    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
		inline Asura_Vector_3& RandomDirection();
		inline Asura_Vector_3& RandomVector();

		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;

		inline Asura_Vector_3& RandomDirectionSeeded();
		inline Asura_Vector_3& RandomVectorSeeded();

		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;

		inline float Normalise(void);
		inline float Magnitude(void) const;
		inline float MagnitudeSquared(void) const;
		inline void Zero(void);

        inline float& operator[]( u_int u ) { return Element[u]; }
        inline float operator[]( u_int u ) const { return Element[u]; }
		inline float GetElement( u_int u ) const { return Element[u]; }
		inline void SetElement( u_int u, float f ) { Element[u] = f; }
		
		inline float GetX(void) const { return m_fX; }
		inline float GetY(void) const { return m_fY; }
		inline float GetZ(void) const { return m_fZ; }
		inline void SetX(float fX) { m_fX = fX; }
		inline void SetY(float fY) { m_fY = fY; }
		inline void SetZ(float fZ) { m_fZ = fZ; }


	public:
		union
		{
			struct
			{
				float x, y, z;
			};
			struct
			{
				float m_fX, m_fY, m_fZ;
			};
			float Element[3];
		};

};

class Asura_Matrix_3x3
{
	public:

        inline Asura_Matrix_3x3( void ) { }

        Asura_Matrix_3x3( float e11, float e12, float e13,
                          float e21, float e22, float e23,
                          float e31, float e32, float e33 )
        {
            this->e11 = e11;
            this->e12 = e12;
            this->e13 = e13;
            this->e21 = e21;
            this->e22 = e22;
            this->e23 = e23;
            this->e31 = e31;
            this->e32 = e32;
            this->e33 = e33;
        }
#if ASURA_MATHS_DEFINE_M3x3_COPY_CTOR
		Asura_Matrix_3x3( const Asura_Matrix_3x3& xOther )
		{
			e11 = xOther.e11;
			e12 = xOther.e12;		
			e13 = xOther.e13;		
			e21 = xOther.e21;		
			e22 = xOther.e22;		
			e23 = xOther.e23;		
			e31 = xOther.e31;		
			e32 = xOther.e32;		
			e33 = xOther.e33;		
		}
#endif
#if ASURA_MATHS_DEFINE_M3x3_OP_EQ
		Asura_Matrix_3x3& operator = ( const Asura_Matrix_3x3& xOther )
		{
			e11 = xOther.e11;
			e12 = xOther.e12;		
			e13 = xOther.e13;		
			e21 = xOther.e21;		
			e22 = xOther.e22;		
			e23 = xOther.e23;		
			e31 = xOther.e31;		
			e32 = xOther.e32;		
			e33 = xOther.e33;	
			return *this;
		}
#endif
        
        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_Vector_3& operator [](int index)
		{
			return reinterpret_cast<Asura_Vector_3&>(Element[index][0]);
		}
		
		inline const Asura_Vector_3& operator [](int index) const
		{
            return reinterpret_cast<const Asura_Vector_3&>(Element[index][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;
		}

		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 Asura_Euler_3& operator += ( const Asura_Euler_3& xEuler);
	    inline Asura_Euler_3& operator -= ( const Asura_Euler_3& xEuler);
		inline Asura_Euler_3& operator *= ( float f);
	    inline Asura_Euler_3& operator /= ( float f);

	    // unary operators
	    inline Asura_Euler_3 operator + () const;
		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 void Zero(void);
};

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

		Asura_Quat(){}

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

        Asura_Quat(const Asura_Euler_3& e) {FromEuler(e);}
        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 void RotateVector(const Asura_Vector_3& p, Asura_Vector_3& result) 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 float Magnitude(void) const;

		inline void ToMatrix(Asura_Matrix_3x3& 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 FromRotation(Asura_Angle angle, const Asura_Vector_3& axis);
        
        


		inline float Normalise(void);
		inline float Dot(const Asura_Quat& q2) const;
		inline void Slerp(const Asura_Quat& input1, const Asura_Quat& input2, float l);
};

class Asura_Comp_Quat
{
	public:
		unsigned int bits;

		Asura_Comp_Quat() {}
		// Compress from quat constructor
		Asura_Comp_Quat( const Asura_Quat* pxQuat ) { FromQuat(pxQuat); }
		
		inline void ToQuat( Asura_Quat* pxQuat ) const;
		inline void FromQuat( const Asura_Quat* pxQuat );
};

#if !defined ( ASURA_USE_HACKED_VECTOR_4 )

// Temporary define for toggling platform implementation. 
// Platforms with their own implementation should define this to 0
#ifndef ASURA_VECTOR_4_IN_INDEPENDENT_CODE
#define ASURA_VECTOR_4_IN_INDEPENDENT_CODE 1
#endif

// KJFL - Friday, November 26, 2004 - I've made the Asura_Vector_4 the same size as Asura_Vector_3 (i.e. removed the w component) so
// we can use the new Physics systems with the old dynamics data. The future of Asura_Vector_4 is still in the balance...
#if ASURA_VECTOR_4_IN_INDEPENDENT_CODE
class Asura_Vector_4
{

	// KJFL - 2004-03-18 12:33:40 - class is currently in development and subject to change. Please DO NOT USE this class in new code
	// while this comment is here.
	public:

		Asura_Vector_4()
		{
		}
		
		Asura_Vector_4( float fX, float fY, float fZ )
		{
			m_fX = fX;
			m_fY = fY;
			m_fZ = fZ;
		}

		//Asura_Vector_4( float fX, float fY, float fZ, float fW )
		//{
		//	m_fX = fX;
		//	m_fY = fY;
		//	m_fZ = fZ;
		//	m_fW = fW;
		//}

		explicit Asura_Vector_4 ( const Asura_Vector_3& xVec3 ) 
		{
			m_fX = xVec3.x;
			m_fY = xVec3.y;
			m_fZ = xVec3.z;
		}



		inline void operator = ( const Asura_Vector_3& xVec3 ) 
		{
			m_fX = xVec3.x;
			m_fY = xVec3.y;
			m_fZ = xVec3.z;
		}



		// assignment operators
		inline Asura_Vector_4& operator += ( const Asura_Vector_4& xV)
		{
			m_fX += xV.m_fX;
			m_fY += xV.m_fY;
			m_fZ += xV.m_fZ;
			return *this;
		}
		inline Asura_Vector_4& operator -= ( const Asura_Vector_4& xV)
		{
			m_fX -= xV.m_fX;
			m_fY -= xV.m_fY;
			m_fZ -= xV.m_fZ;
			return *this;
		}
		inline Asura_Vector_4& operator *= ( float f)
		{
			m_fX *= f;
			m_fY *= f;
			m_fZ *= f;
			return *this;
		}
		inline Asura_Vector_4& operator /= ( float f)
		{
			float fInv = 1.0f / f;
			m_fX *= fInv;
			m_fY *= fInv;
			m_fZ *= fInv;
			return *this;
		}

		// unary operators
		inline Asura_Vector_4 operator + () const
		{
			return *this;
		}
		inline Asura_Vector_4 operator - () const
		{
			return Asura_Vector_4(-m_fX, -m_fY, -m_fZ );
		}

		// binary operators
		inline Asura_Vector_4 operator + ( const Asura_Vector_4& xV) const
		{
			return Asura_Vector_4(m_fX + xV.m_fX, m_fY + xV.m_fY, m_fZ + xV.m_fZ );
		}
		inline Asura_Vector_4 operator - ( const Asura_Vector_4& xV) const
		{
			return Asura_Vector_4(m_fX - xV.m_fX, m_fY - xV.m_fY, m_fZ - xV.m_fZ );
		}
		inline Asura_Vector_4 operator * ( float f) const
		{
			return Asura_Vector_4(m_fX * f, m_fY * f, m_fZ * f );
		}
		inline Asura_Vector_4 operator / ( float f) const
		{
			float fInv = 1.0f / f;
			return Asura_Vector_4(m_fX * fInv, m_fY * fInv, m_fZ * fInv );
		}                                               

		inline Asura_Vector_4 operator ^ ( const Asura_Vector_4& v) const
		{
			return Asura_Vector_4(m_fY*v.m_fZ - m_fZ*v.m_fY, m_fZ*v.m_fX - m_fX*v.m_fZ, m_fX*v.m_fY - m_fY*v.m_fX );
		}

		inline float  operator * ( const Asura_Vector_4& v) const
		{
			return (m_fX*v.m_fX + m_fY*v.m_fY + m_fZ*v.m_fZ);
		}

		inline Asura_Vector_4 operator * ( const Asura_Matrix_3x3& m) const
		{
			class Asura_Vector_4 result;

			result.m_fX = m.e11 * m_fX + m.e12 * m_fY + m.e13 * m_fZ;
			result.m_fY = m.e21 * m_fX + m.e22 * m_fY + m.e23 * m_fZ;
			result.m_fZ = m.e31 * m_fX + m.e32 * m_fY + m.e33 * m_fZ;

			return result;
		}

  		inline bool operator == ( const Asura_Vector_4& v) const
  		{
  			return m_fX == v.m_fX && m_fY == v.m_fY && m_fZ == v.m_fZ;
  		}

  		inline bool ApproximatelyEquals(const Asura_Vector_4& v, float epsilon = 1.0e-6f) const
  		{
  			Asura_Vector_4 delta;
  			delta.m_fX = m_fX-v.m_fX;
  			delta.m_fY = m_fY-v.m_fY;
  			delta.m_fZ = m_fZ-v.m_fZ;

  			return ((delta.m_fX*delta.m_fX+delta.m_fY*delta.m_fY+delta.m_fZ*delta.m_fZ)<epsilon);
      
  		}
  		inline bool operator != ( const Asura_Vector_4& v) const
  		{
  			return m_fX != v.m_fX || m_fY != v.m_fY || m_fZ != v.m_fZ;
  		}


		inline float MagnitudeSquared(void) const
		{   
			return ((m_fX*m_fX) + (m_fY*m_fY) + (m_fZ*m_fZ));
		}

		inline float Magnitude(void) const
		{   
			return Asura_Maths::Sqrt((m_fX*m_fX) + (m_fY*m_fY) + (m_fZ*m_fZ));
		}

		inline float Normalise(void)
		{
			const float fMag = Magnitude();
    
			if (fMag==0.0f) return fMag;

			const float fInvMag = 1.0f/fMag;
			m_fX *= fInvMag;
			m_fY *= fInvMag;
			m_fZ *= fInvMag;

			return fMag;
		}       

		inline void Zero(void) 
		{
			m_fX = m_fY = m_fZ = 0.0f;
		}

		inline float GetX(void) const { return m_fX; }
		inline float GetY(void) const { return m_fY; }
		inline float GetZ(void) const { return m_fZ; }
		inline void SetX(float fX) { m_fX = fX; }
		inline void SetY(float fY) { m_fY = fY; }
		inline void SetZ(float fZ) { m_fZ = fZ; }

		inline float GetElement(u_int u) const { return *(reinterpret_cast<const float*>(this) + u); }
		inline void SetElement(u_int u, float f) { *(reinterpret_cast<float*>(this) + u) = f; }

private:
		float m_fX;
		float m_fY;
		float m_fZ;
		//float m_fW;

};


inline Asura_Vector_3& Asura_Vector_3:: operator = ( const Asura_Vector_4& xV4)
{
	x = xV4.GetX();
	y = xV4.GetY();
	z = xV4.GetZ();

	return *this;
}

// explicit constructor
inline Asura_Vector_3::Asura_Vector_3( const Asura_Vector_4& xV4 )
{
	x = xV4.GetX();
	y = xV4.GetY();
	z = xV4.GetZ();
}
#endif // #if ASURA_VECTOR_4_IN_INDEPENDENT_CODE
#endif // #if !defined ( ASURA_USE_HACKED_VECTOR_4 )


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 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 );
};
 
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;
		}
		
		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;

		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);
		}

        void FromOBB( const Asura_Bounding_Box& xOBB, const Asura_Matrix_3x3& xOri );

		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 );
		}
};


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_Ranrot
{

	public:

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

		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_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].
        static inline u_int GetIntU( void );                          // [0, INT_MAX]. (Note: not UINT_MAX!)
        static inline int GetInt( int iMin, int iMax );               // Range specified is inclusive.
        static inline u_int GetIntU( u_int uMin, u_int uMax );        //               "

        static void SetSeed( int iSeed );
        static inline int GetSeededInt( void );                       // [INT_MIN, INT_MAX].
        static inline u_int GetSeededIntU( void );                    // [0, INT_MAX]. (Note: not UINT_MAX!)
        static inline int GetSeededInt( int iMin, int iMax );         // Range specified is inclusive.
        static inline u_int GetSeededIntU( u_int uMin, u_int uMax );  //               "

        //
        // Uniform distribution floats.
        //

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

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

        //
        // Normal distribution floats.
        //

        static 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.
		static void GetCurrentState(int& iLow, int& iHigh);
		static void GetSeededCurrentState(int& iLow, int& iHigh);
		static void SetCurrentState(int iLow, int iHigh);
		static void SetSeededCurrentState(int iLow, int iHigh);

    private:

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

        static int GetIntInternal( void );
        static int GetSeededIntInternal( void );

        // 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 ); }

};

inline int   Asura_Random::GetInt( void )        { return GetIntInternal();            }
inline u_int Asura_Random::GetIntU( void )       { return I2U(GetIntInternal());       }
inline int   Asura_Random::GetSeededInt( void )  { return GetSeededIntInternal();      }
inline u_int Asura_Random::GetSeededIntU( void ) { return I2U(GetSeededIntInternal()); }

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_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;
			}
		}

		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 )
        {
            Asura_Vector_3& xRed = reinterpret_cast<Asura_Vector_3&>( m_afRedCoefficients[1] );
            xRed = xOri.ApplyTranspose( xRed );

            Asura_Vector_3& xGreen = reinterpret_cast<Asura_Vector_3&>( m_afGreenCoefficients[1] );
            xGreen = xOri.ApplyTranspose( xGreen );

            Asura_Vector_3& xBlue = reinterpret_cast<Asura_Vector_3&>( m_afBlueCoefficients[1] );
            xBlue = xOri.ApplyTranspose( xBlue );
        }

		// 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();
		}

		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];
			};
		};
};

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 Asura_IntVector_2& operator += ( const Asura_IntVector_2& v)
		{
			iX += v.iX;
			iY += v.iY;
			return *this;
		}

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

		inline Asura_IntVector_2& operator *= ( int i )
		{
			iX *= i;
			iY *= i;
			return *this;
		}
		inline Asura_IntVector_2& operator /= ( int i)
		{
			iX /= i;
			iY /= i;
			return *this;
		}

		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"

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

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

#endif//__ASURA_MATHS_H__
