/* * Copyright (C) 2021 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #ifndef ANTKEEPER_MATH_MATRIX_HPP #define ANTKEEPER_MATH_MATRIX_HPP #include "math/vector.hpp" #include #include #include #include #include #include namespace math { /** * *n* by *m* column-major matrix. * * @tparam T Matrix element data type. * @tparam N Number of columns. * @tparam M Number of rows. * * @see https://en.wikipedia.org/wiki/Row-_and_column-major_order */ template struct matrix { /// Matrix element data type. typedef T element_type; /// Number of matrix columns. static constexpr std::size_t column_count = N; /// Number of matrix rows. static constexpr std::size_t row_count = M; /// Number of matrix elements. static constexpr std::size_t element_count = column_count * row_count; /// Matrix column vector data type. typedef vector column_vector_type; /// Matrix row vector data type. typedef vector row_vector_type; /// Array of matrix column vectors. column_vector_type columns[column_count]; /** * Returns a reference to the column vector at a given index. * * @param i Index of a column vector. * * @return Reference to the column vector at index @p i. */ /// @{ constexpr inline column_vector_type& operator[](std::size_t i) noexcept { return columns[i]; } constexpr inline const column_vector_type& operator[](std::size_t i) const noexcept { return columns[i]; } constexpr inline column_vector_type& column(std::size_t i) noexcept { return columns[i]; } constexpr inline const column_vector_type& column(std::size_t i) const noexcept { return columns[i]; } /// @} /** * Returns a reference to the element at a given column-major index. * * @param i Column-major index of a matrix element. * * @return Reference to the element at column-major index @p i. */ /// @{ constexpr inline T& element(std::size_t i) noexcept { return columns[i / row_count][i % row_count]; } constexpr inline const T& element(std::size_t i) const noexcept { return columns[i / row_count][i % row_count]; } /// @} /// Returns a reference to the first column vector. /// @{ constexpr inline column_vector_type& front() noexcept { return columns[0]; } constexpr inline const column_vector_type& front() const noexcept { return columns[0]; } /// @} /// Returns a reference to the last column vector. /// @{ constexpr inline column_vector_type& back() noexcept { return elements[column_count - 1]; } constexpr inline const column_vector_type& back() const noexcept { return elements[column_count - 1]; } /// @} /** * Returns a pointer to the first element. * * @warning If column_vector_type is not a POD type, elements may not be stored contiguously. */ /// @{ constexpr inline element_type* data() noexcept { return &columns[0][0]; }; constexpr inline const element_type* data() const noexcept { return &columns[0][0]; }; /// @} /// Returns an iterator to the first column vector. /// @{ constexpr inline column_vector_type* begin() noexcept { return columns; } constexpr inline const column_vector_type* begin() const noexcept { return columns; } constexpr inline const column_vector_type* cbegin() const noexcept { return columns; } /// @} /// Returns an iterator to the column vector following the last column vector. /// @{ constexpr inline column_vector_type* end() noexcept { return columns + column_count; } constexpr inline const column_vector_type* end() const noexcept { return columns + column_count; } constexpr inline const column_vector_type* cend() const noexcept { return columns + column_count; } /// @} /// Returns a reverse iterator to the first column vector of the reversed matrix. /// @{ constexpr inline std::reverse_iterator rbegin() noexcept { return std::reverse_iterator(columns + column_count); } constexpr inline std::reverse_iterator rbegin() const noexcept { return std::reverse_iterator(columns + column_count); } constexpr inline std::reverse_iterator crbegin() const noexcept { return std::reverse_iterator(columns + column_count); } /// @} /// Returns a reverse iterator to the column vector following the last column vector of the reversed matrix. /// @{ constexpr inline std::reverse_iterator rend() noexcept { return std::reverse_iterator(columns); } constexpr inline std::reverse_iterator rend() const noexcept { return std::reverse_iterator(columns); } constexpr inline std::reverse_iterator crend() const noexcept { return std::reverse_iterator(columns); } /// @} /// Returns the number of elements in the matrix. constexpr inline std::size_t size() const noexcept { return element_count; }; /// @private template constexpr inline matrix type_cast(std::index_sequence) const noexcept { return {vector(columns[I])...}; } /** * Type-casts the elements of this matrix using `static_cast`. * * @tparam U Target element type. * * @return Matrix containing the type-casted elements. */ template constexpr inline explicit operator matrix() const noexcept { return type_cast(std::make_index_sequence{}); } /// @private template constexpr inline matrix size_cast(std::index_sequence) const noexcept { if constexpr (O == M) return {((I < N) ? columns[I] : vector::zero()) ...}; else return {((I < N) ? vector(columns[I]) : vector::zero()) ...}; } /** * Size-casts this matrix to a matrix with different dimensions. Casting to greater dimensions causes new elements to be set to zero. * * @tparam P Target number of columns. * @tparam O Target number of rows. * * @return *p by *o* matrix. */ template constexpr inline explicit operator matrix() const noexcept { return size_cast(std::make_index_sequence

{}); } /// Returns a zero matrix, where every element is equal to zero. static constexpr matrix zero() noexcept { return {}; } /// @private template static constexpr inline matrix one(std::index_sequence) noexcept { //return {column_vector_type::one() ...}; // MSVC bug workaround (I must be referenced for parameter pack expansion) return {(I ? column_vector_type::one() : column_vector_type::one()) ...}; } /// Returns a matrix of ones, where every element is equal to one. static constexpr matrix one() noexcept { return one(std::make_index_sequence{}); } /// @private template static constexpr inline column_vector_type identity_column(std::size_t i, std::index_sequence) noexcept { return {(I == i ? T{1} : T{0}) ...}; } /// @private template static constexpr inline matrix identity(std::index_sequence) noexcept { return {identity_column(I, std::make_index_sequence{}) ...}; } /// Returns an identity matrix, with ones on the main diagonal and zeros elsewhere. static constexpr matrix identity() noexcept { return identity(std::make_index_sequence{}); } }; /// 2x2 matrix. template using matrix2 = matrix; /// 2x2 matrix. template using matrix2x2 = matrix; /// 3x3 matrix. template using matrix3 = matrix; /// 3x3 matrix. template using matrix3x3 = matrix; /// 4x4 matrix. template using matrix4 = matrix; /// 4x4 matrix. template using matrix4x4 = matrix; /** * Adds two matrices. * * @param x First matrix. * @param y Second matrix. * @return Sum of the two matrices. */ template constexpr matrix add(const matrix& x, const matrix& y); /// @copydoc add(const matrix&, const matrix&) template constexpr matrix add(const matrix& x, const matrix& y); /// @copydoc add(const matrix&, const matrix&) template constexpr matrix add(const matrix& x, const matrix& y); /** * Reinterprets data as an `NxM` matrix of type `T`. * * @tparam N Number of columns. * @tparam M Number of rows. * @tparam T Element type. * @param data Data to reinterpret. */ template constexpr matrix& as_matrix(T& data); /** * Calculates the determinant of a matrix. * * @param m Matrix of which to take the determinant. */ template constexpr T determinant(const matrix& m); /// @copydoc determinant(const matrix&) template constexpr T determinant(const matrix& m); /// @copydoc determinant(const matrix&) template constexpr T determinant(const matrix& m); /** * Calculates the inverse of a matrix. * * @param m Matrix of which to take the inverse. */ template constexpr matrix inverse(const matrix& m); /// @copydoc inverse(const matrix&) template constexpr matrix inverse(const matrix& m); /// @copydoc inverse(const matrix&) template constexpr matrix inverse(const matrix& m); /** * Performs a component-wise multiplication of two matrices. * * @param x First matrix multiplicand. * @param y Second matrix multiplicand. */ template constexpr matrix componentwise_mul(const matrix& x, const matrix& y); /// @copydoc componentwise_mul(const matrix&, const matrix&) template constexpr matrix componentwise_mul(const matrix& x, const matrix& y); /// @copydoc componentwise_mul(const matrix&, const matrix&) template constexpr matrix componentwise_mul(const matrix& x, const matrix& y); /** * Creates a viewing transformation matrix. * * @param position Position of the view point. * @param target Position of the target. * @param up Normalized direction of the up vector. * @return Viewing transformation matrix. */ template constexpr matrix look_at(const vector& position, const vector& target, vector up); /** * Multiplies two matrices. * * @param x First matrix. * @param y Second matrix. * @return Product of the two matrices. */ template constexpr matrix mul(const matrix& x, const matrix& y); /// @copydoc mul(const matrix&, const matrix&); template constexpr matrix mul(const matrix& x, const matrix& y); /// @copydoc mul(const matrix&, const matrix&); template constexpr matrix mul(const matrix& x, const matrix& y); /** * Multiplies a matrix by a scalar. * * @param m Matrix. * @param s Scalar. * @return Product of the matrix and the scalar.. */ template constexpr matrix mul(const matrix& m, T s); /** * Transforms a vector by a matrix. * * @param m Matrix. * @param v Vector. * @return Transformed vector. */ template constexpr vector mul(const matrix& m, const vector& v); /// @copydoc mul(const matrix&, const vector&) template constexpr vector mul(const matrix& m, const vector& v); /// @copydoc mul(const matrix&, const vector&) template constexpr vector mul(const matrix& m, const vector& v); /** * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. * * @param left Signed distance to the left clipping plane. * @param right Signed distance to the right clipping plane. * @param bottom Signed distance to the bottom clipping plane. * @param top Signed distance to the top clipping plane. * @param z_near Signed distance to the near clipping plane. * @param z_far Signed distance to the far clipping plane. * @return Orthographic projection matrix. */ template constexpr matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far); /** * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively. * * @param left Signed distance to the left clipping plane. * @param right Signed distance to the right clipping plane. * @param bottom Signed distance to the bottom clipping plane. * @param top Signed distance to the top clipping plane. * @param z_near Signed distance to the near clipping plane. * @param z_far Signed distance to the far clipping plane. * @return Orthographic projection matrix. */ template constexpr matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far); /** * Calculates the outer product of a pair of vectors. * * @param c Parameter to be treated as a column vector. * @param r Parameter to be treated as a row vector. */ template constexpr matrix outer_product(const vector& c, const vector& r); /// @copydoc outer_product(const vector&, const vector&) template constexpr matrix outer_product(const vector& c, const vector& r); /// @copydoc outer_product(const vector&, const vector&) template constexpr matrix outer_product(const vector& c, const vector r); /** * Creates a perspective projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. * * @param vertical_fov Vertical field of view angle, in radians. * @param aspect_ratio Aspect ratio which determines the horizontal field of view. * @param z_near Distance to the near clipping plane. * @param z_far Distance to the far clipping plane. * @return Perspective projection matrix. */ template matrix perspective(T vertical_fov, T aspect_ratio, T z_near, T z_far); /** * Creates a perspective projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively. * * @param vertical_fov Vertical field of view angle, in radians. * @param aspect_ratio Aspect ratio which determines the horizontal field of view. * @param z_near Distance to the near clipping plane. * @param z_far Distance to the far clipping plane. * @return Perspective projection matrix. */ template matrix perspective_half_z(T vertical_fov, T aspect_ratio, T z_near, T z_far); /** * Resizes a matrix. Any new elements will be set to `1` if in the diagonal, and `0` otherwise. * * @param m Matrix to resize. * @return Resized matrix. */ template constexpr matrix resize(const matrix& m); /** * Constructs a rotation matrix. * * @param angle Angle of rotation, in radians. * @param axis Axis of rotation * @return Rotation matrix. */ template matrix rotate(T angle, const vector& axis); /** * Produces a matrix which rotates Cartesian coordinates about the x-axis by a given angle. * * @param angle Angle of rotation, in radians. * @return Rotation matrix. */ template matrix3 rotate_x(T angle); /** * Produces a matrix which rotates Cartesian coordinates about the y-axis by a given angle. * * @param angle Angle of rotation, in radians. * @return Rotation matrix. */ template matrix3 rotate_y(T angle); /** * Produces a matrix which rotates Cartesian coordinates about the z-axis by a given angle. * * @param angle Angle of rotation, in radians. * @return Rotation matrix. */ template matrix3 rotate_z(T angle); /** * Scales a matrix. * * @param m Matrix to scale. * @param v Scale vector. * @return Scaled matrix. */ template constexpr matrix scale(const matrix& m, const vector& v); /** * Subtracts a matrix from another matrix. * * @param x First matrix. * @param y Second matrix. * @return Difference between the two matrices. */ template constexpr matrix sub(const matrix& x, const matrix& y); /// @copydoc sub(const matrix&, const matrix&) template constexpr matrix sub(const matrix& x, const matrix& y); /// @copydoc sub(const matrix&, const matrix&) template constexpr matrix sub(const matrix& x, const matrix& y); /** * Translates a matrix. * * @param m Matrix to translate. * @param v Translation vector. * @return Translated matrix. */ template constexpr matrix translate(const matrix& m, const vector& v); /** * Calculates the transpose of a matrix. * * @param m Matrix of which to take the transpose. */ template constexpr matrix transpose(const matrix& m); /// @copydoc transpose(const matrix&) template constexpr matrix transpose(const matrix& m); /// @copydoc transpose(const matrix&) template constexpr matrix transpose(const matrix& m); /** * Types casts each matrix element and returns a matrix of the casted type. * * @tparam T2 Target matrix element type. * @tparam T1 Source matrix element type. * @tparam N Number of columns. * @tparam M Number of rows. * @param m Matrix to type cast. * @return Type-casted matrix. */ template constexpr matrix type_cast(const matrix& m); template constexpr matrix add(const matrix& x, const matrix& y) { return {{ x[0] + y[0], x[1] + y[1] }}; } template constexpr matrix add(const matrix& x, const matrix& y) { return {{ x[0] + y[0], x[1] + y[1], x[2] + y[2] }}; } template constexpr matrix add(const matrix& x, const matrix& y) { return {{ x[0] + y[0], x[1] + y[1], x[2] + y[2], x[3] + y[3] }}; } template constexpr inline matrix& as_matrix(T& data) { static_assert(std::is_pod>::value); return reinterpret_cast&>(data); } template constexpr T determinant(const matrix& m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } template constexpr T determinant(const matrix& m) { return m[0][0] * m [1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] - m[0][2] * m[1][1] * m[2][0]; } template constexpr T determinant(const matrix& m) { return m[0][3] * m[1][2] * m[2][1] * m[3][0] - m[0][2] * m[1][3] * m[2][1] * m[3][0] - m[0][3] * m[1][1] * m[2][2] * m[3][0] + m[0][1] * m[1][3] * m[2][2] * m[3][0] + m[0][2] * m[1][1] * m[2][3] * m[3][0] - m[0][1] * m[1][2] * m[2][3] * m[3][0] - m[0][3] * m[1][2] * m[2][0] * m[3][1] + m[0][2] * m[1][3] * m[2][0] * m[3][1] + m[0][3] * m[1][0] * m[2][2] * m[3][1] - m[0][0] * m[1][3] * m[2][2] * m[3][1] - m[0][2] * m[1][0] * m[2][3] * m[3][1] + m[0][0] * m[1][2] * m[2][3] * m[3][1] + m[0][3] * m[1][1] * m[2][0] * m[3][2] - m[0][1] * m[1][3] * m[2][0] * m[3][2] - m[0][3] * m[1][0] * m[2][1] * m[3][2] + m[0][0] * m[1][3] * m[2][1] * m[3][2] + m[0][1] * m[1][0] * m[2][3] * m[3][2] - m[0][0] * m[1][1] * m[2][3] * m[3][2] - m[0][2] * m[1][1] * m[2][0] * m[3][3] + m[0][1] * m[1][2] * m[2][0] * m[3][3] + m[0][2] * m[1][0] * m[2][1] * m[3][3] - m[0][0] * m[1][2] * m[2][1] * m[3][3] - m[0][1] * m[1][0] * m[2][2] * m[3][3] + m[0][0] * m[1][1] * m[2][2] * m[3][3]; } template constexpr matrix inverse(const matrix& m) { static_assert(std::is_floating_point::value); const T rd(T(1) / determinant(m)); return {{ { m[1][1] * rd, -m[0][1] * rd}, {-m[1][0] * rd, m[0][0] * rd} }}; } template constexpr matrix inverse(const matrix& m) { static_assert(std::is_floating_point::value); const T rd(T(1) / determinant(m)); return {{ { (m[1][1] * m[2][2] - m[1][2] * m[2][1]) * rd, (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * rd, (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * rd }, { (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * rd, (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * rd, (m[0][2] * m[1][0] - m[0][0] * m[1][2]) * rd }, { (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * rd, (m[0][1] * m[2][0] - m[0][0] * m[2][1]) * rd, (m[0][0] * m[1][1] - m[0][1] * m[1][0]) * rd } }}; } template constexpr matrix inverse(const matrix& m) { static_assert(std::is_floating_point::value); const T rd(T(1) / determinant(m)); return {{ { (m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] + m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] - m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3]) * rd, (m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] - m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] + m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3]) * rd, (m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] + m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] - m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3]) * rd, (m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] - m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] + m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3]) * rd }, { (m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] - m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] + m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3]) * rd, (m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] + m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] - m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3]) * rd, (m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] - m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] + m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3]) * rd, (m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] + m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] - m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3]) * rd }, { (m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] + m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] - m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3]) * rd, (m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] - m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] + m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3]) * rd, (m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] + m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] - m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3]) * rd, (m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] - m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] + m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3]) * rd }, { (m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] - m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] + m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2]) * rd, (m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] + m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] - m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2]) * rd, (m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] - m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] + m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2]) * rd, (m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) * rd } }}; } template constexpr matrix componentwise_mul(const matrix& x, const matrix& y) { return {{ {x[0][0] * y[0][0], x[0][1] * y[0][1]}, {x[1][0] * y[1][0], x[1][1] * y[1][1]} }}; } template constexpr matrix componentwise_mul(const matrix& x, const matrix& y) { return {{ {x[0][0] * y[0][0], x[0][1] * y[0][1], x[0][2] * y[0][2]}, {x[1][0] * y[1][0], x[1][1] * y[1][1], x[1][2] * y[1][2]}, {x[2][0] * y[2][0], x[2][1] * y[2][1], x[2][2] * y[2][2]} }}; } template constexpr matrix componentwise_mul(const matrix& x, const matrix& y) { return {{ {x[0][0] * y[0][0], x[0][1] * y[0][1], x[0][2] * y[0][2], x[0][3] * y[0][3]}, {x[1][0] * y[1][0], x[1][1] * y[1][1], x[1][2] * y[1][2], x[1][3] * y[1][3]}, {x[2][0] * y[2][0], x[2][1] * y[2][1], x[2][2] * y[2][2], x[2][3] * y[2][3]}, {x[3][0] * y[3][0], x[3][1] * y[3][1], x[3][2] * y[3][2], x[3][3] * y[3][3]} }}; } template constexpr matrix look_at(const vector& position, const vector& target, vector up) { vector forward = normalize(sub(target, position)); vector right = normalize(cross(forward, up)); up = cross(right, forward); matrix m = {{ {right[0], up[0], -forward[0], T(0)}, {right[1], up[1], -forward[1], T(0)}, {right[2], up[2], -forward[2], T(0)}, {T(0), T(0), T(0), T(1)} }}; return translate(m, negate(position)); } template constexpr matrix mul(const matrix& x, const matrix& y) { return {{ x[0] * y[0][0] + x[1] * y[0][1], x[0] * y[1][0] + x[1] * y[1][1] }}; } template constexpr matrix mul(const matrix& x, const matrix& y) { return {{ x[0] * y[0][0] + x[1] * y[0][1] + x[2] * y[0][2], x[0] * y[1][0] + x[1] * y[1][1] + x[2] * y[1][2], x[0] * y[2][0] + x[1] * y[2][1] + x[2] * y[2][2] }}; } template constexpr matrix mul(const matrix& x, const matrix& y) { return {{ x[0] * y[0][0] + x[1] * y[0][1] + x[2] * y[0][2] + x[3] * y[0][3], x[0] * y[1][0] + x[1] * y[1][1] + x[2] * y[1][2] + x[3] * y[1][3], x[0] * y[2][0] + x[1] * y[2][1] + x[2] * y[2][2] + x[3] * y[2][3], x[0] * y[3][0] + x[1] * y[3][1] + x[2] * y[3][2] + x[3] * y[3][3] }}; } /// @private template constexpr inline matrix mul(const matrix& m, T s, std::index_sequence) { return {{(m[I] * s)...}}; } template constexpr inline matrix mul(const matrix& m, T s) { return mul(m, s, std::make_index_sequence{}); } template constexpr vector mul(const matrix& m, const vector& v) { return { m[0][0] * v[0] + m[1][0] * v[1], m[0][1] * v[0] + m[1][1] * v[1] }; } template constexpr vector mul(const matrix& m, const vector& v) { return { m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2], m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2], m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] }; } template constexpr vector mul(const matrix& m, const vector& v) { return { m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3], m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3], m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3], m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3] }; } template constexpr matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far) { return {{ {T(2) / (right - left), T(0), T(0), T(0)}, {T(0), T(2) / (top - bottom), T(0), T(0)}, {T(0), T(0), T(-2) / (z_far - z_near), T(0)}, {-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -((z_far + z_near) / (z_far - z_near)), T(1)} }}; } template constexpr matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far) { return {{ {T(2) / (right - left), T(0), T(0), T(0)}, {T(0), T(2) / (top - bottom), T(0), T(0)}, {T(0), T(0), T(-1) / (z_far - z_near), T(0)}, {-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -z_near / (z_far - z_near), T(1)} }}; } template constexpr matrix outer_product(const vector& c, const vector& r) { return {{ {c[0] * r[0], c[1] * r[0]}, {c[0] * r[1], c[1] * r[1]} }}; } template constexpr matrix outer_product(const vector& c, const vector& r) { return {{ {c[0] * r[0], c[1] * r[0], c[2] * r[0]}, {c[0] * r[1], c[1] * r[1], c[2] * r[1]}, {c[0] * r[2], c[1] * r[2], c[2] * r[2]} }}; } template constexpr matrix outer_product(const vector& c, const vector r) { return {{ {c[0] * r[0], c[1] * r[0], c[2] * r[0], c[3] * r[0]}, {c[0] * r[1], c[1] * r[1], c[2] * r[1], c[3] * r[1]}, {c[0] * r[2], c[1] * r[2], c[2] * r[2], c[3] * r[2]}, {c[0] * r[3], c[1] * r[3], c[2] * r[3], c[3] * r[3]} }}; } template matrix perspective(T vertical_fov, T aspect_ratio, T z_near, T z_far) { T half_fov = vertical_fov * T(0.5); T f = std::cos(half_fov) / std::sin(half_fov); return {{ {f / aspect_ratio, T(0), T(0), T(0)}, {T(0), f, T(0), T(0)}, {T(0), T(0), (z_far + z_near) / (z_near - z_far), T(-1)}, {T(0), T(0), (T(2) * z_far * z_near) / (z_near - z_far), T(0)} }}; } template matrix perspective_half_z(T vertical_fov, T aspect_ratio, T z_near, T z_far) { T half_fov = vertical_fov * T(0.5); T f = std::cos(half_fov) / std::sin(half_fov); return {{ {f / aspect_ratio, T(0), T(0), T(0)}, {T(0), f, T(0), T(0)}, {T(0), T(0), z_far / (z_near - z_far), T(-1)}, {T(0), T(0), -(z_far * z_near) / (z_far - z_near), T(0)} }}; } template constexpr matrix resize(const matrix& m) { matrix resized; for (std::size_t i = 0; i < N1; ++i) { for (std::size_t j = 0; j < M1; ++j) { resized[i][j] = (i < N0 && j < M0) ? m[i][j] : ((i == j) ? T(1) : T(0)); } } return resized; } template matrix rotate(T angle, const vector& axis) { const T c = std::cos(angle); const T s = std::sin(angle); const vector temp = mul(axis, T(1) - c); matrix rotation; rotation[0][0] = axis[0] * temp[0] + c; rotation[0][1] = axis[1] * temp[0] + axis[2] * s; rotation[0][2] = axis[2] * temp[0] - axis[1] * s; rotation[1][0] = axis[0] * temp[1] - axis[2] * s; rotation[1][1] = axis[1] * temp[1] + c; rotation[1][2] = axis[2] * temp[1] + axis[0] * s; rotation[2][0] = axis[0] * temp[2] + axis[1] * s; rotation[2][1] = axis[1] * temp[2] - axis[0] * s; rotation[2][2] = axis[2] * temp[2] + c; return rotation; } template matrix3 rotate_x(T angle) { const T c = std::cos(angle); const T s = std::sin(angle); return matrix3 { T(1), T(0), T(0), T(0), c, s, T(0), -s, c }; } template matrix3 rotate_y(T angle) { const T c = std::cos(angle); const T s = std::sin(angle); return matrix3 { c, T(0), -s, T(0), T(1), T(0), s, T(0), c }; } template matrix3 rotate_z(T angle) { const T c = std::cos(angle); const T s = std::sin(angle); return matrix3 { c, s, T(0), -s, c, T(0), T(0), T(0), T(1) }; } template constexpr matrix scale(const matrix& m, const vector& v) { return mul(m, matrix {{ {v[0], T(0), T(0), T(0)}, {T(0), v[1], T(0), T(0)}, {T(0), T(0), v[2], T(0)}, {T(0), T(0), T(0), T(1)} }}); } template constexpr matrix sub(const matrix& x, const matrix& y) { return {{ x[0] - y[0], x[1] - y[1] }}; } template constexpr matrix sub(const matrix& x, const matrix& y) { return {{ x[0] - y[0], x[1] - y[1], x[2] - y[2] }}; } template constexpr matrix sub(const matrix& x, const matrix& y) { return {{ x[0] - y[0], x[1] - y[1], x[2] - y[2], x[3] - y[3] }}; } template constexpr matrix translate(const matrix& m, const vector& v) { return mul(m, matrix {{ {T(1), T(0), T(0), T(0)}, {T(0), T(1), T(0), T(0)}, {T(0), T(0), T(1), T(0)}, {v[0], v[1], v[2], T(1)} }}); } template constexpr matrix transpose(const matrix& m) { return {{ { m[0][0], m[1][0] }, { m[0][1], m[1][1] } }}; } template constexpr matrix transpose(const matrix& m) { return {{ { m[0][0], m[1][0], m[2][0] }, { m[0][1], m[1][1], m[2][1] }, { m[0][2], m[1][2], m[2][2] } }}; } template constexpr matrix transpose(const matrix& m) { return {{ { m[0][0], m[1][0], m[2][0], m[3][0] }, { m[0][1], m[1][1], m[2][1], m[3][1] }, { m[0][2], m[1][2], m[2][2], m[3][2] }, { m[0][3], m[1][3], m[2][3], m[3][3] } }}; } /// @private template constexpr inline matrix type_cast(const matrix& m, std::index_sequence) { return {type_cast(m[I])...}; } template constexpr matrix type_cast(const matrix& m) { return type_cast(m, std::make_index_sequence{}); } } // namespace math /// @copydoc math::add(const math::matrix&, const math::matrix&) template constexpr math::matrix operator+(const math::matrix& x, const math::matrix& y); /// @copydoc math::mul(const math::matrix&, const math::matrix&) template constexpr math::matrix operator*(const math::matrix& x, const math::matrix& y); /// @copydoc math::mul(const math::matrix&, T) template constexpr math::matrix operator*(const math::matrix& m, T s); /// @copydoc math::mul(const math::matrix&, T) template constexpr math::matrix operator*(T s, const math::matrix& m); /// @copydoc math::mul(const math::matrix&, const math::vector&) template constexpr math::vector operator*(const math::matrix& m, const math::vector& v); /// @copydoc math::sub(const math::matrix&, const math::matrix&) template constexpr math::matrix operator-(const math::matrix& x, const math::matrix& y); /** * Writes the elements of a matrix to an output stream, with each element delimeted by a space. * * @param os Output stream. * @param m Matrix. * @return Output stream. */ template std::ostream& operator<<(std::ostream& os, const math::matrix& m); /** * Reads the elements of a matrix from an input stream, with each element delimeted by a space. * * @param is Input stream. * @param m Matrix. * @return Input stream. */ template std::istream& operator>>(std::istream& is, math::matrix& m); template constexpr inline math::matrix operator+(const math::matrix& x, const math::matrix& y) { return math::add(x, y); } template constexpr inline math::matrix operator*(const math::matrix& x, const math::matrix& y) { return math::mul(x, y); } template constexpr inline math::matrix operator*(const math::matrix& m, T s) { return math::mul(m, s); } template constexpr inline math::matrix operator*(T s, const math::matrix& m) { return math::mul(m, s); } template constexpr inline math::vector operator*(const math::matrix& m, const math::vector& v) { return math::mul(m, v); } template constexpr inline math::matrix operator-(const math::matrix& x, const math::matrix& y) { return math::sub(x, y); } template std::ostream& operator<<(std::ostream& os, const math::matrix& m) { for (std::size_t i = 0; i < m.size(); ++i) { if (i) os << ' '; os << m.element(i); } return os; } template std::istream& operator>>(std::istream& is, math::matrix& m) { for (std::size_t i = 0; i < m.size(); ++i) is >> m.element(i); return is; } #endif // ANTKEEPER_MATH_MATRIX_HPP