CSC8503_Advanced_Game_Technologies / Common / Matrix4.cpp
Matrix4.cpp
Raw
/*
Part of Newcastle University's Game Engineering source code.

Use as you see fit!

Comments and queries to: richard-gordon.davison AT ncl.ac.uk
https://research.ncl.ac.uk/game/
*/
#include "Matrix4.h"
#include "Matrix3.h"
#include "Maths.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Quaternion.h"

using namespace NCL;
using namespace NCL::Maths;
Matrix4::Matrix4(void)	{
	ToZero();
	array[0]  = 1.0f;
	array[5]  = 1.0f;
	array[10] = 1.0f;
	array[15] = 1.0f;
}

Matrix4::Matrix4( float elements[16] )	{
	memcpy(this->array,elements,16*sizeof(float));
}

Matrix4::Matrix4(const Matrix3& m3) {
	ToZero();
	array[0] = m3.array[0];
	array[1] = m3.array[1];
	array[2] = m3.array[2];

	array[4] = m3.array[3];
	array[5] = m3.array[4];
	array[6] = m3.array[5];

	array[8] = m3.array[6];
	array[9] = m3.array[7];
	array[10] = m3.array[8];

	array[15] = 1.0f;
}

Matrix4::Matrix4(const Quaternion& quat) : Matrix4() {
	float yy = quat.y * quat.y;
	float zz = quat.z * quat.z;
	float xy = quat.x * quat.y;
	float zw = quat.z * quat.w;
	float xz = quat.x * quat.z;
	float yw = quat.y * quat.w;
	float xx = quat.x * quat.x;
	float yz = quat.y * quat.z;
	float xw = quat.x * quat.w;

	array[0] = 1 - 2 * yy - 2 * zz;
	array[1] = 2 * xy + 2 * zw;
	array[2] = 2 * xz - 2 * yw;

	array[4] = 2 * xy - 2 * zw;
	array[5] = 1 - 2 * xx - 2 * zz;
	array[6] = 2 * yz + 2 * xw;

	array[8] = 2 * xz + 2 * yw;
	array[9] = 2 * yz - 2 * xw;
	array[10] = 1 - 2 * xx - 2 * yy;
}

Matrix4::~Matrix4(void)	{
}

void Matrix4::ToZero()	{
	for(int i = 0; i < 16; i++)	{
		array[i] = 0.0f;
	}
}

Vector3 Matrix4::GetPositionVector() const{
	return Vector3(array[12],array[13],array[14]);
}

void	Matrix4::SetPositionVector(const Vector3& in) {
	array[12] = in.x;
	array[13] = in.y;
	array[14] = in.z;		
}

Vector3 Matrix4::GetDiagonal() const{
	return Vector3(array[0],array[5],array[10]);
}

void	Matrix4::SetDiagonal(const Vector3 &in) {
	array[0]  = in.x;
	array[5]  = in.y;
	array[10] = in.z;		
}

Matrix4 Matrix4::Perspective(float znear, float zfar, float aspect, float fov) {
	Matrix4 m;

	const float h = 1.0f / tan(fov*Maths::PI_OVER_360);
	float neg_depth = znear-zfar;

	m.array[0]		= h / aspect;
	m.array[5]		= h;
	m.array[10]	= (zfar + znear)/neg_depth;
	m.array[11]	= -1.0f;
	m.array[14]	= 2.0f*(znear*zfar)/neg_depth;
	m.array[15]	= 0.0f;

	return m;
}

//http://www.opengl.org/sdk/docs/man/xhtml/glOrtho.xml
Matrix4 Matrix4::Orthographic(float znear, float zfar,float right, float left, float top, float bottom)	{
	Matrix4 m;

	m.array[0]	= 2.0f / (right-left);
	m.array[5]	= 2.0f / (top-bottom);
	m.array[10]	= -2.0f / (zfar-znear);

	m.array[12]  = -(right+left)/(right-left);
	m.array[13]  = -(top+bottom)/(top-bottom);
	m.array[14]  = -(zfar+znear)/(zfar-znear);
	m.array[15]  = 1.0f;

	return m;
}

Matrix4 Matrix4::BuildViewMatrix(const Vector3& from, const Vector3& lookingAt, const Vector3& up)	{
	Matrix4 r;
	r.SetPositionVector(Vector3(-from.x,-from.y,-from.z));

	Matrix4 m;

	Vector3 f = (lookingAt - from);
	f.Normalise();

	Vector3 s = Vector3::Cross(f,up).Normalised();
	Vector3 u = Vector3::Cross(s,f).Normalised();

	m.array[0] = s.x;
	m.array[4] = s.y;
	m.array[8] = s.z;

	m.array[1] = u.x;
	m.array[5] = u.y;
	m.array[9] = u.z;

	m.array[2]  = -f.x;
	m.array[6]  = -f.y;
	m.array[10] = -f.z;

	return m*r;
}

Matrix4 Matrix4::Rotation(float degrees, const Vector3 &inaxis)	 {
	Matrix4 m;

	Vector3 axis = inaxis;

	axis.Normalise();

	float c = cos((float)Maths::DegreesToRadians(degrees));
	float s = sin((float)Maths::DegreesToRadians(degrees));

	m.array[0]  = (axis.x * axis.x) * (1.0f - c) + c;
	m.array[1]  = (axis.y * axis.x) * (1.0f - c) + (axis.z * s);
	m.array[2]  = (axis.z * axis.x) * (1.0f - c) - (axis.y * s);

	m.array[4]  = (axis.x * axis.y) * (1.0f - c) - (axis.z * s);
	m.array[5]  = (axis.y * axis.y) * (1.0f - c) + c;
	m.array[6]  = (axis.z * axis.y) * (1.0f - c) + (axis.x * s);

	m.array[8]  = (axis.x * axis.z) * (1.0f - c) + (axis.y * s);
	m.array[9]  = (axis.y * axis.z) * (1.0f - c) - (axis.x * s);
	m.array[10] = (axis.z * axis.z) * (1.0f - c) + c;

	return m;
}

Matrix4 Matrix4::Scale( const Vector3 &scale )	{
	Matrix4 m;

	m.array[0]  = scale.x;
	m.array[5]  = scale.y;
	m.array[10] = scale.z;	

	return m;
}

Matrix4 Matrix4::Translation( const Vector3 &translation )	{
	Matrix4 m;

	m.array[12] = translation.x;
	m.array[13] = translation.y;
	m.array[14] = translation.z;	

	return m;
}

//Yoinked from the Open Source Doom 3 release - all credit goes to id software!
void    Matrix4::Invert() {
	float det, invDet;

	// 2x2 sub-determinants required to calculate 4x4 determinant
	float det2_01_01 = array[0] * array[5] - array[1] * array[4];
	float det2_01_02 = array[0] * array[6] - array[2] * array[4];
	float det2_01_03 = array[0] * array[7] - array[3] * array[4];
	float det2_01_12 = array[1] * array[6] - array[2] * array[5];
	float det2_01_13 = array[1] * array[7] - array[3] * array[5];
	float det2_01_23 = array[2] * array[7] - array[3] * array[6];

	// 3x3 sub-determinants required to calculate 4x4 determinant
	float det3_201_012 = array[8] * det2_01_12 - array[9] * det2_01_02 + array[10] * det2_01_01;
	float det3_201_013 = array[8] * det2_01_13 - array[9] * det2_01_03 + array[11] * det2_01_01;
	float det3_201_023 = array[8] * det2_01_23 - array[10] * det2_01_03 + array[11] * det2_01_02;
	float det3_201_123 = array[9] * det2_01_23 - array[10] * det2_01_13 + array[11] * det2_01_12;

	det = (-det3_201_123 * array[12] + det3_201_023 * array[13] - det3_201_013 * array[14] + det3_201_012 * array[15]);

	invDet = 1.0f / det;

	// remaining 2x2 sub-determinants
	float det2_03_01 = array[0] * array[13] - array[1] * array[12];
	float det2_03_02 = array[0] * array[14] - array[2] * array[12];
	float det2_03_03 = array[0] * array[15] - array[3] * array[12];
	float det2_03_12 = array[1] * array[14] - array[2] * array[13];
	float det2_03_13 = array[1] * array[15] - array[3] * array[13];
	float det2_03_23 = array[2] * array[15] - array[3] * array[14];

	float det2_13_01 = array[4] * array[13] - array[5] * array[12];
	float det2_13_02 = array[4] * array[14] - array[6] * array[12];
	float det2_13_03 = array[4] * array[15] - array[7] * array[12];
	float det2_13_12 = array[5] * array[14] - array[6] * array[13];
	float det2_13_13 = array[5] * array[15] - array[7] * array[13];
	float det2_13_23 = array[6] * array[15] - array[7] * array[14];

	// remaining 3x3 sub-determinants
	float det3_203_012 = array[8] * det2_03_12 - array[9] * det2_03_02 + array[10] * det2_03_01;
	float det3_203_013 = array[8] * det2_03_13 - array[9] * det2_03_03 + array[11] * det2_03_01;
	float det3_203_023 = array[8] * det2_03_23 - array[10] * det2_03_03 + array[11] * det2_03_02;
	float det3_203_123 = array[9] * det2_03_23 - array[10] * det2_03_13 + array[11] * det2_03_12;

	float det3_213_012 = array[8] * det2_13_12 - array[9] * det2_13_02 + array[10] * det2_13_01;
	float det3_213_013 = array[8] * det2_13_13 - array[9] * det2_13_03 + array[11] * det2_13_01;
	float det3_213_023 = array[8] * det2_13_23 - array[10] * det2_13_03 + array[11] * det2_13_02;
	float det3_213_123 = array[9] * det2_13_23 - array[10] * det2_13_13 + array[11] * det2_13_12;

	float det3_301_012 = array[12] * det2_01_12 - array[13] * det2_01_02 + array[14] * det2_01_01;
	float det3_301_013 = array[12] * det2_01_13 - array[13] * det2_01_03 + array[15] * det2_01_01;
	float det3_301_023 = array[12] * det2_01_23 - array[14] * det2_01_03 + array[15] * det2_01_02;
	float det3_301_123 = array[13] * det2_01_23 - array[14] * det2_01_13 + array[15] * det2_01_12;

	array[0] = -det3_213_123 * invDet;
	array[4] = +det3_213_023 * invDet;
	array[8] = -det3_213_013 * invDet;
	array[12] = +det3_213_012 * invDet;

	array[1] = +det3_203_123 * invDet;
	array[5] = -det3_203_023 * invDet;
	array[9] = +det3_203_013 * invDet;
	array[13] = -det3_203_012 * invDet;

	array[2] = +det3_301_123 * invDet;
	array[6] = -det3_301_023 * invDet;
	array[10] = +det3_301_013 * invDet;
	array[14] = -det3_301_012 * invDet;

	array[3] = -det3_201_123 * invDet;
	array[7] = +det3_201_023 * invDet;
	array[11] = -det3_201_013 * invDet;
	array[15] = +det3_201_012 * invDet;
}

Matrix4 Matrix4::Inverse()	const {
	Matrix4 temp(*this);
	temp.Invert();
	return temp;
}

Vector4 Matrix4::GetRow(unsigned int row) const {
	Vector4 out(0, 0, 0, 1);
	if (row <= 3) {
		int start = row;

		out.x = array[start];
		out.y = array[start + 4];
		out.z = array[start + 8];
		out.w = array[start + 12];
	}
	return out;
}

Vector4 Matrix4::GetColumn(unsigned int column) const {
	Vector4 out(0, 0, 0, 1);

	if (column <= 3) {
		memcpy(&out, &array[4 * column], sizeof(Vector4));
	}

	return out;
}

Vector3 Matrix4::operator*(const Vector3 &v) const {
	Vector3 vec;

	float temp;

	vec.x = v.x*array[0] + v.y*array[4] + v.z*array[8] + array[12];
	vec.y = v.x*array[1] + v.y*array[5] + v.z*array[9] + array[13];
	vec.z = v.x*array[2] + v.y*array[6] + v.z*array[10] + array[14];

	temp = v.x*array[3] + v.y*array[7] + v.z*array[11] + array[15];

	vec.x = vec.x / temp;
	vec.y = vec.y / temp;
	vec.z = vec.z / temp;

	return vec;
}

Vector4 Matrix4::operator*(const Vector4 &v) const {
	return Vector4(
		v.x*array[0] + v.y*array[4] + v.z*array[8] + v.w * array[12],
		v.x*array[1] + v.y*array[5] + v.z*array[9] + v.w * array[13],
		v.x*array[2] + v.y*array[6] + v.z*array[10] + v.w * array[14],
		v.x*array[3] + v.y*array[7] + v.z*array[11] + v.w * array[15]
	);
}