import {
mat4
} from 'gl-matrix';
import Class from '../core/Class';
import Vector3 from './Vector3';
import Quaternion from './Quaternion';
let tempMatrix4;
const tempVector3 = new Vector3();
const tempVector32 = new Vector3();
/**
* 4x4 矩阵
* @class
*/
const Matrix4 = Class.create(/** @lends Matrix4.prototype */ {
/**
* 类名
* @type {String}
* @default Matrix4
*/
className: 'Matrix4',
/**
* @type {Boolean}
* @default true
*/
isMatrix4: true,
/**
* Creates a new identity mat4
* @constructs
*/
constructor() {
/**
* 数据
* @type {Float32Array}
*/
this.elements = mat4.create();
},
/**
* Copy the values from one mat4 to this
* @param {Matrix4} m the source matrix
* @return {Matrix4} this
*/
copy(m) {
mat4.copy(this.elements, m.elements);
return this;
},
/**
* Creates a new mat4 initialized with values from this matrix
* @return {Matrix4} a new Matrix4
*/
clone() {
const m = new this.constructor();
mat4.copy(m.elements, this.elements);
return m;
},
/**
* 转换到数组
* @param {number[]|TypedArray} [array=[]] 数组
* @param {Number} [offset=0] 数组偏移值
* @return {Array}
*/
toArray(array = [], offset = 0) {
const elements = this.elements;
for (let i = 0; i < 16; i++) {
array[offset + i] = elements[i];
}
return array;
},
/**
* 从数组赋值
* @param {number[]|TypedArray} array 数组
* @param {Number} [offset=0] 数组偏移值
* @return {Matrix4} this
*/
fromArray(array, offset = 0) {
const elements = this.elements;
for (let i = 0; i < 16; i++) {
elements[i] = array[offset + i];
}
return this;
},
/**
* Set the components of a mat3 to the given values
* @param {Number} m00
* @param {Number} m01
* @param {Number} m02
* @param {Number} m03
* @param {Number} m10
* @param {Number} m11
* @param {Number} m12
* @param {Number} m13
* @param {Number} m20
* @param {Number} m21
* @param {Number} m22
* @param {Number} m23
* @param {Number} m30
* @param {Number} m31
* @param {Number} m32
* @param {Number} m33
* @return {Matrix4} this
*/
set(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
mat4.set(this.elements, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33);
return this;
},
/**
* Set this to the identity matrix
* @return {Matrix4} this
*/
identity() {
mat4.identity(this.elements);
return this;
},
/**
* Transpose the values of this
* @return {Matrix4} this
*/
transpose() {
mat4.transpose(this.elements, this.elements);
return this;
},
/**
* invert a matrix
* @param {Matrix4} [m=this]
* @return {Matrix4} this
*/
invert(m = this) {
mat4.invert(this.elements, m.elements);
return this;
},
/**
* Calculates the adjugate of a mat4
* @param {Matrix4} [m=this]
* @return {Matrix4} this
*/
adjoint(m = this) {
mat4.adjoint(this.elements, m.elements);
return this;
},
/**
* Calculates the determinant of this
* @return {Matrix4} this
*/
determinant() {
return mat4.determinant(this.elements);
},
/**
* Multiplies two matrix4's
* @param {Matrix4} a
* @param {Matrix4} [b] 如果不传,计算 this 和 a 的乘积
* @return {Matrix4} this
*/
multiply(a, b) {
if (!b) {
b = a;
a = this;
}
mat4.multiply(this.elements, a.elements, b.elements);
return this;
},
/**
* 左乘
* @param {Matrix4} m
* @return {Matrix4} this
*/
premultiply(m) {
this.multiply(m, this);
return this;
},
/**
* Translate this by the given vector
* @param {Vector3} v vector to translate by
* @return {Matrix4} this
*/
translate(v) {
mat4.translate(this.elements, this.elements, v.elements);
return this;
},
/**
* Scales the mat3 by the dimensions in the given vec2
* @param {Vector3} v the vec3 to scale the matrix by
* @return {Matrix4} this
*/
scale(v) {
mat4.scale(this.elements, this.elements, v.elements);
return this;
},
/**
* Rotates this by the given angle
* @param {Number} rad the angle to rotate the matrix by
* @param {Vector3} axis the axis to rotate around
* @return {Matrix4} this
*/
rotate(rad, axis) {
mat4.rotate(this.elements, this.elements, rad, axis.elements);
return this;
},
/**
* Rotates this by the given angle around the X axis
* @param {Number} rad the angle to rotate the matrix by
* @return {Matrix4} this
*/
rotateX(rad) {
mat4.rotateX(this.elements, this.elements, rad);
return this;
},
/**
* Rotates this by the given angle around the Y axis
* @param {Number} rad the angle to rotate the matrix by
* @return {Matrix4} this
*/
rotateY(rad) {
mat4.rotateY(this.elements, this.elements, rad);
return this;
},
/**
* Rotates this by the given angle around the Z axis
* @param {Number} rad the angle to rotate the matrix by
* @return {Matrix4} this
*/
rotateZ(rad) {
mat4.rotateZ(this.elements, this.elements, rad);
return this;
},
/**
* Creates a matrix from a vector translation
* @param {Vector3} transition Translation vector
* @return {Matrix4} this
*/
fromTranslation(v) {
mat4.fromTranslation(this.elements, v.elements);
return this;
},
/**
* Creates a matrix from a vector scaling
* @param {Vector3} v Scaling vector
* @return {Matrix4} this
*/
fromScaling(v) {
mat4.fromScaling(this.elements, v.elements);
return this;
},
/**
* Creates a matrix from a given angle around a given axis
* @param {Number} rad the angle to rotate the matrix by
* @param {Vector3} axis the axis to rotate around
* @return {Matrix4} this
*/
fromRotation(rad, axis) {
mat4.fromRotation(this.elements, rad, axis.elements);
return this;
},
/**
* Creates a matrix from the given angle around the X axis
* @param {Number} rad the angle to rotate the matrix by
* @return {Matrix4} this
*/
fromXRotation(rad) {
mat4.fromXRotation(this.elements, rad);
return this;
},
/**
* Creates a matrix from the given angle around the Y axis
* @param {Number} rad the angle to rotate the matrix by
* @return {Matrix4} this
*/
fromYRotation(rad) {
mat4.fromYRotation(this.elements, rad);
return this;
},
/**
* Creates a matrix from the given angle around the Z axis
* @param {Number} rad the angle to rotate the matrix by
* @return {Matrix4} this
*/
fromZRotation(rad) {
mat4.fromZRotation(this.elements, rad);
return this;
},
/**
* Creates a matrix from a quaternion rotation and vector translation
* @param {Quaternion} q Rotation quaternion
* @param {Vector3} v Translation vector
* @return {Matrix4} this
*/
fromRotationTranslation(q, v) {
mat4.fromRotationTranslation(this.elements, q.elements, v.elements);
return this;
},
/**
* Returns the translation vector component of a transformation
* matrix. If a matrix is built with fromRotationTranslation,
* the returned vector will be the same as the translation vector
* originally supplied.
* @param {Vector3} [out=new Vector3] Vector to receive translation component
* @return {Vector3} out
*/
getTranslation(out = new Vector3()) {
mat4.getTranslation(out.elements, this.elements);
return out;
},
/**
* Returns the scaling factor component of a transformation
* matrix. If a matrix is built with fromRotationTranslationScale
* with a normalized Quaternion paramter, the returned vector will be
* the same as the scaling vector
* originally supplied.
* @param {Vector3} [out=new Vector3] Vector to receive scaling factor component
* @return {Vector3} out
*/
getScaling(out = new Vector3()) {
mat4.getScaling(out.elements, this.elements);
return out;
},
/**
* Returns a quaternion representing the rotational component
* of a transformation matrix. If a matrix is built with
* fromRotationTranslation, the returned quaternion will be the
* same as the quaternion originally supplied.
* @param {Quaternion} [out=new Quaternion] Quaternion to receive the rotation component
* @return {Quaternion} out
*/
getRotation(out = new Quaternion()) {
mat4.getRotation(out.elements, this.elements);
return out;
},
/**
* Creates a matrix from a quaternion rotation, vector translation and vector scale
* @param {Quaternion} q Rotation quaternion
* @param {Vector3} v Translation vector
* @param {Vector3} s Scaling vector
* @return {Matrix4} this
*/
fromRotationTranslationScale(q, v, s) {
mat4.fromRotationTranslationScale(this.elements, q.elements, v.elements, s.elements);
return this;
},
/**
* Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the given origin
* @param {Quaternion} q Rotation quaternion
* @param {Vector3} v Translation vector
* @param {Vector3} s Scaling vector
* @param {Vector3} o The origin vector around which to scale and rotate
* @return {Matrix4} this
*/
fromRotationTranslationScaleOrigin(q, v, s, o) {
mat4.fromRotationTranslationScaleOrigin(this.elements, q.elements, v.elements, s.elements, o.elements);
return this;
},
/**
* Calculates a 4x4 matrix from the given quaternion
* @param {Quaternion} q Quaternion to create matrix from
* @return {Matrix4} this
*/
fromQuat(q) {
mat4.fromQuat(this.elements, q.elements);
return this;
},
/**
* Generates a frustum matrix with the given bounds
* @param {Number} left Left bound of the frustum
* @param {Number} right Right bound of the frustum
* @param {Number} bottom Bottom bound of the frustum
* @param {Number} top Top bound of the frustum
* @param {Number} near Near bound of the frustum
* @param {Number} far Far bound of the frustum
* @return {Matrix4} this
*/
frustum(left, right, bottom, top, near, far) {
mat4.frustum(this.elements, left, right, bottom, top, near, far);
return this;
},
/**
* Generates a perspective projection matrix with the given bounds
* @param {Number} fovy Vertical field of view in radians
* @param {Number} aspect Aspect ratio. typically viewport width/height
* @param {Number} near Near bound of the frustum
* @param {Number} far Far bound of the frustum
* @return {Matrix4} this
*/
perspective(fovy, aspect, near, far) {
mat4.perspective(this.elements, fovy, aspect, near, far);
return this;
},
/**
* Generates a perspective projection matrix with the given field of view.
* @param {Object} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees
* @param {Number} Near bound of the frustum
* @param {Number} far Far bound of the frustum
* @return {Matrix4} this
*/
perspectiveFromFieldOfView(fov, near, far) {
mat4.perspectiveFromFieldOfView(this.elements, fov, near, far);
return this;
},
/**
* Generates a orthogonal projection matrix with the given bounds
* @param {Number} left Left bound of the frustum
* @param {Number} right Right bound of the frustum
* @param {Number} bottom Bottom bound of the frustum
* @param {Number} top Top bound of the frustum
* @param {Number} near Near bound of the frustum
* @param {Number} far Far bound of the frustum
* @return {Matrix4} this
*/
ortho(left, right, bottom, top, near, far) {
mat4.ortho(this.elements, left, right, bottom, top, near, far);
return this;
},
/**
* Generates a look-at matrix with the given eye position, focal point, and up axis
* @param {XYZObject} eye Position of the viewer
* @param {XYZObject} center Point the viewer is looking at
* @param {Vector3} up pointing up
* @return {Matrix4} this
*/
lookAt(eye, center, up) {
if (!eye.isVector3) {
eye = tempVector3.set(eye.x, eye.y, eye.z);
}
if (!center.isVector3) {
center = tempVector32.set(center.x, center.y, center.z);
}
mat4.lookAt(this.elements, eye.elements, center.elements, up.elements);
return this;
},
/**
* Generates a matrix that makes something look at something else.
* @param {XYZObject} eye Position of the viewer
* @param {XYZObject} Point the viewer is looking at
* @param {Vector3} up pointing up
* @return {Matrix4} this
*/
targetTo(eye, target, up) {
if (!eye.isVector3) {
eye = tempVector3.set(eye.x, eye.y, eye.z);
}
if (!target.isVector3) {
target = tempVector32.set(target.x, target.y, target.z);
}
// mat4.targetTo(this.elements, eye.elements, target.elements, up.elements);
eye = eye.elements;
target = target.elements;
up = up.elements;
const out = this.elements;
let eyex = eye[0];
let eyey = eye[1];
let eyez = eye[2];
let upx = up[0];
let upy = up[1];
let upz = up[2];
let z0 = eyex - target[0];
let z1 = eyey - target[1];
let z2 = eyez - target[2];
let len = z0 * z0 + z1 * z1 + z2 * z2;
if (len > 0) {
len = 1 / Math.sqrt(len);
z0 *= len;
z1 *= len;
z2 *= len;
} else {
z2 = 1;
}
let x0 = upy * z2 - upz * z1;
let x1 = upz * z0 - upx * z2;
let x2 = upx * z1 - upy * z0;
len = x0 * x0 + x1 * x1 + x2 * x2;
if (len > 0) {
len = 1 / Math.sqrt(len);
x0 *= len;
x1 *= len;
x2 *= len;
} else {
upx += 0.0000001;
x0 = upy * z2 - upz * z1;
x1 = upz * z0 - upx * z2;
x2 = upx * z1 - upy * z0;
len = x0 * x0 + x1 * x1 + x2 * x2;
len = 1 / Math.sqrt(len);
x0 *= len;
x1 *= len;
x2 *= len;
}
out[0] = x0;
out[1] = x1;
out[2] = x2;
out[3] = 0;
out[4] = z1 * x2 - z2 * x1;
out[5] = z2 * x0 - z0 * x2;
out[6] = z0 * x1 - z1 * x0;
out[7] = 0;
out[8] = z0;
out[9] = z1;
out[10] = z2;
out[11] = 0;
out[12] = eyex;
out[13] = eyey;
out[14] = eyez;
out[15] = 1;
return this;
},
/**
* Returns Frobenius norm of a mat4
* @return {Number} Frobenius norm
*/
frob() {
return mat4.frob(this.elements);
},
/**
* Adds two mat4's
* @param {Matrix4} a
* @param {Matrix4} [b] 如果不传,计算 this 和 a 的和
* @return {Matrix4} this
*/
add(a, b) {
if (!b) {
b = a;
a = this;
}
mat4.add(this.elements, a.elements, b.elements);
return this;
},
/**
* Subtracts matrix b from matrix a
* @param {Matrix4} a
* @param {Matrix4} [b] 如果不传,计算 this 和 a 的差
* @return {Matrix4} this
*/
subtract(a, b) {
if (!b) {
b = a;
a = this;
}
mat4.subtract(this.elements, a.elements, b.elements);
return this;
},
/**
* Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
* @param {Matrix4} a
* @param {Matrix4} [b] 如果不传,比较 this 和 a 是否相等
* @return {Boolean}
*/
exactEquals(a, b) {
if (!b) {
b = a;
a = this;
}
return mat4.exactEquals(a.elements, b.elements);
},
/**
* Returns whether or not the matrices have approximately the same elements in the same position.
* @param {Matrix4} a
* @param {Matrix4} [b] 如果不传,比较 this 和 a 是否近似相等
* @return {Boolean}
*/
equals(a, b) {
if (!b) {
b = a;
a = this;
}
return mat4.equals(a.elements, b.elements);
},
/**
* compose
* @param {Quaternion} q quaternion
* @param {Vector3} v position
* @param {Vector3} s scale
* @param {Vector3} [p] pivot
* @return {Matrix4} this
*/
compose(q, v, s, p) {
if (p) {
this.fromRotationTranslationScaleOrigin(q, v, s, p);
} else {
this.fromRotationTranslationScale(q, v, s);
}
return this;
},
/**
* decompose
* @param {Quaternion} q quaternion
* @param {Vector3} v position
* @param {Vector3} s scale
* @param {Vector3} p [pivot]
* @return {Matrix4} this
*/
decompose(q, v, s, p) {
this.getScaling(s);
this.getTranslation(v);
if (!tempMatrix4) {
tempMatrix4 = new Matrix4();
}
const det = this.determinant();
if (det < 0) s.x *= -1;
tempMatrix4.copy(this);
tempVector3.inverse(s);
tempMatrix4.scale(tempVector3);
q.fromMat4(tempMatrix4);
if (p) {
p.set(0, 0, 0);
}
return this;
}
});
/**
* Alias for {@link Matrix4#subtract}
* @function
*/
Matrix4.prototype.sub = Matrix4.prototype.subtract;
/**
* Alias for {@link Matrix4#multiply}
* @function
*/
Matrix4.prototype.mul = Matrix4.prototype.multiply;
export default Matrix4;
/**
* 含x, y, z属性的对象
* @typedef {object} XYZObject
* @property {Number} x
* @property {Number} y
* @property {Number} z
*/