Home Reference Source

src/Matrix4.js

import { RotationOrder } from "./RotationOrder.js";
import { Vector3 } from "./Vector3.js";

/**
 * A vector.
 *
 * @type {Vector3}
 * @private
 */

const a = new Vector3();

/**
 * A vector.
 *
 * @type {Vector3}
 * @private
 */

const b = new Vector3();

/**
 * A vector.
 *
 * @type {Vector3}
 * @private
 */

const c = new Vector3();

/**
 * A 4x4 matrix.
 */

export class Matrix4 {

	/**
	 * Constructs a new matrix.
	 */

	constructor() {

		/**
		 * The matrix elements.
		 *
		 * @type {Float32Array}
		 */

		this.elements = new Float32Array([

			1, 0, 0, 0,
			0, 1, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1

		]);

	}

	/**
	 * Sets the values of this matrix.
	 *
	 * @param {Number} n00 - The value of the first row, first column.
	 * @param {Number} n01 - The value of the first row, second column.
	 * @param {Number} n02 - The value of the first row, third column.
	 * @param {Number} n03 - The value of the first row, fourth column.
	 * @param {Number} n10 - The value of the second row, first column.
	 * @param {Number} n11 - The value of the second row, second column.
	 * @param {Number} n12 - The value of the second row, third column.
	 * @param {Number} n13 - The value of the second row, fourth column.
	 * @param {Number} n20 - The value of the third row, first column.
	 * @param {Number} n21 - The value of the third row, second column.
	 * @param {Number} n22 - The value of the third row, third column.
	 * @param {Number} n23 - The value of the third row, fourth column.
	 * @param {Number} n30 - The value of the fourth row, first column.
	 * @param {Number} n31 - The value of the fourth row, second column.
	 * @param {Number} n32 - The value of the fourth row, third column.
	 * @param {Number} n33 - The value of the fourth row, fourth column.
	 * @return {Matrix4} This matrix.
	 */

	set(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33) {

		const te = this.elements;

		te[0] = n00; te[4] = n01; te[8] = n02; te[12] = n03;
		te[1] = n10; te[5] = n11; te[9] = n12; te[13] = n13;
		te[2] = n20; te[6] = n21; te[10] = n22; te[14] = n23;
		te[3] = n30; te[7] = n31; te[11] = n32; te[15] = n33;

		return this;

	}

	/**
	 * Sets this matrix to the identity matrix.
	 *
	 * @return {Matrix4} This matrix.
	 */

	identity() {

		this.set(

			1, 0, 0, 0,
			0, 1, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Copies the values of a given matrix.
	 *
	 * @param {Matrix4} matrix - A matrix.
	 * @return {Matrix4} This matrix.
	 */

	copy(matrix) {

		const me = matrix.elements;
		const te = this.elements;

		te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3];
		te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7];
		te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11];
		te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15];

		return this;

	}

	/**
	 * Clones this matrix.
	 *
	 * @return {Matrix4} A clone of this matrix.
	 */

	clone() {

		return new this.constructor().fromArray(this.elements);

	}

	/**
	 * Copies the values of a given array.
	 *
	 * @param {Number[]} array - An array.
	 * @param {Number} [offset=0] - An offset into the array.
	 * @return {Matrix4} This matrix.
	 */

	fromArray(array, offset = 0) {

		const te = this.elements;

		let i;

		for(i = 0; i < 16; ++i) {

			te[i] = array[i + offset];

		}

		return this;

	}

	/**
	 * Stores this matrix in an array.
	 *
	 * @param {Number[]} [array] - A target array.
	 * @param {Number} [offset=0] - An offset into the array.
	 * @return {Number[]} The array.
	 */

	toArray(array = [], offset = 0) {

		const te = this.elements;

		let i;

		for(i = 0; i < 16; ++i) {

			array[i + offset] = te[i];

		}

		return array;

	}

	/**
	 * Returns the largest scale.
	 *
	 * @return {Number} The largest scale of the three axes.
	 */

	getMaxScaleOnAxis() {

		const te = this.elements;

		const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2];
		const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6];
		const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10];

		return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq));

	}

	/**
	 * Copies the position values of a given matrix.
	 *
	 * @param {Matrix4} matrix - A matrix.
	 * @return {Matrix4} This matrix.
	 */

	copyPosition(matrix) {

		const te = this.elements;
		const me = matrix.elements;

		te[12] = me[12];
		te[13] = me[13];
		te[14] = me[14];

		return this;

	}

	/**
	 * Sets the position values of this matrix.
	 *
	 * @param {Vector3} p - A position.
	 * @return {Matrix4} This matrix.
	 */

	setPosition(p) {

		const te = this.elements;

		te[12] = p.x;
		te[13] = p.y;
		te[14] = p.z;

		return this;

	}

	/**
	 * Extracts the basis from this matrix.
	 *
	 * @param {Vector3} xAxis - A vector to store the X-axis column in.
	 * @param {Vector3} yAxis - A vector to store the Y-axis column in.
	 * @param {Vector3} zAxis - A vector to store the Z-axis column in.
	 * @return {Matrix4} This matrix.
	 */

	extractBasis(xAxis, yAxis, zAxis) {

		xAxis.setFromMatrixColumn(this, 0);
		yAxis.setFromMatrixColumn(this, 1);
		zAxis.setFromMatrixColumn(this, 2);

		return this;

	}

	/**
	 * Sets the basis of this matrix.
	 *
	 * @param {Vector3} xAxis - The X-axis.
	 * @param {Vector3} yAxis - The Y-axis.
	 * @param {Vector3} zAxis - The Z-axis.
	 * @return {Matrix4} This matrix.
	 */

	makeBasis(xAxis, yAxis, zAxis) {

		this.set(

			xAxis.x, yAxis.x, zAxis.x, 0,
			xAxis.y, yAxis.y, zAxis.y, 0,
			xAxis.z, yAxis.z, zAxis.z, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Extracts the rotation from a given matrix.
	 *
	 * This method does not support reflection matrices.
	 *
	 * @param {Matrix4} m - A matrix.
	 * @return {Matrix4} This matrix.
	 */

	extractRotation(m) {

		const te = this.elements;
		const me = m.elements;

		const scaleX = 1.0 / a.setFromMatrixColumn(m, 0).length();
		const scaleY = 1.0 / a.setFromMatrixColumn(m, 1).length();
		const scaleZ = 1.0 / a.setFromMatrixColumn(m, 2).length();

		te[0] = me[0] * scaleX;
		te[1] = me[1] * scaleX;
		te[2] = me[2] * scaleX;
		te[3] = 0;

		te[4] = me[4] * scaleY;
		te[5] = me[5] * scaleY;
		te[6] = me[6] * scaleY;
		te[7] = 0;

		te[8] = me[8] * scaleZ;
		te[9] = me[9] * scaleZ;
		te[10] = me[10] * scaleZ;
		te[11] = 0;

		te[12] = 0;
		te[13] = 0;
		te[14] = 0;
		te[15] = 1;

		return this;

	}

	/**
	 * Sets the matrix rotation based on the given Euler angles.
	 *
	 * @param {Euler} euler - The euler angles.
	 * @return {Matrix4} This matrix.
	 */

	makeRotationFromEuler(euler) {

		const te = this.elements;

		const x = euler.x;
		const y = euler.y;
		const z = euler.z;

		const a = Math.cos(x), b = Math.sin(x);
		const c = Math.cos(y), d = Math.sin(y);
		const e = Math.cos(z), f = Math.sin(z);

		let ae, af, be, bf;
		let ce, cf, de, df;
		let ac, ad, bc, bd;

		switch(euler.order) {

			case RotationOrder.XYZ: {

				ae = a * e, af = a * f, be = b * e, bf = b * f;

				te[0] = c * e;
				te[4] = -c * f;
				te[8] = d;

				te[1] = af + be * d;
				te[5] = ae - bf * d;
				te[9] = -b * c;

				te[2] = bf - ae * d;
				te[6] = be + af * d;
				te[10] = a * c;

				break;

			}

			case RotationOrder.YXZ: {

				ce = c * e, cf = c * f, de = d * e, df = d * f;

				te[0] = ce + df * b;
				te[4] = de * b - cf;
				te[8] = a * d;

				te[1] = a * f;
				te[5] = a * e;
				te[9] = -b;

				te[2] = cf * b - de;
				te[6] = df + ce * b;
				te[10] = a * c;

				break;

			}

			case RotationOrder.ZXY: {

				ce = c * e, cf = c * f, de = d * e, df = d * f;

				te[0] = ce - df * b;
				te[4] = -a * f;
				te[8] = de + cf * b;

				te[1] = cf + de * b;
				te[5] = a * e;
				te[9] = df - ce * b;

				te[2] = -a * d;
				te[6] = b;
				te[10] = a * c;

				break;

			}

			case RotationOrder.ZYX: {

				ae = a * e, af = a * f, be = b * e, bf = b * f;

				te[0] = c * e;
				te[4] = be * d - af;
				te[8] = ae * d + bf;

				te[1] = c * f;
				te[5] = bf * d + ae;
				te[9] = af * d - be;

				te[2] = -d;
				te[6] = b * c;
				te[10] = a * c;

				break;

			}

			case RotationOrder.YZX: {

				ac = a * c, ad = a * d, bc = b * c, bd = b * d;

				te[0] = c * e;
				te[4] = bd - ac * f;
				te[8] = bc * f + ad;

				te[1] = f;
				te[5] = a * e;
				te[9] = -b * e;

				te[2] = -d * e;
				te[6] = ad * f + bc;
				te[10] = ac - bd * f;

				break;

			}

			case RotationOrder.XZY: {

				ac = a * c, ad = a * d, bc = b * c, bd = b * d;

				te[0] = c * e;
				te[4] = -f;
				te[8] = d * e;

				te[1] = ac * f + bd;
				te[5] = a * e;
				te[9] = ad * f - bc;

				te[2] = bc * f - ad;
				te[6] = b * e;
				te[10] = bd * f + ac;

				break;

			}

		}

		// Bottom row.
		te[3] = 0;
		te[7] = 0;
		te[11] = 0;

		// Last column.
		te[12] = 0;
		te[13] = 0;
		te[14] = 0;
		te[15] = 1;

		return this;

	}

	/**
	 * Sets the matrix rotation based on the given quaternion.
	 *
	 * @param {Quaternion} q - The quaternion.
	 * @return {Matrix4} This matrix.
	 */

	makeRotationFromQuaternion(q) {

		return this.compose(a.set(0, 0, 0), q, b.set(1, 1, 1));

	}

	/**
	 * Creates a rotation that looks at the given target.
	 *
	 * @param {Vector3} eye - The position of the eye.
	 * @param {Vector3} target - The target to look at.
	 * @param {Vector3} up - The up vector.
	 * @return {Matrix4} This matrix.
	 */

	lookAt(eye, target, up) {

		const te = this.elements;
		const x = a, y = b, z = c;

		z.subVectors(eye, target);

		if(z.lengthSquared() === 0) {

			// Eye and target are at the same position.
			z.z = 1;

		}

		z.normalize();
		x.crossVectors(up, z);

		if(x.lengthSquared() === 0) {

			// Up and z are parallel.
			if(Math.abs(up.z) === 1) {

				z.x += 1e-4;

			} else {

				z.z += 1e-4;

			}

			z.normalize();
			x.crossVectors(up, z);

		}

		x.normalize();
		y.crossVectors(z, x);

		te[0] = x.x; te[4] = y.x; te[8] = z.x;
		te[1] = x.y; te[5] = y.y; te[9] = z.y;
		te[2] = x.z; te[6] = y.z; te[10] = z.z;

		return this;

	}

	/**
	 * Sets this matrix to the product of the given matrices.
	 *
	 * @param {Matrix4} a - A matrix.
	 * @param {Matrix4} b - A matrix.
	 * @return {Matrix4} This matrix.
	 */

	multiplyMatrices(a, b) {

		const te = this.elements;
		const ae = a.elements;
		const be = b.elements;

		const a00 = ae[0], a01 = ae[4], a02 = ae[8], a03 = ae[12];
		const a10 = ae[1], a11 = ae[5], a12 = ae[9], a13 = ae[13];
		const a20 = ae[2], a21 = ae[6], a22 = ae[10], a23 = ae[14];
		const a30 = ae[3], a31 = ae[7], a32 = ae[11], a33 = ae[15];

		const b00 = be[0], b01 = be[4], b02 = be[8], b03 = be[12];
		const b10 = be[1], b11 = be[5], b12 = be[9], b13 = be[13];
		const b20 = be[2], b21 = be[6], b22 = be[10], b23 = be[14];
		const b30 = be[3], b31 = be[7], b32 = be[11], b33 = be[15];

		te[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
		te[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
		te[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
		te[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;

		te[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
		te[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
		te[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
		te[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;

		te[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
		te[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
		te[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
		te[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;

		te[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
		te[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
		te[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
		te[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;

		return this;

	}

	/**
	 * Multiplies this matrix with the given one.
	 *
	 * @param {Matrix4} m - A matrix.
	 * @return {Matrix4} This matrix.
	 */

	multiply(m) {

		return this.multiplyMatrices(this, m);

	}

	/**
	 * Multiplies a given matrix with this one.
	 *
	 * @param {Matrix4} m - A matrix.
	 * @return {Matrix4} This matrix.
	 */

	premultiply(m) {

		return this.multiplyMatrices(m, this);

	}

	/**
	 * Multiplies this matrix with a given scalar.
	 *
	 * @param {Number} s - A scalar.
	 * @return {Matrix4} This matrix.
	 */

	multiplyScalar(s) {

		const te = this.elements;

		te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s;
		te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s;
		te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s;
		te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s;

		return this;

	}

	/**
	 * Calculates the determinant of this matrix.
	 *
	 * For more details see:
	 *  http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
	 *
	 * @return {Number} The determinant.
	 */

	determinant() {

		const te = this.elements;

		const n00 = te[0], n01 = te[4], n02 = te[8], n03 = te[12];
		const n10 = te[1], n11 = te[5], n12 = te[9], n13 = te[13];
		const n20 = te[2], n21 = te[6], n22 = te[10], n23 = te[14];
		const n30 = te[3], n31 = te[7], n32 = te[11], n33 = te[15];

		const n00n11 = n00 * n11, n00n12 = n00 * n12, n00n13 = n00 * n13;
		const n01n10 = n01 * n10, n01n12 = n01 * n12, n01n13 = n01 * n13;
		const n02n10 = n02 * n10, n02n11 = n02 * n11, n02n13 = n02 * n13;
		const n03n10 = n03 * n10, n03n11 = n03 * n11, n03n12 = n03 * n12;

		return (

			n30 * (
				n03n12 * n21 -
				n02n13 * n21 -
				n03n11 * n22 +
				n01n13 * n22 +
				n02n11 * n23 -
				n01n12 * n23
			) +

			n31 * (
				n00n12 * n23 -
				n00n13 * n22 +
				n03n10 * n22 -
				n02n10 * n23 +
				n02n13 * n20 -
				n03n12 * n20
			) +

			n32 * (
				n00n13 * n21 -
				n00n11 * n23 -
				n03n10 * n21 +
				n01n10 * n23 +
				n03n11 * n20 -
				n01n13 * n20
			) +

			n33 * (
				-n02n11 * n20 -
				n00n12 * n21 +
				n00n11 * n22 +
				n02n10 * n21 -
				n01n10 * n22 +
				n01n12 * n20
			)

		);

	}

	/**
	 * Inverts the given matrix and stores the result in this matrix.
	 *
	 * For details see:
	 *  http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
	 *
	 * @param {Matrix4} matrix - The matrix that should be inverted.
	 * @return {Matrix4} This matrix.
	 */

	getInverse(matrix) {

		const te = this.elements;
		const me = matrix.elements;

		const n00 = me[0], n10 = me[1], n20 = me[2], n30 = me[3];
		const n01 = me[4], n11 = me[5], n21 = me[6], n31 = me[7];
		const n02 = me[8], n12 = me[9], n22 = me[10], n32 = me[11];
		const n03 = me[12], n13 = me[13], n23 = me[14], n33 = me[15];

		const t00 = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33;
		const t01 = n03 * n22 * n31 - n02 * n23 * n31 - n03 * n21 * n32 + n01 * n23 * n32 + n02 * n21 * n33 - n01 * n22 * n33;
		const t02 = n02 * n13 * n31 - n03 * n12 * n31 + n03 * n11 * n32 - n01 * n13 * n32 - n02 * n11 * n33 + n01 * n12 * n33;
		const t03 = n03 * n12 * n21 - n02 * n13 * n21 - n03 * n11 * n22 + n01 * n13 * n22 + n02 * n11 * n23 - n01 * n12 * n23;

		const det = n00 * t00 + n10 * t01 + n20 * t02 + n30 * t03;

		let invDet;

		if(det !== 0) {

			invDet = 1.0 / det;

			te[0] = t00 * invDet;
			te[1] = (n13 * n22 * n30 - n12 * n23 * n30 - n13 * n20 * n32 + n10 * n23 * n32 + n12 * n20 * n33 - n10 * n22 * n33) * invDet;
			te[2] = (n11 * n23 * n30 - n13 * n21 * n30 + n13 * n20 * n31 - n10 * n23 * n31 - n11 * n20 * n33 + n10 * n21 * n33) * invDet;
			te[3] = (n12 * n21 * n30 - n11 * n22 * n30 - n12 * n20 * n31 + n10 * n22 * n31 + n11 * n20 * n32 - n10 * n21 * n32) * invDet;

			te[4] = t01 * invDet;
			te[5] = (n02 * n23 * n30 - n03 * n22 * n30 + n03 * n20 * n32 - n00 * n23 * n32 - n02 * n20 * n33 + n00 * n22 * n33) * invDet;
			te[6] = (n03 * n21 * n30 - n01 * n23 * n30 - n03 * n20 * n31 + n00 * n23 * n31 + n01 * n20 * n33 - n00 * n21 * n33) * invDet;
			te[7] = (n01 * n22 * n30 - n02 * n21 * n30 + n02 * n20 * n31 - n00 * n22 * n31 - n01 * n20 * n32 + n00 * n21 * n32) * invDet;

			te[8] = t02 * invDet;
			te[9] = (n03 * n12 * n30 - n02 * n13 * n30 - n03 * n10 * n32 + n00 * n13 * n32 + n02 * n10 * n33 - n00 * n12 * n33) * invDet;
			te[10] = (n01 * n13 * n30 - n03 * n11 * n30 + n03 * n10 * n31 - n00 * n13 * n31 - n01 * n10 * n33 + n00 * n11 * n33) * invDet;
			te[11] = (n02 * n11 * n30 - n01 * n12 * n30 - n02 * n10 * n31 + n00 * n12 * n31 + n01 * n10 * n32 - n00 * n11 * n32) * invDet;

			te[12] = t03 * invDet;
			te[13] = (n02 * n13 * n20 - n03 * n12 * n20 + n03 * n10 * n22 - n00 * n13 * n22 - n02 * n10 * n23 + n00 * n12 * n23) * invDet;
			te[14] = (n03 * n11 * n20 - n01 * n13 * n20 - n03 * n10 * n21 + n00 * n13 * n21 + n01 * n10 * n23 - n00 * n11 * n23) * invDet;
			te[15] = (n01 * n12 * n20 - n02 * n11 * n20 + n02 * n10 * n21 - n00 * n12 * n21 - n01 * n10 * n22 + n00 * n11 * n22) * invDet;

		} else {

			this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

		}

		return this;

	}

	/**
	 * Transposes this matrix.
	 *
	 * @return {Matrix4} This matrix.
	 */

	transpose() {

		const te = this.elements;

		let t;

		t = te[1]; te[1] = te[4]; te[4] = t;
		t = te[2]; te[2] = te[8]; te[8] = t;
		t = te[6]; te[6] = te[9]; te[9] = t;

		t = te[3]; te[3] = te[12]; te[12] = t;
		t = te[7]; te[7] = te[13]; te[13] = t;
		t = te[11]; te[11] = te[14]; te[14] = t;

		return this;

	}

	/**
	 * Scales this matrix.
	 *
	 * @param {Number} sx - The X scale.
	 * @param {Number} sy - The Y scale.
	 * @param {Number} sz - The Z scale.
	 * @return {Matrix4} This matrix.
	 */

	scale(sx, sy, sz) {

		const te = this.elements;

		te[0] *= sx; te[4] *= sy; te[8] *= sz;
		te[1] *= sx; te[5] *= sy; te[9] *= sz;
		te[2] *= sx; te[6] *= sy; te[10] *= sz;
		te[3] *= sx; te[7] *= sy; te[11] *= sz;

		return this;

	}

	/**
	 * Makes this matrix a scale matrix.
	 *
	 * @param {Number} x - The X scale.
	 * @param {Number} y - The Y scale.
	 * @param {Number} z - The Z scale.
	 * @return {Matrix4} This matrix.
	 */

	makeScale(x, y, z) {

		this.set(

			x, 0, 0, 0,
			0, y, 0, 0,
			0, 0, z, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Makes this matrix a translation matrix.
	 *
	 * @param {Number} x - The X offset.
	 * @param {Number} y - The Y offset.
	 * @param {Number} z - The Z offset.
	 * @return {Matrix4} This matrix.
	 */

	makeTranslation(x, y, z) {

		this.set(

			1, 0, 0, x,
			0, 1, 0, y,
			0, 0, 1, z,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Makes this matrix a rotation matrix.
	 *
	 * @param {Number} theta - The angle in radians.
	 * @return {Matrix4} This matrix.
	 */

	makeRotationX(theta) {

		const c = Math.cos(theta), s = Math.sin(theta);

		this.set(

			1, 0, 0, 0,
			0, c, -s, 0,
			0, s, c, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Makes this matrix a rotation matrix with respect to the Y-axis.
	 *
	 * @param {Number} theta - The angle in radians.
	 * @return {Matrix4} This matrix.
	 */

	makeRotationY(theta) {

		const c = Math.cos(theta), s = Math.sin(theta);

		this.set(

			c, 0, s, 0,
			0, 1, 0, 0,
			-s, 0, c, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Makes this matrix a rotation matrix with respect to the Z-axis.
	 *
	 * @param {Number} theta - The angle in radians.
	 * @return {Matrix4} This matrix.
	 */

	makeRotationZ(theta) {

		const c = Math.cos(theta), s = Math.sin(theta);

		this.set(

			c, -s, 0, 0,
			s, c, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Makes this matrix a translation matrix with respect to a specific axis.
	 *
	 * For mor einformation see:
	 *  http://www.gamedev.net/reference/articles/article1199.asp
	 *
	 * @param {Vector3} axis - The axis. Assumed to be normalized.
	 * @param {Number} angle - The angle in radians.
	 * @return {Matrix4} This matrix.
	 */

	makeRotationAxis(axis, angle) {

		const c = Math.cos(angle);
		const s = Math.sin(angle);

		const t = 1.0 - c;

		const x = axis.x, y = axis.y, z = axis.z;
		const tx = t * x, ty = t * y;

		this.set(

			tx * x + c, tx * y - s * z, tx * z + s * y, 0,
			tx * y + s * z, ty * y + c, ty * z - s * x, 0,
			tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Makes this matrix a shear matrix.
	 *
	 * @param {Number} x - The X shear value.
	 * @param {Number} y - The Y shear value.
	 * @param {Number} z - The Z shear value.
	 * @return {Matrix4} This matrix.
	 */

	makeShear(x, y, z) {

		this.set(

			1, y, z, 0,
			x, 1, z, 0,
			x, y, 1, 0,
			0, 0, 0, 1

		);

		return this;

	}

	/**
	 * Sets this matrix based on the given position, rotation and scale.
	 *
	 * @param {Vector3} position - The position.
	 * @param {Quaternion} quaternion - The rotation.
	 * @param {Vector3} scale - The scale.
	 * @return {Matrix4} This matrix.
	 */

	compose(position, quaternion, scale) {

		const te = this.elements;

		const x = quaternion.x, y = quaternion.y, z = quaternion.z, w = quaternion.w;
		const x2 = x + x,	y2 = y + y, z2 = z + z;
		const xx = x * x2, xy = x * y2, xz = x * z2;
		const yy = y * y2, yz = y * z2, zz = z * z2;
		const wx = w * x2, wy = w * y2, wz = w * z2;

		const sx = scale.x, sy = scale.y, sz = scale.z;

		te[0] = (1 - (yy + zz)) * sx;
		te[1] = (xy + wz) * sx;
		te[2] = (xz - wy) * sx;
		te[3] = 0;

		te[4] = (xy - wz) * sy;
		te[5] = (1 - (xx + zz)) * sy;
		te[6] = (yz + wx) * sy;
		te[7] = 0;

		te[8] = (xz + wy) * sz;
		te[9] = (yz - wx) * sz;
		te[10] = (1 - (xx + yy)) * sz;
		te[11] = 0;

		te[12] = position.x;
		te[13] = position.y;
		te[14] = position.z;
		te[15] = 1;

		return this;

	}

	/**
	 * Decomposes this matrix into a position, rotation and scale vector.
	 *
	 * @param {Vector3} position - The target position.
	 * @param {Quaternion} quaternion - The target rotation.
	 * @param {Vector3} scale - The target scale.
	 * @return {Matrix4} This matrix.
	 */

	decompose(position, quaternion, scale) {

		const te = this.elements;

		const n00 = te[0], n10 = te[1], n20 = te[2];
		const n01 = te[4], n11 = te[5], n21 = te[6];
		const n02 = te[8], n12 = te[9], n22 = te[10];

		const det = this.determinant();

		// If the determinant is negative, one scale must be inverted.
		const sx = a.set(n00, n10, n20).length() * ((det < 0) ? -1 : 1);
		const sy = a.set(n01, n11, n21).length();
		const sz = a.set(n02, n12, n22).length();

		const invSX = 1.0 / sx;
		const invSY = 1.0 / sy;
		const invSZ = 1.0 / sz;

		// Export the position.
		position.x = te[12];
		position.y = te[13];
		position.z = te[14];

		// Scale the rotation part.
		te[0] *= invSX; te[1] *= invSX; te[2] *= invSX;
		te[4] *= invSY; te[5] *= invSY; te[6] *= invSY;
		te[8] *= invSZ; te[9] *= invSZ; te[10] *= invSZ;

		// Export the rotation.
		quaternion.setFromRotationMatrix(this);

		// Restore the original values.
		te[0] = n00; te[1] = n10; te[2] = n20;
		te[4] = n01; te[5] = n11; te[6] = n21;
		te[8] = n02; te[9] = n12; te[10] = n22;

		// Export the scale.
		scale.x = sx;
		scale.y = sy;
		scale.z = sz;

		return this;

	}

	/**
	 * Creates a perspective matrix.
	 *
	 * @param {Number} left - The distance to the left plane.
	 * @param {Number} right - The distance to the right plane.
	 * @param {Number} top - The distance to the top plane.
	 * @param {Number} bottom - The distance to the bottom plane.
	 * @param {Number} near - The distance to the near plane.
	 * @param {Number} far - The distance to the far plane.
	 * @return {Matrix4} This matrix.
	 */

	makePerspective(left, right, top, bottom, near, far) {

		const te = this.elements;
		const x = 2 * near / (right - left);
		const y = 2 * near / (top - bottom);

		const a = (right + left) / (right - left);
		const b = (top + bottom) / (top - bottom);
		const c = -(far + near) / (far - near);
		const d = -2 * far * near / (far - near);

		te[0] = x; te[4] = 0; te[8] = a; te[12] = 0;
		te[1] = 0; te[5] = y; te[9] = b; te[13] = 0;
		te[2] = 0; te[6] = 0; te[10] = c; te[14] = d;
		te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0;

		return this;

	}

	/**
	 * Creates an orthographic matrix.
	 *
	 * @param {Number} left - The distance to the left plane.
	 * @param {Number} right - The distance to the right plane.
	 * @param {Number} top - The distance to the top plane.
	 * @param {Number} bottom - The distance to the bottom plane.
	 * @param {Number} near - The distance to the near plane.
	 * @param {Number} far - The distance to the far plane.
	 * @return {Matrix4} This matrix.
	 */

	makeOrthographic(left, right, top, bottom, near, far) {

		const te = this.elements;
		const w = 1.0 / (right - left);
		const h = 1.0 / (top - bottom);
		const p = 1.0 / (far - near);

		const x = (right + left) * w;
		const y = (top + bottom) * h;
		const z = (far + near) * p;

		te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x;
		te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y;
		te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z;
		te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1;

		return this;

	}

	/**
	 * Checks if this matrix equals the given one.
	 *
	 * @param {Matrix4} m - A matrix.
	 * @return {Boolean} Whether the matrix are equal.
	 */

	equals(m) {

		const te = this.elements;
		const me = m.elements;

		let result = true;
		let i;

		for(i = 0; result && i < 16; ++i) {

			if(te[i] !== me[i]) {

				result = false;

			}

		}

		return result;

	}

}