├── test ├── meson.build ├── makefile └── test.cpp ├── meson.build ├── include └── gml │ ├── gml.hpp │ ├── intersect.hpp │ ├── spline.hpp │ ├── util.hpp │ ├── texture.hpp │ ├── quaternion.hpp │ ├── vec.hpp │ └── mat.hpp ├── LICENSE.txt └── README.md /test/meson.build: -------------------------------------------------------------------------------- 1 | test( 2 | 'gml-test', 3 | executable('gml-test', ['test.cpp'], dependencies: [gml_dep]) 4 | ) 5 | 6 | -------------------------------------------------------------------------------- /test/makefile: -------------------------------------------------------------------------------- 1 | all: test.cpp 2 | clang++ -std=c++11 test.cpp -o test -I../include -std=c++11 -Weverything -Wno-c++98-compat -Wno-shadow -Wno-documentation-unknown-command -Wno-padded 3 | 4 | clean: 5 | rm test 6 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'gml', 3 | 'cpp', 4 | version : '0.1.0', 5 | license : 'boost', 6 | default_options : ['cpp_std=c++11'] 7 | ) 8 | 9 | gml_dep = declare_dependency( 10 | include_directories : include_directories('include') 11 | ) 12 | 13 | subdir('test') 14 | 15 | -------------------------------------------------------------------------------- /include/gml/gml.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Markus Ilmola 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_07E9B812BFC543378AA70B7963B4EBDC 7 | #define UUID_07E9B812BFC543378AA70B7963B4EBDC 8 | 9 | #include "util.hpp" 10 | #include "vec.hpp" 11 | #include "quaternion.hpp" 12 | #include "mat.hpp" 13 | #include "spline.hpp" 14 | #include "texture.hpp" 15 | #include "intersect.hpp" 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /include/gml/intersect.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the Boost Software License, Version 1.0. 2 | // (See accompanying file LICENSE.txt or copy at 3 | // http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #ifndef UUID_72960EDDD32341DBBCADBFA777E98A81 6 | #define UUID_72960EDDD32341DBBCADBFA777E98A81 7 | 8 | #include 9 | 10 | #include "vec.hpp" 11 | #include "mat.hpp" 12 | 13 | 14 | namespace gml { 15 | 16 | 17 | 18 | /// Compute a pickray for mouse coordinates. 19 | /// @param mpos The mouse coordinates relative to the BOTTOM left corner of the screen. 20 | /// @param view The view matrix 21 | /// @param proj The projection matrix 22 | /// @param viewportOrigin Bottom left corner of the viewport 23 | /// @param viewportSize Size of the viewport 24 | /// @return Ray origin and unit length ray direction vector. 25 | template 26 | std::tuple, vec> pickRay( 27 | const vec& mpos, 28 | const mat& view, const mat& proj, 29 | const vec& viewportOrigin, const vec& viewportSize 30 | ) { 31 | return pickRay(mpos, inverse(proj * view), viewportOrigin, viewportSize); 32 | } 33 | 34 | 35 | /// Compute a pickray for mouse coordinates. 36 | /// @param mpos The mouse coordinates relative to the BOTTOM left corner of the screen. 37 | /// @param invProjView Inverse view projection matrix: (proj*view)^-1 38 | /// @param viewportOrigin Bottom left corner of the viewport 39 | /// @param viewportSize Size of the viewport 40 | /// @return Ray origin and unit length ray direction vector. 41 | template 42 | std::tuple, vec> pickRay( 43 | const vec& mpos, 44 | const mat& invProjView, 45 | const vec& viewportOrigin, const vec& viewportSize 46 | ) { 47 | const T zero = static_cast(0); 48 | const T one = static_cast(1); 49 | const T half = static_cast(0.5); 50 | 51 | const vec fmpos{ 52 | static_cast(mpos[0]) + half, static_cast(mpos[1]) + half 53 | }; 54 | 55 | const vec frontPos = unProject( 56 | vec{fmpos, zero}, invProjView, viewportOrigin, viewportSize 57 | ); 58 | 59 | const vec backPos = unProject( 60 | vec{fmpos, one}, invProjView, viewportOrigin, viewportSize 61 | ); 62 | 63 | return std::make_tuple(frontPos, normalize(backPos - frontPos)); 64 | } 65 | 66 | 67 | /// Computes the intersection of a ray and a N-plane. 68 | /// A plane if N == 3, a line if N == 2. 69 | /// @param origin Origin of the ray 70 | /// @param direction Unit length direction of the ray. 71 | /// @param center Any point on the plane 72 | /// @param normal Unit length normal vector of the plane 73 | /// @return Returns a tuple consisting of a bool denoting whether there was an 74 | /// intersection (true) or not (false, when the ray is parallel to the plane) 75 | /// and a scalar denoting the distance to the intersection point (positive or 76 | /// negative depending if the point is in front of or behind the origin). 77 | template 78 | std::tuple intersectRayPlane( 79 | const vec& origin, const vec& direction, 80 | const vec& center, const vec& normal 81 | ) { 82 | using std::abs; 83 | 84 | const T divisor = dot(direction, normal); 85 | 86 | if (abs(divisor) < std::numeric_limits::epsilon()) { 87 | return std::make_tuple(false, static_cast(0)); 88 | } 89 | 90 | const T d = dot(center, normal); 91 | 92 | return std::make_tuple(true, (d - dot(origin, normal)) / divisor); 93 | } 94 | 95 | 96 | 97 | /// Computes the intersection of a ray and N-sphere. 98 | /// A sphere if N == 3, circle if N == 2. 99 | /// @param origin Origin of the ray 100 | /// @param direction Unit length direction of the ray. 101 | /// @param center Position of the center of the sphere. 102 | /// @param radius Radius of the sphere. 103 | /// @return Returns a tuple consisting of a bool denoting whether there was an 104 | /// intersection (true) or not (false) and two scalars denoting the distances to 105 | /// the front and back intersection points (positive or negative depending if 106 | /// the point is in front of or behind the origin). 107 | template 108 | std::tuple intersectRaySphere( 109 | const vec& origin, const vec& direction, 110 | const vec& center, T radius 111 | ) { 112 | using std::sqrt; 113 | 114 | const vec L = center - origin; 115 | const T tca = dot(L, direction); 116 | const T d2 = dot(L, L) - tca * tca; 117 | 118 | const T radius2 = radius * radius; 119 | if (d2 > radius2) { 120 | return std::make_tuple(false, static_cast(0), static_cast(0)); 121 | } 122 | 123 | const T thc = sqrt(radius2 - d2); 124 | 125 | return std::make_tuple(true, tca - thc, tca + thc); 126 | } 127 | 128 | 129 | } 130 | 131 | 132 | #endif 133 | 134 | -------------------------------------------------------------------------------- /include/gml/spline.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the Boost Software License, Version 1.0. 2 | // (See accompanying file LICENSE.txt or copy at 3 | // http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #ifndef UUID_C740D32DBADF41BEA4FF5B55C29796CE 6 | #define UUID_C740D32DBADF41BEA4FF5B55C29796CE 7 | 8 | 9 | #include "vec.hpp" 10 | #include "mat.hpp" 11 | 12 | 13 | namespace gml { 14 | 15 | 16 | namespace detail { 17 | 18 | template 19 | vec bezierImpl( 20 | const vec* p, int n, T t1, T t2, int stride = 1 21 | ) { 22 | if (n == 1) return *p; 23 | if (n == 2) return t1 * p[0] + t2 * p[stride]; 24 | return 25 | t1 * bezierImpl(p, n - 1, t1, t2, stride) + 26 | t2 * bezierImpl(p + stride, n - 1, t1, t2, stride); 27 | } 28 | 29 | } 30 | 31 | 32 | /// Evaluates a bezier curve with D control points at t. 33 | /// @tparam D The number of control points. 34 | /// @param p Control points. 35 | /// @param t Interpolation value [0-1]. 36 | template 37 | vec bezier(const vec (&p)[D], T t) 38 | { 39 | static_assert(D > 0, "At least one control point needed."); 40 | 41 | return detail::bezierImpl(&p[0], D, static_cast(1) - t, t); 42 | } 43 | 44 | 45 | /// Evaluates a bezier surface with D0 X D1 control points at t. 46 | /// @tparam D0 The number of control points along t[0] direction. 47 | /// @tparam D1 The number of control points along t[1] direction. 48 | /// @param p Control points. 49 | /// @param t Interpolation value [0-1]. 50 | template 51 | vec bezier2( 52 | const vec (&p)[D1][D0], const vec& t 53 | ) { 54 | static_assert(D0 > 0, "At least one control point needed."); 55 | static_assert(D1 > 0, "At least one control point needed."); 56 | 57 | vec temp[D1]; 58 | for (int i = 0; i < D1; ++i) { 59 | temp[i] = bezier(p[i], t[0]); 60 | } 61 | return bezier(temp, t[1]); 62 | } 63 | 64 | 65 | namespace detail { 66 | 67 | template 68 | struct bezierDerivativeImpl { 69 | static vec calc(const vec (&p)[D], T t) 70 | { 71 | vec temp[D - 1]; 72 | for (int i = 0; i < D - 1; ++i) { 73 | temp[i] = static_cast(D - 1) * (p[i + 1] - p[i]); 74 | } 75 | 76 | return bezierDerivativeImpl::calc(temp, t); 77 | } 78 | }; 79 | 80 | 81 | template 82 | struct bezierDerivativeImpl<0, D, T, N> { 83 | static vec calc(const vec (&p)[D], T t) 84 | { 85 | return bezier(p, t); 86 | } 87 | }; 88 | 89 | template 90 | struct bezierDerivativeImpl<0, 1, T, N> { 91 | static vec calc(const vec (&p)[1], T t) 92 | { 93 | return bezier(p, t); 94 | } 95 | }; 96 | 97 | template 98 | struct bezierDerivativeImpl { 99 | static vec calc(const vec (&)[1], T) 100 | { 101 | return vec{static_cast(0)}; 102 | } 103 | }; 104 | 105 | 106 | } 107 | 108 | 109 | /// Evaluates Oth order derivative of a bezier curve with D control points at t. 110 | /// In the case of O=1 the derivative is tangential to the curve at t. 111 | /// @tparam O The order of the derivative. 1 for the first order. 112 | /// @tparam D The number of control points. 113 | /// @param p Control points. 114 | /// @param t Interpolation value [0-1]. 115 | template 116 | vec bezierDerivative(const vec (&p)[D], T t) 117 | { 118 | static_assert(O > 0, "The derivative order must be at least one."); 119 | static_assert(D > 0, "At least one control point needed."); 120 | 121 | return detail::bezierDerivativeImpl::calc(p, t); 122 | } 123 | 124 | 125 | /// Evaluates the Oth order Jacobian matrix of a bezier surface at point t. 126 | /// In the case of O=1 the two column vectors are tangential to the surface at t 127 | /// and in the case of N=3 the cross product of the columns is perpendicular to 128 | /// the surface. 129 | /// @tparam O The order of the Jacobian matrix. 1 = first order. 130 | /// @tparam D0 The number of control points along the t[0] direction. 131 | /// @tparam D1 The number of control points along the t[1] direction. 132 | /// @param p Control points. 133 | /// @param t Interpolation value [0-1]. 134 | template 135 | mat bezier2Jacobian( 136 | const vec (&p)[D1][D0], const vec& t 137 | ) { 138 | static_assert(O > 0, "Order of the jacobian must be at least one."); 139 | static_assert(D0 > 0, "At least one control point needed."); 140 | static_assert(D1 > 0, "At least one control point needed."); 141 | 142 | vec temp0[D0]; 143 | for (int i = 0; i < D0; ++i) { 144 | temp0[i] = detail::bezierImpl(&p[0][i], D1, static_cast(1) - t[1], t[1], D0); 145 | } 146 | 147 | vec temp1[D1]; 148 | for (int i = 0; i < D1; ++i) { 149 | temp1[i] = bezier(p[i], t[0]); 150 | } 151 | 152 | return mat{ 153 | bezierDerivative(temp0, t[0]), 154 | bezierDerivative(temp1, t[1]) 155 | }; 156 | } 157 | 158 | 159 | } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /include/gml/util.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Markus Ilmola 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_BD878441A4634595B03E6859030E9A7D 7 | #define UUID_BD878441A4634595B03E6859030E9A7D 8 | 9 | #include 10 | #include 11 | 12 | 13 | namespace gml { 14 | 15 | 16 | /// Converts degrees to radians 17 | template 18 | T radians(T degrees) { 19 | return degrees * static_cast(0.017453292519943295769236907684886); 20 | } 21 | 22 | 23 | /// Converts radians to degrees 24 | template 25 | T degrees(T radians) { 26 | return radians * static_cast(57.295779513082320876798154814105); 27 | } 28 | 29 | 30 | template 31 | T clamp(T val, T minVal, T maxVal) { 32 | return std::min(std::max(val, minVal), maxVal); 33 | } 34 | 35 | 36 | /// Wraps a value around to the given interval 37 | /// @param val Value to wrap to the given interval. 38 | /// @param min Start point of the interval (inclusive) 39 | /// @param max End point of the interval (exclusive) 40 | template < 41 | typename T, 42 | typename std::enable_if::value, int>::type = 0 43 | > 44 | T repeat(T val, T min, T max) 45 | { 46 | using std::fmod; 47 | T temp = fmod(val - min, max - min); 48 | if (temp < static_cast(0)) temp += max - min; 49 | return temp + min; 50 | } 51 | 52 | 53 | /// Wraps a value around to the given interval 54 | /// @param val Value to wrap to the given interval. 55 | /// @param min Start point of the interval (inclusive) 56 | /// @param max End point of the interval (exclusive) 57 | template < 58 | typename T, 59 | typename std::enable_if::value, int>::type = 0 60 | > 61 | T repeat(T val, T min, T max) 62 | { 63 | T temp = (val - min) % (max - min); 64 | if (temp < static_cast(0)) temp += max - min; 65 | return temp + min; 66 | } 67 | 68 | 69 | /// Convert an unsigned integral type TI to floating point type TF by mapping it 70 | /// linearly to range [0.0, 1.0]. 71 | /// @tparam TF Must be a floating point type. 72 | /// @tparam TI Must be an unsigned integral type. 73 | template 74 | TF unpackUnorm(TI val) { 75 | 76 | static_assert( 77 | std::is_integral::value && !std::is_signed::value, 78 | "TI must be an unsigned integral type." 79 | ); 80 | 81 | static_assert( 82 | std::is_floating_point::value, 83 | "TF must be a floating point type!" 84 | ); 85 | 86 | return static_cast(val) / static_cast(std::numeric_limits::max()); 87 | } 88 | 89 | 90 | /// Convert a floating point type to an unsigned integral type by first clamping 91 | /// it to range [0.0, 1.0] and then mapping it to the full range of the integer. 92 | /// @tparam TI Must be an unsigned integral type. 93 | /// @tparam TF Must be a floating point type. 94 | template 95 | TI packUnorm(TF val) { 96 | 97 | static_assert( 98 | std::is_integral::value && !std::is_signed::value, 99 | "TI must be an unsigned integral type." 100 | ); 101 | 102 | static_assert( 103 | std::is_floating_point::value, 104 | "TF must be a floating point type!" 105 | ); 106 | 107 | return static_cast( 108 | clamp(val, static_cast(0.0), static_cast(1.0)) * 109 | static_cast(std::numeric_limits::max()) + 110 | static_cast(0.5) 111 | ); 112 | } 113 | 114 | 115 | /// Convert a signed integral type TI to floating point type TF by mapping it 116 | /// linearly to range [-1.0, 1.0]. 117 | /// @tparam TF Must be a floating point type. 118 | /// @tparam TI Must be a signed integral type. 119 | template 120 | TF unpackSnorm(TI val) { 121 | 122 | static_assert( 123 | std::is_integral::value && std::is_signed::value, 124 | "TI must be a signed integral type." 125 | ); 126 | 127 | static_assert( 128 | std::is_floating_point::value, 129 | "TF must be a floating point type!" 130 | ); 131 | 132 | return clamp( 133 | static_cast(val) / 134 | static_cast(std::numeric_limits::max()), 135 | static_cast(-1.0), static_cast(1.0) 136 | ); 137 | } 138 | 139 | 140 | /// Convert a floating point type to a signed integral type by first clamping 141 | /// it to range [-1.0, 1.0] and then mapping it to the full range of the integer. 142 | /// @tparam TI Must be a signed integral type. 143 | /// @tparam TF Must be a floating point type. 144 | template 145 | TI packSnorm(TF val) { 146 | 147 | static_assert( 148 | std::is_integral::value && std::is_signed::value, 149 | "TI must be a signed integral type." 150 | ); 151 | 152 | static_assert( 153 | std::is_floating_point::value, 154 | "TF must be a floating point type!" 155 | ); 156 | 157 | return static_cast( 158 | clamp(val, static_cast(-1.0), static_cast(1.0)) * 159 | static_cast(std::numeric_limits::max()) + 160 | static_cast(0.5) 161 | ); 162 | } 163 | 164 | 165 | 166 | 167 | } 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GML # 2 | 3 | GML (recursive acronym for "GML math library") is a GLSL like minimalist vector, matrix and quaternion math library for C++11. 4 | 5 | It's API is modeled closely after GLSL and is thus easy to use in OpenGL programs. It provides most of the GLSL functionality plus some extra. 6 | 7 | This library should not be confused with [glm](http://glm.g-truc.net) or with [Boost qvm](https://boostorg.github.io/qvm/). 8 | 9 | 10 | ## Setup ## 11 | 12 | This is a header only template library. 13 | Just make sure that the "gml" directory is in the include path and include `gml.hpp`. 14 | ~~~c++ 15 | #include 16 | ~~~ 17 | 18 | Use Doxygen to generate full API documentation. Doxyfile is provided. 19 | 20 | 21 | ## Usage ## 22 | 23 | There are only three class templates `vec` (a vector), `mat` (a matrix) and `quaternion` (a quaternion). 24 | 25 | All gml types and functions are defined under name space `gml` (of which examples in this document omit). 26 | 27 | Although vector and matrix sizes can be anything this library is meant to be used with small vectors and matrices (2-4 columns/rows). 28 | 29 | For optimal performance you may wish to compile with `-O3 -ffast-math -DNODEBUG`. 30 | 31 | ## vec ## 32 | 33 | The `vec` template looks a bit like `std::array`. 34 | 35 | ~~~c++ 36 | template class vec; 37 | ~~~ 38 | 39 | Where `T` is the type of a single component and `N` is the number of components. 40 | 41 | GLSL-like type aliases for common types are provided. They are named `TvecN` where `T` is `i` for `int`, `d` for `double`, `u` for `unsigned`, `b` for `bool` and non-existent for `float`. `N` is 2, 3 or 4. 42 | 43 | For example: 44 | ~~~c++ 45 | vec3 = vec 46 | ivec4 = vec 47 | uvec2 = vec 48 | ~~~ 49 | 50 | Common constructors: 51 | ~~~c++ 52 | vec3 a{}; // All zeros 53 | vec3 b{1.0f}; // All ones 54 | vec3 c{1.0f, 2.0f, 3.0f}; // Vector (1, 2, 3) 55 | vec4 e{a, 1.0f}; // Copy a and add 1 as the last component. 56 | vec2 f{a}; // Copy a but drop the last component 57 | vec3 g{1.0f, 2.0f}; // ERROR: Will not compile. Invalid number of arguments. 58 | ~~~ 59 | 60 | Use the `operator[]` to reference the components and `data()` to get a pointer to the first component. Vectors are also iterable. 61 | 62 | Component-wise arithmetic operators `*`, `/`, `-` and `+` between vectors of same size and between scalars and vectors are provided. 63 | 64 | Most vector operations from GLSL such as `dot` and `cross` are also provided. 65 | 66 | There are no GLSL-like swizzle operations. 67 | 68 | 69 | ## mat ## 70 | 71 | The `mat` template works a bit like nested `std::array`. 72 | 73 | ~~~c++ 74 | template class mat; 75 | ~~~ 76 | Where `T` is the type of a single component, `C` is the number of columns and `R` is the number of rows. 77 | 78 | GLSL-like type aliases for common matrix types are provided. They are named `TmatCxR` where `T` is same as for `vec`. `C` and `R` are 2, 3, or 4. When `C` equals `R` a shorter alias `TmatC` is also available. 79 | 80 | For example: 81 | ~~~c++ 82 | imat4x3 = mat 83 | mat4 = mat 84 | ~~~ 85 | 86 | Common constructors: 87 | ~~~c++ 88 | mat4 a{}; // All zeros 89 | mat4 b{1.0f}; // 1 at the diagonal and 0 elsewhere. (Identity matrix) 90 | const float data[6] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; 91 | mat2x3 c{data, true}; // Matrix with columns (1, 2, 3) and (4, 5, 6). 92 | imat2 d{ivec2{1, 2}, ivec2{3, 4}}; // Matrix with column vectors (1, 2) and (3, 4) 93 | mat3 e{a}; // Copy a and drop last row and column. 94 | ~~~ 95 | 96 | Each column in the matrix is a vector with `R` components. Use `operator[]` to reference the columns. Use `data()` to get pointer to the first component of the first column. Method `size` returns the number of columns (the largest value that can be given to `operator[]`). 97 | 98 | Note: Matrices store data in column major order! 99 | Matrices can be iterated, but they iterate columns not components. 100 | 101 | ~~~c++ 102 | mat3 m{}; 103 | m[0] = vec3{1.0f, 2.0f, 3.0f}; // Set first column to (1,2,3) 104 | m[2][0] = 10.0f; // Set first component of last column to 10. 105 | ~~~ 106 | 107 | Arithmetic operators `*`, `/`, `-` and `+` are provided. 108 | Note that the multiplication is the matrix multiplication and not done component-wise. `matrixCompMult` can be used to do component wise multiplication. 109 | 110 | Most matrix operation function from GLSL are provided such as `inverse`, `transpose` and `determinant`. 111 | 112 | Replacements for the (deprecated) OpenGL and GLU matrix generation functions are provided. They have the same names as OpenGL counter parts, but without any prefix or suffix and they return the matrices they generate: `translate`, `rotate`, `scale`, `perspective`, `ortho`, `lookAt`. 113 | 114 | To transform a 3-vector `v` with 4x4 matrix `M` you need to add 1 as the last component, multiply with the matrix and then drop the last component. 115 | ~~~c++ 116 | vec3 result{M * vec4{v, 1.0f}}; 117 | ~~~ 118 | 119 | A more convenient (and more efficient) function `transform` is provided to do this. 120 | ~~~c++ 121 | vec3 result = transform(M, v); 122 | ~~~ 123 | 124 | 125 | ## quaternion ## 126 | 127 | Even though GLSL has no quaternions, they are provided for their usefulness. The `quaternion` template is modeled after `std::complex` except that the imaginary part is a 3-vector. 128 | 129 | ~~~c++ 130 | template class quaternion; 131 | ~~~ 132 | Where `T` is the type of a single component. 133 | 134 | GLSL-like type aliases are provided in the form of `Tquat`. Where `T` the same as for `vec` and `mat`. 135 | ~~~c++ 136 | quat = quaternion 137 | dquat = quaternion 138 | ~~~ 139 | 140 | Common constructors: 141 | ~~~c++ 142 | quat a{}; // All zero 143 | quat b{1.0f}; // 1 as real part and zero imaginary (Identity quaternion) 144 | quat c{1.0f, vec3{2.0f, 3.0f, 4.0f}}; // Quaternion with 1 as real and (2,3,4) as imaginary part 145 | ~~~ 146 | 147 | Use public members `real` and `imag` to access data. 148 | ~~~c++ 149 | quat q{}; 150 | q.real = 5.0f; // Set real part to 5 151 | q.imag = vec3{1.0f, 2.0f, 3.0f}; // Set imaginary part to (1,2,3) 152 | ~~~ 153 | 154 | All arithmetic operations between quaternions, quaternions and scalars and quaternions and 3-vectors are provided. Scalar is interpreted as the real part and 3-vector as the imaginary part. 155 | 156 | `std::complex` like operations are provided such as `conj` and `inverse`. 157 | 158 | Quaternions are used for presenting orientation or rotation in 3d space (just like rotation matrices). Use `qrotate` to generate a rotation quaternion. 159 | ~~~c++ 160 | // 90dec rotation around x axis. 161 | auto rotation = qrotate(radians(90.0f), vec3{1.0f, 0.0f, 0.0f}); 162 | ~~~ 163 | 164 | You can multiply quaternion and vector, but this does not rotate the vector (like in the case of a rotation matrix). The vector in this case is interpreted as the imaginary part of a quaternion having a zero as real part. 165 | 166 | To rotate a vector `v` with quaternion `q` calculate `q*v*conj(q)` and use the imaginary part as the rotated vector. Overload for the `transform` function is provided to do this more efficiently. 167 | ~~~c++ 168 | vec3 result = transform(q, v); 169 | ~~~ 170 | 171 | 172 | ## Testing ## 173 | 174 | A fairly good fuzz tester is provided with the library. It's in the `test.cpp` and should be run when ever making changes to the library. 175 | 176 | -------------------------------------------------------------------------------- /include/gml/texture.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the Boost Software License, Version 1.0. 2 | // (See accompanying file LICENSE.txt or copy at 3 | // http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #ifndef UUID_D25B56EC1D33424DA124FF943463F380 6 | #define UUID_D25B56EC1D33424DA124FF943463F380 7 | 8 | #include 9 | #include 10 | 11 | #include "vec.hpp" 12 | 13 | namespace gml { 14 | 15 | 16 | /// Calculates the texture coordinate of the center of a texel with given index 17 | /// in a 1D texture with given size. The return value is in the range [0, 1] if 18 | /// the index is in the range [0, size - 1]. 19 | /// @tparam TF Type of the coordinate to return. Must be a floating point type. 20 | /// @tparam TI Type of the index. Must be an integral type. 21 | /// @tparam TS Type of the size. Must be an integral type. 22 | /// @param index Texel index in the texture. 23 | /// @param size Size of the texture in texels. 24 | template 25 | TF texelCenter(TI index, TS size) 26 | { 27 | static_assert(std::is_integral::value, "TI must an integral type."); 28 | static_assert(std::is_integral::value, "TS must an integral type."); 29 | static_assert(std::is_floating_point::value, "TF must a floating point type."); 30 | 31 | if (size == static_cast(0)) return static_cast(0); 32 | return (static_cast(index) + static_cast(0.5)) / static_cast(size); 33 | } 34 | 35 | 36 | /// Calculates the texture coordinate of the center of a texel with given index 37 | /// in a N-D texture with given size. The return value is in the range [0, 1] if 38 | /// the index is in the range [0, size - 1]. 39 | /// @tparam TF Type of the coordinate to return. Must be a floating point type. 40 | /// @tparam TI Type of the index. Must be an integral type. 41 | /// @tparam TS Type of the size. Must be an integral type. 42 | /// @param index Texel index in the texture. 43 | /// @param size Size of the texture in texels. 44 | template 45 | gml::vec texelCenter(const gml::vec& index, const gml::vec& size) 46 | { 47 | gml::vec temp; 48 | for (int i = 0; i < N; ++i) { 49 | temp[i] = texelCenter(index[i], size[i]); 50 | } 51 | return temp; 52 | } 53 | 54 | 55 | /// Calculates the index of the texel with center nearest to the given coordinate 56 | /// in a 1D texture with given size. 57 | /// The return value is in the range [0, size - 1] if input is in range [0, 1[. 58 | /// @tparam TI Type of the index. Must be an integral type. 59 | /// @tparam TF Type of the coordinate. Must be a floating point type. 60 | /// @tparam TS Type of the size. Must be an integral type. 61 | /// @param coord The texture coordinate. 62 | /// @param size Size of the texture in texels. 63 | template 64 | TI nearestTexel(TF coord, TS size) 65 | { 66 | static_assert(std::is_integral::value, "TI must an integral type."); 67 | static_assert(std::is_integral::value, "TS must an integral type."); 68 | static_assert(std::is_floating_point::value, "TF must a floating point type."); 69 | 70 | const TF temp = coord * static_cast(size); 71 | return static_cast(temp) - static_cast(temp < static_cast(0)); 72 | } 73 | 74 | 75 | /// Calculates the index of the texel with center nearest to the given coordinate 76 | /// in a N-D texture with given size. 77 | /// The return value is in the range [0, size - 1] if input is in range [0, 1[. 78 | /// @tparam TI Type of the index. Must be an integral type. 79 | /// @tparam TF Type of the coordinate. Must be a floating point type. 80 | /// @tparam TS Type of the size. Must be an integral type. 81 | /// @param coord The texture coordinate. 82 | /// @param size Size of the texture in texels. 83 | template 84 | vec nearestTexel(const vec& coord, const vec& size) 85 | { 86 | static_assert(std::is_integral::value, "TI must an integral type."); 87 | static_assert(std::is_integral::value, "TS must an integral type."); 88 | static_assert(std::is_floating_point::value, "TF must a floating point type."); 89 | 90 | vec temp; 91 | for (int i = 0; i < N; ++i) { 92 | temp[i] = nearestTexel(coord[i], size[i]); 93 | } 94 | return temp; 95 | } 96 | 97 | 98 | /// Returns the index of a cubemap face at given direction. 99 | /// @param direction The direction vector. Need not be normalized. 100 | /// @return 0: +X, 1: -X, 2: +Y, 3: -Y, 4: +Z, 5: -Z. 101 | template 102 | int faceAt(const gml::vec& direction) 103 | { 104 | static_assert(std::is_floating_point::value, "TF must a floating point type."); 105 | 106 | const gml::vec a = gml::abs(direction); 107 | 108 | const TF zero = static_cast(0); 109 | 110 | if (a[0] >= a[1]) { 111 | if (a[0] >= a[2]) { 112 | if (direction[0] < zero) return 1; 113 | else return 0; 114 | } 115 | else { 116 | if (direction[2] < zero) return 5; 117 | else return 4; 118 | } 119 | } 120 | else { 121 | if (a[1] >= a[2]) { 122 | if (direction[1] < zero) return 3; 123 | else return 2; 124 | } 125 | else { 126 | if (direction[2] < zero) return 5; 127 | else return 4; 128 | } 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | 135 | /// Convert a cube map direction vector to face index texture coordinate pair 136 | /// using the OpenGL conventions. 137 | /// @param direction The direction vector. Need not be normalized. 138 | /// @return 0: +X, 1: -X, 2: +Y, 3: -Y, 4: +Z, 5: -Z. 139 | template 140 | std::pair> cubeTexCoord(const gml::vec& direction) 141 | { 142 | static_assert(std::is_floating_point::value, "TF must a floating point type."); 143 | 144 | auto fn = [] (TF sc, TF tc, TF ma) -> gml::vec 145 | { 146 | using std::abs; 147 | ma = abs(ma); 148 | const TF half = static_cast(1) / static_cast(2); 149 | return gml::vec{ 150 | half * (sc / ma + static_cast(1)), 151 | half * (tc / ma + static_cast(1)) 152 | }; 153 | }; 154 | 155 | // OpenGL spec 156 | // direction target sc tc ma 157 | // ---------- ------------------------------ -- -- -- 158 | // +rx GL_TEXTURE_CUBE_MAP_POSITIVE_X -rz -ry rx 159 | // -rx GL_TEXTURE_CUBE_MAP_NEGATIVE_X +rz -ry rx 160 | // +ry GL_TEXTURE_CUBE_MAP_POSITIVE_Y +rx +rz ry 161 | // -ry GL_TEXTURE_CUBE_MAP_NEGATIVE_Y +rx -rz ry 162 | // +rz GL_TEXTURE_CUBE_MAP_POSITIVE_Z +rx -ry rz 163 | // -rz GL_TEXTURE_CUBE_MAP_NEGATIVE_Z -rx -ry rz 164 | 165 | const int face = faceAt(direction); 166 | switch (face) { 167 | case 0: // +x 168 | return std::make_pair(face, fn(-direction[2], -direction[1], direction[0])); 169 | case 1: // -x 170 | return std::make_pair(face, fn( direction[2], -direction[1], direction[0])); 171 | case 2: // +y 172 | return std::make_pair(face, fn( direction[0], direction[2], direction[1])); 173 | case 3: // -y 174 | return std::make_pair(face, fn( direction[0], -direction[2], direction[1])); 175 | case 4: // +z 176 | return std::make_pair(face, fn( direction[0], -direction[1], direction[2])); 177 | case 5: // -z 178 | return std::make_pair(face, fn(-direction[0], -direction[1], direction[2])); 179 | } 180 | 181 | // Should not happen 182 | return std::make_pair( 183 | 0u, gml::vec{static_cast(1.0), static_cast(0.0)} 184 | ); 185 | } 186 | 187 | 188 | /// Convert a face index texture coordinate pair to cube map direction vector 189 | /// using the OpenGL conventions. 190 | /// @param faceIndex 0: +X, 1: -X, 2: +Y, 3: -Y, 4: +Z, 5: -Z. If the param is 191 | /// not in the range [0, 5] an assertion failure will occur. 192 | /// @param texCoord Texture coordinate in the given face. 193 | /// @return Direction vector. The vector is not normalized. 194 | template 195 | gml::vec cubeDirection(int faceIndex, const gml::vec& texCoord) 196 | { 197 | static_assert(std::is_floating_point::value, "TF must a floating point type."); 198 | 199 | assert(faceIndex >= 0 && faceIndex <= 5); 200 | 201 | const gml::vec temp = static_cast(2.0) * texCoord - static_cast(1.0); 202 | 203 | const TF one = static_cast(1); 204 | 205 | switch (faceIndex) { 206 | case 0: // +x 207 | return gml::vec{ one, -temp[1], -temp[0]}; 208 | case 1: // -x 209 | return gml::vec{-one, -temp[1], temp[0]}; 210 | case 2: // +y 211 | return gml::vec{temp[0], one, temp[1]}; 212 | case 3: // -y 213 | return gml::vec{temp[0], -one, -temp[1]}; 214 | case 4: // +z 215 | return gml::vec{ temp[0], -temp[1], one}; 216 | case 5: // -z 217 | return gml::vec{-temp[0], -temp[1], -one}; 218 | } 219 | 220 | // Should not happen 221 | return gml::vec{one, static_cast(0.0), static_cast(0.0)}; 222 | } 223 | 224 | 225 | } 226 | 227 | #endif 228 | -------------------------------------------------------------------------------- /include/gml/quaternion.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Markus Ilmola 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_B15DF4F752A94088BD3A77BC2D628831 7 | #define UUID_B15DF4F752A94088BD3A77BC2D628831 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "vec.hpp" 18 | 19 | namespace gml { 20 | 21 | 22 | /** 23 | * Complex number with scalar as real part and 3-vector as imaginary part. 24 | * Used for rotations and for storing orientation in 3d space. 25 | */ 26 | template 27 | class quaternion { 28 | public: 29 | 30 | /// Type of the real part and a single component of the imaginary part. 31 | using value_type = T; 32 | 33 | /// Quaternion with zero real an imaginary parts. 34 | quaternion() : real{0}, imag{T{0}} { } 35 | 36 | /// A quaternion with given real part and zero imaginary part 37 | explicit quaternion(const T& real) : real{real}, imag{T{0}} { } 38 | 39 | /// A quaternion with zero real part and given imaginary part 40 | explicit quaternion(const vec& imag) : real{0}, imag{imag} { } 41 | 42 | /// A quaternion with given real and imaginary parts. 43 | quaternion(T real, const vec& imag) : real{real}, imag{imag} { } 44 | 45 | quaternion(const quaternion&) = default; 46 | 47 | quaternion(quaternion&&) = default; 48 | 49 | quaternion& operator=(const quaternion&) = default; 50 | 51 | quaternion& operator=(quaternion&&) = default; 52 | 53 | /// Component-wise addition 54 | quaternion operator+(const quaternion& q) const { 55 | return quaternion{real + q.real, imag + q.imag}; 56 | } 57 | 58 | /// Component-wise addition by interpreting v as imaginary part of a quaternion. 59 | quaternion operator+(const vec& v) const { 60 | return quaternion{real, imag + v}; 61 | } 62 | 63 | /// Component-wise addition. 64 | quaternion operator+(const T& a) const { 65 | return quaternion{real + a, imag}; 66 | } 67 | 68 | /// Components wise subtraction 69 | quaternion operator-(const quaternion& q) const { 70 | return quaternion{real - q.real, imag - q.imag}; 71 | } 72 | 73 | /// Components wise subtraction 74 | quaternion operator-(const vec& v) const { 75 | return quaternion{real, imag - v}; 76 | } 77 | 78 | /// Components wise subtraction 79 | quaternion operator-(const T& a) const { 80 | return quaternion{real - a, imag}; 81 | } 82 | 83 | /// Quaternion multiplication. 84 | /// Not done component wise. 85 | quaternion operator*(const quaternion& q) const { 86 | return quaternion{ 87 | real * q.real - dot(imag, q.imag), 88 | real * q.imag + q.real * imag + cross(imag, q.imag) 89 | }; 90 | } 91 | 92 | /// Multiply quaternion and 3-vector (aka quaternion with zero real part). 93 | /// Note that vector v is not rotated by quaternion q by q*v but q*v*conj(q) 94 | /// (or use qtransform). 95 | quaternion operator*(const vec& v) const { 96 | return quaternion{-dot(imag, v), real*v + cross(imag, v)}; 97 | } 98 | 99 | /// Multiply with scalar as it were the real part of a quaternion. 100 | quaternion operator*(const T& a) const { 101 | return quaternion{real * a, imag * a}; 102 | } 103 | 104 | 105 | quaternion operator/(const T& a) const { 106 | return quaternion{real / a, imag / a}; 107 | } 108 | 109 | 110 | quaternion& operator+=(const quaternion& q) { 111 | real += q.real; 112 | imag += q.imag; 113 | return *this; 114 | } 115 | 116 | 117 | quaternion& operator-=(const quaternion& q) { 118 | real -= q.real; 119 | imag -= q.imag; 120 | return *this; 121 | } 122 | 123 | 124 | quaternion& operator*=(const quaternion& q) { 125 | *this = *this * q; 126 | return *this; 127 | } 128 | 129 | /// Quaternions are equal if both parts are equal. 130 | bool operator==(const quaternion& q) const { 131 | if (real != q.real) return false; 132 | if (imag != q.imag) return false; 133 | return true; 134 | } 135 | 136 | bool operator==(const T& a) const { 137 | if (real != a) return false; 138 | if (imag != vec{0}) return false; 139 | return true; 140 | } 141 | 142 | 143 | bool operator==(const vec& v) const { 144 | if (real != T{0}) return false; 145 | if (imag != v) return false; 146 | return true; 147 | } 148 | 149 | /// Quaternions are not equal if either of the parts are not equal. 150 | bool operator!=(const quaternion& q) const { 151 | if (real != q.real) return true; 152 | if (imag != q.imag) return true; 153 | return false; 154 | } 155 | 156 | 157 | /// The real part of the quaternion 158 | T real; 159 | 160 | /// The imaginary part of the quaternion 161 | vec imag; 162 | 163 | }; 164 | 165 | 166 | /// Multiplies scalar (aka quaternion with zero imaginary part) and quaternion 167 | template 168 | quaternion operator*(const T& a, const quaternion& q) { 169 | return quaternion{a * q.real, a * q.imag}; 170 | } 171 | 172 | 173 | /// Multiply quaternion and vector as it were the imaginary part of a quaternion. 174 | template 175 | quaternion operator*(const vec& v, const quaternion& q) { 176 | return quaternion{-dot(v, q.imag), q.real * v + cross(v, q.imag)}; 177 | } 178 | 179 | 180 | /// Add quaternion and vector as it were the imaginary part of a quaternion. 181 | template 182 | quaternion operator+(vec v, const quaternion& q) { 183 | return quaternion{q.real, v+q.imag}; 184 | } 185 | 186 | 187 | template 188 | quaternion operator+(const T& a, const quaternion& q) { 189 | return quaternion{a + q.real, q.imag}; 190 | } 191 | 192 | 193 | template 194 | quaternion operator-(const quaternion& q) { 195 | return quaternion{-q.real, -q.imag}; 196 | } 197 | 198 | 199 | template 200 | quaternion operator-(const T& a, const quaternion& q) { 201 | return quaternion{a - q.real, -q.imag}; 202 | } 203 | 204 | 205 | template 206 | quaternion operator-(const vec& v, const quaternion& q) { 207 | return quaternion{-q.real, v - q.imag}; 208 | } 209 | 210 | 211 | /// Write a quaternion to a stream inside brackets parts separated by a comma. 212 | template 213 | std::ostream& operator<<(std::ostream& os, const quaternion& q) { 214 | os << '(' << q.real << ',' << q.imag << ')'; 215 | return os; 216 | } 217 | 218 | 219 | /// Reads a quaternion from a stream. 220 | /// The parts must be inside brackets separated be a comma. 221 | template 222 | std::istream& operator>>(std::istream& is, quaternion& q) { 223 | char ch = 0; 224 | is >> ch >> q.real >> ch >> q.imag >> ch; 225 | return is; 226 | } 227 | 228 | 229 | /// Converts a quaternion to std::string. 230 | template 231 | std::string to_string(const quaternion& q) { 232 | std::stringstream ss{}; 233 | ss << q; 234 | return ss.str(); 235 | } 236 | 237 | 238 | /// Returns the squared magnitude of the quaternion q 239 | template 240 | T norm(const quaternion& q) { 241 | return q.real * q.real + dot(q.imag, q.imag); 242 | } 243 | 244 | 245 | /// Returns the magnitude of the quaternion q. 246 | template 247 | T abs(const quaternion& q) { 248 | using std::sqrt; 249 | return sqrt(norm(q)); 250 | } 251 | 252 | 253 | /// Makes the quaternion a unit quaternion. 254 | template 255 | quaternion normalize(const quaternion& q) { 256 | return q / abs(q); 257 | } 258 | 259 | 260 | /// Quaternion conjugate (negates imaginary part) 261 | template 262 | quaternion conj(const quaternion& q) { 263 | return quaternion{q.real, -q.imag}; 264 | } 265 | 266 | 267 | /// Generates rotation quaternion from axis and angle 268 | /// @param angle Rotation angle in radians 269 | /// @param axis Unit length rotation axis. 270 | template 271 | quaternion qrotate(const T& angle, const vec& axis) { 272 | using std::sin; 273 | using std::cos; 274 | 275 | const T a = angle / T{2}; 276 | return quaternion{cos(a), sin(a) * axis}; 277 | } 278 | 279 | 280 | /// Generates rotation quaternion from Euler angles 281 | /// @param angle Euler angles in radians. 282 | template 283 | quaternion qrotate(const vec& angle) { 284 | using std::sin; 285 | using std::cos; 286 | 287 | const T a1 = angle[0] / T{2}; 288 | const T a2 = angle[1] / T{2}; 289 | const T a3 = angle[2] / T{2}; 290 | 291 | const T sx = sin(a1); 292 | const T cx = cos(a1); 293 | const T sy = sin(a2); 294 | const T cy = cos(a2); 295 | const T sz = sin(a3); 296 | const T cz = cos(a3); 297 | 298 | return quaternion{ 299 | cx * cy * cz + sx * sy * sz, 300 | vec{ 301 | cy * cz * sx - cx * sy * sz, 302 | cx * cz * sy + cy * sx * sz, 303 | -cz * sx * sy + cx * cy * sz 304 | } 305 | }; 306 | } 307 | 308 | 309 | /// Decomposes a rotation quaternion to angle and axis. 310 | template 311 | std::tuple> decomposeRotate(const quaternion& q) { 312 | using std::acos; 313 | using std::sqrt; 314 | using std::max; 315 | 316 | const T zero = static_cast(0); 317 | const T one = static_cast(1); 318 | const T two = static_cast(2); 319 | 320 | return std::make_tuple( 321 | two * acos(clamp(q.real, -one, one)), 322 | q.imag / sqrt(max(zero, one - q.real * q.real)) 323 | ); 324 | } 325 | 326 | 327 | /// Linear interpolation between quaternions. 328 | /// the resulting quaternion is NOT normalized 329 | template 330 | quaternion mix(const quaternion& q1, const quaternion& q2, const T& a) { 331 | return (T{1} - a) * q1 + a * q2; 332 | } 333 | 334 | 335 | /// Rotates 3-vector v with quaternion q (computes q*v*conj(q)) 336 | /// Note q has to be a unit quaternion. 337 | template 338 | vec transform(const quaternion& q, const vec& v) { 339 | const vec temp = T{2} * cross(q.imag, v); 340 | return v + q.real * temp + cross(q.imag, temp); 341 | } 342 | 343 | 344 | /// Inverse of quaternion. 345 | template 346 | quaternion inverse(const quaternion& q) { 347 | return conj(q) / norm(q); 348 | } 349 | 350 | 351 | /// Static cast each component from T2 to T1. 352 | template 353 | quaternion static_quaternion_cast(const quaternion& q) { 354 | return quaternion{static_cast(q.real), static_vec_cast(q.imag)}; 355 | } 356 | 357 | 358 | using quat = quaternion; 359 | 360 | using dquat = quaternion; 361 | 362 | using iquat = quaternion; 363 | 364 | using uquat = quaternion; 365 | 366 | using bquat = quaternion; 367 | 368 | using i8quat = quaternion; 369 | 370 | using u8quat = quaternion; 371 | 372 | using i16quat = quaternion; 373 | 374 | using u16quat = quaternion; 375 | 376 | using i32quat = quaternion; 377 | 378 | using u32quat = quaternion; 379 | 380 | using i64quat = quaternion; 381 | 382 | using u64quat = quaternion; 383 | 384 | } 385 | 386 | #endif 387 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Markus Ilmola 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | /* 7 | * This file contains a basic fuzz tester for the library. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | using namespace gml; 17 | 18 | template 19 | using array_t = T[N]; 20 | 21 | template 22 | using array2_t = T[N1][N2]; 23 | 24 | 25 | 26 | // Holds context information for a test case. 27 | // Use macro SC to create one 28 | class SourceContext { 29 | public: 30 | SourceContext(const char* file, int line) : file(file), line(line) { } 31 | const char* file; 32 | const int line; 33 | }; 34 | 35 | #define SC SourceContext(__FILE__, __LINE__) 36 | 37 | 38 | inline std::ostream& operator<<(std::ostream& os, const SourceContext& sc) { 39 | os << sc.file << ":" << sc.line; 40 | return os; 41 | } 42 | 43 | 44 | // Check if doubles are close enough to pass test. 45 | inline bool fcmp(double a, double b) { 46 | const double EPSILON = 0.01; 47 | return std::abs(a - b) <= EPSILON * std::max(std::abs(a), std::abs(b)); 48 | } 49 | 50 | 51 | template 52 | inline void EQ(const SourceContext& sc, const T& a, const T& b) { 53 | if (a != b) { std::cout << sc << ": " << a << " != " << b << "\n"; } 54 | } 55 | 56 | 57 | inline void EQ(const SourceContext& sc, double a, double b) { 58 | if (!fcmp(a, b)) { 59 | std::cout << sc << ": " << a << " != " << b << "\n"; 60 | } 61 | } 62 | 63 | 64 | template 65 | void EQ(const SourceContext& sc, const gml::vec& a, const gml::vec& b) { 66 | if (a != b) { std::cout << sc << ": " << a << " != " << b << "\n"; } 67 | } 68 | 69 | 70 | template 71 | void EQ(const SourceContext& sc, const gml::vec& a, const gml::vec& b) { 72 | if (a != b) { std::cout << sc << ": " << a << " != " << b << "\n"; } 73 | } 74 | 75 | 76 | template 77 | void EQ(const SourceContext& sc, const gml::vec& a, const gml::vec& b) { 78 | for (int i = 0; i < N; ++i) { 79 | if (!fcmp(a[i], b[i])) { 80 | std::cout << sc << ": " << a << " != " << b << "\n"; 81 | return; 82 | } 83 | } 84 | } 85 | 86 | 87 | template 88 | void EQ(const SourceContext& sc, const gml::mat& a, const gml::mat& b) { 89 | if (a != b) { std::cout << sc << ": " << a << " != " << b << "\n"; } 90 | } 91 | 92 | 93 | template 94 | void EQ(const SourceContext& sc, const gml::mat& a, const gml::mat& b) { 95 | for (int c = 0; c < C; ++c) { 96 | for (int r = 0; r < R; ++r) { 97 | if (!fcmp(a[c][r], b[c][r])) { 98 | std::cout << sc << ": " << a << " != " << b << "\n"; 99 | return; 100 | } 101 | } 102 | 103 | } 104 | } 105 | 106 | 107 | inline void EQ(const SourceContext& sc, const gml::quaternion& a, const gml::quaternion& b) { 108 | if (a != b) { std::cout << sc << ": " << a << " != " << b << "\n"; } 109 | } 110 | 111 | 112 | inline void EQ(const SourceContext& sc, const gml::quaternion& a, const gml::quaternion& b) { 113 | if ( 114 | !fcmp(a.real, b.real) || 115 | !fcmp(a.imag[0], b.imag[0]) || 116 | !fcmp(a.imag[1], b.imag[1]) || 117 | !fcmp(a.imag[2], b.imag[2]) 118 | ) { 119 | std::cout << sc << ": " << a << " != " << b << "\n"; 120 | } 121 | } 122 | 123 | 124 | inline double rand(double min, double max) { 125 | static std::mt19937 generator{}; 126 | std::uniform_real_distribution distribution(min, max); 127 | return distribution(generator); 128 | } 129 | 130 | 131 | template 132 | vec randVec(double min, double max) { 133 | vec result{}; 134 | for (auto& value : result) { 135 | value = rand(min, max); 136 | } 137 | return result; 138 | } 139 | 140 | 141 | template 142 | mat randMat(double min, double max) { 143 | mat result{}; 144 | for (auto& value : result) { 145 | value = randVec(min, max); 146 | } 147 | return result; 148 | } 149 | 150 | 151 | inline dquat randQ(double min, double max) { 152 | return dquat{rand(min, max), randVec<3>(min, max)}; 153 | } 154 | 155 | 156 | 157 | 158 | int main() { 159 | 160 | const int testRuns = 10000; 161 | const int min = -20; 162 | const int max = 20; 163 | 164 | dvec3 temp; 165 | dmat4 TEMP; 166 | dquat tempq; 167 | 168 | const double zero = 0.0; 169 | const double one = 1.0; 170 | const dvec3 zeros; 171 | const dvec3 ones(1.0); 172 | const dmat4 I(1.0); 173 | const dmat4 ZEROS; 174 | const dquat iq(1); 175 | 176 | 177 | for (int i=0; i(min, max); 183 | const dvec3 v2 = randVec<3>(min, max); 184 | const dvec3 v3 = randVec<3>(min, max); 185 | const dvec3 v4 = randVec<3>(min, max); 186 | const dmat4 M1 = randMat<4, 4>(min, max); 187 | const dmat4 M2 = randMat<4, 4>(min, max); 188 | const dmat4 M3 = randMat<4, 4>(min, max); 189 | const dquat q1 = randQ(min, max); 190 | const dquat q2 = randQ(min, max); 191 | const dquat q3 = randQ(min, max); 192 | 193 | //-Utils--------------------------------------------------------------- 194 | 195 | EQ(SC, radians(degrees(scalar)), scalar ); 196 | EQ(SC, std::sin(radians(90.0)), one ); 197 | EQ(SC, repeat(scalar, scalar, scalar+1.0), scalar); 198 | EQ(SC, repeat(scalar, scalar-1.0, scalar), scalar-1.0); 199 | EQ(SC, repeat(scalar, scalar-1.0, scalar+1.0), scalar); 200 | 201 | 202 | //-Vectors------------------------------------------------------------- 203 | 204 | EQ(SC, std::is_same::value, true); 205 | EQ(SC, std::is_same::value, true); 206 | 207 | // Contructors 208 | EQ(SC, dvec3{}, dvec3{zero}); 209 | EQ(SC, dvec3{scalar}, dvec3{scalar, scalar, scalar}); 210 | EQ(SC, dvec3{v1.data()}, v1); 211 | EQ(SC, dvec3{v1[0], v1[1], v1[2]}, v1); 212 | EQ(SC, dvec3{dvec2{v1}, v1[2]}, v1); 213 | EQ(SC, dvec3{v1}, v1); 214 | EQ(SC, dvec2{v1}, dvec2{v1, int{2}}); 215 | 216 | // Cast 217 | EQ(SC, 218 | static_vec_cast(v1), 219 | ivec3{static_cast(v1[0]), static_cast(v1[1]), static_cast(v1[2])} 220 | ); 221 | EQ(SC, static_vec_cast(v1), v1); 222 | 223 | // Iteration 224 | int index = 0; 225 | for (auto value : v1) { 226 | EQ(SC, value, v1[index]); 227 | ++index; 228 | } 229 | 230 | // Operators 231 | EQ(SC, v1, v1); 232 | EQ(SC, v1[0], v1.data()[0]); 233 | EQ(SC, v1 + v2, v2 + v1); 234 | EQ(SC, v1 + v1, 2.0 * v1); 235 | EQ(SC, v1 + zero, v1); 236 | EQ(SC, v1 + scalar, v1 + dvec3{scalar}); 237 | EQ(SC, scalar + v1, dvec3{scalar} + v1); 238 | EQ(SC, v1 - scalar, v1 - dvec3{scalar}); 239 | EQ(SC, scalar - v1, dvec3{scalar} - v1); 240 | EQ(SC, v1 - v1, zeros); 241 | EQ(SC, v1 - zeros, v1); 242 | EQ(SC, v1 - v2, (-v2) + v1); 243 | EQ(SC, v1 * v2, v2 * v1); 244 | EQ(SC, zeros * v1, zeros); 245 | EQ(SC, scalar * v1, v1 *scalar); 246 | EQ(SC, v1 * scalar, v1 * dvec3{scalar}); 247 | EQ(SC, v1 / ones, v1); 248 | EQ(SC, v1 / one, v1); 249 | EQ(SC, scalar / ones, dvec3{scalar} / ones); 250 | EQ(SC, -(-v1), v1); 251 | 252 | EQ(SC, temp = v1, v1); 253 | EQ(SC, temp += v2, v1+v2); 254 | EQ(SC, temp *= v3, (v1+v2)*v3); 255 | 256 | // Streaming 257 | std::stringstream vstream; 258 | vstream << v1; 259 | vstream >> temp; 260 | EQ(SC, temp, v1); 261 | EQ(SC, to_string(v1), vstream.str()); 262 | 263 | // vector functions 264 | EQ(SC, static_cast(v1.size()), 3); 265 | EQ(SC, dot(v1, v2), dot(v2, v1)); 266 | EQ(SC, dot(v1, v2 + v3), dot(v1, v2) + dot(v1, v3)); 267 | EQ(SC, dot(v1, scalar * v2 + v3), scalar * dot(v1, v2) + dot(v1, v3)); 268 | EQ(SC, perpDot(dvec2{v1}, dvec2{v1}), zero); 269 | EQ(SC, perpDot(dvec2{v1}, dvec2{v2}), dot(cross(dvec2{v1}), dvec2{v2})); 270 | EQ(SC, cross(v1, v2) , -cross(v2, v1)); 271 | EQ(SC, cross(v1, cross(v2, v3)), v2 * dot(v1, v3) - v3 * dot(v1, v2)); 272 | EQ(SC, dot(cross(dvec2{v1}), dvec2{v1}), zero); 273 | EQ(SC, length(zeros), zero); 274 | EQ(SC, length(normalize(ones)), one); 275 | EQ(SC, area(v1, v1, v1), zero); 276 | EQ(SC, mix(v1, v2, one), v2); 277 | EQ(SC, mix(v1, v2, zero), v1); 278 | EQ(SC, gml::min(zeros, ones), zeros); 279 | EQ(SC, gml::max(zeros, ones), ones); 280 | EQ(SC, gml::min(clamp(v1, zeros, ones), zeros), zeros); 281 | EQ(SC, gml::max(clamp(v1, zeros, ones), ones), ones); 282 | EQ(SC, normal(v1, v2, v3), -normal(v3, v2, v1)); 283 | EQ(SC, pow(sin(v1), dvec3{2.0}) + pow(cos(v1), dvec3{2.0}), ones); 284 | EQ(SC, sin(asin(sin(v1))), sin(v1)); 285 | EQ(SC, cos(acos(cos(v1))), cos(v1)); 286 | EQ(SC, tan(atan(tan(v1))), tan(v1)); 287 | EQ(SC, distance(zeros, v1), length(v1)); 288 | EQ(SC, angle(v1, v1), zero); 289 | EQ(SC, project(ones, ones), ones); 290 | EQ(SC, unpackUnorm(gml::uvec3{}), zeros); 291 | EQ(SC, unpackUnorm(gml::u8vec3{}), zeros); 292 | EQ(SC, unpackUnorm(gml::uvec3{std::numeric_limits::max()}), ones); 293 | EQ(SC, unpackUnorm(gml::u8vec3{std::numeric_limits::max()}), ones); 294 | EQ(SC, packUnorm(zeros), gml::uvec3{}); 295 | EQ(SC, packUnorm(ones), gml::uvec3{std::numeric_limits::max()}); 296 | EQ(SC, unpackUnorm(packUnorm(normalize(v1))), gml::clamp(normalize(v1), zeros, ones)); 297 | EQ(SC, unpackSnorm(gml::ivec3{}), zeros); 298 | EQ(SC, unpackSnorm(gml::ivec3{std::numeric_limits::max()}), ones); 299 | EQ(SC, unpackSnorm(gml::ivec3{std::numeric_limits::min()}), -ones); 300 | EQ(SC, unpackSnorm(packSnorm(normalize(v1))), normalize(v1)); 301 | EQ(SC, packSnorm(zeros), gml::ivec3{}); 302 | EQ(SC, packSrgb(zeros), gml::uvec3()); 303 | EQ(SC, packSrgb(ones), gml::uvec3(std::numeric_limits::max())); 304 | EQ(SC, unpackSrgb(gml::uvec3()), zeros); 305 | EQ(SC, unpackSrgb(gml::u8vec3(std::numeric_limits::max())), ones); 306 | EQ(SC, unpackSrgb(packSrgb(abs(normalize(v1)))), abs(normalize(v1))); 307 | 308 | //-Matrices------------------------------------------------------------- 309 | 310 | EQ(SC, std::is_same::value, true); 311 | EQ(SC, std::is_same::value, true); 312 | 313 | // Constructors 314 | EQ(SC, dmat4{zero}, ZEROS); 315 | EQ(SC, dmat4{M1.data(), true}, M1); 316 | EQ(SC, dmat4{M1}, M1); 317 | EQ(SC, dmat4{M1[0], M1[1], M1[2], M1[3]}, M1); 318 | EQ(SC, dmat3{v1}, dmat3{v1, v1, v1}); 319 | EQ(SC, dmat3{M1}, dmat3{M1, int{3}, int{3}}); 320 | EQ(SC, dmat4{dmat3{I}, 1.0}, I); 321 | EQ(SC, dmat2{dmat4x2{dmat2x4{M1}}}, dmat2{M1}); 322 | 323 | // Casts 324 | EQ(SC, static_mat_cast(M1), M1); 325 | EQ(SC, 326 | static_mat_cast(M1), 327 | imat4{ 328 | static_vec_cast(M1[0]), 329 | static_vec_cast(M1[1]), 330 | static_vec_cast(M1[2]), 331 | static_vec_cast(M1[3]) 332 | } 333 | ); 334 | 335 | // Iteration 336 | int cIndex = 0; 337 | for (const auto& column : M1) { 338 | EQ(SC, column, M1[cIndex]); 339 | ++cIndex; 340 | } 341 | 342 | // Matrix operators 343 | EQ(SC, M1, M1); 344 | EQ(SC, M1[0][0], M1.data()[0]); 345 | EQ(SC, M1 + M2, M2 + M1); 346 | EQ(SC, M1 + ZEROS, M1); 347 | EQ(SC, M1 - M1, ZEROS); 348 | EQ(SC, (M1 * M2) * M3, M1 * (M2 * M3)); 349 | EQ(SC, I * M1, M1); 350 | EQ(SC, M1 * I, M1); 351 | EQ(SC, ZEROS * M1, ZEROS); 352 | EQ(SC, 2.0 * M1, M1 + M1); 353 | EQ(SC, scalar * M1, M1 * scalar); 354 | 355 | EQ(SC, TEMP = M1, M1); 356 | EQ(SC, TEMP += M2, M1 + M2); 357 | EQ(SC, TEMP *= M3, (M1 + M2) * M3); 358 | 359 | // Streaming 360 | std::stringstream mstream; 361 | mstream << M1; 362 | mstream >> TEMP; 363 | EQ(SC, TEMP, M1); 364 | EQ(SC, to_string(M1), mstream.str()); 365 | 366 | EQ(SC, static_cast(M1.size()), 4 ); 367 | 368 | // Matrix functions 369 | EQ(SC, M1, transpose(transpose(M1))); 370 | EQ(SC, dmat4{M1.data(), false}, transpose(M1)); 371 | EQ(SC, determinant(I), one); 372 | EQ(SC, determinant(M1), determinant(transpose(M1))); 373 | EQ(SC, determinant(M1 * M2), determinant(M1) * determinant(M2)); 374 | EQ(SC, inverse(I), I); 375 | if (determinant(M1) != 0.0) EQ(SC, inverse(inverse(M1)), M1); 376 | EQ(SC, outerProduct(v1, v2), mat{v1} * transpose(mat{v2})); 377 | EQ(SC, matrixCompMult(M1, M2), dmat4{M1[0] * M2[0], M1[1] * M2[1], M1[2] * M2[2], M1[3] * M2[3]}); 378 | EQ(SC, trace(M1), M1[0][0] + M1[1][1] + M1[2][2] + M1[3][3]); 379 | EQ(SC, trace(M1), trace(transpose(M1))); 380 | 381 | // Matrix and vector 382 | EQ(SC, I * dvec4{v1, scalar}, dvec4{v1, scalar}); 383 | EQ(SC, (M1 * M2) * dvec4{v1, scalar}, M1 * (M2 * dvec4{v1, scalar})); 384 | EQ(SC, transform(M1, v1), dvec3{M1 * dvec4{v1, one}}); 385 | 386 | // Transformation matrices 387 | EQ(SC, translate(zeros), I); 388 | EQ(SC, inverse(translate(v1)), translate(-v1)); 389 | EQ(SC, rotate(zeros), I); 390 | EQ(SC, scale(ones), I); 391 | EQ(SC, transform(translate(v1), v2), v1 + v2); 392 | EQ(SC, transform(scale(v1), v2), v1 * v2); 393 | EQ(SC, transform(rotate(radians(90.0)), dvec2{v1}), cross(dvec2{v1})); 394 | EQ(SC, rotate(dvec3{zero, zero, scalar}), rotate(scalar, dvec3{zero, zero, one})); 395 | EQ(SC, rotate(dvec3{zero, scalar, zero}), rotate(scalar, dvec3{zero, one, zero})); 396 | EQ(SC, rotate(dvec3{scalar, zero, zero}), rotate(scalar, dvec3{one, zero, zero})); 397 | EQ(SC, ortho(-one, one, -one, one, one, -one), I); 398 | EQ(SC, lookAt(zeros, dvec3{zero, zero, -one}, dvec3{zero, one, zero}), I); 399 | EQ(SC, translateRotateScale(zeros, zero, dvec3{1.0, 0.0, 0.0}, ones), I); 400 | EQ(SC, normalMatrix(rotate(v1)), dmat3{rotate(v1)}); 401 | { 402 | const auto trs = translateRotateScale(v1, scalar, normalize(v2), v3); 403 | EQ(SC, trs, translate(v1) * rotate(scalar, normalize(v2)) * scale(v3)); 404 | 405 | const auto temp = decomposeTrs(trs); 406 | EQ(SC, 407 | trs, 408 | translateRotateScale( 409 | std::get<0>(temp), 410 | std::get<1>(temp), std::get<2>(temp), 411 | std::get<3>(temp) 412 | ) 413 | ); 414 | } 415 | { 416 | const auto rq = qrotate(scalar, normalize(v2)); 417 | 418 | const auto trs = translateRotateScale(v1, rq, v3); 419 | EQ(SC, trs, translate(v1) * rotate(rq) * scale(v3)); 420 | 421 | const auto temp = qdecomposeTrs(trs); 422 | EQ(SC, 423 | trs, 424 | translateRotateScale( 425 | std::get<0>(temp), std::get<1>(temp), std::get<2>(temp) 426 | ) 427 | ); 428 | } 429 | 430 | const auto tempProj = perspective(one, one, one, 2.0); 431 | EQ(SC, project(dvec3{zero, zero, -one}, I, tempProj, ivec2{-1, -1}, ivec2{2, 2}), zeros); 432 | EQ(SC, unProject(zeros, I, tempProj, ivec2{-1, -1}, ivec2{2, 2}), dvec3{zero, zero, -one}); 433 | 434 | 435 | //-Quaternions---------------------------------------------------------- 436 | 437 | EQ(SC, std::is_same::value, true); 438 | EQ(SC, std::is_same::value, true); 439 | 440 | // Constructors 441 | EQ(SC, dquat{q1.real}.real, q1.real); 442 | EQ(SC, dquat{q1.imag}.imag, q1.imag); 443 | EQ(SC, dquat{q1.real, q1.imag}, q1); 444 | EQ(SC, dquat{q1}, q1); 445 | 446 | // Casts 447 | EQ(SC, static_quaternion_cast(q1), q1); 448 | EQ(SC, 449 | static_quaternion_cast(q1), 450 | iquat{static_cast(q1.real), static_vec_cast(q1.imag)} 451 | ); 452 | 453 | // Quaternion operators 454 | EQ(SC, -(-q1), q1); 455 | EQ(SC, (-q1) + q1, dquat{0.0}); 456 | EQ(SC, q1 + q2, q2 + q1); 457 | EQ(SC, q1 + v1, q1 + dquat{v1}); 458 | EQ(SC, v1 + q1, dquat{v1} + q1); 459 | EQ(SC, scalar + q1, dquat{scalar} + q1); 460 | EQ(SC, q1 + scalar, q1 + dquat{scalar}); 461 | EQ(SC, q1 - q1, dquat{0.0}); 462 | EQ(SC, q1 - v1, q1 - dquat{v1}); 463 | EQ(SC, v1 - q1, dquat{v1} - q1); 464 | EQ(SC, q1 - scalar, q1 - dquat{scalar}); 465 | EQ(SC, scalar - q1, dquat{scalar} - q1); 466 | EQ(SC, iq * q1, q1); 467 | EQ(SC, q1 * iq, q1); 468 | EQ(SC, scalar * q1, dquat{scalar} * q1); 469 | EQ(SC, q1 * scalar, q1 * dquat{scalar}); 470 | EQ(SC, q1 * v1, q1 * dquat{v1}); 471 | EQ(SC, v1 * q1, dquat{v1} * q1); 472 | 473 | EQ(SC, (tempq = q1), q1); 474 | EQ(SC, (tempq += q2), q1 + q2); 475 | EQ(SC, (tempq *= q3), (q1 + q2) * q3); 476 | 477 | // Streaming 478 | std::stringstream qstream; 479 | qstream << q1; 480 | qstream >> tempq; 481 | EQ(SC, tempq, q1); 482 | EQ(SC, to_string(q1), qstream.str()); 483 | 484 | // Quaternion functions 485 | EQ(SC, conj(q1 * q2), conj(q2) * conj(q1)); 486 | EQ(SC, conj(conj(q1)), q1); 487 | EQ(SC, mix(q1, q2, zero), q1); 488 | EQ(SC, mix(q1, q2, one), q2); 489 | EQ(SC, inverse(iq), iq); 490 | EQ(SC, inverse(inverse(q1)), q1); 491 | EQ(SC, abs(iq), one); 492 | EQ(SC, norm(iq), one); 493 | 494 | // Transformation 495 | EQ(SC, qrotate(zeros), iq); 496 | EQ(SC, qrotate(dvec3{scalar, zero, zero}), qrotate(scalar, dvec3{one, zero, zero})); 497 | EQ(SC, qrotate(dvec3{zero, scalar, zero}), qrotate(scalar, dvec3{zero, one, zero})); 498 | EQ(SC, qrotate(dvec3{zero, zero, scalar}), qrotate(scalar, dvec3{zero, zero, one})); 499 | EQ(SC, rotate(iq), I); 500 | EQ(SC, rotate(qrotate(v1)), rotate(v1)); 501 | EQ(SC, rotate(qdecomposeRotate(rotate(qrotate(v1)))), rotate(v1)); 502 | 503 | EQ(SC, 504 | qdecomposeRotate(dmat4{ 505 | dvec4{-1.0, 0.0, 0.0, 0.0}, 506 | dvec4{ 0.0, 1.0, 0.0, 0.0}, 507 | dvec4{ 0.0, 0.0, -1.0, 0.0}, 508 | dvec4{ 0.0, 0.0, 0.0, 1.0} 509 | }), 510 | dquat{0.0, {0.0, 1.0, 0.0}} 511 | ); 512 | 513 | EQ(SC, transform(qrotate(zeros), v1), v1); 514 | EQ(SC, transform(qrotate(dvec3{radians(180.0), zero, zero}), v1), dvec3{v1[0], -v1[1], -v1[2]}); 515 | EQ(SC, transform(normalize(q1), v1), (normalize(q1) * v1 * conj(normalize(q1))).imag); 516 | EQ(SC, abs(qrotate(scalar, normalize(v1))), 1.0); 517 | { 518 | auto temp = decomposeRotate(qrotate(scalar, normalize(v1))); 519 | EQ(SC, qrotate(std::get<0>(temp), std::get<1>(temp)), qrotate(scalar, normalize(v1))); 520 | } 521 | 522 | 523 | //-Splines-------------------------------------------------------------- 524 | 525 | auto B3 = [] (gml::dvec3 P0, gml::dvec3 P1, gml::dvec3 P2, gml::dvec3 P3, double t) 526 | { 527 | return pow(1.0-t,3.0)*P0+3.0*pow(1.0-t,2.0)*t*P1+3.0*(1.0-t)*t*t*P2+pow(t,3.0)*P3; 528 | }; 529 | 530 | auto dB3 = [] (gml::dvec3 P0, gml::dvec3 P1, gml::dvec3 P2, gml::dvec3 P3, double t) 531 | { 532 | return 3.0*pow(1.0-t,2.0)*(P1-P0)+6.0*(1.0-t)*t*(P2-P1)+3.0*t*t*(P3-P2); 533 | }; 534 | 535 | auto ddB3 = [] (gml::dvec3 P0, gml::dvec3 P1, gml::dvec3 P2, gml::dvec3 P3, double t) 536 | { 537 | return 6.0*(1.0-t)*(P2-2.0*P1+P0)+6.0*t*(P3-2.0*P2+P1); 538 | }; 539 | 540 | EQ(SC, bezier(array_t{v1}, t), v1); 541 | EQ(SC, bezier(array_t{v1, v2, v3, v4}, zero), v1); 542 | EQ(SC, bezier(array_t{v1, v2, v3, v4}, one), v4); 543 | EQ(SC, bezier(array_t{v1, v2}, 0.5), 0.5 * (v1 + v2)); 544 | EQ(SC, bezier(array_t{v1, v2}, t), mix(v1, v2, t)); 545 | EQ(SC, bezier(array_t{v1, v2, v3, v4}, t), B3(v1, v2, v3, v4, t)); 546 | 547 | EQ(SC, bezier2(array2_t{{v1}}, gml::dvec2{t, t}), v1); 548 | EQ(SC, bezier2(array2_t{{v1, v2, v3, v4}}, gml::dvec2{t, 0.0}), B3(v1, v2, v3, v4, t)); 549 | EQ(SC, bezier2(array2_t{{v1}, {v2}, {v3}, {v4}}, gml::dvec2{0.0, t}), B3(v1, v2, v3, v4, t)); 550 | EQ(SC, bezier2(array2_t{{v1, v2}, {v3, v4}}, gml::dvec2{0.0, 0.0}), v1); 551 | EQ(SC, bezier2(array2_t{{v1, v2}, {v3, v4}}, gml::dvec2{1.0, 0.0}), v2); 552 | EQ(SC, bezier2(array2_t{{v1, v2}, {v3, v4}}, gml::dvec2{0.0, 1.0}), v3); 553 | EQ(SC, bezier2(array2_t{{v1, v2}, {v3, v4}}, gml::dvec2{1.0, 1.0}), v4); 554 | EQ(SC, bezier2(array2_t{{v1, v2}, {v3, v4}}, gml::dvec2{0.5, 0.5}), 0.25 * (v1 + v2 + v3 + v4)); 555 | 556 | EQ(SC, bezierDerivative<1>(array_t{v1}, t), zeros); 557 | EQ(SC, bezierDerivative<1>(array_t{v1, v2}, t), v2 - v1); 558 | EQ(SC, bezierDerivative<2>(array_t{v1, v2}, t), zeros); 559 | EQ(SC, bezierDerivative<3>(array_t{v1, v2}, t), zeros); 560 | EQ(SC, bezierDerivative<1>(array_t{v1, v2, v3, v4}, t), dB3(v1, v2, v3, v4, t)); 561 | EQ(SC, bezierDerivative<2>(array_t{v1, v2, v3, v4}, t), ddB3(v1, v2, v3, v4, t)); 562 | 563 | const auto J = bezier2Jacobian<1>( 564 | array2_t{ 565 | {gml::dvec3{0.0, 0.0, 0.0}, gml::dvec3{1.0, 0.0, 0.0}}, 566 | {gml::dvec3{0.0, 1.0, 0.0}, gml::dvec3{1.0, 1.0, 0.0}} 567 | }, 568 | gml::dvec2{t, 1.0 - t} 569 | ); 570 | EQ(SC, cross(J[0], J[1]), gml::dvec3{0.0, 0.0, 1.0}); 571 | 572 | 573 | //-Texture-------------------------------------------------------------- 574 | 575 | EQ(SC, texelCenter(gml::ivec2{0, 0}, gml::zvec2{1u, 2u}), gml::dvec2{0.5, 0.25}); 576 | 577 | EQ(SC, nearestTexel(ones, gml::zvec3{1u, 2u, 3u}), gml::ivec3{1,2,3}); 578 | 579 | const auto size = abs(static_vec_cast(v2)) + 1; 580 | EQ(SC, 581 | nearestTexel(texelCenter(static_vec_cast(v1), size), size), 582 | static_vec_cast(v1) 583 | ); 584 | 585 | EQ(SC, faceAt(gml::dvec3{ 1.0, 0.0, 0.0} + 0.01 * v1), 0u); 586 | EQ(SC, faceAt(gml::dvec3{-1.0, 0.0, 0.0} + 0.01 * v1), 1u); 587 | EQ(SC, faceAt(gml::dvec3{ 0.0, 1.0, 0.0} + 0.01 * v1), 2u); 588 | EQ(SC, faceAt(gml::dvec3{ 0.0, -1.0, 0.0} + 0.01 * v1), 3u); 589 | EQ(SC, faceAt(gml::dvec3{ 0.0, 0.0, 1.0} + 0.01 * v1), 4u); 590 | EQ(SC, faceAt(gml::dvec3{ 0.0, 0.0, -1.0} + 0.01 * v1), 5u); 591 | 592 | EQ(SC, cubeTexCoord(v1).first, cubeTexCoord(normalize(v1)).first); 593 | EQ(SC, cubeTexCoord(v1).second, cubeTexCoord(normalize(v1)).second); 594 | 595 | const auto cubeCoord = cubeTexCoord(v1); 596 | EQ(SC, normalize(cubeDirection(cubeCoord.first, cubeCoord.second)), normalize(v1)); 597 | 598 | 599 | 600 | //-Intersect------------------------------------------------------------ 601 | { 602 | const ivec2 mpos{static_vec_cast(v1)}; 603 | const auto proj = ortho2D(-1.0, 1.0, -1.0, 1.0); 604 | const auto ray = pickRay(mpos, I, proj, mpos, ivec2{2}); 605 | EQ(SC, std::get<0>(ray), dvec3{-0.5, -0.5, 1.0}); 606 | EQ(SC, std::get<1>(ray), dvec3{0.0, 0.0, -1.0}); 607 | } 608 | 609 | { 610 | const auto intersection = intersectRayPlane(v1, dvec3{0.0, 0.0, -1.0}, v2, dvec3{0.0, 0.0, 1.0}); 611 | EQ(SC, std::get<0>(intersection), true); 612 | EQ(SC, std::get<1>(intersection), v1[2] - v2[2]); 613 | } 614 | 615 | { 616 | const auto intersection = intersectRayPlane(v1, dvec3{1.0, 0.0, 0.0}, v2, dvec3{0.0, 0.0, 1.0}); 617 | EQ(SC, std::get<0>(intersection), false); 618 | } 619 | 620 | { 621 | const auto intersection = intersectRaySphere( 622 | dvec3{0.0, 0.0, v1[0]}, dvec3{0.0, 0.0, -1.0}, dvec3{0.0, 0.0, v2[0]}, std::abs(scalar) 623 | ); 624 | EQ(SC, std::get<0>(intersection), true); 625 | EQ(SC, std::get<1>(intersection), v1[0] - (v2[0] + std::abs(scalar))); 626 | EQ(SC, std::get<2>(intersection), v1[0] - (v2[0] - std::abs(scalar))); 627 | } 628 | } 629 | 630 | std::cout << "All tests done.\n"; 631 | 632 | return 0; 633 | } 634 | -------------------------------------------------------------------------------- /include/gml/vec.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Markus Ilmola 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_98EC23264CC64D72B996E6FC39002D48 7 | #define UUID_98EC23264CC64D72B996E6FC39002D48 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace gml { 19 | 20 | 21 | /** 22 | * A vector with N components. 23 | * @tparam T Type of a single component 24 | * @tparam N Number of components. Must not be zero! 25 | */ 26 | template 27 | class vec { 28 | private: 29 | 30 | template 31 | struct _all_convertible : std::true_type {}; 32 | 33 | template 34 | struct _all_convertible { 35 | static constexpr bool value = 36 | std::is_convertible::value && 37 | _all_convertible::value; 38 | }; 39 | 40 | public: 41 | 42 | static_assert(N > 0, "N must be greater than zero!"); 43 | 44 | /// The type of a single component. 45 | using value_type = T; 46 | 47 | /// Initializes all components to zero 48 | vec() : vec{T{0}} { } 49 | 50 | /// Initializes all components to the given value 51 | explicit vec(const T& a) { 52 | for (int i = 0; i < N; ++i) data_[i] = a; 53 | } 54 | 55 | /// Initializes components from a C-array. 56 | /// The data MUST have at least N components or behaviour is undefined. 57 | /// If data is null assertion error will occur. 58 | explicit vec(const T* data) { 59 | assert( data != nullptr ); 60 | for (int i = 0; i < N; ++i) data_[i] = data[i]; 61 | } 62 | 63 | /// Initializes components from N values directly. 64 | template < 65 | typename... Args, 66 | typename std::enable_if< 67 | N == sizeof...(Args) && _all_convertible::value, int 68 | >::type = 0 69 | > 70 | vec(const Args&... args) : 71 | data_{ args... } 72 | { 73 | static_assert(sizeof...(args) == N, "Invalid number of arguments!"); 74 | } 75 | 76 | /// Initializes from a smaller vector by padding with given value. 77 | template < 78 | int M, 79 | typename std::enable_if<(M < N), int>::type = 0 80 | > 81 | explicit vec(const vec& v, const T& a = T{0}) { 82 | for (int i = 0; i < M; ++i) data_[i] = v[i]; 83 | for (int i = M; i < N; ++i) data_[i] = a; 84 | } 85 | 86 | /// Initializes from a from a bigger vector by dropping trailing components. 87 | template < 88 | int M, 89 | typename std::enable_if<(M > N), int>::type = 0 90 | > 91 | explicit vec(const vec& v) { 92 | for (int i = 0; i < N; ++i) data_[i] = v[i]; 93 | } 94 | 95 | /// Initializes from a vector by dropping component with index i. 96 | /// If i is larger than N assertion error will occure. 97 | vec(const vec& v, int i) { 98 | assert(i <= N); 99 | for (int j = 0; j < N; ++j) { 100 | if (j < i) data_[j] = v[j]; 101 | else data_[j] = v[j+1]; 102 | } 103 | } 104 | 105 | vec(const vec&) = default; 106 | 107 | vec(vec&&) = default; 108 | 109 | vec& operator=(const vec&) = default; 110 | 111 | vec& operator=(vec&&) = default; 112 | 113 | /// Returns a reference to the i:th component of the vector 114 | T& operator[](int i) noexcept { 115 | assert(i >= 0 && i < N); 116 | return data_[i]; 117 | } 118 | 119 | /// Returns a reference to the i:th component of the vector 120 | const T& operator[](int i) const noexcept { 121 | assert(i >= 0 && i < N); 122 | return data_[i]; 123 | } 124 | 125 | /// Component-wise sum 126 | vec operator+(const vec& v) const { 127 | vec temp{*this}; 128 | temp += v; 129 | return temp; 130 | } 131 | 132 | /// Component-wise sum 133 | vec operator+(const T& a) const { 134 | vec temp{*this}; 135 | temp += a; 136 | return temp; 137 | } 138 | 139 | /// Component-wise subtraction 140 | vec operator-(const vec& v) const { 141 | vec temp{*this}; 142 | temp -= v; 143 | return temp; 144 | } 145 | 146 | /// Component-wise subtraction 147 | vec operator-(const T& a) const { 148 | vec temp{*this}; 149 | temp -= a; 150 | return temp; 151 | } 152 | 153 | /// Component-wise multiplication 154 | vec operator*(const vec& v) const { 155 | vec temp{*this}; 156 | temp *= v; 157 | return temp; 158 | } 159 | 160 | /// Component-wise multiplication 161 | vec operator*(const T& a) const { 162 | vec temp{*this}; 163 | temp *= a; 164 | return temp; 165 | } 166 | 167 | /// Component-wise division 168 | vec operator/(const vec& v) const { 169 | vec temp{*this}; 170 | temp /= v; 171 | return temp; 172 | } 173 | 174 | /// Component-wise division 175 | vec operator/(const T& a) const { 176 | vec temp{*this}; 177 | temp /= a; 178 | return temp; 179 | } 180 | 181 | /// Component-wise sum 182 | vec& operator+=(const vec& v) { 183 | for (int i = 0; i < N; ++i) { 184 | data_[i] += v.data_[i]; 185 | } 186 | return *this; 187 | } 188 | 189 | /// Component-wise sum 190 | vec& operator+=(const T& a) { 191 | for (int i = 0; i < N; ++i) { 192 | data_[i] += a; 193 | } 194 | return *this; 195 | } 196 | 197 | /// Component-wise subtraction 198 | vec& operator-=(const vec& v) { 199 | for (int i = 0; i < N; ++i) { 200 | data_[i] -= v.data_[i]; 201 | } 202 | return *this; 203 | } 204 | 205 | /// Component-wise subtraction 206 | vec& operator-=(const T& a) { 207 | for (int i = 0; i < N; ++i) { 208 | data_[i] -= a; 209 | } 210 | return *this; 211 | } 212 | 213 | /// Component-wise multiplication 214 | vec& operator*=(const vec& v) { 215 | for (int i = 0; i < N; ++i) { 216 | data_[i] *= v.data_[i]; 217 | } 218 | return *this; 219 | } 220 | 221 | /// Component-wise multiplication 222 | vec& operator*=(const T& a) { 223 | for (int i = 0; i < N; ++i) { 224 | data_[i] *= a; 225 | } 226 | return *this; 227 | } 228 | 229 | /// Component-wise division 230 | vec& operator/=(const vec& v) { 231 | for (int i = 0; i < N; ++i) { 232 | data_[i] /= v.data_[i]; 233 | } 234 | return *this; 235 | } 236 | 237 | /// Component-wise division 238 | vec& operator/=(const T& a) { 239 | for (int i = 0; i < N; ++i) { 240 | data_[i] /= a; 241 | } 242 | return *this; 243 | } 244 | 245 | /// Vectors are equal if all of the corresponding components are equal 246 | bool operator==(const vec& v) const { 247 | for (int i = 0; i < N; ++i) { 248 | if (data_[i] != v.data_[i]) return false; 249 | } 250 | return true; 251 | } 252 | 253 | /// Vectors are not equal if any of the corresponding components are not equal 254 | bool operator!=(const vec& v) const { 255 | for (int i = 0; i < N; ++i) { 256 | if (data_[i] != v.data_[i]) return true; 257 | } 258 | return false; 259 | } 260 | 261 | /// Lexicographical less-than comparison. 262 | /// Used to make vec function as map key 263 | bool operator<(const vec& v) const { 264 | for (int i = 0; i < N; ++i) { 265 | if (data_[i] < v.data_[i]) return true; 266 | if (v.data_[i] < data_[i]) return false; 267 | } 268 | return false; 269 | } 270 | 271 | /// Returns a pointer to the first component 272 | const T* data() const noexcept { return data_; } 273 | 274 | /// Returns the number of components in the vector. 275 | /// The maximum value that can be given to []-operator. 276 | static int size() noexcept { return N; } 277 | 278 | /// Iterator to the first component. 279 | T* begin() noexcept { return data_; } 280 | const T* begin() const noexcept { return data_; } 281 | 282 | /// Iterator to one past last component. 283 | T* end() noexcept { return data_ + N; } 284 | const T* end() const noexcept { return data_ + N; } 285 | 286 | private: 287 | 288 | T data_[N]; 289 | 290 | }; 291 | 292 | 293 | 294 | /// Negates all components 295 | template 296 | vec operator-(const vec& v) { 297 | vec temp; 298 | for (int i = 0; i < N; ++i) { 299 | temp[i] = -v[i]; 300 | } 301 | return temp; 302 | } 303 | 304 | 305 | /// Component-wise sum 306 | template 307 | vec operator+(const T& a, const vec& v) { 308 | vec temp; 309 | for (int i = 0; i < N; ++i) { 310 | temp[i] = a + v[i]; 311 | } 312 | return temp; 313 | } 314 | 315 | 316 | /// Component-wise subtraction 317 | template 318 | vec operator-(const T& a, const vec& v) { 319 | vec temp; 320 | for (int i = 0; i < N; ++i) { 321 | temp[i] = a - v[i]; 322 | } 323 | return temp; 324 | } 325 | 326 | 327 | /// Component-wise multiplication 328 | template 329 | vec operator*(const T& a, const vec& v) { 330 | vec temp; 331 | for (int i = 0; i < N; ++i) { 332 | temp[i] = a * v[i]; 333 | } 334 | return temp; 335 | } 336 | 337 | 338 | /// Component-wise division 339 | template 340 | vec operator/(const T& a, const vec& v) { 341 | vec temp; 342 | for (int i = 0; i < N; ++i) { 343 | temp[i] = a / v[i]; 344 | } 345 | return temp; 346 | } 347 | 348 | 349 | /// Prints the vector to a stream inside brackets components separated by a comma. 350 | template 351 | std::ostream& operator<<(std::ostream& os, const vec& v) { 352 | os << '('; 353 | for (int i = 0; i < N; ++i) { 354 | if (i > 0) os << ','; 355 | os << v[i]; 356 | } 357 | os << ')'; 358 | return os; 359 | } 360 | 361 | 362 | /// Read a vector from a stream. 363 | /// The vector must be inside brackets components separated by a comma. 364 | template 365 | std::istream& operator>>(std::istream& is, vec& v) { 366 | char tmp; 367 | is >> tmp; 368 | for (int i = 0; i < N; ++i) { 369 | is >> v[i]; 370 | is >> tmp; 371 | } 372 | return is; 373 | } 374 | 375 | 376 | /// Converts a vec to std::string. 377 | template 378 | std::string to_string(const vec& v) { 379 | std::stringstream ss{}; 380 | ss << v; 381 | return ss.str(); 382 | } 383 | 384 | 385 | /// Dot products of vectors v1 and v2 386 | template 387 | T dot(const vec& v1, const vec& v2) { 388 | T temp = v1[0] * v2[0]; 389 | for (int i = 1; i < N; ++i) { 390 | temp += v1[i] * v2[i]; 391 | } 392 | return temp; 393 | } 394 | 395 | 396 | /// Dot product where v1 is replaced by vector rotated 90 degrees counter clockwise 397 | template 398 | T perpDot(const vec& v1, const vec& v2) { 399 | return v1[0] * v2[1] - v1[1] * v2[0]; 400 | } 401 | 402 | 403 | /// Cross product of two 3 component vectors 404 | template 405 | vec cross(const vec& v1, const vec& v2) { 406 | return vec{ 407 | v1[1] * v2[2] - v1[2] * v2[1], 408 | v1[2] * v2[0] - v1[0] * v2[2], 409 | v1[0] * v2[1] - v1[1] * v2[0] 410 | }; 411 | } 412 | 413 | 414 | /// Returns a perpendicular vector (counter clockwise rotation by 90 degrees) 415 | /// See: http://reference.wolfram.com/language/ref/Cross.html 416 | template 417 | vec cross(const vec& v) { 418 | return vec{-v[1], v[0]}; 419 | } 420 | 421 | 422 | /// Returns the geometric length of the vector. 423 | template 424 | T length(const vec& v) { 425 | using std::sqrt; 426 | return sqrt(dot(v, v)); 427 | } 428 | 429 | 430 | /// Returns a unit length copy of the vector 431 | /// The vector must not have a zero length! 432 | template 433 | vec normalize(const vec& v) { 434 | return v / length(v); 435 | } 436 | 437 | 438 | /// Returns the length of the vector from p1 to p2 439 | template 440 | T distance(const vec& p1, const vec& p2) { 441 | return length(p2 - p1); 442 | } 443 | 444 | 445 | /// Computes the area between the 3 points 446 | template 447 | T area(const vec& p1, const vec& p2, const vec& p3) { 448 | return length(cross(p2 - p1, p3 - p1)) / T{2}; 449 | } 450 | 451 | 452 | /// Calculate the reflection direction for an incident vector. 453 | /// n must be normalized 454 | template 455 | vec reflect(const vec& v, const vec& n) { 456 | return v - T{2} * dot(v, n) * n; 457 | } 458 | 459 | 460 | /// Projects one vector on to another. 461 | template 462 | vec project(const vec& v, const vec& u) { 463 | return dot(v, u) / dot(u, u) * u; 464 | } 465 | 466 | 467 | /// Calculate the refraction direction for an incident vector 468 | /// @param v The incident vector 469 | /// @param n The normal vector 470 | /// @param eta Index of refraction 471 | template 472 | vec refract(const vec& v, const vec& n, const T& eta) { 473 | using std::sqrt; 474 | T d = dot(n, v); 475 | T k = T{1} - eta * eta * (T{1} - d * d); 476 | if (k < T{0}) return vec{T{0}}; 477 | return eta * v - (eta * d + sqrt(k)) * n; 478 | } 479 | 480 | 481 | /// Linear interpolation between v1 and v2 using a as factor 482 | template 483 | vec mix(const vec& v1, const vec& v2, const T& a) { 484 | return (T{1} - a) * v1 + a * v2; 485 | } 486 | 487 | 488 | /// Linear interpolation between v1 and v2 using a as factor 489 | template 490 | vec mix(const vec& v1, const vec& v2, const vec& a) { 491 | return (vec{1} - a) * v1 + a * v2; 492 | } 493 | 494 | 495 | /// Linear spherical interpolation between v1 and v2 using a as factor 496 | template 497 | vec slerp(const vec& v1, const vec& v2, const T& a) { 498 | using std::sin; 499 | const T theta = angle(v1, v2); 500 | const T sine = sin(theta); 501 | return sin((T{1} - a) * theta) / sine * v1 + sin(a * theta) / sine * v2; 502 | } 503 | 504 | 505 | /// Returns the angle (in radians) between vectors v1 and v2 506 | /// If v1 or v2 is zero length zero is returned. 507 | template 508 | T angle(const vec& v1, const vec& v2) { 509 | using std::sqrt; 510 | using std::acos; 511 | using std::numeric_limits; 512 | 513 | const T len = sqrt(dot(v1, v1) * dot(v2, v2)); 514 | if (len <= numeric_limits::epsilon()) return T{0}; 515 | return acos(clamp(dot(v1, v2) / len, T{-1}, T{1})); 516 | } 517 | 518 | 519 | /// Component-wise min 520 | template 521 | vec min(const vec& v1, const vec& v2) { 522 | vec temp; 523 | for (int i = 0; i < N; ++i) { 524 | temp[i] = v1[i] < v2[i] ? v1[i] : v2[i]; 525 | } 526 | return temp; 527 | } 528 | 529 | 530 | /// Component-wise min 531 | template 532 | vec min(const vec& v, const T& a) { 533 | vec temp; 534 | for (int i = 0; i < N; ++i) { 535 | temp[i] = v[i] 543 | vec max(const vec& v1, const vec& v2) { 544 | vec temp; 545 | for (int i = 0; i < N; ++i) { 546 | temp[i] = v1[i] > v2[i] ? v1[i] : v2[i]; 547 | } 548 | return temp; 549 | } 550 | 551 | 552 | /// Component-wise max 553 | template 554 | vec max(const vec& v, const T& a) { 555 | vec temp; 556 | for (int i = 0; i < N; ++i) { 557 | temp[i] = v[i] > a ? v[i] : a; 558 | } 559 | return temp; 560 | } 561 | 562 | 563 | /// Constrain components to lie between given values 564 | template 565 | vec clamp(const vec& v, const vec& minVal, const vec& maxVal) { 566 | return min(max(v, minVal), maxVal); 567 | } 568 | 569 | 570 | /// Constrain components to lie between given values 571 | template 572 | vec clamp(const vec& v, const T& minVal, const T& maxVal) { 573 | return min(max(v, minVal), maxVal); 574 | } 575 | 576 | 577 | /// Computes the unit length normal of a triangle with vertices p1, p2 and p3 578 | /// The points must not lie on the same line! 579 | template 580 | vec normal(const vec& p1, const vec& p2, const vec& p3) { 581 | return normalize(cross(p2 - p1, p3 - p1)); 582 | } 583 | 584 | 585 | /// Component-wise sin 586 | template 587 | vec sin(const vec& v) { 588 | using std::sin; 589 | vec temp; 590 | for (int i = 0; i < N; ++i) temp[i] = sin(v[i]); 591 | return temp; 592 | } 593 | 594 | 595 | /// Component-wise cos 596 | template 597 | vec cos(const vec& v) { 598 | using std::cos; 599 | vec temp; 600 | for (int i = 0; i < N; ++i) temp[i] = cos(v[i]); 601 | return temp; 602 | } 603 | 604 | 605 | /// Component-wise tan 606 | template 607 | vec tan(const vec& v) { 608 | using std::tan; 609 | vec temp; 610 | for (int i = 0; i < N; ++i) temp[i] = tan(v[i]); 611 | return temp; 612 | } 613 | 614 | 615 | /// Component-wise asin 616 | template 617 | vec asin(const vec& v) { 618 | using std::asin; 619 | vec temp; 620 | for (int i = 0; i < N; ++i) temp[i] = asin(v[i]); 621 | return temp; 622 | } 623 | 624 | 625 | /// Component-wise acos 626 | template 627 | vec acos(const vec& v) { 628 | using std::acos; 629 | vec temp; 630 | for (int i = 0; i < N; ++i) temp[i] = acos(v[i]); 631 | return temp; 632 | } 633 | 634 | 635 | /// Component-wise atan 636 | template 637 | vec atan(const vec& v) { 638 | using std::atan; 639 | vec temp; 640 | for (int i = 0; i < N; ++i) temp[i] = atan(v[i]); 641 | return temp; 642 | } 643 | 644 | 645 | /// Component-wise sqrt 646 | template 647 | vec sqrt(const vec& v) { 648 | using std::sqrt; 649 | vec temp; 650 | for (int i = 0; i < N; ++i) temp[i] = sqrt(v[i]); 651 | return temp; 652 | } 653 | 654 | 655 | /// Component-wise abs 656 | template 657 | vec abs(const vec& v) { 658 | using std::abs; 659 | vec temp; 660 | for (int i = 0; i < N; ++i) temp[i] = abs(v[i]); 661 | return temp; 662 | } 663 | 664 | 665 | /// Component-wise pow 666 | template 667 | vec pow(const vec& v1, const vec& v2) { 668 | using std::pow; 669 | vec temp; 670 | for (int i = 0; i < N; ++i) temp[i] = pow(v1[i], v2[i]); 671 | return temp; 672 | } 673 | 674 | 675 | /// Component-wise exp 676 | template 677 | vec exp(const vec& v) { 678 | using std::exp; 679 | vec temp; 680 | for (int i = 0; i < N; ++i) temp[i] = exp(v[i]); 681 | return temp; 682 | } 683 | 684 | 685 | /// Component-wise log 686 | template 687 | vec log(const vec& v) { 688 | using std::log; 689 | vec temp; 690 | for (int i = 0; i < N; ++i) temp[i] = log(v[i]); 691 | return temp; 692 | } 693 | 694 | 695 | /// Component-wise radians 696 | template 697 | vec radians(const vec& v) { 698 | vec temp; 699 | for (int i = 0; i < N; ++i) temp[i] = radians(v[i]); 700 | return temp; 701 | } 702 | 703 | 704 | /// Component-wise degrees 705 | template 706 | vec degrees(const vec& v) { 707 | vec temp; 708 | for (int i = 0; i < N; ++i) temp[i] = degrees(v[i]); 709 | return temp; 710 | } 711 | 712 | 713 | /// Static cast each component from T2 to T1. 714 | template 715 | vec static_vec_cast(const vec& v) { 716 | vec temp; 717 | for (int i = 0; i < N; ++i) 718 | temp[i] = static_cast(v[i]); 719 | return temp; 720 | } 721 | 722 | 723 | /// Component-wise unpackUnorm 724 | template 725 | vec unpackUnorm(const vec& v) { 726 | vec temp; 727 | for (int i = 0; i < N; ++i) temp[i] = unpackUnorm(v[i]); 728 | return temp; 729 | } 730 | 731 | 732 | /// Component-wise packUnorm 733 | template 734 | vec packUnorm(const vec& v) { 735 | vec temp; 736 | for (int i = 0; i < N; ++i) temp[i] = packUnorm(v[i]); 737 | return temp; 738 | } 739 | 740 | 741 | /// Component-wise unpackSnorm 742 | template 743 | vec unpackSnorm(const vec& v) { 744 | vec temp; 745 | for (int i = 0; i < N; ++i) temp[i] = unpackSnorm(v[i]); 746 | return temp; 747 | } 748 | 749 | 750 | /// Component-wise packSnorm 751 | template 752 | vec packSnorm(const vec& v) { 753 | vec temp; 754 | for (int i = 0; i < N; ++i) temp[i] = packSnorm(v[i]); 755 | return temp; 756 | } 757 | 758 | 759 | /// Pack a linear srgb color value to (non-linear) fixed-point. 760 | /// @tparam TI Must be an unsigned integral type. 761 | /// @tparam TF Must be a floating point type. 762 | template 763 | vec packSrgb(const vec& v) 764 | { 765 | const auto delinearize = [] (TF cl) -> TF 766 | { 767 | using std::pow; 768 | if (cl <= static_cast(0)) return static_cast(0); 769 | if (cl >= static_cast(1)) return static_cast(1); 770 | if (cl < static_cast(0.0031308)) return static_cast(12.92) * cl; 771 | return static_cast(1.055) * pow(cl, static_cast(0.41666)) - static_cast(0.055); 772 | }; 773 | return packUnorm( 774 | vec(delinearize(v[0]), delinearize(v[1]), delinearize(v[2])) 775 | ); 776 | } 777 | 778 | 779 | /// Pack a linear srgba color value to (non-linear) fixed-point. 780 | /// Alpha is unpacked as is and is not delinearized. 781 | /// @tparam TI Must be an unsigned integral type. 782 | /// @tparam TF Must be a floating point type. 783 | template 784 | vec packSrgba(const vec& v) 785 | { 786 | const vec temp = packSrgb(vec(v[0], v[1], v[2])); 787 | return vec(temp[0], temp[1], temp[2], packUnorm(v[3])); 788 | } 789 | 790 | 791 | /// Unpack a linear srgb color value from (non-linear) fixed-point. 792 | /// @tparam TF Must be a floating point type. 793 | /// @tparam TI Must be an unsigned integral type. 794 | template 795 | vec unpackSrgb(const vec& v) 796 | { 797 | const auto linearize = [] (TF cs) -> TF 798 | { 799 | using std::pow; 800 | if (cs <= static_cast(0.04045)) return cs / static_cast(12.92); 801 | return pow((cs + static_cast(0.055)) / static_cast(1.055), static_cast(2.4)); 802 | }; 803 | const vec temp = unpackUnorm(v); 804 | return vec(linearize(temp[0]), linearize(temp[1]), linearize(temp[2])); 805 | } 806 | 807 | 808 | /// Unpack a linear srgb color value from (non-linear) fixed-point. 809 | /// Alpha is unpacked as is and is not linearized. 810 | /// @tparam TF Must be a floating point type. 811 | /// @tparam TI Must be an unsigned integral type. 812 | template 813 | vec unpackSrgba(const vec& v) 814 | { 815 | const vec temp = unpackSrgb(vec(v[0], v[1], v[2])); 816 | return vec(temp[0], temp[1], temp[2], unpackUnorm(v[3])); 817 | } 818 | 819 | 820 | using vec2 = vec; 821 | using vec3 = vec; 822 | using vec4 = vec; 823 | 824 | using dvec2 = vec; 825 | using dvec3 = vec; 826 | using dvec4 = vec; 827 | 828 | using ivec2 = vec; 829 | using ivec3 = vec; 830 | using ivec4 = vec; 831 | 832 | using uvec2 = vec; 833 | using uvec3 = vec; 834 | using uvec4 = vec; 835 | 836 | using bvec2 = vec; 837 | using bvec3 = vec; 838 | using bvec4 = vec; 839 | 840 | using zvec2 = vec; 841 | using zvec3 = vec; 842 | using zvec4 = vec; 843 | 844 | using cvec2 = vec; 845 | using cvec3 = vec; 846 | using cvec4 = vec; 847 | 848 | using i8vec2 = vec; 849 | using i8vec3 = vec; 850 | using i8vec4 = vec; 851 | 852 | using u8vec2 = vec; 853 | using u8vec3 = vec; 854 | using u8vec4 = vec; 855 | 856 | using i16vec2 = vec; 857 | using i16vec3 = vec; 858 | using i16vec4 = vec; 859 | 860 | using u16vec2 = vec; 861 | using u16vec3 = vec; 862 | using u16vec4 = vec; 863 | 864 | using i32vec2 = vec; 865 | using i32vec3 = vec; 866 | using i32vec4 = vec; 867 | 868 | using u32vec2 = vec; 869 | using u32vec3 = vec; 870 | using u32vec4 = vec; 871 | 872 | using i64vec2 = vec; 873 | using i64vec3 = vec; 874 | using i64vec4 = vec; 875 | 876 | using u64vec2 = vec; 877 | using u64vec3 = vec; 878 | using u64vec4 = vec; 879 | 880 | } 881 | 882 | 883 | #endif 884 | -------------------------------------------------------------------------------- /include/gml/mat.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Markus Ilmola 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_8B39B0365617488895EAC6FEC2A32C6E 7 | #define UUID_8B39B0365617488895EAC6FEC2A32C6E 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "vec.hpp" 18 | #include "quaternion.hpp" 19 | 20 | namespace gml { 21 | 22 | 23 | /** 24 | * A matrix with C Columns and R rows 25 | * @tparam C Number of columns 26 | * @tparam R Number of rows (length of column vectors) 27 | */ 28 | template 29 | class mat { 30 | public: 31 | 32 | static_assert(C > 0, "Columns must be greater than zero!"); 33 | static_assert(R > 0, "Rows must be greater than zero!"); 34 | 35 | /// The type of a single component (Not the type of single column!) 36 | using value_type = T; 37 | 38 | /// Initialize all components to zero. 39 | mat() { } 40 | 41 | /// Initialize the diagonal to given value and 0 elsewhere 42 | explicit mat(const T& a) { 43 | for (int i = 0; i < std::min(C, R); ++i) data_[i][i] = a; 44 | } 45 | 46 | /// Initialize all columns to v. 47 | explicit mat(const vec& v) { 48 | for (int i = 0; i < C; ++i) data_[i] = v; 49 | } 50 | 51 | /// Initialize from C column vectors with R components each. 52 | template < 53 | typename... Args, 54 | typename std::enable_if::type = 0 55 | > 56 | mat(const Args&... args) : 57 | data_{ args... } 58 | { 59 | static_assert(sizeof...(args) == C, "Invalid number of arguments!"); 60 | } 61 | 62 | /// Initialize from a c-array. 63 | /// The array must have at least C*R components or behaviour is undefined. 64 | /// If data is null assertion error will occur. 65 | /// @param data Pointer to the first element. 66 | /// @param columnMajor Are the components in the input in column major order 67 | mat(const T* data, bool columnMajor) { 68 | assert( data != nullptr ); 69 | if (columnMajor) { 70 | for (int i = 0; i < C; ++i) { 71 | data_[i] = vec(&data[i * R]); 72 | } 73 | } 74 | else { 75 | for (int i = 0; i < C; ++i) { 76 | for (int j = 0; j < R; ++j) { 77 | data_[i][j] = data[j * C + i]; 78 | } 79 | } 80 | } 81 | } 82 | 83 | /// Initialize from a smaller matrix by adding given value to the diagonal 84 | /// and zero elsewhere. 85 | template < 86 | int CC, int RR, 87 | typename std::enable_if<(CC < C || RR < R), int>::type = 0 88 | > 89 | explicit mat(const mat& m, const T& a = T{0}) { 90 | for (int i = 0; i < std::min(C, CC); ++i) { 91 | data_[i] = vec{m[i]}; 92 | } 93 | for (int i = std::min(CC, RR); i < std::min(C, R); ++i) { 94 | data_[i][i] = a; 95 | } 96 | } 97 | 98 | /// Initialize from bigger matrix by dropping trailing rows and columns. 99 | template < 100 | int CC, int RR, 101 | typename std::enable_if<((CC != C || RR != R) && CC >= C && RR >= R), int>::type = 0 102 | > 103 | explicit mat(const mat& m) { 104 | for (int i = 0; i < C; ++i) data_[i] = vec{m[i]}; 105 | } 106 | 107 | /// Creates a sub matrix by removing a row and a column 108 | /// @param m Input matrix. 109 | /// @param col Zero based index of the column to remove 110 | /// @param row Zero based index of the row to remove 111 | mat(const mat& m, int col, int row) { 112 | assert(col <= C && row <= R); 113 | for (int i = 0; i < C; ++i) { 114 | if (i < col) data_[i] = vec{m[i], row}; 115 | else data_[i] = vec{m[i+1], row}; 116 | } 117 | } 118 | 119 | mat(const mat&) = default; 120 | 121 | mat(mat&&) = default; 122 | 123 | mat& operator=(const mat&) = default; 124 | 125 | mat& operator=(mat&&) = default; 126 | 127 | /// Returns a reference to the i:th column vector. 128 | /// If i is not in the range [0, C) and assertion failure will occur. 129 | const vec& operator[](int i) const noexcept { 130 | assert(i >= 0 && i < C); 131 | return data_[i]; 132 | } 133 | 134 | /// Returns a reference to the i:th column vector. 135 | /// If i is not in the range [0, C) and assertion failure will occur. 136 | vec& operator[](int i) noexcept { 137 | assert(i >= 0 && i < C); 138 | return data_[i]; 139 | } 140 | 141 | /// Component-wise sum 142 | mat operator+(const mat& m) const { 143 | mat temp{*this}; 144 | temp += m; 145 | return temp; 146 | } 147 | 148 | /// Component-wise subtraction 149 | mat operator-(const mat& m) const { 150 | mat temp{*this}; 151 | temp -= m; 152 | return temp; 153 | } 154 | 155 | /// Returns the product of matrices 156 | template 157 | mat operator*(const mat& m) const { 158 | mat temp; 159 | for (int i = 0; i < N; ++i) { 160 | for (int r = 0; r < R; ++r) { 161 | for (int c = 0; c < C; ++c) { 162 | temp[i][r] += data_[c][r] * m[i][c]; 163 | } 164 | } 165 | } 166 | return temp; 167 | } 168 | 169 | /// The product of a matrix and a vector as if the vector was a column of a matrix. 170 | /// Note: you can't multiply 4x4-matrix and 3-vector. Use transform instead. 171 | vec operator*(const vec& v) const { 172 | vec temp; 173 | for (int r = 0; r < R; ++r) { 174 | for (int c = 0; c < C; ++c) { 175 | temp[r] += data_[c][r] * v[c]; 176 | } 177 | } 178 | return temp; 179 | } 180 | 181 | /// Returns the product of a matrix and a scalar 182 | mat operator*(const T& a) const { 183 | mat temp{*this}; 184 | temp *= a; 185 | return temp; 186 | } 187 | 188 | /// Component-wise sum 189 | mat& operator+=(const mat& m) { 190 | for (int i = 0; i < C; ++i) { 191 | data_[i] += m.data_[i]; 192 | } 193 | return *this; 194 | } 195 | 196 | /// Component wise subtraction 197 | mat& operator-=(const mat& m) { 198 | for (int i = 0; i < C; ++i) { 199 | data_[i] -= m.data_[i]; 200 | } 201 | return *this; 202 | } 203 | 204 | /// Same as M = M * a 205 | mat& operator*=(const T& a) { 206 | for (int i = 0; i < C; ++i) { 207 | data_[i] *= a; 208 | } 209 | return *this; 210 | } 211 | 212 | /// Same as M = M * m 213 | mat& operator*=(const mat& m) { 214 | *this = *this * m; 215 | return *this; 216 | } 217 | 218 | /// Matrices are equal if all corresponding components are equal. 219 | bool operator==(const mat& m) const { 220 | for (int i = 0; i < C; ++i) { 221 | if (data_[i] != m.data_[i]) return false; 222 | } 223 | return true; 224 | } 225 | 226 | /// Matrices are not equal if any of the corresponding components are not equal 227 | bool operator!=(const mat& m) const { 228 | for (int i = 0; i < C; ++i) { 229 | if (data_[i] != m.data_[i]) return true; 230 | } 231 | return false; 232 | } 233 | 234 | /// Returns pointer to the first component. 235 | /// The matrix data is in column major order 236 | const T* data() const noexcept { return data_[0].data(); } 237 | 238 | /// Returns the number of columns in the matrix 239 | /// (NOT the number of components) 240 | /// This is the largest value that can be given to the [] -operator. 241 | static int size() noexcept { return C; } 242 | 243 | /// Iterator to the first column 244 | vec* begin() noexcept { return data_; } 245 | const vec* begin() const noexcept { return data_; } 246 | 247 | /// Iterator to the one past the last column 248 | vec* end() noexcept { return data_ + C; } 249 | const vec* end() const noexcept { return data_ + C; } 250 | 251 | private: 252 | 253 | vec data_[C]; 254 | 255 | }; 256 | 257 | 258 | /// Multiplies all components of the matrix with a scalar 259 | template 260 | mat operator*(const T& a, const mat& m) { 261 | mat temp; 262 | for (int i = 0; i < C; ++i) { 263 | temp[i] = a * m[i]; 264 | } 265 | return temp; 266 | } 267 | 268 | 269 | /// Prints the matrix to a stream inside brackets columns separated by a comma. 270 | template 271 | std::ostream& operator<<(std::ostream& os, const mat& m) { 272 | os << '('; 273 | for (int i = 0; i < C; ++i) { 274 | if (i > 0) os << ','; 275 | os << m[i]; 276 | } 277 | os << ')'; 278 | return os; 279 | } 280 | 281 | 282 | /// Read matrix from a stream. 283 | /// The matrix must be inside brackets columns separeted by a comma. 284 | template 285 | std::istream& operator>>(std::istream& is, mat& m) { 286 | char tmp; 287 | is >> tmp; 288 | for (int i = 0; i < C; ++i) { 289 | is >> m[i]; 290 | is >> tmp; 291 | } 292 | return is; 293 | } 294 | 295 | 296 | /// Converts a mat to std::string. 297 | template 298 | std::string to_string(const mat& m) { 299 | std::stringstream ss{}; 300 | ss << m; 301 | return ss.str(); 302 | } 303 | 304 | 305 | /// Returns the transpose of the matrix. 306 | template 307 | mat transpose(const mat& m) { 308 | mat temp; 309 | for (int i = 0; i < C; ++i) { // C 310 | for (int j = 0; j < R; ++j) { // R 311 | temp[j][i] = m[i][j]; 312 | } 313 | } 314 | return temp; 315 | } 316 | 317 | 318 | /// Component wise multiplication of matrices 319 | template 320 | mat matrixCompMult(const mat& m1, const mat& m2) { 321 | mat temp; 322 | for (int i = 0; i < C; ++i) { 323 | temp[i] = m1[i] * m2[i]; 324 | } 325 | return temp; 326 | } 327 | 328 | 329 | /// Treats the first vector as matrix with one column and second as matrix with one row 330 | template 331 | mat outerProduct(const vec& v1, const vec& v2) { 332 | mat temp; 333 | for (int i = 0; i < C; ++i) { 334 | for (int j = 0; j < R; ++j) { 335 | temp[i][j] = v1[j] * v2[i]; 336 | } 337 | } 338 | return temp; 339 | } 340 | 341 | 342 | /// Calculates the first minor (determinant of a submatrix) 343 | /// @param col Index of the column to remove (must be [0, N - 1]) 344 | /// @param row Index of the row to remove (must be [0, N - 1]) 345 | template 346 | T firstMinor(const mat& m, int col, int row) { 347 | static_assert(N > 1, "N must be greater than 1."); 348 | 349 | return determinant(mat{m, col, row}); 350 | } 351 | 352 | 353 | template 354 | T firstMinor(const mat& m, int col, int row) { 355 | assert(col >= 0 && col < 3); 356 | assert(row >= 0 && row < 3); 357 | 358 | const int c0 = (col == 0 ? 1 : 0); 359 | const int c1 = (col == 2 ? 1 : 2); 360 | const int r0 = (row == 0 ? 1 : 0); 361 | const int r1 = (row == 2 ? 1 : 2); 362 | 363 | return m[c0][r0] * m[c1][r1] - m[c1][r0] * m[c0][r1]; 364 | } 365 | 366 | 367 | template 368 | T firstMinor(const mat& m, int col, int row) { 369 | assert(col >= 0 && col < 2); 370 | assert(row >= 0 && row < 2); 371 | 372 | return m[!col][!row]; 373 | } 374 | 375 | 376 | /// Computes the determinant of a matrix 377 | template 378 | T determinant(const mat& m) { 379 | T det = 0; 380 | for (int i = 0; i < N; ++i) { 381 | if (i % 2 == 0) 382 | det += m[i][0] * firstMinor(m, i, 0); 383 | else 384 | det -= m[i][0] * firstMinor(m, i, 0); 385 | } 386 | return det; 387 | } 388 | 389 | 390 | /// Computes the determinant of a 1x1 matrix 391 | template 392 | T determinant(const mat& m) { 393 | return m[0][0]; 394 | } 395 | 396 | 397 | /// Computes the determinant of a 2x2 matrix 398 | template 399 | T determinant(const mat& m) { 400 | return m[0][0] * m[1][1] - m[1][0] * m[0][1]; 401 | } 402 | 403 | 404 | /// Computes the inverse of a matrix 405 | template 406 | mat inverse(const mat& m) { 407 | mat temp; 408 | const T a = determinant(m); 409 | for (int i = 0; i < N; ++i) { 410 | for (int j = 0; j < N; ++j) { 411 | if ((i + j) % 2 == 0) 412 | temp[j][i] = firstMinor(m, i, j) / a; 413 | else 414 | temp[j][i] = -firstMinor(m, i, j) / a; 415 | } 416 | } 417 | return temp; 418 | } 419 | 420 | 421 | /// Computes the inverse of a 1x1 matrix 422 | template 423 | mat inverse(const mat& m) { 424 | return mat{T{1} / m[0][0]}; 425 | } 426 | 427 | 428 | /// Generates a translation matrix so that transform(translate(v), p) = p + v 429 | /// This the same matrix that would generated by glTranslate 430 | template 431 | mat translate(const vec& v) { 432 | mat result{T{1}}; 433 | for (int i = 0; i < N; ++i) { 434 | result[N][i] = v[i]; 435 | } 436 | return result; 437 | } 438 | 439 | 440 | /// Generates a scaling matrix so that transform(scale(v), p) = v * p 441 | /// This the same matrix that would generated by glScale 442 | template 443 | mat scale(const vec& v) { 444 | mat result{T{1}}; 445 | for (int i = 0; i < N; ++i) { 446 | result[i][i] = v[i]; 447 | } 448 | return result; 449 | } 450 | 451 | 452 | /// Generates rotations matrix so that 453 | template 454 | mat rotate(const T& angle) { 455 | using std::sin; 456 | using std::cos; 457 | 458 | const T s = sin(angle); 459 | const T c = cos(angle); 460 | 461 | const T data[9] = { 462 | c, -s, T{0}, 463 | s, c, T{0}, 464 | T{0}, T{0}, T{1} 465 | }; 466 | return mat{data, false}; 467 | } 468 | 469 | 470 | /// Generates rotation matrix from Eular angles 471 | template 472 | mat rotate(const vec& angle) { 473 | using std::sin; 474 | using std::cos; 475 | 476 | const T sy = sin(angle[2]); 477 | const T cy = cos(angle[2]); 478 | const T sp = sin(angle[1]); 479 | const T cp = cos(angle[1]); 480 | const T sr = sin(angle[0]); 481 | const T cr = cos(angle[0]); 482 | 483 | const T data[16] = { 484 | cp * cy, sr * sp * cy + cr * -sy, cr * sp * cy + -sr * -sy, T{0}, 485 | cp * sy, sr * sp * sy + cr * cy, cr * sp * sy + -sr * cy, T{0}, 486 | -sp, sr * cp, cr * cp, T{0}, 487 | T{0}, T{0}, T{0}, T{1} 488 | }; 489 | return mat{data, false}; 490 | } 491 | 492 | 493 | /// Generates rotation matrix from angle and axis 494 | template 495 | mat rotate(const T& angle, const vec& axis) { 496 | using std::cos; 497 | using std::sin; 498 | 499 | const T rcos = cos(angle); 500 | const T rsin = sin(angle); 501 | const T rcos_1 = T{1} - rcos; 502 | const T xz = axis[0] * axis[2]; 503 | const T yz = axis[1] * axis[2]; 504 | const T xy = axis[0] * axis[1]; 505 | const T xx = axis[0] * axis[0]; 506 | const T yy = axis[1] * axis[1]; 507 | const T zz = axis[2] * axis[2]; 508 | 509 | const T data[16] = { 510 | rcos + xx * rcos_1, -axis[2] * rsin + xy * rcos_1, axis[1] * rsin + xz * rcos_1, T{0}, 511 | axis[2] * rsin + xy * rcos_1, rcos + yy * rcos_1, -axis[0] * rsin + yz * rcos_1, T{0}, 512 | -axis[1] * rsin + xz * rcos_1, axis[0] * rsin + yz * rcos_1, rcos + zz * rcos_1, T{0}, 513 | T{0}, T{0}, T{0}, T{1} 514 | }; 515 | return mat{data, false}; 516 | } 517 | 518 | 519 | /// Generates rotation matrix from a rotation quaternion 520 | template 521 | mat rotate(const quaternion& q) { 522 | 523 | const T xx = q.imag[0] * q.imag[0]; 524 | const T xy = q.imag[0] * q.imag[1]; 525 | const T xz = q.imag[0] * q.imag[2]; 526 | const T xw = q.imag[0] * q.real; 527 | const T yy = q.imag[1] * q.imag[1]; 528 | const T yz = q.imag[1] * q.imag[2]; 529 | const T yw = q.imag[1] * q.real; 530 | const T zz = q.imag[2] * q.imag[2]; 531 | const T zw = q.imag[2] * q.real; 532 | 533 | const T data[16] = { 534 | T{1} - T{2} * (yy + zz), T{2} * (xy - zw), T{2} * (xz + yw), T{0}, 535 | T{2} * (xy + zw), T{1} - T{2} * (xx + zz), T{2} * (yz - xw), T{0}, 536 | T{2} * (xz - yw), T{2} * (yz + xw), T{1} - T{2} * (xx + yy), T{0}, 537 | T{0}, T{0}, T{0}, T{1} 538 | }; 539 | return mat{data, false}; 540 | } 541 | 542 | 543 | /// Generates a TRS matrix. Same as translate(translation) * 544 | /// rotate(angle, axis) * scale(scaling), but potentially faster. 545 | template 546 | mat translateRotateScale( 547 | const vec& translation, 548 | T angle, const vec& axis, 549 | const gml::vec& scaling 550 | ) { 551 | return translateRotateScale( 552 | translation, qrotate(angle, axis), scaling 553 | ); 554 | } 555 | 556 | 557 | /// Generates a TRS matrix. Same as translate(translation) * 558 | /// rotate(rotation) * scale(scaling), but potentially faster. 559 | template 560 | mat translateRotateScale( 561 | const vec& translation, 562 | const quaternion& rotation, 563 | const vec& scaling 564 | ) { 565 | mat m = rotate(rotation); 566 | 567 | m[0] *= scaling[0]; 568 | m[1] *= scaling[1]; 569 | m[2] *= scaling[2]; 570 | 571 | m[3][0] = translation[0]; 572 | m[3][1] = translation[1]; 573 | m[3][2] = translation[2]; 574 | 575 | return m; 576 | } 577 | 578 | 579 | /// Decomposes a rotation matrix to a rotation quaternion. 580 | /// The input matrix is assumed to be a valid rotation matrix. 581 | template 582 | quaternion qdecomposeRotate(const mat& m) 583 | { 584 | using std::sqrt; 585 | 586 | const T one = static_cast(1); 587 | const T two = static_cast(2); 588 | 589 | const T trace = m[0][0] + m[1][1] + m[2][2]; 590 | 591 | if (trace > static_cast(0)) { 592 | const T s = static_cast(0.5) / sqrt(trace + one); 593 | 594 | return quaternion{ 595 | static_cast(0.25) / s, 596 | vec{ 597 | (m[1][2] - m[2][1]) * s, 598 | (m[2][0] - m[0][2]) * s, 599 | (m[0][1] - m[1][0]) * s 600 | } 601 | }; 602 | } 603 | else if (m[0][0] > m[1][1] && m[0][0] > m[2][2]) { 604 | const T s = two * sqrt(one + m[0][0] - m[1][1] - m[2][2]); 605 | 606 | return quaternion{ 607 | (m[1][2] - m[2][1]) / s, 608 | vec{ 609 | static_cast(0.25) * s, 610 | (m[1][0] + m[0][1]) / s, 611 | (m[2][0] + m[0][2]) / s 612 | } 613 | }; 614 | } 615 | else if (m[1][1] > m[2][2]) { 616 | const T s = two * sqrt(one + m[1][1] - m[0][0] - m[2][2]); 617 | 618 | return quaternion{ 619 | (m[2][0] - m[0][2]) / s, 620 | vec{ 621 | (m[1][0] + m[0][1]) / s, 622 | static_cast(0.25) * s, 623 | (m[2][1] + m[1][2]) / s 624 | } 625 | }; 626 | } 627 | else { 628 | const T s = two * sqrt(one + m[2][2] - m[0][0] - m[1][1]); 629 | 630 | return quaternion{ 631 | (m[0][1] - m[1][0]) / s, 632 | vec{ 633 | (m[2][0] + m[0][2]) / s, 634 | (m[2][1] + m[1][2]) / s, 635 | static_cast(0.25) * s 636 | } 637 | }; 638 | } 639 | } 640 | 641 | 642 | /// Decomposes a rotation matrix to an angle and a axis. 643 | /// The input matrix is assumed to be a valid rotation matrix. 644 | template 645 | std::tuple> decomposeRotate(const mat& m) { 646 | return decomposeRotate(qdecomposeRotate(m)); 647 | } 648 | 649 | 650 | /// Decomposes translate, rotate, scale -matrix to translation, angle, axis and scale. 651 | template 652 | std::tuple, T, vec, vec> decomposeTrs( 653 | const gml::mat& m 654 | ) { 655 | std::tuple, T, vec, vec> temp{}; 656 | 657 | quaternion q{}; 658 | std::tie(std::get<0>(temp), q, std::get<3>(temp)) = qdecomposeTrs(m); 659 | 660 | std::tie(std::get<1>(temp), std::get<2>(temp)) = decomposeRotate(q); 661 | 662 | return temp; 663 | } 664 | 665 | 666 | /// Decomposes translate, rotate, scale -matrix to translation, rotation quaternion and scale. 667 | template 668 | std::tuple, quaternion, vec> qdecomposeTrs( 669 | const gml::mat& m 670 | ) { 671 | gml::mat temp = m; 672 | 673 | const T s = firstMinor(m, 3, 3) < 0 ? static_cast(-1) : static_cast(1); 674 | 675 | vec scaling{s * length(m[0]), length(m[1]), length(m[2])}; 676 | 677 | temp[0] /= scaling[0]; 678 | temp[1] /= scaling[1]; 679 | temp[2] /= scaling[2]; 680 | 681 | return std::make_tuple( 682 | vec{m[3]}, 683 | qdecomposeRotate(temp), 684 | scaling 685 | ); 686 | } 687 | 688 | 689 | /// Generates a perspective projection matrix. 690 | /// This is the same matrix that glFrustum would generate. 691 | template 692 | mat frustum( 693 | const T& left, const T& right, const T& bottom, const T& top, 694 | const T& zNear, const T& zFar 695 | ) { 696 | const T dX = right - left; 697 | const T dY = top - bottom; 698 | const T dZ = zFar - zNear; 699 | const T data[16] = { 700 | (T{2} * zNear) / dX, T{0}, (right + left) / dX, T{0}, 701 | T{0}, (T{2} * zNear) / dY, (top + bottom) / dY, T{0}, 702 | T{0}, T{0}, -(zFar + zNear) / dZ, (T{-2} * zFar * zNear) / dZ, 703 | T{0}, T{0}, T{-1}, T{0} 704 | }; 705 | return mat{data, false}; 706 | } 707 | 708 | 709 | /// Returns a perspective projection matrix. 710 | /// This is the same matrix that gluPerspective would generate. 711 | template 712 | mat perspective( 713 | const T& fovy, const T& aspect, const T& zNear, const T& zFar 714 | ) { 715 | using std::tan; 716 | const T ymax = zNear * tan(fovy / T{2}); 717 | const T ymin = -ymax; 718 | const T xmin = ymin * aspect; 719 | const T xmax = ymax * aspect; 720 | return frustum(xmin, xmax, ymin, ymax, zNear, zFar); 721 | } 722 | 723 | 724 | /// Returns an orthographic projection matrix 725 | /// This is the same matrix that glOrtho would generate. 726 | template 727 | mat ortho( 728 | const T& left, const T& right, const T& bottom, const T& top, 729 | const T& zNear, const T& zFar 730 | ) { 731 | const T dX = right - left; 732 | const T dY = top - bottom; 733 | const T dZ = zFar - zNear; 734 | const T data[16] = { 735 | T{2} / dX, T{0}, T{0}, -(right + left) / dX, 736 | T{0}, T{2} / dY, T{0}, -(top + bottom) / dY, 737 | T{0}, T{0}, T{-2} / dZ, -(zFar + zNear) / dZ, 738 | T{0}, T{0}, T{0}, T{1} 739 | }; 740 | return mat{data, false}; 741 | } 742 | 743 | 744 | /// Returns an orthographic projection matrix 745 | template 746 | mat ortho2D( 747 | const T& left, const T& right, const T& bottom, const T& top 748 | ) { 749 | return ortho(left, right, bottom, top, T{-1}, T{1}); 750 | } 751 | 752 | 753 | /// Returns same matrix as gluLookAt would generate 754 | template 755 | mat lookAt( 756 | const vec& eye, const vec& center, const vec& up 757 | ) { 758 | const vec z = normalize(center - eye); 759 | const vec x = normalize(cross(z, up)); 760 | const vec y = cross(x, z); 761 | 762 | const T data[16] = { 763 | x[0], x[1], x[2], T{0}, 764 | y[0], y[1], y[2], T{0}, 765 | -z[0], -z[1], -z[2], T{0}, 766 | T{0}, T{0}, T{0}, T{1} 767 | }; 768 | return mat{data, false} * translate(-eye); 769 | } 770 | 771 | 772 | /// Map object coordinates to window coordinates (same as gluProject). 773 | /// @param v Object coordinates to project. 774 | /// @param modelView The model view matrix 775 | /// @param proj The projection matrix 776 | /// @param viewportOrigin Lower left corner of the viewport. 777 | /// @param viewportSize Size of the viewport 778 | template 779 | vec project( 780 | const vec& v, 781 | const mat& modelView, const mat& proj, 782 | const vec& viewportOrigin, const vec& viewportSize 783 | ) { 784 | return project(v, proj * modelView, viewportOrigin, viewportSize); 785 | } 786 | 787 | 788 | /// Map object coordinates to window coordinates (same as gluProject). 789 | /// @param v Object coordinates to project. 790 | /// @param modelViewProj The model view projection matrix (proj * modelView). 791 | /// @param viewportOrigin Lower left corner of the viewport. 792 | /// @param viewportSize Size of the viewport 793 | template 794 | vec project( 795 | const vec& v, 796 | const mat& modelViewProj, 797 | const vec& viewportOrigin, const vec& viewportSize 798 | ) { 799 | vec in = modelViewProj * vec{v, static_cast(1)}; 800 | 801 | in[0] /= in[3]; 802 | in[1] /= in[3]; 803 | in[2] /= in[3]; 804 | 805 | const T half = static_cast(0.5); 806 | 807 | in[0] = in[0] * half + half; 808 | in[1] = in[1] * half + half; 809 | in[2] = in[2] * half + half; 810 | 811 | in[0] = in[0] * static_cast(viewportSize[0]) + static_cast(viewportOrigin[0]); 812 | in[1] = in[1] * static_cast(viewportSize[1]) + static_cast(viewportOrigin[1]); 813 | 814 | return vec{in, 3u}; 815 | } 816 | 817 | 818 | /// Map window coordinates to object coordinates (same as gluUnProject). 819 | /// @param v Window coordinates to map. 820 | /// @param modelView The model view matrix. 821 | /// @param proj The projection matrix. 822 | /// @param viewportOrigin Lower left corner of the viewport. 823 | /// @param viewportSize Size of the viewport 824 | template 825 | vec unProject( 826 | const vec& v, 827 | const mat& modelView, const mat& proj, 828 | const vec& viewportOrigin, const vec& viewportSize 829 | ) { 830 | return unProject(v, inverse(proj * modelView), viewportOrigin, viewportSize); 831 | } 832 | 833 | 834 | /// Map window coordinates to object coordinates (same as gluUnProject). 835 | /// @param v Window coordinates to map. 836 | /// @param invModelViewProj Inverse model view projection matrix (proj * modelView)^-1. 837 | /// @param viewportOrigin Lower left corner of the viewport. 838 | /// @param viewportSize Size of the viewport 839 | template 840 | vec unProject( 841 | const vec& v, 842 | const mat& invModelViewProj, 843 | const vec& viewportOrigin, const vec& viewportSize 844 | ) { 845 | vec in{v, static_cast(1)}; 846 | 847 | in[0] = (in[0] - static_cast(viewportOrigin[0])) / static_cast(viewportSize[0]); 848 | in[1] = (in[1] - static_cast(viewportOrigin[1])) / static_cast(viewportSize[1]); 849 | 850 | const T one = static_cast(1); 851 | const T two = static_cast(2); 852 | 853 | in[0] = in[0] * two - one; 854 | in[1] = in[1] * two - one; 855 | in[2] = in[2] * two - one; 856 | 857 | vec out = invModelViewProj * in; 858 | 859 | out[0] /= out[3]; 860 | out[1] /= out[3]; 861 | out[2] /= out[3]; 862 | 863 | return vec{out, 3u}; 864 | } 865 | 866 | 867 | /// Multiply 4x4 matrix by 3-vector by adding 1 as last component to the vector. 868 | template 869 | vec transform(const mat& m, const vec& v) { 870 | vec temp{m[N-1]}; 871 | for (int c = 0; c < N-1; ++c) { 872 | for (int r = 0; r < N-1; ++r) { 873 | temp[r] += m[c][r] * v[c]; 874 | } 875 | } 876 | return temp; 877 | } 878 | 879 | 880 | /// Static cast each component from T2 to T1. 881 | template 882 | mat static_mat_cast(const mat& m) { 883 | mat temp; 884 | for (int i = 0; i < C; ++i) 885 | temp[i] = static_vec_cast(m[i]); 886 | return temp; 887 | } 888 | 889 | 890 | 891 | /// Returns the trace of a matrix (sum of the diagonal elements). 892 | template 893 | T trace(const mat& m) { 894 | T temp = m[0][0]; 895 | for (int i = 1; i < N; ++i) { 896 | temp += m[i][i]; 897 | } 898 | return temp; 899 | } 900 | 901 | 902 | /// Returns the transpose of the inverse of the upper leftmost 3x3 of the matrix 903 | template 904 | mat normalMatrix(const mat& m) 905 | { 906 | return transpose(inverse(mat{m})); 907 | } 908 | 909 | 910 | using mat2x2 = mat; 911 | using mat2x3 = mat; 912 | using mat2x4 = mat; 913 | using mat3x2 = mat; 914 | using mat3x3 = mat; 915 | using mat3x4 = mat; 916 | using mat4x2 = mat; 917 | using mat4x3 = mat; 918 | using mat4x4 = mat; 919 | 920 | using mat2 = mat; 921 | using mat3 = mat; 922 | using mat4 = mat; 923 | 924 | using dmat2x2 = mat; 925 | using dmat2x3 = mat; 926 | using dmat2x4 = mat; 927 | using dmat3x2 = mat; 928 | using dmat3x3 = mat; 929 | using dmat3x4 = mat; 930 | using dmat4x2 = mat; 931 | using dmat4x3 = mat; 932 | using dmat4x4 = mat; 933 | 934 | using dmat2 = mat; 935 | using dmat3 = mat; 936 | using dmat4 = mat; 937 | 938 | using imat2x2 = mat; 939 | using imat2x3 = mat; 940 | using imat2x4 = mat; 941 | using imat3x2 = mat; 942 | using imat3x3 = mat; 943 | using imat3x4 = mat; 944 | using imat4x2 = mat; 945 | using imat4x3 = mat; 946 | using imat4x4 = mat; 947 | 948 | using imat2 = mat; 949 | using imat3 = mat; 950 | using imat4 = mat; 951 | 952 | using umat2x2 = mat; 953 | using umat2x3 = mat; 954 | using umat2x4 = mat; 955 | using umat3x2 = mat; 956 | using umat3x3 = mat; 957 | using umat3x4 = mat; 958 | using umat4x2 = mat; 959 | using umat4x3 = mat; 960 | using umat4x4 = mat; 961 | 962 | using umat2 = mat; 963 | using umat3 = mat; 964 | using umat4 = mat; 965 | 966 | using bmat2x2 = mat; 967 | using bmat2x3 = mat; 968 | using bmat2x4 = mat; 969 | using bmat3x2 = mat; 970 | using bmat3x3 = mat; 971 | using bmat3x4 = mat; 972 | using bmat4x2 = mat; 973 | using bmat4x3 = mat; 974 | using bmat4x4 = mat; 975 | 976 | using bmat2 = mat; 977 | using bmat3 = mat; 978 | using bmat4 = mat; 979 | 980 | using i8mat2x2 = mat; 981 | using i8mat2x3 = mat; 982 | using i8mat2x4 = mat; 983 | using i8mat3x2 = mat; 984 | using i8mat3x3 = mat; 985 | using i8mat3x4 = mat; 986 | using i8mat4x2 = mat; 987 | using i8mat4x3 = mat; 988 | using i8mat4x4 = mat; 989 | 990 | using i8mat2 = mat; 991 | using i8mat3 = mat; 992 | using i8mat4 = mat; 993 | 994 | using u8mat2x2 = mat; 995 | using u8mat2x3 = mat; 996 | using u8mat2x4 = mat; 997 | using u8mat3x2 = mat; 998 | using u8mat3x3 = mat; 999 | using u8mat3x4 = mat; 1000 | using u8mat4x2 = mat; 1001 | using u8mat4x3 = mat; 1002 | using u8mat4x4 = mat; 1003 | 1004 | using u8mat2 = mat; 1005 | using u8mat3 = mat; 1006 | using u8mat4 = mat; 1007 | 1008 | using i16mat2x2 = mat; 1009 | using i16mat2x3 = mat; 1010 | using i16mat2x4 = mat; 1011 | using i16mat3x2 = mat; 1012 | using i16mat3x3 = mat; 1013 | using i16mat3x4 = mat; 1014 | using i16mat4x2 = mat; 1015 | using i16mat4x3 = mat; 1016 | using i16mat4x4 = mat; 1017 | 1018 | using i16mat2 = mat; 1019 | using i16mat3 = mat; 1020 | using i16mat4 = mat; 1021 | 1022 | using u16mat2x2 = mat; 1023 | using u16mat2x3 = mat; 1024 | using u16mat2x4 = mat; 1025 | using u16mat3x2 = mat; 1026 | using u16mat3x3 = mat; 1027 | using u16mat3x4 = mat; 1028 | using u16mat4x2 = mat; 1029 | using u16mat4x3 = mat; 1030 | using u16mat4x4 = mat; 1031 | 1032 | using u16mat2 = mat; 1033 | using u16mat3 = mat; 1034 | using u16mat4 = mat; 1035 | 1036 | using i32mat2x2 = mat; 1037 | using i32mat2x3 = mat; 1038 | using i32mat2x4 = mat; 1039 | using i32mat3x2 = mat; 1040 | using i32mat3x3 = mat; 1041 | using i32mat3x4 = mat; 1042 | using i32mat4x2 = mat; 1043 | using i32mat4x3 = mat; 1044 | using i32mat4x4 = mat; 1045 | 1046 | using i32mat2 = mat; 1047 | using i32mat3 = mat; 1048 | using i32mat4 = mat; 1049 | 1050 | using u32mat2x2 = mat; 1051 | using u32mat2x3 = mat; 1052 | using u32mat2x4 = mat; 1053 | using u32mat3x2 = mat; 1054 | using u32mat3x3 = mat; 1055 | using u32mat3x4 = mat; 1056 | using u32mat4x2 = mat; 1057 | using u32mat4x3 = mat; 1058 | using u32mat4x4 = mat; 1059 | 1060 | using u32mat2 = mat; 1061 | using u32mat3 = mat; 1062 | using u32mat4 = mat; 1063 | 1064 | using i64mat2x2 = mat; 1065 | using i64mat2x3 = mat; 1066 | using i64mat2x4 = mat; 1067 | using i64mat3x2 = mat; 1068 | using i64mat3x3 = mat; 1069 | using i64mat3x4 = mat; 1070 | using i64mat4x2 = mat; 1071 | using i64mat4x3 = mat; 1072 | using i64mat4x4 = mat; 1073 | 1074 | using i64mat2 = mat; 1075 | using i64mat3 = mat; 1076 | using i64mat4 = mat; 1077 | 1078 | using u64mat2x2 = mat; 1079 | using u64mat2x3 = mat; 1080 | using u64mat2x4 = mat; 1081 | using u64mat3x2 = mat; 1082 | using u64mat3x3 = mat; 1083 | using u64mat3x4 = mat; 1084 | using u64mat4x2 = mat; 1085 | using u64mat4x3 = mat; 1086 | using u64mat4x4 = mat; 1087 | 1088 | using u64mat2 = mat; 1089 | using u64mat3 = mat; 1090 | using u64mat4 = mat; 1091 | 1092 | } 1093 | 1094 | #endif 1095 | --------------------------------------------------------------------------------