From 13288eda0186a172c9c75a531594fdfd23bcfe6b Mon Sep 17 00:00:00 2001 From: Slendi Date: Thu, 4 Dec 2025 16:21:00 +0200 Subject: [PATCH] Matrix stuff Signed-off-by: Slendi --- include/smath.hpp | 241 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 230 insertions(+), 11 deletions(-) diff --git a/include/smath.hpp b/include/smath.hpp index 369c714..2e302af 100644 --- a/include/smath.hpp +++ b/include/smath.hpp @@ -553,6 +553,15 @@ struct Mat : std::array, C> { using Base = std::array, C>; using Base::operator[]; + constexpr auto operator[](std::size_t const row, std::size_t const column) + -> T & { + return col(column)[row]; + } + constexpr auto operator[](std::size_t const row, + std::size_t const column) const -> T const & { + return col(column)[row]; + } + constexpr Mat() noexcept { for (auto &col : *this) col = Vec{}; @@ -636,28 +645,31 @@ struct Mat : std::array, C> { return lhs; } - constexpr auto operator==(Mat const &rhs) const noexcept -> bool { + [[nodiscard]] constexpr auto operator==(Mat const &rhs) const noexcept + -> bool { for (std::size_t c = 0; c < C; ++c) if (!((*this)[c] == rhs[c])) return false; return true; } - constexpr auto operator!=(Mat const &rhs) const noexcept -> bool { + [[nodiscard]] constexpr auto operator!=(Mat const &rhs) const noexcept + -> bool { return !(*this == rhs); } static constexpr T EPS_DEFAULT = T(1e-6); template requires std::is_floating_point_v - constexpr auto approx_equal(Mat const &rhs, - U eps = EPS_DEFAULT) const noexcept -> bool { + [[nodiscard]] constexpr auto approx_equal(Mat const &rhs, + U eps = EPS_DEFAULT) const noexcept + -> bool { for (std::size_t c = 0; c < C; ++c) if (!(*this)[c].approx_equal(rhs[c], eps)) return false; return true; } - constexpr auto transposed() const noexcept -> Mat { + [[nodiscard]] constexpr auto transposed() const noexcept -> Mat { Mat r{}; for (std::size_t c = 0; c < C; ++c) for (std::size_t r_idx = 0; r_idx < R; ++r_idx) @@ -665,7 +677,7 @@ struct Mat : std::array, C> { return r; } - static constexpr auto identity() noexcept -> Mat + [[nodiscard]] static constexpr auto identity() noexcept -> Mat requires(R == C) { Mat m{}; @@ -676,8 +688,8 @@ struct Mat : std::array, C> { }; template -constexpr Vec operator*(Mat const &m, - Vec const &v) noexcept { +[[nodiscard]] constexpr Vec operator*(Mat const &m, + Vec const &v) noexcept { Vec out{}; for (std::size_t c = 0; c < C; ++c) out += m.col(c) * v[c]; @@ -686,20 +698,227 @@ constexpr Vec operator*(Mat const &m, // Matrix * Matrix template -constexpr Mat operator*(Mat const &a, - Mat const &b) noexcept { +[[nodiscard]] constexpr Mat operator*(Mat const &a, + Mat const &b) noexcept { Mat out{}; for (std::size_t k = 0; k < K; ++k) { for (std::size_t r = 0; r < R; ++r) { T sum = T(0); for (std::size_t c = 0; c < C; ++c) - sum += a(r, c) * b(r, k); + sum += a(r, c) * b(c, k); out(r, k) = sum; } } return out; } +// Mat3 transformations +template +[[nodiscard]] inline auto translate(Mat<3, 3, T> const &m, Vec<2, T> const &v) + -> Mat<3, 3, T> { + Mat<3, 3, T> res{m}; + res[2] = m[0] * v[0] + m[1] * v[1] + m[2]; + return res; +} + +template +[[nodiscard]] inline auto rotate(Mat<3, 3, T> const &m, T const angle) + -> Mat<3, 3, T> { + Mat<3, 3, T> res; + + T const c{std::cos(angle)}; + T const s{std::sin(angle)}; + + res[0] = m[0] * c + m[1] * s; + res[1] = m[0] * -s + m[1] * c; + res[2] = m[2]; + + return res; +} + +template +[[nodiscard]] inline auto scale(Mat<3, 3, T> const &m, Vec<2, T> const &v) + -> Mat<3, 3, T> { + Mat<3, 3, T> res; + + res[0] = m[0] * v[0]; + res[1] = m[1] * v[1]; + res[2] = m[2]; + + return res; +} + +template +[[nodiscard]] inline auto shear_x(Mat<3, 3, T> const &m, T const v) + -> Mat<3, 3, T> { + Mat<3, 3, T> res{1}; + res[1][0] = v; + return m * res; +} + +template +[[nodiscard]] inline auto shear_y(Mat<3, 3, T> const &m, T const v) + -> Mat<3, 3, T> { + Mat<3, 3, T> res{1}; + res[0][1] = v; + return m * res; +} + +// Mat4 transformations +template +[[nodiscard]] inline auto translate(Mat<4, 4, T> const &m, Vec<3, T> const &v) + -> Mat<4, 4, T> { + Mat<4, 4, T> res{m}; + res[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3]; + return res; +} + +template +[[nodiscard]] inline auto rotate(Mat<4, 4, T> const &m, T const angle) + -> Mat<4, 4, T> { + Mat<4, 4, T> res; + + T const c{std::cos(angle)}; + T const s{std::sin(angle)}; + + res[0] = m[0] * c + m[1] * s; + res[1] = m[0] * -s + m[1] * c; + res[2] = m[2]; + res[3] = m[3]; + + return res; +} + +template +[[nodiscard]] inline auto scale(Mat<4, 4, T> const &m, Vec<3, T> const &v) + -> Mat<4, 4, T> { + Mat<4, 4, T> res; + + res[0] = m[0] * v[0]; + res[1] = m[1] * v[1]; + res[2] = m[2] * v[2]; + res[3] = m[3]; + + return res; +} + +template +[[nodiscard]] inline auto shear_x(Mat<4, 4, T> const &m, T const v) + -> Mat<4, 4, T> { + Mat<4, 4, T> res{1}; + res[0, 1] = v; + return m * res; +} + +template +[[nodiscard]] inline auto shear_y(Mat<4, 4, T> const &m, T const v) + -> Mat<4, 4, T> { + Mat<4, 4, T> res{1}; + res[1, 0] = v; + return m * res; +} + +template +[[nodiscard]] inline auto shear_z(Mat<4, 4, T> const &m, T const v) + -> Mat<4, 4, T> { + Mat<4, 4, T> res{1}; + res[2, 0] = v; + return m * res; +} + +template +[[nodiscard]] inline auto +matrix_ortho3d(T const left, T const right, T const bottom, T const top, + T const near, T const far, bool const flip_z_axis = true) + -> Mat<4, 4, T> { + Mat<4, 4, T> res{}; + + res[0, 0] = 2 / (right - left); + res[1, 1] = 2 / (top - bottom); + res[2, 2] = -2 / (far - near); + res[0, 3] = -(right + left) / (right - left); + res[1, 3] = -(top + bottom) / (top - bottom); + res[2, 3] = -(far + near) / (far - near); + res[3, 3] = 1; + + if (flip_z_axis) { + res[2] = -res[2]; + } + + return res; +} + +template +inline auto matrix_perspective(T fovy, T aspect, T znear, T zfar, + bool flip_z_axis = false) -> Mat<4, 4, T> { + Mat<4, 4, T> m{}; + + T const f{1 / std::tan(fovy / T(2))}; + + m(0, 0) = f / aspect; + m(1, 1) = f; + + if (!flip_z_axis) { + m(2, 2) = -(zfar + znear) / (zfar - znear); + m(2, 3) = -(T(2) * zfar * znear) / (zfar - znear); + m(3, 2) = -1; + } else { + m(2, 2) = (zfar + znear) / (zfar - znear); + m(2, 3) = (T(2) * zfar * znear) / (zfar - znear); + m(3, 2) = 1; + } + + return m; +} + +template +[[nodiscard]] inline auto +matrix_look_at(Vec<3, T> const eye, Vec<3, T> const center, Vec<3, T> const up, + bool flip_z_axis = false) -> Mat<4, 4, T> { + auto f = (center - eye).normalized(); + auto s = f.cross(up).normalized(); + auto u = s.cross(f); + + if (!flip_z_axis) { + return { + {s.x(), u.x(), -f.x(), 0}, + {s.y(), u.y(), -f.y(), 0}, + {s.z(), u.z(), -f.z(), 0}, + {-s.dot(eye), -u.dot(eye), f.dot(eye), 1}, + }; + } else { + return { + {s.x(), u.x(), f.x(), 0}, + {s.y(), u.y(), f.y(), 0}, + {s.z(), u.z(), f.z(), 0}, + {-s.dot(eye), -u.dot(eye), -f.dot(eye), 1}, + }; + } +} + +template +[[nodiscard]] inline auto +matrix_infinite_perspective(T const fovy, T const aspect, T const znear, + bool flip_z_axis = false) -> Mat<4, 4, T> { + Mat<4, 4, T> m{}; + + T const f = 1 / std::tan(fovy / T(2)); + m(0, 0) = f / aspect; + m(1, 1) = f; + + if (!flip_z_axis) { + m(2, 2) = -1; + m(2, 3) = -T(2) * znear; + m(3, 2) = -1; + } else { + m(2, 2) = 1; + m(2, 3) = T(2) * znear; + m(3, 2) = 1; + } + + return m; +} + } // namespace smath template