Source: ui/gl_matrix/matrix_4x4.js

  1. /*! @license
  2. * glMatrix: https://github.com/toji/gl-matrix/
  3. * Copyright 2015-2021, Brandon Jones, Colin MacKenzie IV
  4. * SPDX-License-Identifier: MIT
  5. */
  6. goog.provide('shaka.ui.Matrix4x4');
  7. /**
  8. * 4x4 Matrix
  9. * Format: column-major, when typed out it looks like row-major
  10. * The matrices are being post multiplied.
  11. */
  12. shaka.ui.Matrix4x4 = class {
  13. /**
  14. * Creates a new identity 4x4 Matrix
  15. *
  16. * @return {!Float32Array} a new 4x4 matrix
  17. */
  18. static create() {
  19. const out = new Float32Array(16);
  20. out[0] = 1;
  21. out[5] = 1;
  22. out[10] = 1;
  23. out[15] = 1;
  24. return out;
  25. }
  26. /**
  27. * Generates a look-at matrix with the given eye position, focal point, and
  28. * up axis.
  29. *
  30. * @param {!Float32Array} out 4x4 matrix frustum matrix will be written into
  31. * @param {!Array.<number>} eye Position of the viewer
  32. * @param {!Array.<number>} center Point the viewer is looking at
  33. * @param {!Array.<number>} up Vector pointing up
  34. */
  35. static lookAt(out, eye, center, up) {
  36. let x0;
  37. let x1;
  38. let x2;
  39. let y0;
  40. let y1;
  41. let y2;
  42. let z0;
  43. let z1;
  44. let z2;
  45. let len;
  46. const eyex = eye[0];
  47. const eyey = eye[1];
  48. const eyez = eye[2];
  49. const upx = up[0];
  50. const upy = up[1];
  51. const upz = up[2];
  52. const centerx = center[0];
  53. const centery = center[1];
  54. const centerz = center[2];
  55. if (Math.abs(eyex - centerx) < shaka.ui.Matrix4x4.EPSILON_ &&
  56. Math.abs(eyey - centery) < shaka.ui.Matrix4x4.EPSILON_ &&
  57. Math.abs(eyez - centerz) < shaka.ui.Matrix4x4.EPSILON_) {
  58. return shaka.ui.Matrix4x4.identity_(out);
  59. }
  60. z0 = eyex - centerx;
  61. z1 = eyey - centery;
  62. z2 = eyez - centerz;
  63. len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
  64. z0 *= len;
  65. z1 *= len;
  66. z2 *= len;
  67. x0 = upy * z2 - upz * z1;
  68. x1 = upz * z0 - upx * z2;
  69. x2 = upx * z1 - upy * z0;
  70. len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
  71. if (!len) {
  72. x0 = 0;
  73. x1 = 0;
  74. x2 = 0;
  75. } else {
  76. len = 1 / len;
  77. x0 *= len;
  78. x1 *= len;
  79. x2 *= len;
  80. }
  81. y0 = z1 * x2 - z2 * x1;
  82. y1 = z2 * x0 - z0 * x2;
  83. y2 = z0 * x1 - z1 * x0;
  84. len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
  85. if (!len) {
  86. y0 = 0;
  87. y1 = 0;
  88. y2 = 0;
  89. } else {
  90. len = 1 / len;
  91. y0 *= len;
  92. y1 *= len;
  93. y2 *= len;
  94. }
  95. out[0] = x0;
  96. out[1] = y0;
  97. out[2] = z0;
  98. out[3] = 0;
  99. out[4] = x1;
  100. out[5] = y1;
  101. out[6] = z1;
  102. out[7] = 0;
  103. out[8] = x2;
  104. out[9] = y2;
  105. out[10] = z2;
  106. out[11] = 0;
  107. out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
  108. out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
  109. out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
  110. out[15] = 1;
  111. return out;
  112. }
  113. /**
  114. * Scales the 4x4 matrix by the dimensions in the given vector not using
  115. * vectorization
  116. *
  117. * @param {!Float32Array} out the receiving matrix
  118. * @param {!Float32Array} a the matrix to scale
  119. * @param {!Array.<number>} v the vector to scale the matrix by
  120. */
  121. static scale(out, a, v) {
  122. const x = v[0];
  123. const y = v[1];
  124. const z = v[2];
  125. out[0] = a[0] * x;
  126. out[1] = a[1] * x;
  127. out[2] = a[2] * x;
  128. out[3] = a[3] * x;
  129. out[4] = a[4] * y;
  130. out[5] = a[5] * y;
  131. out[6] = a[6] * y;
  132. out[7] = a[7] * y;
  133. out[8] = a[8] * z;
  134. out[9] = a[9] * z;
  135. out[10] = a[10] * z;
  136. out[11] = a[11] * z;
  137. out[12] = a[12];
  138. out[13] = a[13];
  139. out[14] = a[14];
  140. out[15] = a[15];
  141. }
  142. /**
  143. * Generates a perspective projection matrix with the given bounds.
  144. * The near/far clip planes correspond to a normalized device coordinate Z
  145. * range of [-1, 1], which matches WebGL's clip volume.
  146. * Passing null/undefined/no value for far will generate infinite projection
  147. * matrix.
  148. *
  149. * @param {!Float32Array} out 4x4 matrix frustum matrix will be written into
  150. * @param {number} fovy Vertical field of view in radians
  151. * @param {number} aspect Aspect ratio. typically viewport width/height
  152. * @param {number} near Near bound of the frustum
  153. * @param {number} far Far bound of the frustum, can be null or Infinity
  154. */
  155. static perspective(out, fovy, aspect, near, far) {
  156. const f = 1.0 / Math.tan(fovy / 2);
  157. out[0] = f / aspect;
  158. out[1] = 0;
  159. out[2] = 0;
  160. out[3] = 0;
  161. out[4] = 0;
  162. out[5] = f;
  163. out[6] = 0;
  164. out[7] = 0;
  165. out[8] = 0;
  166. out[9] = 0;
  167. out[11] = -1;
  168. out[12] = 0;
  169. out[13] = 0;
  170. out[15] = 0;
  171. if (far != null && far !== Infinity) {
  172. const nf = 1 / (near - far);
  173. out[10] = (far + near) * nf;
  174. out[14] = 2 * far * near * nf;
  175. } else {
  176. out[10] = -1;
  177. out[14] = -2 * near;
  178. }
  179. return out;
  180. }
  181. /**
  182. * Multiplies two 4x4 matrix
  183. *
  184. * @param {!Float32Array} out the receiving matrix
  185. * @param {!Float32Array} a the first operand
  186. * @param {!Float32Array} b the second operand
  187. */
  188. static multiply(out, a, b) {
  189. const a00 = a[0];
  190. const a01 = a[1];
  191. const a02 = a[2];
  192. const a03 = a[3];
  193. const a10 = a[4];
  194. const a11 = a[5];
  195. const a12 = a[6];
  196. const a13 = a[7];
  197. const a20 = a[8];
  198. const a21 = a[9];
  199. const a22 = a[10];
  200. const a23 = a[11];
  201. const a30 = a[12];
  202. const a31 = a[13];
  203. const a32 = a[14];
  204. const a33 = a[15];
  205. // Cache only the current line of the second matrix
  206. let b0 = b[0];
  207. let b1 = b[1];
  208. let b2 = b[2];
  209. let b3 = b[3];
  210. out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  211. out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  212. out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  213. out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  214. b0 = b[4];
  215. b1 = b[5];
  216. b2 = b[6];
  217. b3 = b[7];
  218. out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  219. out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  220. out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  221. out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  222. b0 = b[8];
  223. b1 = b[9];
  224. b2 = b[10];
  225. b3 = b[11];
  226. out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  227. out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  228. out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  229. out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  230. b0 = b[12];
  231. b1 = b[13];
  232. b2 = b[14];
  233. b3 = b[15];
  234. out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  235. out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  236. out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  237. out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  238. }
  239. /**
  240. * Generates a frustum matrix with the given bounds
  241. *
  242. * @param {!Float32Array} out 4x4 matrix frustum matrix will be written into
  243. * @param {number} left Left bound of the frustum
  244. * @param {number} right Right bound of the frustum
  245. * @param {number} bottom Bottom bound of the frustum
  246. * @param {number} top Top bound of the frustum
  247. * @param {number} near Near bound of the frustum
  248. * @param {number} far Far bound of the frustum
  249. */
  250. static frustum(out, left, right, bottom, top, near, far) {
  251. const rl = 1 / (right - left);
  252. const tb = 1 / (top - bottom);
  253. const nf = 1 / (near - far);
  254. out[0] = near * 2 * rl;
  255. out[1] = 0;
  256. out[2] = 0;
  257. out[3] = 0;
  258. out[4] = 0;
  259. out[5] = near * 2 * tb;
  260. out[6] = 0;
  261. out[7] = 0;
  262. out[8] = (right + left) * rl;
  263. out[9] = (top + bottom) * tb;
  264. out[10] = (far + near) * nf;
  265. out[11] = -1;
  266. out[12] = 0;
  267. out[13] = 0;
  268. out[14] = far * near * 2 * nf;
  269. out[15] = 0;
  270. return out;
  271. }
  272. /**
  273. * Rotates a matrix by the given angle around the X axis
  274. *
  275. * @param {!Float32Array} out the receiving matrix
  276. * @param {!Float32Array} a the matrix to rotate
  277. * @param {number} rad the angle to rotate the matrix by
  278. */
  279. static rotateX(out, a, rad) {
  280. const s = Math.sin(rad);
  281. const c = Math.cos(rad);
  282. const a10 = a[4];
  283. const a11 = a[5];
  284. const a12 = a[6];
  285. const a13 = a[7];
  286. const a20 = a[8];
  287. const a21 = a[9];
  288. const a22 = a[10];
  289. const a23 = a[11];
  290. if (a !== out) {
  291. // If the source and destination differ, copy the unchanged rows
  292. out[0] = a[0];
  293. out[1] = a[1];
  294. out[2] = a[2];
  295. out[3] = a[3];
  296. out[12] = a[12];
  297. out[13] = a[13];
  298. out[14] = a[14];
  299. out[15] = a[15];
  300. }
  301. // Perform axis-specific matrix multiplication
  302. out[4] = a10 * c + a20 * s;
  303. out[5] = a11 * c + a21 * s;
  304. out[6] = a12 * c + a22 * s;
  305. out[7] = a13 * c + a23 * s;
  306. out[8] = a20 * c - a10 * s;
  307. out[9] = a21 * c - a11 * s;
  308. out[10] = a22 * c - a12 * s;
  309. out[11] = a23 * c - a13 * s;
  310. }
  311. /**
  312. * Rotates a matrix by the given angle around the Y axis
  313. *
  314. * @param {!Float32Array} out the receiving matrix
  315. * @param {!Float32Array} a the matrix to rotate
  316. * @param {number} rad the angle to rotate the matrix by
  317. */
  318. static rotateY(out, a, rad) {
  319. const s = Math.sin(rad);
  320. const c = Math.cos(rad);
  321. const a00 = a[0];
  322. const a01 = a[1];
  323. const a02 = a[2];
  324. const a03 = a[3];
  325. const a20 = a[8];
  326. const a21 = a[9];
  327. const a22 = a[10];
  328. const a23 = a[11];
  329. if (a !== out) {
  330. // If the source and destination differ, copy the unchanged rows
  331. out[4] = a[4];
  332. out[5] = a[5];
  333. out[6] = a[6];
  334. out[7] = a[7];
  335. out[12] = a[12];
  336. out[13] = a[13];
  337. out[14] = a[14];
  338. out[15] = a[15];
  339. }
  340. // Perform axis-specific matrix multiplication
  341. out[0] = a00 * c - a20 * s;
  342. out[1] = a01 * c - a21 * s;
  343. out[2] = a02 * c - a22 * s;
  344. out[3] = a03 * c - a23 * s;
  345. out[8] = a00 * s + a20 * c;
  346. out[9] = a01 * s + a21 * c;
  347. out[10] = a02 * s + a22 * c;
  348. out[11] = a03 * s + a23 * c;
  349. }
  350. /**
  351. * Rotates a matrix by the given angle around the Z axis
  352. *
  353. * @param {!Float32Array} out the receiving matrix
  354. * @param {!Float32Array} a the matrix to rotate
  355. * @param {number} rad the angle to rotate the matrix by
  356. */
  357. static rotateZ(out, a, rad) {
  358. const s = Math.sin(rad);
  359. const c = Math.cos(rad);
  360. const a00 = a[0];
  361. const a01 = a[1];
  362. const a02 = a[2];
  363. const a03 = a[3];
  364. const a10 = a[4];
  365. const a11 = a[5];
  366. const a12 = a[6];
  367. const a13 = a[7];
  368. if (a !== out) {
  369. // If the source and destination differ, copy the unchanged last row
  370. out[8] = a[8];
  371. out[9] = a[9];
  372. out[10] = a[10];
  373. out[11] = a[11];
  374. out[12] = a[12];
  375. out[13] = a[13];
  376. out[14] = a[14];
  377. out[15] = a[15];
  378. }
  379. // Perform axis-specific matrix multiplication
  380. out[0] = a00 * c + a10 * s;
  381. out[1] = a01 * c + a11 * s;
  382. out[2] = a02 * c + a12 * s;
  383. out[3] = a03 * c + a13 * s;
  384. out[4] = a10 * c - a00 * s;
  385. out[5] = a11 * c - a01 * s;
  386. out[6] = a12 * c - a02 * s;
  387. out[7] = a13 * c - a03 * s;
  388. }
  389. /**
  390. * Returns a quaternion representing the rotational component
  391. * of a transformation matrix. If a matrix is built with
  392. * fromRotationTranslation, the returned quaternion will be the
  393. * same as the quaternion originally supplied.
  394. * @param {!Float32Array} out Quaternion to receive the rotation component
  395. * @param {!Float32Array} mat Matrix to be decomposed (input)
  396. */
  397. static getRotation(out, mat) {
  398. const scaling = new Float32Array(3);
  399. shaka.ui.Matrix4x4.getScaling_(scaling, mat);
  400. const is1 = 1 / scaling[0];
  401. const is2 = 1 / scaling[1];
  402. const is3 = 1 / scaling[2];
  403. const sm11 = mat[0] * is1;
  404. const sm12 = mat[1] * is2;
  405. const sm13 = mat[2] * is3;
  406. const sm21 = mat[4] * is1;
  407. const sm22 = mat[5] * is2;
  408. const sm23 = mat[6] * is3;
  409. const sm31 = mat[8] * is1;
  410. const sm32 = mat[9] * is2;
  411. const sm33 = mat[10] * is3;
  412. const trace = sm11 + sm22 + sm33;
  413. let S = 0;
  414. if (trace > 0) {
  415. S = Math.sqrt(trace + 1.0) * 2;
  416. out[3] = 0.25 * S;
  417. out[0] = (sm23 - sm32) / S;
  418. out[1] = (sm31 - sm13) / S;
  419. out[2] = (sm12 - sm21) / S;
  420. } else if (sm11 > sm22 && sm11 > sm33) {
  421. S = Math.sqrt(1.0 + sm11 - sm22 - sm33) * 2;
  422. out[3] = (sm23 - sm32) / S;
  423. out[0] = 0.25 * S;
  424. out[1] = (sm12 + sm21) / S;
  425. out[2] = (sm31 + sm13) / S;
  426. } else if (sm22 > sm33) {
  427. S = Math.sqrt(1.0 + sm22 - sm11 - sm33) * 2;
  428. out[3] = (sm31 - sm13) / S;
  429. out[0] = (sm12 + sm21) / S;
  430. out[1] = 0.25 * S;
  431. out[2] = (sm23 + sm32) / S;
  432. } else {
  433. S = Math.sqrt(1.0 + sm33 - sm11 - sm22) * 2;
  434. out[3] = (sm12 - sm21) / S;
  435. out[0] = (sm31 + sm13) / S;
  436. out[1] = (sm23 + sm32) / S;
  437. out[2] = 0.25 * S;
  438. }
  439. return out;
  440. }
  441. /**
  442. * Calculates a 4x4 matrix from the given quaternion
  443. *
  444. * @param {!Float32Array} out 4x4 matrix receiving operation result
  445. * @param {!Float32Array} q Quaternion to create matrix from
  446. */
  447. static fromQuat(out, q) {
  448. const x = q[0];
  449. const y = q[1];
  450. const z = q[2];
  451. const w = q[3];
  452. const x2 = x + x;
  453. const y2 = y + y;
  454. const z2 = z + z;
  455. const xx = x * x2;
  456. const yx = y * x2;
  457. const yy = y * y2;
  458. const zx = z * x2;
  459. const zy = z * y2;
  460. const zz = z * z2;
  461. const wx = w * x2;
  462. const wy = w * y2;
  463. const wz = w * z2;
  464. out[0] = 1 - yy - zz;
  465. out[1] = yx + wz;
  466. out[2] = zx - wy;
  467. out[3] = 0;
  468. out[4] = yx - wz;
  469. out[5] = 1 - xx - zz;
  470. out[6] = zy + wx;
  471. out[7] = 0;
  472. out[8] = zx + wy;
  473. out[9] = zy - wx;
  474. out[10] = 1 - xx - yy;
  475. out[11] = 0;
  476. out[12] = 0;
  477. out[13] = 0;
  478. out[14] = 0;
  479. out[15] = 1;
  480. }
  481. /**
  482. * Set a 4x4 matrix to the identity matrix
  483. *
  484. * @param {!Float32Array} out the receiving matrix
  485. * @private
  486. */
  487. static identity_(out) {
  488. for (let i = 0; i < 16; i++) {
  489. out[i] = (i % 5) == 0 ? 1 : 0;
  490. }
  491. }
  492. /**
  493. * Returns the scaling factor component of a transformation
  494. * matrix. If a matrix is built with fromRotationTranslationScale
  495. * with a normalized Quaternion paramter, the returned vector will be
  496. * the same as the scaling vector
  497. * originally supplied.
  498. * @param {!Float32Array} out Vector to receive scaling factor component
  499. * @param {!Float32Array} mat Matrix to be decomposed (input)
  500. * @private
  501. */
  502. static getScaling_(out, mat) {
  503. const m11 = mat[0];
  504. const m12 = mat[1];
  505. const m13 = mat[2];
  506. const m21 = mat[4];
  507. const m22 = mat[5];
  508. const m23 = mat[6];
  509. const m31 = mat[8];
  510. const m32 = mat[9];
  511. const m33 = mat[10];
  512. out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);
  513. out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);
  514. out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);
  515. }
  516. };
  517. /**
  518. * @const {number}
  519. * @private
  520. */
  521. shaka.ui.Matrix4x4.EPSILON_ = 0.000001;