/* (c) 1999-2000 Tino Schwarze, see COPYING for details */
/**@pkg math */
/*
 * matrix math class plus implementation
 *
 * contains:
 * - class cMatrix - 4x4 matrix class interfacing nicely with cVertex
 */

#ifndef cMatrix_hh
#define cMatrix_hh

#include "common.hh"
#include "cVertex.hh"	// aquire class cVertex

/**
 * A class for representing a 4x4 matrix. It supports matrix math too.
 *
 * this really simplifies matrix math! :-)
 * oh, not to forget: my matrix is built as follows:
 * (due to OpenGL's convention to store matrices column-major - argh!)
 *<PRE>
 *  _             _
 * |               |
 * | x   y   z   0 |
 * |  x   x   x    |
 * |               |
 * | x   y   z   0 |
 * |  y   y   y    |
 * |               |
 * | x   y   z   0 |
 * |  z   z   z    |
 * |               |
 * | p   p   p   1 |
 * |  x   y   z    |
 * |_             _|
 *</PRE>
 *
 * (x<SUB>x</SUB>, x<SUB>y</SUB>, x<SUB>z</SUB>) is the x-axis
 *
 * (y<SUB>x</SUB>, y<SUB>y</SUB>, y<SUB>z</SUB>) is the y-axis
 *
 * (z<SUB>x</SUB>, z<SUB>y</SUB>, z<SUB>z</SUB>) is the z-axis
 *
 * (p<SUB>x</SUB>, p<SUB>y</SUB>, p<SUB>z</SUB>) is the translation
 */
class cMatrix
{
	public:
		/** 
		 * Matrix data made public to allow direct access
		 *
		 * (operator[] doesnt work with 2-dimensional fields... :-( )
		 * has anyone a better idea? */
		GLfloat mMatrix[4][4];


		/**
		 * default constructor (initializes to identity)
		 	*/
		cMatrix () 
		{ 
			ENTER_METHOD("cMatrix::cMatrix()");
			Identity (); 
		}

#if DEBUG
		/**
		 * destructor (for debugging purposes only)
		 	*/
		~cMatrix ()
		{
			ENTER_METHOD("cMatrix::~cMatrix()");
		}
#endif

#if DEBUG
		/**
		 * copy constructor for debugging purposes only
		 	* @param m cMatrix to copy
		 	*/
		cMatrix (const cMatrix &m)
		{
			ENTER_METHOD("cMatrix::cMatrix(const cMatrix &)");
			memcpy ((void *)&mMatrix, (void *)&m.mMatrix, sizeof (mMatrix));
		}
#endif


// OPERATORS
		/**
		 * an operator for matrix multiplication
		 	* @param m matrix to multiply with
			* @return reference to self
		 	*/
		/**@#-*/
		inline class cMatrix &operator*= (const class cMatrix &m)
		{
			GLfloat temp[4][4];      // temporary mMatrix for storing result
			int i, j;               // row and column counters

			for (j = 0; j < 4; j++)         // transform by columns first
				for (i = 0; i < 4; i++)     // then by rows
					temp[i][j] = mMatrix[i][0] * m.mMatrix[0][j] + mMatrix[i][1] * m.mMatrix[1][j] +
						mMatrix[i][2] * m.mMatrix[2][j] + mMatrix[i][3] * m.mMatrix[3][j];

			// copy temp into mMatrix (ANSI does not allow array
			// assignment)
			memcpy ((void *)&mMatrix, (void *)&temp, sizeof (temp));

			return *this;
		};
		/**@#+*/


		/**
		 * an operator to multiply a vector with an matrix
		 * (which means transforming the vector by the matrix)
		 	*/
		inline cVertex operator* (const cVertex &v)
		{
			cVertex tmp;

			tmp[0] = mMatrix[0][0]*v.GetX() + mMatrix[1][0]*v.GetY() + mMatrix[2][0]*v.GetZ() + mMatrix[3][0];
			tmp[1] = mMatrix[0][1]*v.GetX() + mMatrix[1][1]*v.GetY() + mMatrix[2][1]*v.GetZ() + mMatrix[3][1];
			tmp[2] = mMatrix[0][2]*v.GetX() + mMatrix[1][2]*v.GetY() + mMatrix[2][2]*v.GetZ() + mMatrix[3][2];

			return tmp;
		};

		/**
		 * an operator to inversely multiply a vector with an matrix
		 * (which means transforming the vector by the matrix's inverse)
		 	*/
		inline cVertex operator/ (const cVertex &v)
		{
			cVertex tmp;

			tmp[0] = mMatrix[0][0]*v.GetX() + mMatrix[0][1]*v.GetY() + mMatrix[0][2]*v.GetZ();// + mMatrix[3][0];
			tmp[1] = mMatrix[1][0]*v.GetX() + mMatrix[1][1]*v.GetY() + mMatrix[1][2]*v.GetZ();// + mMatrix[3][1];
			tmp[2] = mMatrix[2][0]*v.GetX() + mMatrix[2][1]*v.GetY() + mMatrix[2][2]*v.GetZ();// + mMatrix[3][2];

			return tmp;
		};

		// assignment operator not neccessary (generated by compiler)

		/**
		 * operator to "convert" matrix to an pointer to GLfloat
		 	*/
		inline operator GLfloat *()
		{
			return (GLfloat *)mMatrix;
		}


// OPERATIONS
		/**
		 * Initialize matrix to identity
		 	* @return reference to cMatrix object (for convenience)
		 	*/
		inline cMatrix &Identity ()
		{
			memset ((void *)&mMatrix, 0, sizeof (mMatrix));

			for (int i = 0; i < 4; i++) mMatrix[i][i] = 1;

			return *this;
		};

		/**
		 * "normalize" the matrix by normalizing the axis components
		 	*
			* This is a very special operation which is only sometimes
			* useful to get orthonormal bases.
			*
		 	* @return reference to cMatrix object
		 	*/
		inline cMatrix &Normalize () // normalize the axis components
		{ 

			for (int c = 0; c < 3; c++) // process each column
			{
				const GLfloat nl = 1/sqrt (mMatrix[0][c]*mMatrix[0][c] + mMatrix[1][c]*mMatrix[1][c] + mMatrix[2][c]*mMatrix[2][c]);

				mMatrix[0][c] *= nl;
				mMatrix[1][c] *= nl;
				mMatrix[2][c] *= nl;
			}

			return *this;
		};

		/**
		 * translate/move origin of matrix
		 	* @param v amount to translate
			*/
		inline void Translate (const cVertex &v)
		{
			mMatrix[3][0] += v.GetX();
			mMatrix[3][1] += v.GetY();
			mMatrix[3][2] += v.GetZ();
		};

		/**
		 * translate/move origin of matrix
		 	* @param x distance to translate
		 	* @param y distance to translate
		 	* @param z distance to translate
			*/
		inline void Translate (
			const GLfloat x, 
			const GLfloat y, 
			const GLfloat z)
		{
			mMatrix[3][0] += x;
			mMatrix[3][1] += y;
			mMatrix[3][2] += z;
		};

};

#endif // ifndef cMatrix_hh
/* vim:ts=4:smartindent:
*/

