#region --- License ---
/* Copyright (c) 2006, 2007 the OpenTK team
* See license.txt for license info
*
* Implemented by Andy Gill
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace OpenTK.Math
{
///
/// Represents a 4x4 Matrix
///
[StructLayout(LayoutKind.Sequential)]
public struct Matrix4
{
#region Fields
///
/// Top row of the matrix
///
public Vector4 Row0;
///
/// 2nd row of the matrix
///
public Vector4 Row1;
///
/// 3rd row of the matrix
///
public Vector4 Row2;
///
/// Bottom row of the matrix
///
public Vector4 Row3;
///
/// The identity matrix
///
public static Matrix4 Identity = new Matrix4(Vector4.UnitX, Vector4.UnitY, Vector4.UnitZ, Vector4.UnitW);
#endregion
#region Constructors
///
/// Construct a new matrix from 4 vectors representing each row
///
/// Top row of the matrix
/// 2nd row of the matrix
/// 3rd row of the matrix
/// Bottom row of the matrix
public Matrix4(Vector4 row0, Vector4 row1, Vector4 row2, Vector4 row3)
{
Row0 = row0;
Row1 = row1;
Row2 = row2;
Row3 = row3;
}
#endregion
#region Functions
#region public void Invert()
public void Invert()
{
this = Matrix4.Invert(this);
}
#endregion
#region public void Transpose()
public void Transpose()
{
this = Matrix4.Transpose(this);
}
#endregion
#endregion
#region Properties
///
/// The determinant of this matrix
///
public float Determinant
{
get
{
return
Row0.X * Row1.Y * Row2.Z * Row3.W - Row0.X * Row1.Y * Row2.W * Row3.Z + Row0.X * Row1.Z * Row2.W * Row3.Y - Row0.X * Row1.Z * Row2.Y * Row3.W
+ Row0.X * Row1.W * Row2.Y * Row3.Z - Row0.X * Row1.W * Row2.Z * Row3.Y - Row0.Y * Row1.Z * Row2.W * Row3.X + Row0.Y * Row1.Z * Row2.X * Row3.W
- Row0.Y * Row1.W * Row2.X * Row3.Z + Row0.Y * Row1.W * Row2.Z * Row3.X - Row0.Y * Row1.X * Row2.Z * Row3.W + Row0.Y * Row1.X * Row2.W * Row3.Z
+ Row0.Z * Row1.W * Row2.X * Row3.Y - Row0.Z * Row1.W * Row2.Y * Row3.X + Row0.Z * Row1.X * Row2.Y * Row3.W - Row0.Z * Row1.X * Row2.W * Row3.Y
+ Row0.Z * Row1.Y * Row2.W * Row3.X - Row0.Z * Row1.Y * Row2.X * Row3.W - Row0.W * Row1.X * Row2.Y * Row3.Z + Row0.W * Row1.X * Row2.Z * Row3.Y
- Row0.W * Row1.Y * Row2.Z * Row3.X + Row0.W * Row1.Y * Row2.X * Row3.Z - Row0.W * Row1.Z * Row2.X * Row3.Y + Row0.W * Row1.Z * Row2.Y * Row3.X;
}
}
///
/// The first column of this matrix
///
public Vector4 Column0
{
get { return new Vector4(Row0.X, Row1.X, Row2.X, Row3.X); }
}
///
/// The second column of this matrix
///
public Vector4 Column1
{
get { return new Vector4(Row0.Y, Row1.Y, Row2.Y, Row3.Y); }
}
///
/// The third column of this matrix
///
public Vector4 Column2
{
get { return new Vector4(Row0.Z, Row1.Z, Row2.Z, Row3.Z); }
}
///
/// The fourth column of this matrix
///
public Vector4 Column3
{
get { return new Vector4(Row0.W, Row1.W, Row2.W, Row3.W); }
}
#endregion
#region Operator overloads
///
/// Matrix multiplication
///
/// left-hand operand
/// right-hand operand
/// A new Matrix44 which holds the result of the multiplication
public static Matrix4 operator *(Matrix4 left, Matrix4 right)
{
return Matrix4.Mult(left, right);
}
public float get(int x, int y)
{
throw new NotImplementedException();
}
#endregion
#region Static functions
#region Scale Functions
///
/// Build a scaling matrix
///
/// Single scale factor for x,y and z axes
/// A scaling matrix
public static Matrix4 Scale(float scale)
{
return Scale(scale, scale, scale);
}
///
/// Build a scaling matrix
///
/// Scale factors for x,y and z axes
/// A scaling matrix
public static Matrix4 Scale(Vector3 scale)
{
return Scale(scale.X, scale.Y, scale.Z);
}
///
/// Build a scaling matrix
///
/// Scale factor for x-axis
/// Scale factor for y-axis
/// Scale factor for z-axis
/// A scaling matrix
public static Matrix4 Scale(float x, float y, float z)
{
Matrix4 result;
result.Row0 = Vector4.UnitX * x;
result.Row1 = Vector4.UnitY * y;
result.Row2 = Vector4.UnitZ * z;
result.Row3 = Vector4.UnitW;
return result;
}
#endregion
#region Translation Functions
///
/// Build a translation matrix with the given translation
///
/// The vector to translate along
/// A Translation matrix
public static Matrix4 Translation(Vector3 trans)
{
return Translation(trans.X, trans.Y, trans.Z);
}
///
/// Build a translation matrix with the given translation
///
/// X translation
/// Y translation
/// Z translation
/// A Translation matrix
public static Matrix4 Translation(float x, float y, float z)
{
Matrix4 result = Identity;
result.Row3 = new Vector4(x, y, z, 1.0f);
return result;
}
#endregion
#region Rotation Functions
///
/// Build a rotation matrix that rotates about the x-axis
///
/// angle in radians to rotate counter-clockwise around the x-axis
/// A rotation matrix
public static Matrix4 RotateX(float angle)
{
float cos = (float)System.Math.Cos(angle);
float sin = (float)System.Math.Sin(angle);
Matrix4 result;
result.Row0 = Vector4.UnitX;
result.Row1 = new Vector4(0.0f, cos, sin, 0.0f);
result.Row2 = new Vector4(0.0f, -sin, cos, 0.0f);
result.Row3 = Vector4.UnitW;
return result;
}
///
/// Build a rotation matrix that rotates about the y-axis
///
/// angle in radians to rotate counter-clockwise around the y-axis
/// A rotation matrix
public static Matrix4 RotateY(float angle)
{
float cos = (float)System.Math.Cos(angle);
float sin = (float)System.Math.Sin(angle);
Matrix4 result;
result.Row0 = new Vector4(cos, 0.0f, -sin, 0.0f);
result.Row1 = Vector4.UnitY;
result.Row2 = new Vector4(sin, 0.0f, cos, 0.0f);
result.Row3 = Vector4.UnitW;
return result;
}
///
/// Build a rotation matrix that rotates about the z-axis
///
/// angle in radians to rotate counter-clockwise around the z-axis
/// A rotation matrix
public static Matrix4 RotateZ(float angle)
{
float cos = (float)System.Math.Cos(angle);
float sin = (float)System.Math.Sin(angle);
Matrix4 result;
result.Row0 = new Vector4(cos, sin, 0.0f, 0.0f);
result.Row1 = new Vector4(-sin, cos, 0.0f, 0.0f);
result.Row2 = Vector4.UnitZ;
result.Row3 = Vector4.UnitW;
return result;
}
///
/// Build a rotation matrix to rotate about the given axis
///
/// the axis to rotate about
/// angle in radians to rotate counter-clockwise (looking in the direction of the given axis)
/// A rotation matrix
public static Matrix4 Rotate(Vector3 axis, float angle)
{
float cos = (float)System.Math.Cos(-angle);
float sin = (float)System.Math.Sin(-angle);
float t = 1.0f - cos;
axis.Normalize();
Matrix4 result;
result.Row0 = new Vector4(t * axis.X * axis.X + cos, t * axis.X * axis.Y - sin * axis.Z, t * axis.X * axis.Z + sin * axis.Y, 0.0f);
result.Row1 = new Vector4(t * axis.X * axis.Y + sin * axis.Z, t * axis.Y * axis.Y + cos, t * axis.Y * axis.Z - sin * axis.X, 0.0f);
result.Row2 = new Vector4(t * axis.X * axis.Z - sin * axis.Y, t * axis.Y * axis.Z + sin * axis.X, t * axis.Z * axis.Z + cos, 0.0f);
result.Row3 = Vector4.UnitW;
return result;
}
///
/// Build a rotation matrix from a quaternion
///
/// the quaternion
/// A rotation matrix
public static Matrix4 Rotate(Quaternion q)
{
Vector3 axis;
float angle;
q.ToAxisAngle(out axis, out angle);
return Rotate(axis, angle);
}
#endregion
#region Camera Helper Functions
///
/// Build a world space to camera space matrix
///
/// Eye (camera) position in world space
/// Target position in world space
/// Up vector in world space (should not be parallel to the camera direction, that is target - eye)
/// A Matrix that transforms world space to camera space
public static Matrix4 LookAt(Vector3 eye, Vector3 target, Vector3 up)
{
Vector3 z = Vector3.Normalize(eye - target);
Vector3 x = Vector3.Normalize(Vector3.Cross(up, z));
Vector3 y = Vector3.Normalize(Vector3.Cross(z, x));
Matrix4 rot = new Matrix4(new Vector4(x.X, y.X, z.X, 0.0f),
new Vector4(x.Y, y.Y, z.Y, 0.0f),
new Vector4(x.Z, y.Z, z.Z, 0.0f),
Vector4.UnitW);
Matrix4 trans = Matrix4.Translation(-eye);
return trans * rot;
}
///
/// Build a projection matrix
///
/// Left edge of the view frustum
/// Right edge of the view frustum
/// Bottom edge of the view frustum
/// Top edge of the view frustum
/// Distance to the near clip plane
/// Distance to the far clip plane
/// A projection matrix that transforms camera space to raster space
public static Matrix4 Frustum(float left, float right, float bottom, float top, float near, float far)
{
float invRL = 1.0f / (right - left);
float invTB = 1.0f / (top - bottom);
float invFN = 1.0f / (far - near);
return new Matrix4(new Vector4(2.0f * near * invRL, 0.0f, 0.0f, 0.0f),
new Vector4(0.0f, 2.0f * near * invTB, 0.0f, 0.0f),
new Vector4((right + left) * invRL, (top + bottom) * invTB, -(far + near) * invFN, -1.0f),
new Vector4(0.0f, 0.0f, -2.0f * far * near * invFN, 0.0f));
}
///
/// Build a projection matrix
///
/// Angle of the field of view in the y direction (in radians)
/// Aspect ratio of the view (width / height)
/// Distance to the near clip plane
/// Distance to the far clip plane
/// A projection matrix that transforms camera space to raster space
public static Matrix4 Perspective(float fovy, float aspect, float near, float far)
{
float yMax = near * (float)System.Math.Tan(0.5f * fovy);
float yMin = -yMax;
float xMin = yMin * aspect;
float xMax = yMax * aspect;
return Frustum(xMin, xMax, yMin, yMax, near, far);
}
#endregion
#region Multiply Functions
///
/// Post multiply this matrix by another matrix
///
/// The matrix to multiply
/// A new Matrix44 that is the result of the multiplication
public static Matrix4 Mult(Matrix4 left, Matrix4 right)
{
Vector4 col0 = right.Column0;
Vector4 col1 = right.Column1;
Vector4 col2 = right.Column2;
Vector4 col3 = right.Column3;
left.Row0 = new Vector4(Vector4.Dot(left.Row0, col0), Vector4.Dot(left.Row0, col1), Vector4.Dot(left.Row0, col2), Vector4.Dot(left.Row0, col3));
left.Row1 = new Vector4(Vector4.Dot(left.Row1, col0), Vector4.Dot(left.Row1, col1), Vector4.Dot(left.Row1, col2), Vector4.Dot(left.Row1, col3));
left.Row2 = new Vector4(Vector4.Dot(left.Row2, col0), Vector4.Dot(left.Row2, col1), Vector4.Dot(left.Row2, col2), Vector4.Dot(left.Row2, col3));
left.Row3 = new Vector4(Vector4.Dot(left.Row3, col0), Vector4.Dot(left.Row3, col1), Vector4.Dot(left.Row3, col2), Vector4.Dot(left.Row3, col3));
return left;
}
public static void Mult(ref Matrix4 left, ref Matrix4 right, out Matrix4 result)
{
Vector4 col0 = right.Column0;
Vector4 col1 = right.Column1;
Vector4 col2 = right.Column2;
Vector4 col3 = right.Column3;
result.Row0 = new Vector4(Vector4.Dot(left.Row0, col0), Vector4.Dot(left.Row0, col1), Vector4.Dot(left.Row0, col2), Vector4.Dot(left.Row0, col3));
result.Row1 = new Vector4(Vector4.Dot(left.Row1, col0), Vector4.Dot(left.Row1, col1), Vector4.Dot(left.Row1, col2), Vector4.Dot(left.Row1, col3));
result.Row2 = new Vector4(Vector4.Dot(left.Row2, col0), Vector4.Dot(left.Row2, col1), Vector4.Dot(left.Row2, col2), Vector4.Dot(left.Row2, col3));
result.Row3 = new Vector4(Vector4.Dot(left.Row3, col0), Vector4.Dot(left.Row3, col1), Vector4.Dot(left.Row3, col2), Vector4.Dot(left.Row3, col3));
}
#endregion
#region Invert Functions
///
/// Calculate the inverse of the given matrix
///
/// The matrix to invert
/// The inverse of the given matrix if it has one, or the input if it is singular
/// Thrown if the Matrix4 is singular.
public static Matrix4 Invert(Matrix4 mat)
{
int[] colIdx = { 0, 0, 0, 0 };
int[] rowIdx = { 0, 0, 0, 0 };
int[] pivotIdx = { -1, -1, -1, -1 };
// convert the matrix to an array for easy looping
float[,] inverse = {{mat.Row0.X, mat.Row0.Y, mat.Row0.Z, mat.Row0.W},
{mat.Row1.X, mat.Row1.Y, mat.Row1.Z, mat.Row1.W},
{mat.Row2.X, mat.Row2.Y, mat.Row2.Z, mat.Row2.W},
{mat.Row3.X, mat.Row3.Y, mat.Row3.Z, mat.Row3.W} };
int icol = 0;
int irow = 0;
for (int i = 0; i < 4; i++)
{
// Find the largest pivot value
float maxPivot = 0.0f;
for (int j = 0; j < 4; j++)
{
if (pivotIdx[j] != 0)
{
for (int k = 0; k < 4; ++k)
{
if (pivotIdx[k] == -1)
{
float absVal = System.Math.Abs(inverse[j, k]);
if (absVal > maxPivot)
{
maxPivot = absVal;
irow = j;
icol = k;
}
}
else if (pivotIdx[k] > 0)
{
return mat;
}
}
}
}
++(pivotIdx[icol]);
// Swap rows over so pivot is on diagonal
if (irow != icol)
{
for (int k = 0; k < 4; ++k)
{
float f = inverse[irow, k];
inverse[irow, k] = inverse[icol, k];
inverse[icol, k] = f;
}
}
rowIdx[i] = irow;
colIdx[i] = icol;
float pivot = inverse[icol, icol];
// check for singular matrix
if (pivot == 0.0f)
{
throw new InvalidOperationException("Matrix is singular and cannot be inverted.");
//return mat;
}
// Scale row so it has a unit diagonal
float oneOverPivot = 1.0f / pivot;
inverse[icol, icol] = 1.0f;
for (int k = 0; k < 4; ++k)
inverse[icol, k] *= oneOverPivot;
// Do elimination of non-diagonal elements
for (int j = 0; j < 4; ++j)
{
// check this isn't on the diagonal
if (icol != j)
{
float f = inverse[j, icol];
inverse[j, icol] = 0.0f;
for (int k = 0; k < 4; ++k)
inverse[j, k] -= inverse[icol, k] * f;
}
}
}
for (int j = 3; j >= 0; --j)
{
int ir = rowIdx[j];
int ic = colIdx[j];
for (int k = 0; k < 4; ++k)
{
float f = inverse[k, ir];
inverse[k, ir] = inverse[k, ic];
inverse[k, ic] = f;
}
}
mat.Row0 = new Vector4(inverse[0, 0], inverse[0, 1], inverse[0, 2], inverse[0, 3]);
mat.Row1 = new Vector4(inverse[1, 0], inverse[1, 1], inverse[1, 2], inverse[1, 3]);
mat.Row2 = new Vector4(inverse[2, 0], inverse[2, 1], inverse[2, 2], inverse[2, 3]);
mat.Row3 = new Vector4(inverse[3, 0], inverse[3, 1], inverse[3, 2], inverse[3, 3]);
return mat;
}
#endregion
#region Transpose
///
/// Calculate the transpose of the given matrix
///
/// The matrix to transpose
/// The transpose of the given matrix
public static Matrix4 Transpose(Matrix4 mat)
{
return new Matrix4(mat.Column0, mat.Column1, mat.Column2, mat.Column3);
}
///
/// Calculate the transpose of the given matrix
///
/// The matrix to transpose
public static void Transpose(ref Matrix4 mat, out Matrix4 result)
{
result.Row0 = mat.Column0;
result.Row1 = mat.Column1;
result.Row2 = mat.Column2;
result.Row3 = mat.Column3;
}
#endregion
#endregion
#region public override string ToString()
///
/// Returns a System.String that represents the current Matrix44.
///
///
public override string ToString()
{
return String.Format("{0}\n{1}\n{2}\n{3}", Row0, Row1, Row2, Row3);
}
#endregion
}
}