package org.sunflow.math;
/**
* This class is used to represent general affine transformations in 3D. The
* bottom row of the matrix is assumed to be [0,0,0,1]. Note that the rotation
* matrices assume a right-handed convention.
*/
public final class Matrix4 {
// matrix elements, m(row,col)
private float m00;
private float m01;
private float m02;
private float m03;
private float m10;
private float m11;
private float m12;
private float m13;
private float m20;
private float m21;
private float m22;
private float m23;
// usefull constant matrices
public static final Matrix4 ZERO = new Matrix4();
public static final Matrix4 IDENTITY = Matrix4.scale(1);
/**
* Creates an empty matrix. All elements are 0.
*/
private Matrix4() {
}
/**
* Creates a matrix with the specified elements
*
* @param m00 value at row 0, col 0
* @param m01 value at row 0, col 1
* @param m02 value at row 0, col 2
* @param m03 value at row 0, col 3
* @param m10 value at row 1, col 0
* @param m11 value at row 1, col 1
* @param m12 value at row 1, col 2
* @param m13 value at row 1, col 3
* @param m20 value at row 2, col 0
* @param m21 value at row 2, col 1
* @param m22 value at row 2, col 2
* @param m23 value at row 2, col 3
*/
public Matrix4(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23) {
this.m00 = m00;
this.m01 = m01;
this.m02 = m02;
this.m03 = m03;
this.m10 = m10;
this.m11 = m11;
this.m12 = m12;
this.m13 = m13;
this.m20 = m20;
this.m21 = m21;
this.m22 = m22;
this.m23 = m23;
}
/**
* Initialize a matrix from the specified 16 element array. The matrix may
* be given in row or column major form.
*
* @param m a 16 element array in row or column major form
* @param rowMajor true
if the array is in row major form,
* false
if it is in column major form
*/
public Matrix4(float[] m, boolean rowMajor) {
if (rowMajor) {
m00 = m[0];
m01 = m[1];
m02 = m[2];
m03 = m[3];
m10 = m[4];
m11 = m[5];
m12 = m[6];
m13 = m[7];
m20 = m[8];
m21 = m[9];
m22 = m[10];
m23 = m[11];
if (m[12] != 0 || m[13] != 0 || m[14] != 0 || m[15] != 1) {
throw new RuntimeException(String.format("Matrix is not affine! Bottom row is: [%.3f, %.3f, %.3f, %.3f]", m[12], m[13], m[14], m[15]));
}
} else {
m00 = m[0];
m01 = m[4];
m02 = m[8];
m03 = m[12];
m10 = m[1];
m11 = m[5];
m12 = m[9];
m13 = m[13];
m20 = m[2];
m21 = m[6];
m22 = m[10];
m23 = m[14];
if (m[3] != 0 || m[7] != 0 || m[11] != 0 || m[15] != 1) {
throw new RuntimeException(String.format("Matrix is not affine! Bottom row is: [%.3f, %.3f, %.3f, %.3f]", m[12], m[13], m[14], m[15]));
}
}
}
public final boolean isIndentity() {
return equals(IDENTITY);
}
public final boolean equals(Matrix4 m) {
if (m == null) {
return false;
}
if (this == m) {
return true;
}
return m00 == m.m00 && m01 == m.m01 && m02 == m.m02 && m03 == m.m03 && m10 == m.m10 && m11 == m.m11 && m12 == m.m12 && m13 == m.m13 && m20 == m.m20 && m21 == m.m21 && m22 == m.m22 && m23 == m.m23;
}
public final float[] asRowMajor() {
return new float[]{m00, m01, m02, m03, m10, m11, m12, m13, m20, m21,
m22, m23, 0, 0, 0, 1};
}
public final float[] asColMajor() {
return new float[]{m00, m10, m20, 0, m01, m11, m21, 0, m02, m12, m22,
0, m03, m13, m23, 1};
}
/**
* Compute the matrix determinant.
*
* @return determinant of this matrix
*/
public final float determinant() {
float A0 = m00 * m11 - m01 * m10;
float A1 = m00 * m12 - m02 * m10;
float A3 = m01 * m12 - m02 * m11;
return A0 * m22 - A1 * m21 + A3 * m20;
}
/**
* Compute the inverse of this matrix and return it as a new object. If the
* matrix is not invertible,
* null
is returned.
*
* @return the inverse of this matrix, or null
if not
* invertible
*/
public final Matrix4 inverse() {
float A0 = m00 * m11 - m01 * m10;
float A1 = m00 * m12 - m02 * m10;
float A3 = m01 * m12 - m02 * m11;
float det = A0 * m22 - A1 * m21 + A3 * m20;
if (Math.abs(det) < 1e-12f) {
return null; // matrix is not invertible
}
float invDet = 1 / det;
float A2 = m00 * m13 - m03 * m10;
float A4 = m01 * m13 - m03 * m11;
float A5 = m02 * m13 - m03 * m12;
Matrix4 inv = new Matrix4();
inv.m00 = (+m11 * m22 - m12 * m21) * invDet;
inv.m10 = (-m10 * m22 + m12 * m20) * invDet;
inv.m20 = (+m10 * m21 - m11 * m20) * invDet;
inv.m01 = (-m01 * m22 + m02 * m21) * invDet;
inv.m11 = (+m00 * m22 - m02 * m20) * invDet;
inv.m21 = (-m00 * m21 + m01 * m20) * invDet;
inv.m02 = +A3 * invDet;
inv.m12 = -A1 * invDet;
inv.m22 = +A0 * invDet;
inv.m03 = (-m21 * A5 + m22 * A4 - m23 * A3) * invDet;
inv.m13 = (+m20 * A5 - m22 * A2 + m23 * A1) * invDet;
inv.m23 = (-m20 * A4 + m21 * A2 - m23 * A0) * invDet;
return inv;
}
/**
* Computes this*m and return the result as a new Matrix4
*
* @param m right hand side of the multiplication
* @return a new Matrix4 object equal to this*m
*/
public final Matrix4 multiply(Matrix4 m) {
// matrix multiplication is m[r][c] = (row[r]).(col[c])
float rm00 = m00 * m.m00 + m01 * m.m10 + m02 * m.m20;
float rm01 = m00 * m.m01 + m01 * m.m11 + m02 * m.m21;
float rm02 = m00 * m.m02 + m01 * m.m12 + m02 * m.m22;
float rm03 = m00 * m.m03 + m01 * m.m13 + m02 * m.m23 + m03;
float rm10 = m10 * m.m00 + m11 * m.m10 + m12 * m.m20;
float rm11 = m10 * m.m01 + m11 * m.m11 + m12 * m.m21;
float rm12 = m10 * m.m02 + m11 * m.m12 + m12 * m.m22;
float rm13 = m10 * m.m03 + m11 * m.m13 + m12 * m.m23 + m13;
float rm20 = m20 * m.m00 + m21 * m.m10 + m22 * m.m20;
float rm21 = m20 * m.m01 + m21 * m.m11 + m22 * m.m21;
float rm22 = m20 * m.m02 + m21 * m.m12 + m22 * m.m22;
float rm23 = m20 * m.m03 + m21 * m.m13 + m22 * m.m23 + m23;
return new Matrix4(rm00, rm01, rm02, rm03, rm10, rm11, rm12, rm13, rm20, rm21, rm22, rm23);
}
/**
* Transforms each corner of the specified axis-aligned bounding box and
* returns a new bounding box which incloses the transformed corners.
*
* @param b original bounding box
* @return a new BoundingBox object which encloses the transform version of
* b
*/
public final BoundingBox transform(BoundingBox b) {
if (b.isEmpty()) {
return new BoundingBox();
}
// special case extreme corners
BoundingBox rb = new BoundingBox(transformP(b.getMinimum()));
rb.include(transformP(b.getMaximum()));
// do internal corners
for (int i = 1; i < 7; i++) {
rb.include(transformP(b.getCorner(i)));
}
return rb;
}
/**
* Computes this*v and returns the result as a new Vector3 object. This
* method assumes the bottom row of the matrix is
* [0,0,0,1]
.
*
* @param v vector to multiply
* @return a new Vector3 object equal to this*v
*/
public final Vector3 transformV(Vector3 v) {
Vector3 rv = new Vector3();
rv.x = m00 * v.x + m01 * v.y + m02 * v.z;
rv.y = m10 * v.x + m11 * v.y + m12 * v.z;
rv.z = m20 * v.x + m21 * v.y + m22 * v.z;
return rv;
}
/**
* Computes (this^T)*v and returns the result as a new Vector3 object. This
* method assumes the bottom row of the matrix is
* [0,0,0,1]
.
*
* @param v vector to multiply
* @return a new Vector3 object equal to (this^T)*v
*/
public final Vector3 transformTransposeV(Vector3 v) {
Vector3 rv = new Vector3();
rv.x = m00 * v.x + m10 * v.y + m20 * v.z;
rv.y = m01 * v.x + m11 * v.y + m21 * v.z;
rv.z = m02 * v.x + m12 * v.y + m22 * v.z;
return rv;
}
/**
* Computes this*p and returns the result as a new Point3 object. This
* method assumes the bottom row of the matrix is
* [0,0,0,1]
.
*
* @param p point to multiply
* @return a new Point3 object equal to this*v
*/
public final Point3 transformP(Point3 p) {
Point3 rp = new Point3();
rp.x = m00 * p.x + m01 * p.y + m02 * p.z + m03;
rp.y = m10 * p.x + m11 * p.y + m12 * p.z + m13;
rp.z = m20 * p.x + m21 * p.y + m22 * p.z + m23;
return rp;
}
/**
* Computes the x component of this*(x,y,z,0).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return x coordinate transformation result
*/
public final float transformVX(float x, float y, float z) {
return m00 * x + m01 * y + m02 * z;
}
/**
* Computes the y component of this*(x,y,z,0).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return y coordinate transformation result
*/
public final float transformVY(float x, float y, float z) {
return m10 * x + m11 * y + m12 * z;
}
/**
* Computes the z component of this*(x,y,z,0).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return z coordinate transformation result
*/
public final float transformVZ(float x, float y, float z) {
return m20 * x + m21 * y + m22 * z;
}
/**
* Computes the x component of (this^T)*(x,y,z,0).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return x coordinate transformation result
*/
public final float transformTransposeVX(float x, float y, float z) {
return m00 * x + m10 * y + m20 * z;
}
/**
* Computes the y component of (this^T)*(x,y,z,0).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return y coordinate transformation result
*/
public final float transformTransposeVY(float x, float y, float z) {
return m01 * x + m11 * y + m21 * z;
}
/**
* Computes the z component of (this^T)*(x,y,z,0).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return zcoordinate transformation result
*/
public final float transformTransposeVZ(float x, float y, float z) {
return m02 * x + m12 * y + m22 * z;
}
/**
* Computes the x component of this*(x,y,z,1).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return x coordinate transformation result
*/
public final float transformPX(float x, float y, float z) {
return m00 * x + m01 * y + m02 * z + m03;
}
/**
* Computes the y component of this*(x,y,z,1).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return y coordinate transformation result
*/
public final float transformPY(float x, float y, float z) {
return m10 * x + m11 * y + m12 * z + m13;
}
/**
* Computes the z component of this*(x,y,z,1).
*
* @param x x coordinate of the vector to multiply
* @param y y coordinate of the vector to multiply
* @param z z coordinate of the vector to multiply
* @return z coordinate transformation result
*/
public final float transformPZ(float x, float y, float z) {
return m20 * x + m21 * y + m22 * z + m23;
}
/**
* Create a translation matrix for the specified vector.
*
* @param x x component of translation
* @param y y component of translation
* @param z z component of translation
* @return a new Matrix4 object representing the translation
*/
public final static Matrix4 translation(float x, float y, float z) {
Matrix4 m = new Matrix4();
m.m00 = m.m11 = m.m22 = 1;
m.m03 = x;
m.m13 = y;
m.m23 = z;
return m;
}
/**
* Creates a rotation matrix about the X axis.
*
* @param theta angle to rotate about the X axis in radians
* @return a new Matrix4 object representing the rotation
*/
public final static Matrix4 rotateX(float theta) {
Matrix4 m = new Matrix4();
float s = (float) Math.sin(theta);
float c = (float) Math.cos(theta);
m.m00 = 1;
m.m11 = m.m22 = c;
m.m12 = -s;
m.m21 = +s;
return m;
}
/**
* Creates a rotation matrix about the Y axis.
*
* @param theta angle to rotate about the Y axis in radians
* @return a new Matrix4 object representing the rotation
*/
public final static Matrix4 rotateY(float theta) {
Matrix4 m = new Matrix4();
float s = (float) Math.sin(theta);
float c = (float) Math.cos(theta);
m.m11 = 1;
m.m00 = m.m22 = c;
m.m02 = +s;
m.m20 = -s;
return m;
}
/**
* Creates a rotation matrix about the Z axis.
*
* @param theta angle to rotate about the Z axis in radians
* @return a new Matrix4 object representing the rotation
*/
public final static Matrix4 rotateZ(float theta) {
Matrix4 m = new Matrix4();
float s = (float) Math.sin(theta);
float c = (float) Math.cos(theta);
m.m22 = 1;
m.m00 = m.m11 = c;
m.m01 = -s;
m.m10 = +s;
return m;
}
/**
* Creates a rotation matrix about the specified axis. The axis vector need
* not be normalized.
*
* @param x x component of the axis vector
* @param y y component of the axis vector
* @param z z component of the axis vector
* @param theta angle to rotate about the axis in radians
* @return a new Matrix4 object representing the rotation
*/
public final static Matrix4 rotate(float x, float y, float z, float theta) {
Matrix4 m = new Matrix4();
float invLen = 1 / (float) Math.sqrt(x * x + y * y + z * z);
x *= invLen;
y *= invLen;
z *= invLen;
float s = (float) Math.sin(theta);
float c = (float) Math.cos(theta);
float t = 1 - c;
m.m00 = t * x * x + c;
m.m11 = t * y * y + c;
m.m22 = t * z * z + c;
float txy = t * x * y;
float sz = s * z;
m.m01 = txy - sz;
m.m10 = txy + sz;
float txz = t * x * z;
float sy = s * y;
m.m02 = txz + sy;
m.m20 = txz - sy;
float tyz = t * y * z;
float sx = s * x;
m.m12 = tyz - sx;
m.m21 = tyz + sx;
return m;
}
/**
* Create a uniform scaling matrix.
*
* @param s scale factor for all three axes
* @return a new Matrix4 object representing the uniform scale
*/
public final static Matrix4 scale(float s) {
Matrix4 m = new Matrix4();
m.m00 = m.m11 = m.m22 = s;
return m;
}
/**
* Creates a non-uniform scaling matrix.
*
* @param sx scale factor in the x dimension
* @param sy scale factor in the y dimension
* @param sz scale factor in the z dimension
* @return a new Matrix4 object representing the non-uniform scale
*/
public final static Matrix4 scale(float sx, float sy, float sz) {
Matrix4 m = new Matrix4();
m.m00 = sx;
m.m11 = sy;
m.m22 = sz;
return m;
}
/**
* Creates a rotation matrix from an OrthonormalBasis.
*
* @param basis
* @return Matrix4
*/
public final static Matrix4 fromBasis(OrthoNormalBasis basis) {
Matrix4 m = new Matrix4();
Vector3 u = basis.transform(new Vector3(1, 0, 0));
Vector3 v = basis.transform(new Vector3(0, 1, 0));
Vector3 w = basis.transform(new Vector3(0, 0, 1));
m.m00 = u.x;
m.m01 = v.x;
m.m02 = w.x;
m.m10 = u.y;
m.m11 = v.y;
m.m12 = w.y;
m.m20 = u.z;
m.m21 = v.z;
m.m22 = w.z;
return m;
}
/**
* Creates a camera positioning matrix from the given eye and target points
* and up vector.
*
* @param eye location of the eye
* @param target location of the target
* @param up vector pointing upwards
* @return
*/
public final static Matrix4 lookAt(Point3 eye, Point3 target, Vector3 up) {
Matrix4 m = Matrix4.fromBasis(OrthoNormalBasis.makeFromWV(Point3.sub(eye, target, new Vector3()), up));
return Matrix4.translation(eye.x, eye.y, eye.z).multiply(m);
}
public final static Matrix4 blend(Matrix4 m0, Matrix4 m1, float t) {
Matrix4 m = new Matrix4();
m.m00 = (1 - t) * m0.m00 + t * m1.m00;
m.m01 = (1 - t) * m0.m01 + t * m1.m01;
m.m02 = (1 - t) * m0.m02 + t * m1.m02;
m.m03 = (1 - t) * m0.m03 + t * m1.m03;
m.m10 = (1 - t) * m0.m10 + t * m1.m10;
m.m11 = (1 - t) * m0.m11 + t * m1.m11;
m.m12 = (1 - t) * m0.m12 + t * m1.m12;
m.m13 = (1 - t) * m0.m13 + t * m1.m13;
m.m20 = (1 - t) * m0.m20 + t * m1.m20;
m.m21 = (1 - t) * m0.m21 + t * m1.m21;
m.m22 = (1 - t) * m0.m22 + t * m1.m22;
m.m23 = (1 - t) * m0.m23 + t * m1.m23;
return m;
}
}