omath::Mat — Matrix class (C++20/23)
Header: your project’s
mat.hpp(requiresvector3.hpp) Namespace:omathRequires: C++23 (uses multi-parameteroperator[]) SIMD (optional): defineOMATH_USE_AVX2to enable AVX2-accelerated multiplication forfloat/double.
Overview
omath::Mat<Rows, Columns, Type, StoreType> is a compile-time, fixed-size matrix with:
- Row/column counts as template parameters (no heap allocations).
- Row-major or column-major storage (compile-time via
MatStoreType). - Arithmetic and linear algebra: matrix × matrix, scalar ops, transpose, determinant, inverse (optional), etc.
- Transform helpers: translation, axis rotations, look-at, perspective & orthographic projections.
- I/O helpers:
to_string/to_wstring/to_u8stringandstd::formatterspecializations.
Template parameters
| Parameter | Description | Default |
|---|---|---|
Rows |
Number of rows (size_t, compile-time) | — |
Columns |
Number of columns (size_t, compile-time) | — |
Type |
Element type (arithmetic) | float |
StoreType |
Storage order: MatStoreType::ROW_MAJOR or MatStoreType::COLUMN_MAJOR |
ROW_MAJOR |
enum class MatStoreType : uint8_t { ROW_MAJOR = 0, COLUMN_MAJOR };
Quick start
#include "mat.hpp"
using omath::Mat;
// 4x4 float, row-major
Mat<4,4> I = {
{1,0,0,0},
{0,1,0,0},
{0,0,1,0},
{0,0,0,1},
};
// Multiply 4x4 transforms
Mat<4,4> A = { {1,2,3,0},{0,1,4,0},{5,6,0,0},{0,0,0,1} };
Mat<4,4> B = { {2,0,0,0},{0,2,0,0},{0,0,2,0},{0,0,0,1} };
Mat<4,4> C = A * B; // matrix × matrix
// Scalar ops
auto D = C * 0.5f; // scale all entries
// Indexing (C++23 multi-parameter operator[])
float a03 = A[0,3]; // same as A.at(0,3)
A[1,2] = 42.0f;
// Transpose, determinant, inverse
auto AT = A.transposed();
float det = A.determinant(); // only for square matrices
auto inv = A.inverted(); // std::optional<Mat>; std::nullopt if non-invertible
Note Multiplication requires the same
StoreTypeandTypeon both operands, and dimensions must match at compile time.
Construction
Mat(); // zero-initialized
Mat(std::initializer_list<std::initializer_list<Type>> rows);
explicit Mat(const Type* raw_data); // copies Rows*Columns elements
Mat(const Mat&); Mat(Mat&&);
- Zeroing/setting
cpp
m.clear(); // set all entries to 0
m.set(3.14f); // set all entries to a value
- Shape & metadata
cpp
Mat<>::row_count(); // constexpr size_t
Mat<>::columns_count(); // constexpr size_t
Mat<>::size(); // constexpr MatSize {rows, columns}
Mat<>::get_store_ordering(); // constexpr MatStoreType
using ContainedType = Type; // alias
Element access
T& at(size_t r, size_t c);
T const& at(size_t r, size_t c) const;
T& operator[](size_t r, size_t c); // C++23
T const& operator[](size_t r, size_t c) const; // C++23
Bounds checking In debug builds you may enable/disable range checks via your compile-time macros (see the source guard around
at()).
Arithmetic
- Matrix × matrix
cpp
// (Rows x Columns) * (Columns x OtherColumns) -> (Rows x OtherColumns)
template<size_t OtherColumns>
Mat<Rows, OtherColumns, Type, StoreType>
operator*(const Mat<Columns, OtherColumns, Type, StoreType>&) const;
* Complexity: `O(Rows * Columns * OtherColumns)`.
* AVX2-accelerated when `OMATH_USE_AVX2` is defined and `Type` is `float` or `double`.
- Scalars
cpp
Mat operator*(const Type& s) const; Mat& operator*=(const Type& s);
Mat operator/(const Type& s) const; Mat& operator/=(const Type& s);
- Transpose
cpp
Mat<Columns, Rows, Type, StoreType> transposed() const noexcept;
- Determinant (square only)
cpp
Type determinant() const; // 1x1, 2x2 fast path; larger uses Laplace expansion
- Inverse (square only)
cpp
std::optional<Mat> inverted() const; // nullopt if det == 0
- Minors & cofactors (square only)
cpp
Mat<Rows-1, Columns-1, Type, StoreType> strip(size_t r, size_t c) const;
Type minor(size_t r, size_t c) const;
Type alg_complement(size_t r, size_t c) const; // cofactor
- Utilities
cpp
Type sum() const noexcept;
auto& raw_array(); // std::array<Type, Rows*Columns>&
auto const& raw_array() const;
- Comparison / formatting
```cpp bool operator==(const Mat&) const; bool operator!=(const Mat&) const;
std::string to_string() const noexcept; std::wstring to_wstring() const noexcept; std::u8string to_u8string() const noexcept; ```
// std::formatter specialization provided for char, wchar_t, char8_t
---
## Storage order notes
- **Row-major**: `index = row * Columns + column`
- **Column-major**: `index = row + column * Rows`
Choose one **consistently** across your math types and shader conventions. Mixed orders are supported by the type system but not for cross-multiplying (store types must match).
---
## Transform helpers
### From vectors
```cpp
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<1,4,T,St> mat_row_from_vector(const Vector3<T>& v);
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,1,T,St> mat_column_from_vector(const Vector3<T>& v);
Translation
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_translation(const Vector3<T>& d) noexcept;
Axis rotations
// Angle type must provide angle.cos() and angle.sin()
template<class T=float, MatStoreType St=ROW_MAJOR, class Angle>
Mat<4,4,T,St> mat_rotation_axis_x(const Angle& a) noexcept;
template<class T=float, MatStoreType St=ROW_MAJOR, class Angle>
Mat<4,4,T,St> mat_rotation_axis_y(const Angle& a) noexcept;
template<class T=float, MatStoreType St=ROW_MAJOR, class Angle>
Mat<4,4,T,St> mat_rotation_axis_z(const Angle& a) noexcept;
Camera/view
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_camera_view(const Vector3<T>& forward,
const Vector3<T>& right,
const Vector3<T>& up,
const Vector3<T>& camera_origin) noexcept;
Perspective projections
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_perspective_left_handed (float fov_deg, float aspect, float near, float far) noexcept;
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_perspective_right_handed(float fov_deg, float aspect, float near, float far) noexcept;
Orthographic projections
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_ortho_left_handed (T left, T right, T bottom, T top, T near, T far) noexcept;
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_ortho_right_handed(T left, T right, T bottom, T top, T near, T far) noexcept;
Look-at matrices
template<class T=float, MatStoreType St=COLUMN_MAJOR>
Mat<4,4,T,St> mat_look_at_left_handed (const Vector3<T>& eye,
const Vector3<T>& center,
const Vector3<T>& up);
template<class T=float, MatStoreType St=COLUMN_MAJOR>
Mat<4,4,T,St> mat_look_at_right_handed(const Vector3<T>& eye,
const Vector3<T>& center,
const Vector3<T>& up);
Screen-space helper
template<class Type=float>
static constexpr Mat<4,4> to_screen_mat(const Type& screen_w, const Type& screen_h) noexcept;
// Maps NDC to screen space (origin top-left, y down)
Examples
1) Building a left-handed camera and perspective
using V3 = omath::Vector3<float>;
using M4 = omath::Mat<4,4,float, omath::MatStoreType::COLUMN_MAJOR>;
V3 eye{0, 1, -5}, center{0, 0, 0}, up{0, 1, 0};
M4 view = omath::mat_look_at_left_handed<float, omath::MatStoreType::COLUMN_MAJOR>(eye, center, up);
float fov = 60.f, aspect = 16.f/9.f, n = 0.1f, f = 100.f;
M4 proj = omath::mat_perspective_left_handed<float, omath::MatStoreType::COLUMN_MAJOR>(fov, aspect, n, f);
// final VP
M4 vp = proj * view;
2) Inverting a transform safely
omath::Mat<4,4> T = omath::mat_translation(omath::Vector3<float>{2,3,4});
if (auto inv = T.inverted()) {
// use *inv
} else {
// handle non-invertible
}
3) Formatting for logs
omath::Mat<2,2> A = { {1,2},{3,4} };
std::string s = A.to_string(); // "[[ 1.000, 2.000]\n [ 3.000, 4.000]]"
std::string f = std::format("A = {}", A); // uses std::formatter
Performance
- Cache-friendly kernels per storage order when AVX2 is not enabled.
- AVX2 path (
OMATH_USE_AVX2) forfloat/doubleimplements FMAs with 256-bit vectors for both row-major and column-major multiplication. - Complexity for
A(R×K) * B(K×C): O(RKC) regardless of storage order.
Constraints & concepts
template<typename M1, typename M2>
concept MatTemplateEqual =
(M1::rows == M2::rows) &&
(M1::columns == M2::columns) &&
std::is_same_v<typename M1::value_type, typename M2::value_type> &&
(M1::store_type == M2::store_type);
Use this concept to constrain generic functions that operate on like-shaped matrices.
Exceptions
std::invalid_argument— initializer list dimensions mismatch.std::out_of_range— out-of-bounds inat()when bounds checking is active (see source guard).inverted()does not throw; returnsstd::nulloptifdeterminant() == 0.
Build switches
OMATH_USE_AVX2— enable AVX2 vectorized multiplication paths (<immintrin.h>required).- Debug checks — the
at()method contains a conditional range check; refer to the preprocessor guard in the code to enable/disable in your configuration.
Known requirements & interoperability
- C++23 is required for multi-parameter
operator[]. If you target pre-C++23, useat(r,c)instead. - All binary operations require matching
TypeandStoreType. Convert explicitly if needed.
See also
omath::Vector3<T>- Projection helpers:
mat_perspective_*,mat_ortho_* - View helpers:
mat_look_at_*,mat_camera_view - Construction helpers:
mat_row_from_vector,mat_column_from_vector,mat_translation,mat_rotation_axis_*
Appendix: API summary (signatures)
// Core
Mat(); Mat(const Mat&); Mat(Mat&&);
Mat(std::initializer_list<std::initializer_list<Type>>);
explicit Mat(const Type* raw);
Mat& operator=(const Mat&); Mat& operator=(Mat&&);
static constexpr size_t row_count();
static constexpr size_t columns_count();
static consteval MatSize size();
static constexpr MatStoreType get_store_ordering();
T& at(size_t r, size_t c);
T const& at(size_t r, size_t c) const;
T& operator[](size_t r, size_t c);
T const& operator[](size_t r, size_t c) const;
void clear();
void set(const Type& v);
Type sum() const noexcept;
template<size_t OC> Mat<Rows,OC,Type,StoreType> operator*(const Mat<Columns,OC,Type,StoreType>&) const;
Mat& operator*=(const Type&); Mat operator*(const Type&) const;
Mat& operator/=(const Type&); Mat operator/(const Type&) const;
Mat<Columns,Rows,Type,StoreType> transposed() const noexcept;
Type determinant() const; // square only
std::optional<Mat> inverted() const; // square only
Mat<Rows-1,Columns-1,Type,StoreType> strip(size_t r, size_t c) const;
Type minor(size_t r, size_t c) const;
Type alg_complement(size_t r, size_t c) const;
auto& raw_array(); auto const& raw_array() const;
std::string to_string() const noexcept;
std::wstring to_wstring() const noexcept;
std::u8string to_u8string() const noexcept;
bool operator==(const Mat&) const;
bool operator!=(const Mat&) const;
// Helpers (see sections above)
Last updated: 31 Oct 2025