Adds documentation for the `omath::ViewAngles` struct, clarifying its purpose, common usage patterns, and the definition of the types of pitch, yaw and roll. Also, adds short explanations of how to use ViewAngles and what tradeoffs exist between using raw float types and strongly typed Angle<> types.
12 KiB
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
m.clear(); // set all entries to 0 m.set(3.14f); // set all entries to a value -
Shape & metadata
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
// (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_AVX2is defined andTypeisfloatordouble.
- Complexity:
-
Scalars
Mat operator*(const Type& s) const; Mat& operator*=(const Type& s); Mat operator/(const Type& s) const; Mat& operator/=(const Type& s); -
Transpose
Mat<Columns, Rows, Type, StoreType> transposed() const noexcept; -
Determinant (square only)
Type determinant() const; // 1x1, 2x2 fast path; larger uses Laplace expansion -
Inverse (square only)
std::optional<Mat> inverted() const; // nullopt if det == 0 -
Minors & cofactors (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; // cofactor -
Utilities
Type sum() const noexcept; auto& raw_array(); // std::array<Type, Rows*Columns>& auto const& raw_array() const; -
Comparison / formatting
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