package edu.nus.cs4243.recon.model.linalg;
/**
* Matrix class for 3d graphics. See respective man pages.
*
* @author johannes
*/
public class Matrix4 {
private double[] m = new double[16];
public Matrix4() {
}
/**
* <p>
* replace the matrix with the identity matrix.
* </p>
* <p>
* <code>loadIdentity</code> replaces the matrix with the identity matrix:
* </p>
*
* <pre>
* 1 0 0 0
* 0 1 0 0
* 0 0 1 0
* 0 0 0 1
* </pre>
*/
public void loadIdentity() {
for (int i = 0; i < 16; i++) {
int row = i / 4;
int col = i % 4;
m[i] = row == col ? 1 : 0;
}
}
/**
* <p>
* multiply the matrix by a translation matrix.
* </p>
* <p>
* <code>translate</code> produces a translation by (x,y,z). The matrix is multiplied by this
* translation matrix, with the product replacing the matrix, as if <code>multMatrix</code> were
* called with the following matrix for its argument:
* </p>
*
* <pre>
* 1 0 0 x
* 0 1 0 y
* 0 0 1 z
* 0 0 0 1
* </pre>
*
* This function performs 9 adds and 9 muls.
*
* @param x
* Specifies the x coordinate of a translation vector.
* @param y
* Specifies the x coordinate of a translation vector.
* @param z
* Specifies the x coordinate of a translation vector.
*/
public void translate(double x, double y, double z) {
for (int i = 0; i <= 8; i += 4) {
m[i + 3] += m[i] * x + m[i + 1] * y + m[i + 2] * z;
}
}
/**
* <p>
* define a viewing transformation.
* </p>
* <p>
* lookAt creates a viewing matrix derived from an eye point, a reference point indicating the
* center of the scene, and an UP vector.
* </p>
* <p>
* The matrix maps the reference point to the negative z axis and the eye point to the origin.
* When a typical projection matrix is used, the center of the scene therefore maps to the
* center of the viewport. Similarly, the direction described by the UP vector projected onto
* the viewing plane is mapped to the positive y axis so that it points upward in the viewport.
* The UP vector must not be parallel to the line of sight from the eye point to the reference
* point.
* </p>
* <p>
* Let
*
* <pre>
* centerX - eyeX
* F = centerY - eyeY
* centerZ - eyeZ
* </pre>
*
* </p>
* <p>
* Let UP be the vector (upX, upY, upZ).
* </p>
* <p>
* Then normalize as follows: <code>f = F/ || F ||</code>
* </p>
* <p>
* <code>UP' = UP/|| UP ||</code>
* </p>
* <p>
* Finally, let <code>s = f X UP'</code>, and <code>u = s X f</code>.
* </p>
* <p>
* M is then constructed as follows:
* </p>
*
* <pre>
* s[0] s[1] s[2] 0
* u[0] u[1] u[2] 0
* M = -f[0] -f[1] -f[2] 0
* 0 0 0 1
* </pre>
*
* and lookAt is equivalent to
*
* <pre>
* multMatrixf(M);
* translate(-eyex, -eyey, -eyez);
* </pre>
*
* @param eyeX
* Specifies the x coordinate of the eye.
* @param eyeY
* Specifies the y coordinate of the eye.
* @param eyeZ
* Specifies the z coordinate of the eye.
* @param centerX
* Specifies the x coordinate of the point that the camera looks at.
* @param centerY
* Specifies the y coordinate of the point that the camera looks at.
* @param centerZ
* Specifies the z coordinate of the point that the camera looks at.
* @param upX
* Specifies the x coordinate of the up vector.
* @param upY
* Specifies the y coordinate of the up vector.
* @param upZ
* Specifies the z coordinate of the up vector.
*/
public void lookAt(double eyeX, double eyeY, double eyeZ, double centerX, double centerY,
double centerZ, double upX, double upY, double upZ) {
Vector3 f = new Vector3(centerX - eyeX, centerY - eyeY, centerZ - eyeZ), up = new Vector3(
upX, upY, upZ);
f.normalize();
up.normalize();
Vector3 s = f.cross(up, new Vector3()), u = s.cross(f, new Vector3());
s.normalize();
u.normalize();
for (int i = 0; i <= 12; i += 4) {
double temp1 = m[i], temp2 = m[i + 1], temp3 = m[i + 2];
m[i + 0] = temp1 * s.x + temp2 * u.x - temp3 * f.x;
m[i + 1] = temp1 * s.y + temp2 * u.y - temp3 * f.y;
m[i + 2] = temp1 * s.z + temp2 * u.z - temp3 * f.z;
}
translate(-eyeX, -eyeY, -eyeZ);
}
/**
* <p>
* set up a perspective projection matrix
* </p>
* <p>
* <code>perspective</code> specifies a viewing frustum into the world coordinate system. In
* general, the aspect ratio in <code>perspective</code> should match the aspect ratio of the
* associated viewport. For example, <code>aspect = 2.0</code> means the viewer's angle of view
* is twice as wide in x as it is in y. If the viewport is twice as wide as it is tall, it
* displays the image without distortion.
* </p>
* <p>
* The matrix generated by perspective is multipled by the current matrix, just as if
* <code>multMatrix</code> were called with the generated matrix. To load the perspective matrix
* onto the current matrix stack instead, precede the call to <code>perspective</code> with a
* call to <code>loadIdentity</code>.
* <p/p>
* <p>
* Given f defined as follows:
* </p>
*
* <pre>
* f = cotangent(fovy / 2)
* </pre>
* <p>
* The generated matrix is
* </p>
*
* <pre>
* f
* ------------ 0 0 0
* aspect
*
* 0 f 0 0
*
* zFar+zNear 2*zFar*zNear
* 0 0 ---------- ------------
* zNear-zFar zNear-zFar
*
* 0 0 -1 0
* </pre>
*
* Note: Depth buffer precision is affected by the values specified for <code>zNear</code> and
* <code>zFar</code>. The greater the ratio of <code>zFar</code> to <code>zNear</code> is, the
* less effective the depth buffer will be at distinguishing between surfaces that are near each
* other. If
*
* <pre>
* r = zFar / zNear
* </pre>
*
* roughly <code>log2(r)</Code> bits of depth buffer precision are lost. Because <code>r</code>
* approaches infinity as <code>zNear</code> approaches 0, <code>zNear</code> must never be set
* to 0.
*
* @param fovy
* Specifies the field of view angle (radian), in the y direction.
* @param aspect
* Specifies the aspect ratio that determines the field of view in the x direction.
* The aspect ratio is the ratio of x (width) to y (height).
* @param zNear
* Specifies the distance from the viewer to the near clipping plane (always
* positive).
* @param zFar
* Specifies the distance from the viewer to the far clipping plane (always
* positive).
*/
public void perspective(double fovy, double aspect, double zNear, double zFar) {
double m33 = (zFar + zNear) / (zNear - zFar);
double m34 = 2 * zFar * zNear / (zNear - zFar);
double f = 1 / Math.tan(fovy / 2);
double fa = f / aspect;
for (int i = 0; i <= 12; i += 4) {
m[i + 0] *= fa;
m[i + 1] *= f;
double temp3 = m[i + 2];
m[i + 2] = temp3 * m33 - m[i + 3];
m[i + 3] = temp3 * m34;
}
}
/**
* Calculate <code>v = this * v</code>
*
* @param v
* the vector to multiply this matrix with
*/
public void mul(Vector4 v) {
double x = v.x, y = v.y, z = v.z, w = v.w;
v.x = m[0] * x + m[1] * y + m[2] * z + m[3] * w;
v.y = m[4] * x + m[5] * y + m[6] * z + m[7] * w;
v.z = m[8] * x + m[9] * y + m[10] * z + m[11] * w;
v.w = m[12] * x + m[13] * y + m[14] * z + m[15] * w;
}
}