// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "tdm/Types.h" #include "tdm/Mat.h" #include "tdm/Vec.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4365 4987) #endif #include #include #include #ifdef _MSC_VER #pragma warning(pop) #endif namespace tdm { template inline vec3 cross(const vec3& lhs, const vec3& rhs) { return vec3{ lhs[1] * rhs[2] - lhs[2] * rhs[1], lhs[2] * rhs[0] - lhs[0] * rhs[2], lhs[0] * rhs[1] - lhs[1] * rhs[0] }; } template inline T dot(const vec& lhs, const vec& rhs) { return (lhs * rhs).sum(); } template inline T dot(const quat& q1, const quat& q2) { return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; } template inline vec negate(vec v) { return v.negate(); } template inline mat negate(mat m) { return m.negate(); } template inline quat negate(quat q) { return q.negate(); } template inline typename std::enable_if::value, T>::type length(const vec& v) { return v.length(); } template inline typename std::enable_if::value, T>::type length(const quat& q) { return q.length(); } template inline typename std::enable_if::value, vec >::type normalize(vec v) { return v.normalize(); } template inline typename std::enable_if::value, quat >::type normalize(quat q) { return q.normalize(); } template inline quat conjugate(const quat& q) { return quat{-q.x, -q.y, -q.z, q.w}; } template inline quat inverse(const quat& q) { return conjugate(q) * (static_cast(1.0) / q.length2()); } template inline quat lerp(const quat& q1, const quat& q2, T t) { return q1 * (static_cast(1.0) - t) + q2 * t; } template inline quat slerp(const quat& q1, const quat& q2, T t) { T costheta = dot(q1, q2); quat tmp = q2; if (costheta < static_cast(0.0)) { tmp.negate(); costheta = -costheta; } T t1; T t2; if (costheta > (static_cast(1.0) - std::numeric_limits::epsilon())) { t1 = static_cast(1.0) - t; t2 = t; } else { const T theta = std::acos(costheta); const T sintheta = std::sin(theta); const T rsintheta = static_cast(1.0) / sintheta; t1 = std::sin((static_cast(1.0) - t) * theta) * rsintheta; t2 = std::sin(t * theta) * rsintheta; } return q1 * t1 + tmp * t2; } template inline mat transpose(const mat& m) { using row_type = typename mat::row_type; mat ret; ret.apply([&m](row_type& row, dim_t i) { row = m.column(i); }); return ret; } namespace impl { #pragma push_macro("minor") #undef minor template inline void minor(const mat& input, dim_t dimensions, dim_t i, dim_t j, mat& output) { for (dim_t outRow{}, inRow{}; inRow < dimensions; ++inRow) { for (dim_t outCol{}, inCol{}; inCol < dimensions; ++inCol) { if ((inRow != i) && (inCol != j)) { output(outRow, outCol) = input(inRow, inCol); ++outCol; if (outCol == (dimensions - static_cast(1))) { outCol = {}; ++outRow; } } } } } template inline T determinant(const mat& m, dim_t dimensions) { if (dimensions == static_cast(1)) { return m(0, 0); } T result{}; mat temp; auto sign = static_cast(1); const dim_t i{}; for (dim_t j{}; j < dimensions; ++j) { minor(m, dimensions, i, j, temp); result += (sign * m(i, j) * determinant(temp, dimensions - 1)); sign = -sign; } return result; } template inline mat adjoint(const mat& m) { if (m.rows() == static_cast(1)) { return mat{static_cast(1)}; } mat result; mat temp; for (dim_t row{}; row < m.rows(); ++row) { for (dim_t col{}; col < m.columns(); ++col) { minor(m, N, row, col, temp); const T sign = static_cast((row + col) % 2u == 0u ? 1 : -1); result(col, row) = (sign * determinant(temp, N - 1)); } } return result; } #pragma pop_macro("minor") } // namespace impl template inline T determinant(const mat& m) { return impl::determinant(m, N); } template inline mat inverse(const mat& m) { T det = determinant(m); if (det == T{}) { return {}; } mat adj = impl::adjoint(m); mat inv; for (dim_t row{}; row < m.rows(); ++row) { for (dim_t col{}; col < m.columns(); ++col) { inv(row, col) = adj(row, col) / det; } } return inv; } template inline T trace(const mat& m) { T trace{0}; for (dim_t row{}; row < m.rows(); ++row) { trace += m(row, row); } return trace; } namespace lu { // Based on the algorithms found in : // Numerical Recipes in C - The Art of Scientific Computing - Second Edition template inline bool decompose(mat& a, vec& permute) { constexpr T absmin = static_cast(1.0e-20); vec scale; for (dim_t i = {}; i < N; ++i) { T rowMax = {}; for (dim_t j = {}; j < N; ++j) { rowMax = std::max(rowMax, std::abs(a(i, j))); } if (rowMax == static_cast(0.0)) { return false; } scale[i] = static_cast(1.0) / rowMax; } for (dim_t j = {}; j < N; ++j) { for (dim_t i = {}; i < j; ++i) { T sum = a(i, j); for (dim_t k = {}; k < i; ++k) { sum -= a(i, k) * a(k, j); } a(i, j) = sum; } dim_t iMax = {}; T colMax = {}; for (dim_t i = j; i < N; ++i) { T sum = a(i, j); for (dim_t k = {}; k < j; ++k) { sum -= a(i, k) * a(k, j); } a(i, j) = sum; const T temp = scale[i] * std::abs(sum); if (temp >= colMax) { colMax = temp; iMax = i; } } if (j != iMax) { for (dim_t k = {}; k < N; ++k) { std::swap(a(iMax, k), a(j, k)); } scale[iMax] = scale[j]; } permute[j] = iMax; if (a[j][j] == static_cast(0.0)) { a[j][j] = absmin; } if (j != (N - 1)) { const T temp = static_cast(1.0) / a(j, j); for (dim_t i = j + 1; i < N; ++i) { a(i, j) *= temp; } } } return true; } template inline void substitute(const mat& a, const vec& permute, vec& b) { dim_t ii = {}; for (dim_t i = {}; i < N; ++i) { const dim_t ip = permute[i]; T sum = b[ip]; b[ip] = b[i]; if (ii) { for (dim_t j = (ii - 1); j < i; ++j) { sum -= a(i, j) * b[j]; } } else if (sum != static_cast(0.0)) { ii = i + 1; } b[i] = sum; } for (dim_t ipo = N; ipo > 0; --ipo) { const dim_t i = ipo - 1; T sum = b[i]; for (dim_t j = i + 1; j < N; ++j) { sum -= a(i, j) * b[j]; } b[i] = sum / a(i, i); } } template inline mat inverse(mat m) { vec permute; if (!decompose(m, permute)) { return {}; } mat inv; for (dim_t j = {}; j < N; ++j) { vec col; col[j] = static_cast(1.0); substitute(m, permute, col); for (dim_t i = {}; i < N; ++i) { inv[i][j] = col[i]; } } return inv; } } // namespace lu } // namespace tdm