export module math.mat4; import preliminary; import math.vec2; import math.vec3; import math.vec4; export namespace lt::math { /** A 4 by 4 matrix, column major order * * @todo(Light): Use std::simd when it's implemented. */ template requires(std::is_arithmetic_v) struct mat4_impl { using Column_T = vec4_impl; using Underlying_T = Column_T::Underlying_T; static constexpr auto num_elements = 4u * 4u; constexpr explicit mat4_impl(T scalar = T {}) : values( { Column_T { scalar }, Column_T { scalar }, Column_T { scalar }, Column_T { scalar }, } ) { } constexpr mat4_impl( // clang-format off const T& x0, const T& x1, const T& x2, const T& x3, const T& y0, const T& y1, const T& y2, const T& y3, const T& z0, const T& z1, const T& z2, const T& z3, const T& w0, const T& w1, const T& w2, const T& w3) // clang-format on : values({ { x0, x1, x2, x3 }, { y0, y1, y2, y3 }, { z0, z1, z2, z3 }, { w0, w1, w2, w3 } }) { } constexpr mat4_impl( const Column_T &column_x, const Column_T &column_y, const Column_T &column_z, const Column_T &column_w ) : values({ column_x, column_y, column_z, column_w }) { } [[nodiscard]] static constexpr auto identity() -> mat4_impl { return mat4_impl { { 1 }, {}, {}, {}, // {}, { 1 }, {}, {}, // {}, {}, { 1 }, {}, // {}, {}, {}, { 1 }, // }; } [[nodiscard]] constexpr auto operator*(const mat4_impl &other) const -> mat4_impl { const auto &[a_x, a_y, a_z, a_w] = values; const auto &[b_x, b_y, b_z, b_w] = other.values; return mat4_impl( // X column a_x.x * b_x.x + a_y.x * b_x.y + a_z.x * b_x.z + a_w.x * b_x.w, a_x.y * b_x.x + a_y.y * b_x.y + a_z.y * b_x.z + a_w.y * b_x.w, a_x.z * b_x.x + a_y.z * b_x.y + a_z.z * b_x.z + a_w.z * b_x.w, a_x.w * b_x.x + a_y.w * b_x.y + a_z.w * b_x.z + a_w.w * b_x.w, // Y column a_x.x * b_y.x + a_y.x * b_y.y + a_z.x * b_y.z + a_w.x * b_y.w, a_x.y * b_y.x + a_y.y * b_y.y + a_z.y * b_y.z + a_w.y * b_y.w, a_x.z * b_y.x + a_y.z * b_y.y + a_z.z * b_y.z + a_w.z * b_y.w, a_x.w * b_y.x + a_y.w * b_y.y + a_z.w * b_y.z + a_w.w * b_y.w, // Z column a_x.x * b_z.x + a_y.x * b_z.y + a_z.x * b_z.z + a_w.x * b_z.w, a_x.y * b_z.x + a_y.y * b_z.y + a_z.y * b_z.z + a_w.y * b_z.w, a_x.z * b_z.x + a_y.z * b_z.y + a_z.z * b_z.z + a_w.z * b_z.w, a_x.w * b_z.x + a_y.w * b_z.y + a_z.w * b_z.z + a_w.w * b_z.w, // W column a_x.x * b_w.x + a_y.x * b_w.y + a_z.x * b_w.z + a_w.x * b_w.w, a_x.y * b_w.x + a_y.y * b_w.y + a_z.y * b_w.z + a_w.y * b_w.w, a_x.z * b_w.x + a_y.z * b_w.y + a_z.z * b_w.z + a_w.z * b_w.w, a_x.w * b_w.x + a_y.w * b_w.y + a_z.w * b_w.z + a_w.w * b_w.w ); } [[nodiscard]] constexpr auto operator[](size_t idx) -> Column_T & { debug_check(idx < num_elements, "mat4 out of bound access: {}", idx); return values[idx]; } [[nodiscard]] constexpr auto operator[](size_t idx) const -> const Column_T & { return values[idx]; } [[nodiscard]] static constexpr auto transpose(const mat4_impl &mat) -> mat4_impl { const auto &[x, y, z, w] = mat.values; return mat4_impl { x.x, y.x, z.x, w.x, x.y, y.y, z.y, w.y, x.z, y.z, z.z, w.z, x.w, y.w, z.w, w.w, }; } [[nodiscard]] static constexpr auto translate(const vec3_impl &vec) -> mat4_impl { return mat4_impl( T { 1 }, T { 0 }, T { 0 }, T { 0 }, T { 0 }, T { 1 }, T { 0 }, T { 0 }, T { 0 }, T { 0 }, T { 1 }, T { 0 }, vec.x, vec.y, vec.z, T { 1 } ); } [[nodiscard]] static constexpr auto scale(const vec3_impl &vec) -> mat4_impl { return mat4_impl( vec.x, T { 0 }, T { 0 }, T { 0 }, T { 0 }, vec.y, T { 0 }, T { 0 }, T { 0 }, T { 0 }, vec.z, T { 0 }, T { 0 }, T { 0 }, T { 0 }, T { 1 } ); } std::array values; }; using mat4 = mat4_impl; using mat4_f32 = mat4; using mat4_f64 = mat4_impl; using mat4_i8 = mat4_impl; using mat4_i16 = mat4_impl; using mat4_i32 = mat4_impl; using mat4_i64 = mat4_impl; using mat4_u8 = mat4_impl; using mat4_u16 = mat4_impl; using mat4_u32 = mat4_impl; using mat4_u64 = mat4_impl; } // namespace lt::math