"use strict"; /** * overwrites a 4x4 matrix with the identity matrix */ function mat4Identity(mat) { for (let i = 0; i < 16; i++) { mat[i] = (i % 5 == 0) ? 1 : 0; } } /** * copies a mat4 from src to dst */ function mat4Copy(src, dst) { for (let i = 0; i < 16; i++) { dst[i] = src[i]; } } /** * sets all the values in a mat4 to zero */ function mat4Empty(mat) { for (let i = 0; i < 16; i++) { mat[i] = 0; } } /** * mutliplies A with B and stores the result in result */ function mat4Multiply(result, A, B) { // if result is one of the arguments, modify copy instead of result directly if (result == A || result == B) { let tempResult = []; mat4Multiply(tempResult, A, B); mat4Copy(tempResult, result); return; } // loops over cells of the result matrix for (let i = 0; i < 16; i++) { let col = (i / 4) | 0; let row = i % 4; // initialise current cell with 0 result[i] = 0; // loop over the row of A and column of B // continuously adding the multiplication of the two values to the result cell for (let j = 0; j < 4; j++) { result[i] += A[row + j * 4] * B[j + col * 4]; } } } /** * prints a mat4 to the screen */ function mat4Print(m) { let str = ""; for (let i = 0; i < 4; i++) { for (let j = 0; j < 4; j++) { str += m[j * 4 + i] + " "; } str += "\n"; } console.log(str); } /** * translates by the vector v */ function mat4Translate(out, inm, v) { let t = []; mat4Identity(t); t[12] = v[0]; t[13] = v[1]; t[14] = v[2]; mat4Multiply(out, t, inm); } /** * scales by the vector v */ function mat4Scale(out, inm, v) { let t = []; mat4Identity(t); t[0] = v[0]; t[5] = v[1]; t[10] = v[2]; mat4Multiply(out, t, inm); } /** * rotates around the X axis by the angle a (in radians) */ function mat4RotateX(out, inm, a) { let t = []; mat4Identity(t); t[5] = Math.cos(a); t[6] = Math.sin(a); t[9] = -Math.sin(a); t[10] = Math.cos(a); mat4Multiply(out, t, inm); } /** * rotates around the Y axis by the angle a (in radians) */ function mat4RotateY(out, inm, a) { let t = []; mat4Identity(t); t[0] = Math.cos(a); t[2] = -Math.sin(a); t[8] = Math.sin(a); t[10] = Math.cos(a); mat4Multiply(out, t, inm); } /** * rotates around the Z axis by the angle a (in radians) */ function mat4RotateZ(out, inm, a) { let t = []; mat4Identity(t); t[0] = Math.cos(a); t[1] = Math.sin(a); t[4] = -Math.sin(a); t[5] = Math.cos(a); mat4Multiply(out, t, inm); } /** * builds a look-at matrix, overwriting out * eye is the position of the camera * look is the position of the target to be looked at * up is the up vector */ function mat4BuildLookAt(out, eye, look, up) { let n = []; let u = []; let v = []; let t = []; vec3Subtract(n, eye, look); vec3CrossProduct(u, up, n); vec3CrossProduct(v, n, u); vec3Normalise(n, n); vec3Normalise(u, u); vec3Normalise(v, v); t[0] = - vec3DotProduct(u, eye); t[1] = - vec3DotProduct(v, eye); t[2] = - vec3DotProduct(n, eye); out[0] = u[0]; out[4] = u[1]; out[8] = u[2]; out[12] = t[0]; out[1] = v[0]; out[5] = v[1]; out[9] = v[2]; out[13] = t[1]; out[2] = n[0]; out[6] = n[1]; out[10] = n[2]; out[14] = t[2]; out[3] = 0; out[7] = 0; out[11] = 0; out[15] = 1; } /** * builds a projection matrix, overwriting out * r, l, t, b are the right, left, top and bottom of the frustum * n and f are the distance of the near and far planes from the camera */ function mat4BuildProjection(out, r, l, t, b, n, f) { mat4Identity(out); out[0] = 2.0 / (r - l); out[5] = 2.0 / (t - b); out[8] = 1.0 / n * (r + l) / (r - l); out[9] = 1.0 / n * (t + b) / (t - b); out[10] = -1.0 / n * (f + n) / (f - n); out[11] = -1.0 / n; out[14] = - 2.0 * f / (f - n); out[15] = 0.0; } /** * builds a perspective projection matrix, overwriting out * fovy is the field of view in the y direction * aspect is the aspect ratio * n and f are the distance of the near and far planes from the camera */ function mat4BuildPerspective(out, fovy, aspect, n, f) { let t = n * Math.tan(0.5 * fovy); let b = -t; let r = aspect * t; let l = -r; mat4BuildProjection(out, r, l, t, b, n, f); } /** * linearly interpolates between a and b, storing the result in out */ function mat4Interpolate(out, a, b, f) { for (let i = 0; i < 16; i++) { out[i] = (1 - f) * a[i] + f * b[i]; } } function mat4From3(out, inm) { mat4Identity(out); out[0] = inm[0]; out[1] = inm[1]; out[2] = inm[2]; out[4] = inm[3]; out[5] = inm[4]; out[6] = inm[5]; out[8] = inm[6]; out[9] = inm[7]; out[10] = inm[8]; } /** * https://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix */ function mat4Inverse(out, m) { let inv = []; let det; let i; inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; if (det == 0) { mat4Copy(m, out); // singular matrix, can't invert return; } det = 1.0 / det; for (i = 0; i < 16; i++) out[i] = inv[i] * det; return; } /** * subtracts b from a, storing the result in out */ function vec3Subtract(out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; } /** * calculates the cross product of a and b, storing the result in out */ function vec3CrossProduct(out, a, b) { let result = []; result[0] = a[1] * b[2] - a[2] * b[1]; result[1] = a[2] * b[0] - a[0] * b[2]; result[2] = a[0] * b[1] - a[1] * b[0]; out[0] = result[0]; out[1] = result[1]; out[2] = result[2]; } /** * normalizes in storing the result in out */ function vec3Normalise(out, inv) { let length = vec3Length(inv); out[0] = inv[0] / length; out[1] = inv[1] / length; out[2] = inv[2] / length; } /** * returns the length of the vector in */ function vec3Length(inv) { return Math.sqrt(inv[0] * inv[0] + inv[1] * inv[1] + inv[2] * inv[2]); } /** * returns the dot product of the vectors a and b */ function vec3DotProduct(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } /** * sets the values of out to x, y and z */ function vec3Set(out, x, y, z) { out[0] = x; out[1] = y; out[2] = z; } function mat3Copy(src, dst) { for (let i = 0; i < 9; i++) { dst[i] = src[i]; } } function mat3From4(out, inm) { out[0] = inm[0]; out[1] = inm[1]; out[2] = inm[2]; out[3] = inm[4]; out[4] = inm[5]; out[5] = inm[6]; out[6] = inm[8]; out[7] = inm[9]; out[8] = inm[10]; } function mat3Transpose(out, inm) { let result = []; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { result[i * 3 + j] = inm[j * 3 + i]; } } mat3Copy(result, out); } function mat3Minor(out, inm) { let result = []; // TODO: check if this is correct result[0] = inm[4] * inm[8] - inm[5] * inm[7]; result[1] = inm[3] * inm[8] - inm[5] * inm[6]; result[2] = inm[3] * inm[7] - inm[4] * inm[6]; result[3] = inm[1] * inm[8] - inm[2] * inm[7]; result[4] = inm[0] * inm[8] - inm[2] * inm[6]; result[5] = inm[0] * inm[7] - inm[1] * inm[6]; result[6] = inm[1] * inm[5] - inm[2] * inm[4]; result[7] = inm[0] * inm[5] - inm[2] * inm[3]; result[8] = inm[0] * inm[4] - inm[1] * inm[3]; mat3Copy(result, out); } function mat3Cofactor(out, inm) { mat3Minor(out, inm); out[1] *= -1; out[3] *= -1; out[5] *= -1; out[7] *= -1; } function mat3Adjoint(out, inm) { mat3Cofactor(out, inm); mat3Transpose(out, inm); } function mat3MultiplyScalar(out, inm, x) { for (let i = 0; i < 9; i++) { out[i] = inm[i] * x; } } function mat3Determinant(m) { return + m[0] * m[4] * m[8] + m[3] * m[7] * m[2] + m[6] * m[1] * m[5] - m[2] * m[4] * m[6] - m[5] * m[7] * m[0] - m[8] * m[1] * m[3]; } function mat3Inverse(out, inm) { let result = []; mat3Adjoint(result, inm); mat3MultiplyScalar(result, result, 1 / mat3Determinant(inm)); mat3Copy(result, out); }