//-----------------------------------------------------------------------------------------------
// Matrix.hpp
//
// Defines various Matrix math classes and utility functions.
//-----------------------------------------------------------------------------------------------
#ifndef include_Matrix
#define include_Matrix
#pragma once

//-----------------------------------------------------------------------------------------------
#include "Engine/Core/MathBase.hpp"
#include "Engine/Core/Vector4.hpp"
#include "Engine/Core/Vector3.hpp"
#include "Engine/Core/Vector2.hpp"

/////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Matrix44 : a 4x4 float matrix, designed especially (but not exclusively) to support the
//	2D/3D/4D transforms most commonly used in games.
//
//	Despite the examples illustrated below (written in column-major notation), this class
//	is perfectly fit for use as-is in either OpenGL or DirectX, and is not intrinsically
//	tied to either column-major nor row-major notation nor storage.
//
//	However, examples and comments tend to use following general 3D game math conventions:
//	* Right-handed coordinate system
//	* Column-major representation (both notation and storage)
//	* Translation stored in positions [12,13,14]
//	* +X forward (east), +Y left (north), +Z up
//	* Identity (rest) orientation is facing east (+X), with +Z up (and +Y to the left)
//	* Yaw is right-handed rotation about +Z (i.e. "turning to your left")
//	* Pitch is right-handed rotation about +Y (i.e. "leaning forward, nose-down")
//	* Roll is right-handed rotation about +X (i.e. "leaning to your right")
//	* Matrix terms are laid out (using column-major notation) as:
//		Ix [0]		Jx [4]		Kx [8]		Tx [12]
//		Iy [1]		Jy [5]		Ky [9]		Ty [13]
//		Iz [2]		Jz [6]		Kz [10]		Tz [14]
//		Iw [3]		Jw [7]		Kw [11]		Tw [15]
//	* ...with Ixyz [0-2] being the 3D X basis vector (i) of the transform (identity is 1,0,0)
//	* ...with Jxyz [4-6] being the 3D Y basis vector (j) of the transform (identity is 0,1,0)
//	* ...with Kxyz [8-10] being the 3D Z basis vector (k) of the transform (identity is 0,0,1)
//	* ...with Txyz [12-14] being the 3D translation of the transform (identity is 0,0,0)
//	* ...with *(w) [3,7,11,15] being homogeneous coordinates (identity is 0,0,0,1) for bases
//
// NOTE: For purposes of compatibility with DirectX and row-major notation, I have intentionally
//	omitted an overloaded operator * for Matrix44.  Why?  Because OpenGL (column-major) and
//	DirectX (row-major) agree in terms of memory storage (translation is in 12,13,14 in both).
//	The disagreement comes in how we choose to program our matrix multiplication function.
//	To apply a new transformation N to an existing matrix M, for column major we write (and code)
//	M*N; for row major we write (and code) N*M.  This means that we have to CHOOSE A FAVORITE
//	for our operator * method when we write it, and we'd better multiply in the correct order
//	for the way we chose to write the operator *.  Consider the difference between:
//		column-major multiply:	(M*N)[0] = M[0]*N[0] + M[4]*N[1] + M[8]*N[2] + M[12]*N[3]
//		row-major multiply:		(N*M)[0] = N[0]*M[0] + N[1]*M[4] + N[2]*M[8] + N[3]*M[12]
//	...so we have to write our matrix multiply differently if we want M*N to mean "Apply N to M"
//	(in OpenGL), verses if we want N*M to mean "Apply N to M" (in DirectX).
//
// This problem is solved (avoided) by instead offering more explicit "Apply" functions, which
//	are the equivalent of concatenating a new transformation into the "end" of the current
//	one (e.g. right-hand-multiply in OpenGL, such as is done by glRotate/glTranslate/glScale,
//	or a left-hand-multiply in DirectX).
//
// Note that a similar tension exists when using operators to transform points by a matrix;
//	in column-major you transform a point P by a matrix M by writing M*P; in DirectX, P*M.
//	By not providing an ambiguous operator and instead offering "TransformPoint()" and others
//	we avoid the issue while maintaining 100% clarity and 100% compatibility with either convention.
//
// Lastly, by using a simple 1D array of 16 floats, we avoid the need to say [row][column] or
//	use any other syntax that is potentially confusing / conflating / conflicting.
//
// In short: You can use this matrix class as-is for either OpenGL or DirectX, without transposing,
//	and since we have eliminated the only true ambiguities (multiplication order and column/row
//	storage) we have avoided the row-major vs. column-major issue altogether.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
class Matrix44
{
public:
	float					m_values[ 16 ]; // Translation in [12,13,14]
	static const Matrix44	IDENTITY;

	enum
	{
		MATRIX_TERM_Ix,
		MATRIX_TERM_Iy,
		MATRIX_TERM_Iz,
		MATRIX_TERM_Iw,
		MATRIX_TERM_Jx,
		MATRIX_TERM_Jy,
		MATRIX_TERM_Jz,
		MATRIX_TERM_Jw,
		MATRIX_TERM_Kx,
		MATRIX_TERM_Ky,
		MATRIX_TERM_Kz,
		MATRIX_TERM_Kw,
		MATRIX_TERM_Tx,
		MATRIX_TERM_Ty,
		MATRIX_TERM_Tz,
		MATRIX_TERM_Tw,
	};

public:
	Matrix44(); // Defaults to identity
	Matrix44( const Matrix44& copy );
	explicit Matrix44( const Vector2& xBasis2D, const Vector2& yBasis2D );
	explicit Matrix44( const Vector2& xBasis2D, const Vector2& yBasis2D, const Vector2& translation2D );
	explicit Matrix44( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D );
	explicit Matrix44( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D, const Vector3& translation3D );
	explicit Matrix44( const Vector4& xBasis4D, const Vector4& yBasis4D, const Vector4& zBasis4D, const Vector4& translation4D );

	Matrix44& operator = ( const Matrix44& rhs );
	bool operator == ( const Matrix44& compare ) const;
	bool operator != ( const Matrix44& compare ) const;

	static Matrix44 CreateZRotationDegrees( float zRotationDegrees );
	static Matrix44 CreateZRotationRadians( float zRotationRadians );
	static Matrix44 CreateYRotationDegrees( float yRotationDegrees );
	static Matrix44 CreateYRotationRadians( float yRotationDegrees );
	static Matrix44 CreateXRotationDegrees( float xRotationDegrees );
	static Matrix44 CreateXRotationRadians( float xRotationDegrees );
	static Matrix44 CreateRotationAboutAxisDegrees( const Vector3& rotationAxis, float rotationDegrees );
	static Matrix44 CreateRotationAboutAxisRadians( const Vector3& rotationAxis, float rotationRadians );
	static Matrix44 CreateScale( float uniformScale );
	static Matrix44 CreateScale( const Vector2& nonUniformScale2D );
	static Matrix44 CreateScale( const Vector3& nonUniformScale3D );
	static Matrix44 CreateTranslation( const Vector2& translation2D );
	static Matrix44 CreateTranslation( const Vector3& translation3D );
	static Matrix44 CreatePerpective( float fovDegreesY, float aspect, float zNearDist, float zFarDist );
	static Matrix44 CreateOrtho( float leftX, float rightX, float bottomY, float topY, float nearZ, float farZ );

	void MakeIdentity();
	Matrix44& ApplyZRotationDegrees( float zRotationDegrees );
	Matrix44& ApplyZRotationRadians( float zRotationRadians );
	Matrix44& ApplyYRotationDegrees( float yRotationDegrees );
	Matrix44& ApplyYRotationRadians( float yRotationRadians );
	Matrix44& ApplyXRotationDegrees( float xRotationDegrees );
	Matrix44& ApplyXRotationRadians( float xRotationRadians );
	Matrix44& ApplyRotationAboutAxisDegrees( const Vector3& rotationAxis, float rotationDegrees );
	Matrix44& ApplyRotationAboutAxisRadians( const Vector3& rotationAxis, float rotationRadians );
	Matrix44& ApplyScale( float uniformScale );
	Matrix44& ApplyScale( const Vector2& nonUniformScale2D );
	Matrix44& ApplyScale( const Vector3& nonUniformScale3D );
	Matrix44& ApplyTranslation( const Vector2& translation2D );
	Matrix44& ApplyTranslation( const Vector3& translation3D );
	Matrix44& ApplyTransformation( const Matrix44& newTransformationToApply );

	Vector2 TransformPosition( const Vector2& position2D ) const;
	Vector3 TransformPosition( const Vector3& position3D ) const;
	Vector2 TransformDirection( const Vector2& direction2D ) const;
	Vector3 TransformDirection( const Vector3& direction3D ) const;
	Vector4 TransformPoint( const Vector4& homogeneousPoint ) const;
	Matrix44 TransformMatrix( const Matrix44& matrix ) const;

	float CalcDeterminant() const;
	void Transpose();
	bool Invert();

	Vector2& GetXBasis2D();
	Vector2& GetYBasis2D();
	Vector2& GetTranslation2D();
	Vector3& GetXBasis3D();
	Vector3& GetYBasis3D();
	Vector3& GetZBasis3D();
	Vector3& GetTranslation3D();
	Vector4& GetXBasis4D();
	Vector4& GetYBasis4D();
	Vector4& GetZBasis4D();
	Vector4& GetTranslation4D();

	const Vector2& GetXBasis2D() const;
	const Vector2& GetYBasis2D() const;
	const Vector2& GetTranslation2D() const;
	const Vector3& GetXBasis3D() const;
	const Vector3& GetYBasis3D() const;
	const Vector3& GetZBasis3D() const;
	const Vector3& GetTranslation3D() const;
	const Vector4& GetXBasis4D() const;
	const Vector4& GetYBasis4D() const;
	const Vector4& GetZBasis4D() const;
	const Vector4& GetTranslation4D() const;

	const float* GetAsFloatArray() const { return &m_values[0]; }
	float* GetAsFloatArray() { return &m_values[0]; }

	void SetXBasis2D( const Vector2& xBasis2D );
	void SetYBasis2D( const Vector2& yBasis2D );
	void SetTranslation2D( const Vector2& translation2D );
	void SetBasisVectors2D( const Vector2& xBasis2D, const Vector2& yBasis2D );
	void SetBasisVectors2D( const Vector2& xBasis2D, const Vector2& yBasis2D, const Vector2& translation2D );
	void SetXBasis3D( const Vector3& xBasis3D );
	void SetYBasis3D( const Vector3& yBasis3D );
	void SetZBasis3D( const Vector3& zBasis3D );
	void SetTranslation3D( const Vector3& translation3D );
	void SetBasisVectors3D( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D );
	void SetBasisVectors3D( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D, const Vector3& translation3D );
	void SetXBasis4D( const Vector4& xBasis4D );
	void SetYBasis4D( const Vector4& yBasis4D );
	void SetZBasis4D( const Vector4& zBasis4D );
	void SetTranslation4D( const Vector4& translation4D );
	void SetBasisVectors4D( const Vector4& xBasis4D, const Vector4& yBasis4D, const Vector4& zBasis4D );
	void SetBasisVectors4D( const Vector4& xBasis4D, const Vector4& yBasis4D, const Vector4& zBasis4D, const Vector4& translation4D );
};


//-----------------------------------------------------------------------------------------------
inline Matrix44::Matrix44()
{
	m_values[ 0 ]	= 1.f;
	m_values[ 1 ]	= 0.f;
	m_values[ 2 ]	= 0.f;
	m_values[ 3 ]	= 0.f;

	m_values[ 4 ]	= 0.f;
	m_values[ 5 ]	= 1.f;
	m_values[ 6 ]	= 0.f;
	m_values[ 7 ]	= 0.f;

	m_values[ 8 ]	= 0.f;
	m_values[ 9 ]	= 0.f;
	m_values[ 10 ]	= 1.f;
	m_values[ 11 ]	= 0.f;

	m_values[ 12 ]	= 0.f;
	m_values[ 13 ]	= 0.f;
	m_values[ 14 ]	= 0.f;
	m_values[ 15 ]	= 1.f;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44::Matrix44( const Matrix44& copy )
{
	memcpy( m_values, copy.m_values, sizeof( m_values ) );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44::Matrix44( const Vector2& xBasis2D, const Vector2& yBasis2D )
{
	m_values[ 0 ]	= xBasis2D.x;
	m_values[ 1 ]	= xBasis2D.y;
	m_values[ 2 ]	= 0.f;
	m_values[ 3 ]	= 0.f;

	m_values[ 4 ]	= yBasis2D.x;
	m_values[ 5 ]	= yBasis2D.y;
	m_values[ 6 ]	= 0.f;
	m_values[ 7 ]	= 0.f;

	m_values[ 8 ]	= 0.f;
	m_values[ 9 ]	= 0.f;
	m_values[ 10 ]	= 1.f;
	m_values[ 11 ]	= 0.f;

	m_values[ 12 ]	= 0.f;
	m_values[ 13 ]	= 0.f;
	m_values[ 14 ]	= 0.f;
	m_values[ 15 ]	= 1.f;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44::Matrix44( const Vector2& xBasis2D, const Vector2& yBasis2D, const Vector2& translation2D )
{
	m_values[ 0 ]	= xBasis2D.x;
	m_values[ 1 ]	= xBasis2D.y;
	m_values[ 2 ]	= 0.f;
	m_values[ 3 ]	= 0.f;

	m_values[ 4 ]	= yBasis2D.x;
	m_values[ 5 ]	= yBasis2D.y;
	m_values[ 6 ]	= 0.f;
	m_values[ 7 ]	= 0.f;

	m_values[ 8 ]	= 0.f;
	m_values[ 9 ]	= 0.f;
	m_values[ 10 ]	= 1.f;
	m_values[ 11 ]	= 0.f;

	m_values[ 12 ]	= translation2D.x;
	m_values[ 13 ]	= translation2D.y;
	m_values[ 14 ]	= 0.f;
	m_values[ 15 ]	= 1.f;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44::Matrix44( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D )
{
	m_values[ 0 ]	= xBasis3D.x;
	m_values[ 1 ]	= xBasis3D.y;
	m_values[ 2 ]	= xBasis3D.z;
	m_values[ 3 ]	= 0.f;

	m_values[ 4 ]	= yBasis3D.x;
	m_values[ 5 ]	= yBasis3D.y;
	m_values[ 6 ]	= yBasis3D.z;
	m_values[ 7 ]	= 0.f;

	m_values[ 8 ]	= zBasis3D.x;
	m_values[ 9 ]	= zBasis3D.y;
	m_values[ 10 ]	= zBasis3D.z;
	m_values[ 11 ]	= 0.f;

	m_values[ 12 ]	= 0.f;
	m_values[ 13 ]	= 0.f;
	m_values[ 14 ]	= 0.f;
	m_values[ 15 ]	= 1.f;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44::Matrix44( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D, const Vector3& translation3D )
{
	m_values[ 0 ]	= xBasis3D.x;
	m_values[ 1 ]	= xBasis3D.y;
	m_values[ 2 ]	= xBasis3D.z;
	m_values[ 3 ]	= 0.f;

	m_values[ 4 ]	= yBasis3D.x;
	m_values[ 5 ]	= yBasis3D.y;
	m_values[ 6 ]	= yBasis3D.z;
	m_values[ 7 ]	= 0.f;

	m_values[ 8 ]	= zBasis3D.x;
	m_values[ 9 ]	= zBasis3D.y;
	m_values[ 10 ]	= zBasis3D.z;
	m_values[ 11 ]	= 0.f;

	m_values[ 12 ]	= translation3D.x;
	m_values[ 13 ]	= translation3D.y;
	m_values[ 14 ]	= translation3D.z;
	m_values[ 15 ]	= 1.f;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44::Matrix44( const Vector4& xBasis4D, const Vector4& yBasis4D, const Vector4& zBasis4D, const Vector4& translation4D )
{
	m_values[ 0 ]	= xBasis4D.x;
	m_values[ 1 ]	= xBasis4D.y;
	m_values[ 2 ]	= xBasis4D.z;
	m_values[ 3 ]	= xBasis4D.w;

	m_values[ 4 ]	= yBasis4D.x;
	m_values[ 5 ]	= yBasis4D.y;
	m_values[ 6 ]	= yBasis4D.z;
	m_values[ 7 ]	= yBasis4D.w;

	m_values[ 8 ]	= zBasis4D.x;
	m_values[ 9 ]	= zBasis4D.y;
	m_values[ 10 ]	= zBasis4D.z;
	m_values[ 11 ]	= zBasis4D.w;

	m_values[ 12 ]	= translation4D.x;
	m_values[ 13 ]	= translation4D.y;
	m_values[ 14 ]	= translation4D.z;
	m_values[ 15 ]	= translation4D.w;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::operator = ( const Matrix44& rhs )
{
	memcpy( m_values, rhs.m_values, sizeof( m_values ) );
	return *this;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::MakeIdentity()
{
	m_values[ 0 ]	= 1.f;
	m_values[ 1 ]	= 0.f;
	m_values[ 2 ]	= 0.f;
	m_values[ 3 ]	= 0.f;

	m_values[ 4 ]	= 0.f;
	m_values[ 5 ]	= 1.f;
	m_values[ 6 ]	= 0.f;
	m_values[ 7 ]	= 0.f;

	m_values[ 8 ]	= 0.f;
	m_values[ 9 ]	= 0.f;
	m_values[ 10 ]	= 1.f;
	m_values[ 11 ]	= 0.f;

	m_values[ 12 ]	= 0.f;
	m_values[ 13 ]	= 0.f;
	m_values[ 14 ]	= 0.f;
	m_values[ 15 ]	= 1.f;
}


//-----------------------------------------------------------------------------------------------
// Rotates this matrix around the +z axis, similar to OpenGL's glRotatef( zRotationDegrees, 0.f, 0.f, 1.f ).
// 
// Effectively, this amounts to a matrix multiply, with "this" matrix being the one transformed
//	by a new translation matrix, e.g.:
//
//	This' = This * NewRotation (in column-major notation), or
//	This' = NewRotation * This (in row-major notation)
//
inline Matrix44& Matrix44::ApplyZRotationDegrees( float zRotationDegrees )
{
	const float zRotationRadians = ConvertDegreesToRadians( zRotationDegrees );
	ApplyZRotationRadians( zRotationRadians );
	return *this;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::ApplyYRotationDegrees( float yRotationDegrees )
{
	const float yRotationRadians = ConvertDegreesToRadians( yRotationDegrees );
	ApplyYRotationRadians( yRotationRadians );
	return *this;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::ApplyXRotationDegrees( float xRotationDegrees )
{
	const float xRotationRadians = ConvertDegreesToRadians( xRotationDegrees );
	ApplyXRotationRadians( xRotationRadians );
	return *this;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::ApplyRotationAboutAxisDegrees( const Vector3& rotationAxis, float rotationDegrees )
{
	const Matrix44 angleAxisRotationMatrix = CreateRotationAboutAxisDegrees( rotationAxis, rotationDegrees );
	ApplyTransformation( angleAxisRotationMatrix );
	return *this;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::ApplyRotationAboutAxisRadians( const Vector3& rotationAxis, float rotationRadians )
{
	const Matrix44 angleAxisRotationMatrix = CreateRotationAboutAxisRadians( rotationAxis, rotationRadians );
	ApplyTransformation( angleAxisRotationMatrix );
	return *this;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::ApplyScale( float uniformScale )
{
	m_values[0] *= uniformScale;
	m_values[1] *= uniformScale;
	m_values[2] *= uniformScale;

	m_values[4] *= uniformScale;
	m_values[5] *= uniformScale;
	m_values[6] *= uniformScale;

	m_values[8] *= uniformScale;
	m_values[9] *= uniformScale;
	m_values[10] *= uniformScale;

	m_values[12] *= uniformScale;
	m_values[13] *= uniformScale;
	m_values[14] *= uniformScale;

	return *this;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::ApplyScale( const Vector2& nonUniformScale2D )
{
	m_values[0] *= nonUniformScale2D.x;
	m_values[1] *= nonUniformScale2D.y;

	m_values[4] *= nonUniformScale2D.x;
	m_values[5] *= nonUniformScale2D.y;

	m_values[8] *= nonUniformScale2D.x;
	m_values[9] *= nonUniformScale2D.y;

	m_values[12] *= nonUniformScale2D.x;
	m_values[13] *= nonUniformScale2D.y;

	return *this;
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& Matrix44::ApplyScale( const Vector3& nonUniformScale3D )
{
	m_values[0] *= nonUniformScale3D.x;
	m_values[1] *= nonUniformScale3D.y;
	m_values[2] *= nonUniformScale3D.z;

	m_values[4] *= nonUniformScale3D.x;
	m_values[5] *= nonUniformScale3D.y;
	m_values[6] *= nonUniformScale3D.z;

	m_values[8] *= nonUniformScale3D.x;
	m_values[9] *= nonUniformScale3D.y;
	m_values[10] *= nonUniformScale3D.z;

	m_values[12] *= nonUniformScale3D.x;
	m_values[13] *= nonUniformScale3D.y;
	m_values[14] *= nonUniformScale3D.z;

	return *this;
}


//-----------------------------------------------------------------------------------------------
// See comment at 3D version, below
//
inline Matrix44& Matrix44::ApplyTranslation( const Vector2& translation2D )
{
	const Vector2& t = translation2D;
	m_values[12] += (t.x * m_values[0]) + (t.y * m_values[4]);
	m_values[13] += (t.x * m_values[1]) + (t.y * m_values[5]);
	m_values[14] += (t.x * m_values[2]) + (t.y * m_values[6]);
	m_values[15] += (t.x * m_values[3]) + (t.y * m_values[7]);
	return *this;
}


//-----------------------------------------------------------------------------------------------
// Translates this matrix, similar to OpenGL's glTranslate().
// 
// Effectively, this amounts to a matrix multiply, with "this" matrix being the one transformed
//	by a new translation matrix, and therefore "this" is on the left and "transformToApply" is on the right
//	(in right-handed, column-major, OpenGL-style notation), e.g.:
//
//	This' = This * Transform (in column-major notation), or
//	This' = Transform * This (in row-major notation)
//
inline Matrix44& Matrix44::ApplyTranslation( const Vector3& translation3D )
{
	const Vector3& t = translation3D;
	m_values[12] += (t.x * m_values[0]) + (t.y * m_values[4]) + (t.z * m_values[8]);
	m_values[13] += (t.x * m_values[1]) + (t.y * m_values[5]) + (t.z * m_values[9]);
	m_values[14] += (t.x * m_values[2]) + (t.y * m_values[6]) + (t.z * m_values[10]);
	m_values[15] += (t.x * m_values[3]) + (t.y * m_values[7]) + (t.z * m_values[11]);
	return *this;
}


//-----------------------------------------------------------------------------------------------
// Transforms a 2D vector known to represent a position, i.e. understood to have a homogeneous
//	w-coordinate of one (but a z-coordinate of zero).  Thus: (Px, Py, 0, 1)
//
// Note that positions, unlike directions, ARE affected by translation, since the w=1 dots
//	against the translation ([12],[13],[14],[15]) matrix values, thereby applying them.
//	However, the Z basis vector ([8],[9],[10],[11]) is effectively ignored due to Pz=0.
//
// This effectively treats the matrix as if it were a 2x3 2D affine matrix.
//
inline Vector2 Matrix44::TransformPosition( const Vector2& position2D ) const
{
	const Vector2& p = position2D;
	Vector2 transformedPosition;
	transformedPosition.x = (m_values[0] * p.x) + (m_values[4] * p.y) + (m_values[12]);
	transformedPosition.y = (m_values[1] * p.x) + (m_values[5] * p.y) + (m_values[13]);
	return transformedPosition;
}


//-----------------------------------------------------------------------------------------------
// Transforms a 3D vector known to represent a position, i.e. understood to have a homogeneous
//	w-coordinate of one.  Thus: (Px, Py, Pz, 1)
//
// Note that positions, unlike directions, ARE affected by translation, since the w=1 dots
//	against the translation ([12],[13],[14],[15]) matrix values, thereby applying them.
//
// This effectively treats the matrix as if it were a 3x4 3D affine matrix.
//
inline Vector3 Matrix44::TransformPosition( const Vector3& position3D ) const
{
	const Vector3& p = position3D;
	Vector3 transformedPosition;
	transformedPosition.x = (m_values[0] * p.x) + (m_values[4] * p.y) + (m_values[8] * p.z) + (m_values[12]);
	transformedPosition.y = (m_values[1] * p.x) + (m_values[5] * p.y) + (m_values[9] * p.z) + (m_values[13]);
	transformedPosition.z = (m_values[2] * p.x) + (m_values[6] * p.y) + (m_values[10] * p.z) + (m_values[14]);
	return transformedPosition;
}


//-----------------------------------------------------------------------------------------------
// Transforms a 2D vector known to represent a direction, i.e. understood to have a homogeneous
//	w-coordinate of zero (as well as a z-coordinate of zero).  Thus: (Dx, Dy, 0, 0)
//
// Note that directions are effectively "immune to" translation (since the w=0 dots against the
//	[12], [13], [14], [15] matrix values, thereby zeroing them out of the equation).
//
// This effectively treats the matrix as if it were a 2x2 2D linear matrix.
//
inline Vector2 Matrix44::TransformDirection( const Vector2& direction2D ) const
{
	const Vector2& d = direction2D;
	Vector2 transformedDirection;
	transformedDirection.x = (m_values[0] * d.x) + (m_values[4] * d.y);
	transformedDirection.y = (m_values[1] * d.x) + (m_values[5] * d.y);
	return transformedDirection;
}


//-----------------------------------------------------------------------------------------------
// Transforms a 3D vector known to represent a direction, i.e. understood to have a homogeneous
//	w-coordinate of zero.  Thus: (Dx, Dy, Dz, 0)
//
// Note that directions are effectively "immune to" translation (since the w=0 dots against the
//	[12], [13], [14], [15] matrix values, thereby zeroing them out of the equation).
//
// This effectively treats the matrix as if it were a 3x3 3D linear matrix.
//
inline Vector3 Matrix44::TransformDirection( const Vector3& direction3D ) const
{
	const Vector3& d = direction3D;
	Vector3 transformedDirection;
	transformedDirection.x = (m_values[0] * d.x) + (m_values[4] * d.y) + (m_values[8] * d.z);
	transformedDirection.y = (m_values[1] * d.x) + (m_values[5] * d.y) + (m_values[9] * d.z);
	transformedDirection.z = (m_values[2] * d.x) + (m_values[6] * d.y) + (m_values[10] * d.z);
	return transformedDirection;
}


//-----------------------------------------------------------------------------------------------
// Transforms a 4D (3D homogeneous) point by the matrix.  This is the "general case".
// 
// This treats the matrix as if it is a full 4x4 3D homogeneous matrix.  The point itself
//	provides its homogeneous w-coordinate, which in this case will determine what fraction of
//	the translation present in the matrix transformation (if any) it will receive.
//
inline Vector4 Matrix44::TransformPoint( const Vector4& homogeneousPoint ) const
{
	const Vector4& p = homogeneousPoint;
	Vector4 transformedPoint;
	transformedPoint.x = (m_values[0] * p.x) + (m_values[4] * p.y) + (m_values[8] * p.z) + (m_values[12] * p.w);
	transformedPoint.y = (m_values[1] * p.x) + (m_values[5] * p.y) + (m_values[9] * p.z) + (m_values[13] * p.w);
	transformedPoint.z = (m_values[2] * p.x) + (m_values[6] * p.y) + (m_values[10] * p.z) + (m_values[14] * p.w);
	transformedPoint.w = (m_values[3] * p.x) + (m_values[7] * p.y) + (m_values[11] * p.z) + (m_values[15] * p.w);
	return transformedPoint;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::Transpose()
{
	const float value1	= m_values[ 1 ];
	const float value2	= m_values[ 2 ];
	const float value3	= m_values[ 3 ];
	const float value6	= m_values[ 6 ];
	const float value7	= m_values[ 7 ];
	const float value11	= m_values[ 11 ];

	m_values[ 1 ]	= m_values[ 4 ];
	m_values[ 2 ]	= m_values[ 8 ];
	m_values[ 3 ]	= m_values[ 12 ];
	m_values[ 6 ]	= m_values[ 9 ];
	m_values[ 7 ]	= m_values[ 13 ];
	m_values[ 11 ]	= m_values[ 14 ];

	m_values[ 4 ]	= value1;
	m_values[ 8 ]	= value2;
	m_values[ 12 ]	= value3;
	m_values[ 9 ]	= value6;
	m_values[ 13 ]	= value7;
	m_values[ 14 ]	= value11;
}


//-----------------------------------------------------------------------------------------------
inline Vector2& Matrix44::GetXBasis2D()
{
	return *reinterpret_cast< Vector2* >( &m_values[ 0 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector2& Matrix44::GetXBasis2D() const
{
	return *reinterpret_cast< const Vector2* >( &m_values[ 0 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector2& Matrix44::GetYBasis2D()
{
	return *reinterpret_cast< Vector2* >( &m_values[ 4 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector2& Matrix44::GetYBasis2D() const
{
	return *reinterpret_cast< const Vector2* >( &m_values[ 4 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector2& Matrix44::GetTranslation2D()
{
	return *reinterpret_cast< Vector2* >( &m_values[ 12 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector2& Matrix44::GetTranslation2D() const
{
	return *reinterpret_cast< const Vector2* >( &m_values[ 12 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector3& Matrix44::GetXBasis3D()
{
	return *reinterpret_cast< Vector3* >( &m_values[ 0 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector3& Matrix44::GetXBasis3D() const
{
	return *reinterpret_cast< const Vector3* >( &m_values[ 0 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector3& Matrix44::GetYBasis3D()
{
	return *reinterpret_cast< Vector3* >( &m_values[ 4 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector3& Matrix44::GetYBasis3D() const
{
	return *reinterpret_cast< const Vector3* >( &m_values[ 4 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector3& Matrix44::GetZBasis3D()
{
	return *reinterpret_cast< Vector3* >( &m_values[ 8 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector3& Matrix44::GetZBasis3D() const
{
	return *reinterpret_cast< const Vector3* >( &m_values[ 8 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector3& Matrix44::GetTranslation3D()
{
	return *reinterpret_cast< Vector3* >( &m_values[ 12 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector3& Matrix44::GetTranslation3D() const
{
	return *reinterpret_cast< const Vector3* >( &m_values[ 12 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector4& Matrix44::GetXBasis4D()
{
	return *reinterpret_cast< Vector4* >( &m_values[ 0 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector4& Matrix44::GetXBasis4D() const
{
	return *reinterpret_cast< const Vector4* >( &m_values[ 0 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector4& Matrix44::GetYBasis4D()
{
	return *reinterpret_cast< Vector4* >( &m_values[ 4 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector4& Matrix44::GetYBasis4D() const
{
	return *reinterpret_cast< const Vector4* >( &m_values[ 4 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector4& Matrix44::GetZBasis4D()
{
	return *reinterpret_cast< Vector4* >( &m_values[ 8 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector4& Matrix44::GetZBasis4D() const
{
	return *reinterpret_cast< const Vector4* >( &m_values[ 8 ] );
}


//-----------------------------------------------------------------------------------------------
inline Vector4& Matrix44::GetTranslation4D()
{
	return *reinterpret_cast< Vector4* >( &m_values[ 12 ] );
}


//-----------------------------------------------------------------------------------------------
inline const Vector4& Matrix44::GetTranslation4D() const
{
	return *reinterpret_cast< const Vector4* >( &m_values[ 12 ] );
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetXBasis2D( const Vector2& xBasis2D )
{
	m_values[0] = xBasis2D.x;
	m_values[1] = xBasis2D.y;
	m_values[2] = 0.f;
	m_values[3] = 0.f;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetYBasis2D( const Vector2& yBasis2D )
{
	m_values[4] = yBasis2D.x;
	m_values[5] = yBasis2D.y;
	m_values[6] = 0.f;
	m_values[7] = 0.f;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetTranslation2D( const Vector2& translation2D )
{
	m_values[12] = translation2D.x;
	m_values[13] = translation2D.y;
	m_values[14] = 0.f;
	m_values[15] = 1.f;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetBasisVectors2D( const Vector2& xBasis2D, const Vector2& yBasis2D )
{
	SetXBasis2D( xBasis2D );
	SetYBasis2D( yBasis2D );
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetBasisVectors2D( const Vector2& xBasis2D, const Vector2& yBasis2D, const Vector2& translation2D )
{
	SetXBasis2D( xBasis2D );
	SetYBasis2D( yBasis2D );
	SetTranslation2D( translation2D );
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetXBasis3D( const Vector3& xBasis3D )
{
	m_values[0] = xBasis3D.x;
	m_values[1] = xBasis3D.y;
	m_values[2] = xBasis3D.z;
	m_values[3] = 0.f;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetYBasis3D( const Vector3& yBasis3D )
{
	m_values[4] = yBasis3D.x;
	m_values[5] = yBasis3D.y;
	m_values[6] = yBasis3D.z;
	m_values[7] = 0.f;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetZBasis3D( const Vector3& zBasis3D )
{
	m_values[8]  = zBasis3D.x;
	m_values[9]  = zBasis3D.y;
	m_values[10] = zBasis3D.z;
	m_values[11] = 0.f;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetTranslation3D( const Vector3& translation3D )
{
	m_values[12] = translation3D.x;
	m_values[13] = translation3D.y;
	m_values[14] = translation3D.z;
	m_values[15] = 1.f;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetBasisVectors3D( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D )
{
	SetXBasis3D( xBasis3D );
	SetYBasis3D( yBasis3D );
	SetZBasis3D( zBasis3D );
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetBasisVectors3D( const Vector3& xBasis3D, const Vector3& yBasis3D, const Vector3& zBasis3D, const Vector3& translation3D )
{
	SetXBasis3D( xBasis3D );
	SetYBasis3D( yBasis3D );
	SetZBasis3D( zBasis3D );
	SetTranslation3D( translation3D );
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetXBasis4D( const Vector4& xBasis4D )
{
	m_values[0] = xBasis4D.x;
	m_values[1] = xBasis4D.y;
	m_values[2] = xBasis4D.z;
	m_values[3] = xBasis4D.w;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetYBasis4D( const Vector4& yBasis4D )
{
	m_values[4] = yBasis4D.x;
	m_values[5] = yBasis4D.y;
	m_values[6] = yBasis4D.z;
	m_values[7] = yBasis4D.w;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetZBasis4D( const Vector4& zBasis4D )
{
	m_values[8]  = zBasis4D.x;
	m_values[9]  = zBasis4D.y;
	m_values[10] = zBasis4D.z;
	m_values[11] = zBasis4D.w;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetTranslation4D( const Vector4& translation4D )
{
	m_values[12] = translation4D.x;
	m_values[13] = translation4D.y;
	m_values[14] = translation4D.z;
	m_values[15] = translation4D.w;
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetBasisVectors4D( const Vector4& xBasis4D, const Vector4& yBasis4D, const Vector4& zBasis4D )
{
	SetXBasis4D( xBasis4D );
	SetYBasis4D( yBasis4D );
	SetZBasis4D( zBasis4D );
}


//-----------------------------------------------------------------------------------------------
inline void Matrix44::SetBasisVectors4D( const Vector4& xBasis4D, const Vector4& yBasis4D, const Vector4& zBasis4D, const Vector4& translation4D )
{
	SetXBasis4D( xBasis4D );
	SetYBasis4D( yBasis4D );
	SetZBasis4D( zBasis4D );
	SetTranslation4D( translation4D );}


//-----------------------------------------------------------------------------------------------
inline STATIC Matrix44 Matrix44::CreateScale( float uniformScale )
{
	Vector3 xBasis( uniformScale, 0.f, 0.f );
	Vector3 yBasis( 0.f, uniformScale, 0.f );
	Vector3 zBasis( 0.f, 0.f, uniformScale );
	return Matrix44( xBasis, yBasis, zBasis );
}


//-----------------------------------------------------------------------------------------------
inline STATIC Matrix44 Matrix44::CreateScale( const Vector2& nonUniformScale )
{
	Vector2 xBasis( nonUniformScale.x, 0.f );
	Vector2 yBasis( 0.f, nonUniformScale.y );
	return Matrix44( xBasis, yBasis );
}


//-----------------------------------------------------------------------------------------------
inline STATIC Matrix44 Matrix44::CreateScale( const Vector3& nonUniformScale )
{
	Vector3 xBasis( nonUniformScale.x, 0.f, 0.f );
	Vector3 yBasis( 0.f, nonUniformScale.y, 0.f );
	Vector3 zBasis( 0.f, 0.f, nonUniformScale.z );
	return Matrix44( xBasis, yBasis, zBasis );
}


//-----------------------------------------------------------------------------------------------
inline STATIC Matrix44 Matrix44::CreateTranslation( const Vector2& translation )
{
	Matrix44 translationMatrix;
	translationMatrix.m_values[ 12 ] = translation.x;
	translationMatrix.m_values[ 13 ] = translation.y;
	return translationMatrix;
}


//-----------------------------------------------------------------------------------------------
inline STATIC Matrix44 Matrix44::CreateTranslation( const Vector3& translation )
{
	Matrix44 translationMatrix;
	translationMatrix.m_values[ 12 ] = translation.x;
	translationMatrix.m_values[ 13 ] = translation.y;
	translationMatrix.m_values[ 14 ] = translation.z;
	return translationMatrix;
}


/////////////////////////////////////////////////////////////////////////////////////////////////
//
//	MatrixStack44
//
//	A push-pop-able 4x4 matrix stack.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
class MatrixStack44
{
public:
	MatrixStack44();

	void Push();
	void Pop();
	void MakeIdentity();
	void SetCurrent( const Matrix44& overrideMatrix );
	const Matrix44& GetCurrent() const;
	Matrix44& GetCurrent();

	Matrix44& ApplyZRotationDegrees( float zRotationDegrees );
	Matrix44& ApplyZRotationRadians( float zRotationRadians );
	Matrix44& ApplyYRotationDegrees( float yRotationDegrees );
	Matrix44& ApplyYRotationRadians( float yRotationRadians );
	Matrix44& ApplyXRotationDegrees( float xRotationDegrees );
	Matrix44& ApplyXRotationRadians( float xRotationRadians );
	Matrix44& ApplyRotationAboutAxisDegrees( const Vector3& rotationAxis, float rotationDegrees );
	Matrix44& ApplyRotationAboutAxisRadians( const Vector3& rotationAxis, float rotationRadians );
	Matrix44& ApplyScale( float uniformScale );
	Matrix44& ApplyScale( const Vector2& nonUniformScale );
	Matrix44& ApplyScale( const Vector3& nonUniformScale );
	Matrix44& ApplyTranslation( const Vector2& translation );
	Matrix44& ApplyTranslation( const Vector3& translation );

private:
	std::vector< Matrix44 >		m_matrixStack;
};


//-----------------------------------------------------------------------------------------------
inline MatrixStack44::MatrixStack44()
{
	m_matrixStack.resize( 1 );  // Initialize with one [identity] matrix (minimum required) already on stack
}


//-----------------------------------------------------------------------------------------------
inline void MatrixStack44::Push()
{
	m_matrixStack.push_back( m_matrixStack.back() );
}


//-----------------------------------------------------------------------------------------------
inline void MatrixStack44::Pop()
{
	if( m_matrixStack.size() > 1 )
		m_matrixStack.pop_back();
	else
		m_matrixStack.back().MakeIdentity(); // Should we allow "extra" Pops to pop us "all the way back to identity"?
}


//-----------------------------------------------------------------------------------------------
inline void MatrixStack44::MakeIdentity()
{
	m_matrixStack.back().MakeIdentity();
}


//-----------------------------------------------------------------------------------------------
inline void MatrixStack44::SetCurrent( const Matrix44& overrideMatrix )
{
	m_matrixStack.back() = overrideMatrix;
}


//-----------------------------------------------------------------------------------------------
inline const Matrix44& MatrixStack44::GetCurrent() const
{
	return m_matrixStack.back();
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::GetCurrent()
{
	return m_matrixStack.back();
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyZRotationDegrees( float zRotationDegrees )
{
	m_matrixStack.back().ApplyZRotationDegrees( zRotationDegrees );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyZRotationRadians( float zRotationRadians )
{
	m_matrixStack.back().ApplyZRotationRadians( zRotationRadians );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyYRotationDegrees( float yRotationDegrees )
{
	m_matrixStack.back().ApplyYRotationDegrees( yRotationDegrees );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyYRotationRadians( float yRotationRadians )
{
	m_matrixStack.back().ApplyYRotationRadians( yRotationRadians );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyXRotationDegrees( float xRotationDegrees )
{
	m_matrixStack.back().ApplyXRotationDegrees( xRotationDegrees );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyXRotationRadians( float xRotationRadians )
{
	m_matrixStack.back().ApplyXRotationRadians( xRotationRadians );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyRotationAboutAxisDegrees( const Vector3& rotationAxis, float rotationDegrees )
{
	m_matrixStack.back().ApplyRotationAboutAxisDegrees( rotationAxis, rotationDegrees );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyRotationAboutAxisRadians( const Vector3& rotationAxis, float rotationRadians )
{
	m_matrixStack.back().ApplyRotationAboutAxisRadians( rotationAxis, rotationRadians );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyScale( float uniformScale )
{
	m_matrixStack.back().ApplyScale( uniformScale );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyScale( const Vector2& nonUniformScale )
{
	m_matrixStack.back().ApplyScale( nonUniformScale );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyScale( const Vector3& nonUniformScale )
{
	m_matrixStack.back().ApplyScale( nonUniformScale );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyTranslation( const Vector2& translation )
{
	m_matrixStack.back().ApplyTranslation( translation );
}


//-----------------------------------------------------------------------------------------------
inline Matrix44& MatrixStack44::ApplyTranslation( const Vector3& translation )
{
	m_matrixStack.back().ApplyTranslation( translation );
}



#endif // include_Matrix
