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