UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

125 lines (103 loc) 4 kB
import { v2_length } from "../../vec2/v2_length.js"; /** * * @param {number[]} mat4 * @param {number} row_index * @param {number} column_index * @return {number} * @ignore */ function coeff(mat4, row_index, column_index) { return mat4[row_index + column_index * 4]; } const sin = Math.sin; const cos = Math.cos; const atan2 = Math.atan2; /** * * returns the Euler-angles of the rotation matrix \c *this using the convention defined by the triplet (`a0`,`a1`,`a2`) * * Each of the three parameters `a0`, `a1`, `a2` represents the respective rotation axis as an integer in {0,1,2}. * For instance, in: * ``` * Vector3f ea = mat.eulerAngles(2, 0, 2); * ``` * "2" represents the z axis and "0" the x axis, etc. The returned angles are such that * we have the following equality: * ``` * mat == AngleAxisf(ea[0], Vector3f::UnitZ()) * * AngleAxisf(ea[1], Vector3f::UnitX()) * * AngleAxisf(ea[2], Vector3f::UnitZ()); * ``` * This corresponds to the right-multiply conventions (with right hand side frames). * * The returned angles are in the ranges [0:pi]x[-pi:pi]x[-pi:pi]. * NOTE: ported from Eigen C++ library * * @see https://gitlab.com/libeigen/eigen/-/blob/master/Eigen/src/Geometry/EulerAngles.h * @see https://stackoverflow.com/questions/11514063/extract-yaw-pitch-and-roll-from-a-rotationmatrix * * @param {number[]} output * @param {number[]|Float32Array|mat4} m4 source matrix to extract rotation from * @param {number} a0 axis index (0 = X, 1 = Y, 2 = Z) * @param {number} a1 axis index * @param {number} a2 axis index */ export function eulerAnglesFromMatrix( output, m4, a0, a1, a2 ) { const odd = ((a0 + 1) % 3 === a1) ? 0 : 1; const i = a0; const j = (a0 + 1 + odd) % 3; const k = (a0 + 2 - odd) % 3; if (a0 === a2) { output[0] = atan2(coeff(m4, j, i), coeff(m4, k, i)); if ((odd && output[0] < 0) || ((~odd) && output[0] > 0)) { if (output[0] > 0) { output[0] -= Math.PI; } else { output[0] += Math.PI; } const s2 = v2_length(coeff(m4, j, i), coeff(m4, k, i)); output[1] = -atan2(s2, coeff(m4, i, i)); } else { const s2 = v2_length(coeff(m4, j, i), coeff(m4, k, i)); output[1] = atan2(s2, coeff(m4, i, i)); } // With a=(0,1,0), we have i=0; j=1; k=2, and after computing the first two angles, // we can compute their respective rotation, and apply its inverse to M. Since the result must // be a rotation around x, we have: // // c2 s1.s2 c1.s2 1 0 0 // 0 c1 -s1 * M = 0 c3 s3 // -s2 s1.c2 c1.c2 0 -s3 c3 // // Thus: m11.c1 - m21.s1 = c3 & m12.c1 - m22.s1 = s3 const s1 = sin(output[0]); const c1 = cos(output[0]); output[2] = atan2(c1 * coeff(m4, j, k) - s1 * coeff(m4, k, k), c1 * coeff(m4, j, j) - s1 * coeff(m4, k, j)); } else { output[0] = atan2(coeff(m4, j, k), coeff(m4, k, k)); const c2 = v2_length(coeff(m4, i, i), coeff(m4, i, j)); if ((odd && output[0] < 0) || ((~odd) && output[0] > 0)) { if (output[0] > 0) { output[0] -= Math.PI; } else { output[0] += Math.PI; } output[1] = atan2(-coeff(m4, i, k), -c2); } else { output[1] = atan2(-coeff(m4, i, k), c2); } const s1 = sin(output[0]); const c1 = cos(output[0]); output[2] = atan2(s1 * coeff(m4, k, i) - c1 * coeff(m4, j, i), c1 * coeff(m4, j, j) - s1 * coeff(m4, k, j)); } if (odd === 0) { // invert result output[0] = -output[0]; output[1] = -output[1]; output[2] = -output[2]; } }