├── .codecov.sh ├── codecov.yml ├── .build.vcxproj ├── display.lldb ├── .gitignore ├── .github └── workflows │ └── test.yaml ├── display.natvis ├── license.md ├── readme.md ├── quat.h ├── util.h ├── mat.h ├── swizzle.h ├── vec.h └── maths.h /.codecov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for filename in `find . | egrep '\.cpp'`; 3 | do 4 | gcov-5 -n -o . $filename > /dev/null; 5 | done 6 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: no 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | parsers: 16 | gcov: 17 | branch_detection: 18 | conditional: yes 19 | loop: yes 20 | method: no 21 | macro: no 22 | 23 | comment: 24 | layout: "header, diff" 25 | behavior: default 26 | require_changes: no 27 | 28 | ignore: 29 | - ".test/catch.hpp" 30 | -------------------------------------------------------------------------------- /.build.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | Win32 6 | 7 | 8 | 9 | 10 | Application 11 | v140 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /display.lldb: -------------------------------------------------------------------------------- 1 | type summary add --summary-string "${var.x}, ${var.y}" vec2f 2 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}" vec3f 3 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}, ${var.w}" vec4f 4 | type summary add --summary-string "${var.x}, ${var.y}" vec2d 5 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}" vec3d 6 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}, ${var.w}" vec4d 7 | type summary add --summary-string "${var.x}, ${var.y}" vec2i 8 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}" vec3i 9 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}, ${var.w}" vec4i 10 | type summary add --summary-string "${var.x}, ${var.y}" vec2u 11 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}" vec3u 12 | type summary add --summary-string "${var.x}, ${var.y}, ${var.z}, ${var.w}" vec4u 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .test/test 2 | *.gcno 3 | *.gcda 4 | 5 | # Windows image file caches 6 | Thumbs.db 7 | ehthumbs.db 8 | 9 | # Folder config file 10 | Desktop.ini 11 | 12 | # Recycle Bin used on file shares 13 | $RECYCLE.BIN/ 14 | 15 | # Windows Installer files 16 | *.cab 17 | *.msi 18 | *.msm 19 | *.msp 20 | 21 | # Windows shortcuts 22 | *.lnk 23 | 24 | # ========================= 25 | # Operating System Files 26 | # ========================= 27 | 28 | # OSX 29 | # ========================= 30 | 31 | .DS_Store 32 | .AppleDouble 33 | .LSOverride 34 | 35 | examples/*ios_files* 36 | **__pycache__** 37 | *.pyc 38 | **.idea/ 39 | tools/venv 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear in the root of a volume 45 | .DocumentRevisions-V100 46 | .fseventsd 47 | .Spotlight-V100 48 | .TemporaryItems 49 | .Trashes 50 | .VolumeIcon.icns 51 | 52 | # Directories potentially created on remote AFP share 53 | .AppleDB 54 | .AppleDesktop 55 | Network Trash Folder 56 | Temporary Items 57 | .apdisk -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: gcc/clang/msvc 2 | on: [push] 3 | jobs: 4 | gcc: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - run: g++ --version 9 | - run: g++ --std=c++17 -Wno-braced-scalar-init .test/test.cpp -o .test/test && ./".test/test" 10 | - run: g++ --std=c++14 -Wno-braced-scalar-init .test/test.cpp -o .test/test && ./".test/test" 11 | - run: g++ --std=c++11 -Wno-braced-scalar-init -fprofile-arcs -ftest-coverage -fPIC -fno-inline -fno-inline-small-functions -fno-default-inline --coverage .test/test.cpp -o .test/test && ./".test/test" 12 | clang: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - run: clang++ --version 17 | - run: clang++ --std=c++17 -Wno-braced-scalar-init .test/test.cpp -o .test/test && ./".test/test" 18 | - run: clang++ --std=c++14 -Wno-braced-scalar-init .test/test.cpp -o .test/test && ./".test/test" 19 | - run: clang++ --std=c++11 -Wno-braced-scalar-init .test/test.cpp -o .test/test && ./".test/test" 20 | msvc: 21 | runs-on: windows-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: microsoft/setup-msbuild@v1.1 25 | - run: msbuild .build.vcxproj /p:PlatformToolset=v142 26 | - run: msbuild .build.vcxproj /p:PlatformToolset=v143 27 | -------------------------------------------------------------------------------- /display.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{{v[0]}, {v[1]}}} 5 | 6 | 7 | 8 | {{{v[0]}, {v[1]}, {v[2]}}} 9 | 10 | 11 | 12 | {{{v[0]}, {v[1]}, {v[2]}, {v[3]}}} 13 | 14 | 15 | 16 | {{{v[0]}, {v[1]}, {v[2]}, {v[3]}}} 17 | 18 | 19 | 20 | {{{v[0]}, {v[1]}, {v[2]}}} 21 | 22 | 23 | 24 | {{{v[0]}, {v[1]}}} 25 | 26 | 27 | 28 | {{{v[0]}, {v[1]}, {v[2]}, {v[3]}}} 29 | 30 | 31 | 32 | {{{v[0]}, {v[1]}, {v[2]}}} 33 | 34 | 35 | 36 | {{{v[0]}, {v[1]}}} 37 | 38 | 39 | 40 | {{ {v[$T3]} }} 41 | {{ {v[$T3]}, {v[$T4]} }} 42 | {{ {v[$T3]}, {v[$T4]}, {v[$T5]} }} 43 | {{ {v[$T3]}, {v[$T4]}, {v[$T5]}, {v[$T6]} }} 44 | 45 | 46 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alex Dixon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | # SDFGen 25 | 26 | ### vec.h and util.h are heavily modified versions of the math api in sdf-gen 27 | 28 | The MIT License (MIT) 29 | 30 | Copyright (c) 2015, Christopher Batty 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in all 40 | copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 48 | SOFTWARE. 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # maths 3 | 4 | [![gcc_clang](https://github.com/polymonster/maths/actions/workflows/test.yaml/badge.svg)](https://github.com/polymonster/maths/actions) 5 | [![vc2017](https://ci.appveyor.com/api/projects/status/uny5ae4bf3kp2p0m?svg=true)](https://ci.appveyor.com/project/polymonster/maths) 6 | [![codecov](https://codecov.io/gh/polymonster/maths/branch/master/graph/badge.svg)](https://codecov.io/gh/polymonster/maths) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 7 | 8 | A C++ maths library... you might find this useful for games and graphics dev, it has a lot of useful intersection, geometric test and conversion functions, vector swizzling, gjk implementation and other handy features. 9 | 10 | There is a [Live Demo](https://www.polymonster.co.uk/pmtech/examples/maths_functions.html) via WebAssembly and WebGL. 11 | 12 | ## Requirements 13 | 14 | Supported Compilers: MSVC 2017+, GCC 7.0+, Clang 6.0+, EMCC 2.0. 15 | 16 | C++11 or later is required. Tested with C++20, C++17, C++14 and C++11. 17 | 18 | ## Features 19 | 20 | The entire library is header only, add the maths directory to your include search path and simply include: 21 | 22 | ```c++ 23 | #include "maths.h" // intersection, geometric tests and conversion functions 24 | #include "util.h" // min, max, swap, smoothstep, scalar functions.. etc 25 | #include "vec.h" // vector of any dimension and type 26 | #include "mat.h" // matrix of any dimension and type 27 | #include "quat.h" // quaternion of any type 28 | 29 | void maths() { 30 | // templated by size and type 31 | Vec<3, float> float3; 32 | Vec<2, int> int3; 33 | Mat<4, 4, double> double4x4; 34 | Quat q; 35 | 36 | // common abbreviations for convenience 37 | Vec<3, float> vec3f; 38 | Vec<2, float> vec2f; 39 | Mat<3, 3, float> mat3f; 40 | // + common sizes and types for 2, 3, 4 dimensions, float, doubles, ints 41 | 42 | // quick common constructors 43 | vec3f zero = vec3f::zero(); 44 | vec3f one = vec3f::one(); 45 | vec3f red = vec3f::red(); 46 | mat4f identity = mat4f::identity(); 47 | // ... and so on 48 | 49 | // arithmetic operators 50 | vec3f result_add = zero + one; 51 | vec3f result_mul = zero * one; 52 | result_add += red; 53 | mat4f mat_result = mata * matb; 54 | // etc 55 | 56 | // overloaded functions and operations that feel lightweight and expressive 57 | vec3f norm = normalize(va); 58 | f32 dp = dot(va, vb); 59 | vec3f cp = cross(va, vb); 60 | quat q = normalize(q2); 61 | quat qd = dot(q, q); 62 | // + more 63 | 64 | // shader style funcs with scalar variants 65 | vec3f lerp = lerp(va, vb, 0.5f); 66 | f32 lerp = lerp(fa, fb, 0.75f); 67 | vec3f sat = saturate(va); 68 | quat q = slerp(qa, qb, 0.25f); 69 | // yeah! 70 | 71 | // cmath functions for vectors and swizzles 72 | vec2f v2_sin = sin(result_add.xy); 73 | vec3f v3_cos = cos(result_mul); 74 | vec2f v2_floor = floor(v2); 75 | // ... you get it! 76 | 77 | // swizzles for that shader like feeling! 78 | vec4f swizz = v.wzyx; // construct from swizzle 79 | swizz = v.xxxx; // assign from swizzle 80 | swizz.wyxz = v.xxyy; // assign swizzle to swizzle 81 | vec2f v2 = swizz.yz; // construct truncated 82 | swizz.wx = v.xy; // assign truncated 83 | swizz.xyz *= swizz2.www; // arithmetic on swizzles 84 | vec2 v2 = swizz.xy * 2.0f; // swizzle / scalar arithmetic 85 | 86 | // sometimes you may need to cast from swizzle to vec if c++ cant apply implicit casts 87 | f32 dp = dot((vec2f)swizz.xz, (vec2f)swizz.yy): 88 | } 89 | ``` 90 | 91 | ### Functions 92 | 93 | Plane Classification: `point_vs_plane, aabb_vs_plane, sphere_vs_plane, capsule_vs_plane, cone_vs_plane`. 94 | 95 | Overlaps: `sphere_vs_sphere, sphere_vs_aabb, sphere_vs_obb, aabb_vs_aabb, aabb_vs_frustum, sphere_vs_frustum, sphere_vs_capsule, capsule_vs_capsule, obb_vs_obb, aabb_vs_obb, convex_hull_vs_convex_hull, gjk_2d, gjk_3d`. 96 | 97 | Point Inside: `point_inside_aabb, point_inside_sphere, point_inside_obb, point_inside_triangle, point_inside_cone, point_inside_convex_hull, point_inside_poly, point_inside_frustum`. 98 | 99 | Closest Point: `closest_point_on_aabb, closest_point_on_line, closest_point_on_plane, closest_point_on_obb, closest_point_on_sphere, closest_point_on_ray, closest_point_on_triangle, closest_point_on_polygon, closest_point_on_convex_hull, closest_point_on_cone`. 100 | 101 | Point Distance: `point_aabb_distance, point_segment_distance, point_triangle_distance, distance_on_line, point_plane_distance, plane_distance, point_sphere_distance, point_polygon_distance, point_convex_hull_distance, point_cone_distance, point_obb_distance`. 102 | 103 | Ray / Line: `ray_vs_plane, ray_vs_triangle, ray_vs_sphere, ray_vs_line_segment, ray_vs_aabb, ray_vs_obb, ray_vs_capsule, ray_vs_cylinder, line_vs_line, line_vs_poly, shortest_line_segment_between_lines, shortest_line_segment_between_line_segments`. 104 | 105 | Shader Style Functions: `dot, cross, normalize, mag, mag2, dist, dist2, triple, vector_triple, lerp, nlerp, slerp, saturate, clamp, normalize, chebyshev_normalize, all, any, min, max, min_union, max_union, smoothstep, step, round, floor, ceil, abs, frac, trunc, exp, exp2, log, log2, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh`. 106 | 107 | Graph Functions: `smooth_start, smooth_stop, impulse, cubic_pulse, exp_step, parabola, pcurve, exp_sustained_impulse, sinc, gain, almost_identity, integral_smoothstep, quad_impulse, poly_impulse`. 108 | 109 | \+ More included! 110 | 111 | ### Running Tests 112 | 113 | ```shell 114 | c++ --std=c++11 -Wno-braced-scalar-init .test/test.cpp -o .test/test && ./".test/test" 115 | ``` 116 | 117 | ### Debugger Tools 118 | 119 | There is a provided [display.natvis](https://github.com/polymonster/maths/blob/master/display.natvis) file which can be used with visual studio or vscode, this will display swizzles correctly when hovering in the debugger and prevent the huge union expansion from the swizzles. 120 | 121 | Append the contents of [display.lldb](https://github.com/polymonster/maths/blob/master/display.lldb) to your `~/.lldbinit` for improved readability in xcode or commandline lldb debugging. 122 | -------------------------------------------------------------------------------- /quat.h: -------------------------------------------------------------------------------- 1 | // quat.h 2 | // Copyright 2014 - 2020 Alex Dixon. 3 | // License: https://github.com/polymonster/maths/blob/master/license.md 4 | 5 | #pragma once 6 | 7 | #include "mat.h" 8 | #include "vec.h" 9 | 10 | #include 11 | #include 12 | 13 | template 14 | struct Quat 15 | { 16 | union 17 | { 18 | struct { 19 | T x, y, z, w; 20 | }; 21 | 22 | struct { 23 | T v[4]; 24 | }; 25 | }; 26 | 27 | Quat(); 28 | Quat(T z_theta, T y_theta, T x_theta); 29 | Quat(T x, T y, T z, T w); 30 | 31 | Quat operator*(const T& scale) const; 32 | Quat operator/(const T& scale) const; 33 | Quat operator+(const Quat& q) const; 34 | Quat operator=(const Vec<4, T>& v) const; 35 | Quat operator-() const; 36 | Quat operator*(const Quat& rhs) const; 37 | Quat& operator*=(const Quat& rhs); 38 | Quat& operator*=(const T& scale); 39 | 40 | void euler_angles(T z_theta, T y_theta, T x_theta); 41 | void axis_angle(Vec<3, T> axis, T w); 42 | void axis_angle(T lx, T ly, T lz, T lw); 43 | void axis_angle(Vec<4, T> v); 44 | void get_matrix(Mat<4, 4, T>& lmatrix); 45 | void get_matrix(Mat<4, 4, T>& lmatrix) const; 46 | void from_matrix(Mat<4, 4, T> m); 47 | Vec<3, T> to_euler() const; 48 | }; 49 | 50 | // free funcs 51 | template 52 | maths_inline T dot(const Quat& l, const Quat& r) 53 | { 54 | return l.x * r.x + l.y * r.y + l.z * r.z + l.w * r.w; 55 | } 56 | 57 | template 58 | maths_inline Quat normalize(const Quat& q) 59 | { 60 | Quat q2; 61 | T rmag = (T)1 / sqrt(q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z); 62 | for(size_t i = 0; i < 4; ++i) 63 | q2.v[i] = q.v[i] * rmag; 64 | return q2; 65 | } 66 | 67 | template 68 | maths_inline T mag2(const Quat& q) 69 | { 70 | return dot(q, q); 71 | } 72 | 73 | template 74 | maths_inline Quat lerp(const Quat& l, const Quat& r, T t) 75 | { 76 | return (l * ((T)1 - t) + r * t); 77 | } 78 | 79 | template 80 | maths_inline Quat nlerp(const Quat& l, const Quat& r, T t) 81 | { 82 | return normalize(lerp(l, r, t)); 83 | } 84 | 85 | template 86 | maths_inline Quat slerp(Quat q1, Quat q2, T t) 87 | { 88 | T w1, x1, y1, z1, w2, x2, y2, z2; 89 | T theta, mult1, mult2; 90 | 91 | w1 = q1.w; x1 = q1.x; y1 = q1.y; z1 = q1.z; 92 | w2 = q2.w; x2 = q2.x; y2 = q2.y; z2 = q2.z; 93 | 94 | // reverse the sign of q2 if q1.q2 < 0. 95 | if (w1*w2 + x1*x2 + y1*y2 + z1*z2 < 0) 96 | { 97 | w2 = -w2; x2 = -x2; y2 = -y2; z2 = -z2; 98 | } 99 | 100 | theta = acos(w1*w2 + x1*x2 + y1*y2 + z1*z2); 101 | 102 | constexpr T k_epsilon = (T)0.000001; 103 | if (theta > k_epsilon) 104 | { 105 | mult1 = sin( (1-t)*theta ) / sin( theta ); 106 | mult2 = sin( t*theta ) / sin( theta ); 107 | } 108 | else 109 | { 110 | mult1 = 1 - t; 111 | mult2 = t; 112 | } 113 | 114 | Quat out_quat; 115 | out_quat.w = mult1*w1 + mult2*w2; 116 | out_quat.x = mult1*x1 + mult2*x2; 117 | out_quat.y = mult1*y1 + mult2*y2; 118 | out_quat.z = mult1*z1 + mult2*z2; 119 | 120 | return out_quat; 121 | } 122 | 123 | // constructors 124 | template 125 | maths_inline Quat::Quat() 126 | { 127 | x = (T)0; 128 | y = (T)0; 129 | z = (T)0; 130 | w = (T)1; 131 | }; 132 | 133 | template 134 | maths_inline Quat::Quat(T z_theta, T y_theta, T x_theta) 135 | { 136 | euler_angles(z_theta, y_theta, x_theta); 137 | } 138 | 139 | template 140 | maths_inline Quat::Quat(T x, T y, T z, T w) 141 | { 142 | this->x = x; 143 | this->y = y; 144 | this->z = z; 145 | this->w = w; 146 | } 147 | 148 | // operators 149 | template 150 | maths_inline Quat Quat::operator*(const T& scale) const 151 | { 152 | Quat out_quat; 153 | for(size_t i = 0; i < 4; ++i) 154 | out_quat.v[i] = v[i] * scale; 155 | 156 | return out_quat; 157 | } 158 | 159 | template 160 | maths_inline Quat Quat::operator/(const T& scale) const 161 | { 162 | Quat out_quat; 163 | for(size_t i = 0; i < 4; ++i) 164 | out_quat.v[i] = v[i] / scale; 165 | 166 | return out_quat; 167 | } 168 | 169 | template 170 | maths_inline Quat Quat::operator+(const Quat& q) const 171 | { 172 | Quat out_quat; 173 | for(size_t i = 0; i < 4; ++i) 174 | out_quat.v[i] = v[i] + q.v[i]; 175 | 176 | return out_quat; 177 | } 178 | 179 | template 180 | maths_inline Quat Quat::operator=(const Vec<4, T>& _v) const 181 | { 182 | Quat out_quat; 183 | for(size_t i = 0; i < 4; ++i) 184 | out_quat.v[i] = _v[i]; 185 | 186 | return out_quat; 187 | } 188 | 189 | template 190 | maths_inline Quat Quat::operator-() const // Unary minus 191 | { 192 | Quat out_quat; 193 | for(size_t i = 0; i < 4; ++i) 194 | out_quat.v[i] = -v[i]; 195 | 196 | return out_quat; 197 | } 198 | 199 | // non commutative multiply 200 | template 201 | maths_inline Quat Quat::operator*(const Quat& rhs) const 202 | { 203 | Quat res; 204 | res.w = w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z; 205 | res.x = w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y; 206 | res.y = w * rhs.y - x * rhs.z + y * rhs.w + z * rhs.z; 207 | res.z = w * rhs.z + x * rhs.y - y * rhs.x + z * rhs.w; 208 | 209 | return res; 210 | } 211 | 212 | template 213 | maths_inline Quat& Quat::operator*=(const Quat& rhs) 214 | { 215 | Quat res; 216 | res.w = w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z; 217 | res.x = w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y; 218 | res.y = w * rhs.y - x * rhs.z + y * rhs.w + z * rhs.z; 219 | res.z = w * rhs.z + x * rhs.y - y * rhs.x + z * rhs.w; 220 | 221 | *this = res; 222 | return *this; 223 | } 224 | 225 | template 226 | maths_inline Quat& Quat::operator*=(const T& scale) 227 | { 228 | for(size_t i = 0; i < 4; ++i) 229 | v[i] *= scale; 230 | return *this; 231 | } 232 | 233 | // member funcs 234 | template 235 | inline void Quat::euler_angles(T z_theta, T y_theta, T x_theta) 236 | { 237 | T half_z = (T)0.5 * z_theta; 238 | T half_x = (T)0.5 * x_theta; 239 | T half_y = (T)0.5 * y_theta; 240 | 241 | T cos_z_2 = (T)cos(half_z); 242 | T cos_y_2 = (T)cos(half_y); 243 | T cos_x_2 = (T)cos(half_x); 244 | 245 | T sin_z_2 = (T)sin(half_z); 246 | T sin_y_2 = (T)sin(half_y); 247 | T sin_x_2 = (T)sin(half_x); 248 | 249 | // compute quat 250 | w = cos_z_2 * cos_y_2 * cos_x_2 + sin_z_2 * sin_y_2 * sin_x_2; 251 | x = cos_z_2 * cos_y_2 * sin_x_2 - sin_z_2 * sin_y_2 * cos_x_2; 252 | y = cos_z_2 * sin_y_2 * cos_x_2 + sin_z_2 * cos_y_2 * sin_x_2; 253 | z = sin_z_2 * cos_y_2 * cos_x_2 - cos_z_2 * sin_y_2 * sin_x_2; 254 | 255 | *this = normalize(*this); 256 | } 257 | 258 | template 259 | maths_inline void Quat::axis_angle(Vec<3, T> axis, T w) 260 | { 261 | axis_angle(axis.x, axis.y, axis.z, w); 262 | } 263 | 264 | template 265 | maths_inline void Quat::axis_angle(T lx, T ly, T lz, T lw) 266 | { 267 | T half_angle = lw * (T)0.5; 268 | 269 | w = (T)cos(half_angle); 270 | x = lx * (T)sin(half_angle); 271 | y = ly * (T)sin(half_angle); 272 | z = lz * (T)sin(half_angle); 273 | 274 | *this = normalize(*this); 275 | } 276 | 277 | template 278 | maths_inline void Quat::axis_angle(Vec<4, T> v) 279 | { 280 | axis_angle(v.x, v.y, v.z, v.w); 281 | } 282 | 283 | template 284 | inline void Quat::get_matrix(Mat<4, 4, T>& lmatrix) 285 | { 286 | *this = normalize(*this); 287 | 288 | static const T _0 = (T)0; 289 | static const T _1 = (T)1; 290 | static const T _2 = (T)2; 291 | 292 | lmatrix.m[0] = _1 - _2 * y * y - _2 * z * z; 293 | lmatrix.m[1] = _2 * x * y - _2 * z * w; 294 | lmatrix.m[2] = _2 * x * z + _2 * y * w; 295 | lmatrix.m[3] = _0; 296 | 297 | lmatrix.m[4] = _2 * x * y + _2 * z * w; 298 | lmatrix.m[5] = _1 - _2 * x * x - _2 * z * z; 299 | lmatrix.m[6] = _2 * y * z - _2 * x * w; 300 | lmatrix.m[7] = _0; 301 | 302 | lmatrix.m[8] = _2 * x * z - _2 * y * w; 303 | lmatrix.m[9] = _2 * y * z + _2 * x * w; 304 | lmatrix.m[10] = _1 - _2 * x * x - _2 * y * y; 305 | lmatrix.m[11] = _0; 306 | 307 | lmatrix.m[12] = _0; 308 | lmatrix.m[13] = _0; 309 | lmatrix.m[14] = _0; 310 | lmatrix.m[15] = _1; 311 | } 312 | 313 | template 314 | inline void Quat::get_matrix(Mat<4, 4, T>& lmatrix) const 315 | { 316 | auto cp = normalize(*this); 317 | cp.get_matrix(lmatrix); 318 | } 319 | 320 | template 321 | inline void Quat::from_matrix(Mat<4, 4, T> m) 322 | { 323 | // thanks! 324 | // .. https://math.stackexchange.com/questions/893984/conversion-of-rotation-matrix-to-quaternion 325 | 326 | const T& m00 = m.m[0]; 327 | const T& m01 = m.m[4]; 328 | const T& m02 = m.m[8]; 329 | const T& m10 = m.m[1]; 330 | const T& m11 = m.m[5]; 331 | const T& m12 = m.m[9]; 332 | const T& m20 = m.m[2]; 333 | const T& m21 = m.m[6]; 334 | const T& m22 = m.m[10]; 335 | 336 | T t = 0.0f; 337 | 338 | if (m22 < 0) 339 | { 340 | if (m00 > m11) 341 | { 342 | t = 1 + m00 -m11 -m22; 343 | 344 | x = t; 345 | y = m01 + m10; 346 | z = m20 + m02; 347 | w = m12 - m21; 348 | } 349 | else 350 | { 351 | t = 1 -m00 + m11 -m22; 352 | 353 | x = m01+m10; 354 | y = t; 355 | z = m12+m21; 356 | w = m20-m02; 357 | } 358 | } 359 | else 360 | { 361 | if (m00 < -m11) 362 | { 363 | t = 1 -m00 -m11 + m22; 364 | 365 | x = m20+m02; 366 | y = m12+m21; 367 | z = t; 368 | w = m01-m10; 369 | } 370 | else 371 | { 372 | t = 1 + m00 + m11 + m22; 373 | 374 | x = m12-m21; 375 | y = m20-m02; 376 | z = m01-m10; 377 | w = t; 378 | } 379 | } 380 | 381 | T srt = (T)0.5 / (T)sqrt(t); 382 | *this *= srt; 383 | } 384 | 385 | template 386 | inline Vec<3, T> Quat::to_euler() const 387 | { 388 | Vec<3, T> euler; 389 | 390 | T two = (T)2; 391 | T one = (T)1; 392 | 393 | // roll (x-axis rotation) 394 | T sinr = two * (T)(w * x + y * z); 395 | T cosr = one - two * (T)(x * x + y * y); 396 | euler.x = atan2(sinr, cosr); 397 | 398 | // pitch (y-axis rotation) 399 | T sinp = two * (w * y - z * x); 400 | if (abs(sinp) >= 1) 401 | euler.y = (T)copysign(M_PI / two, sinp); // use 90 degrees if out of range 402 | else 403 | euler.y = asin(sinp); 404 | 405 | // yaw (z-axis rotation) 406 | T siny = two * (w * z + x * y); 407 | T cosy = one - two * (y * y + z * z); 408 | euler.z = atan2(siny, cosy); 409 | 410 | return euler; 411 | } 412 | 413 | template 414 | maths_inline std::ostream& operator<<(std::ostream& out, const Quat& q) 415 | { 416 | out << q.v[0]; 417 | for (size_t i = 1; i < 4; ++i) 418 | out << ", " << q.v[i]; 419 | return out; 420 | } 421 | 422 | typedef Quat quat; 423 | typedef Quat quatf; 424 | typedef Quat quatd; 425 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | // util.h 2 | // Copyright 2014 - 2020 Alex Dixon. 3 | // License: https://github.com/polymonster/maths/blob/master/license.md 4 | // The initial implementation and some functions came from https://github.com/christopherbatty/SDFGen 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if __linux__ 15 | #include 16 | #endif 17 | 18 | #define MATHS_ALWAYS_INLINE 19 | 20 | #ifndef MATHS_ALWAYS_INLINE 21 | #define maths_inline inline 22 | #else 23 | #ifdef _MSC_VER 24 | #define maths_inline __forceinline 25 | #else 26 | #define maths_inline inline __attribute__((always_inline)) 27 | #endif 28 | #endif 29 | 30 | #if defined(__GNUC__) || defined(__clang__) 31 | #define maths_deprecated __attribute__((deprecated)) 32 | #elif defined(_MSC_VER) 33 | #define maths_deprecated __declspec(deprecated) 34 | #else 35 | #pragma message("WARNING: deprecated not implemented for this compiler") 36 | #define maths_deprecated 37 | #endif 38 | 39 | #ifndef M_PI 40 | const double M_PI = 3.1415926535897932384626433832795; 41 | #endif 42 | 43 | #define F_PI 3.1415926535897932384626433832795f 44 | 45 | #ifdef WIN32 46 | #undef min 47 | #undef max 48 | #elif __linux__ 49 | #include 50 | #endif 51 | 52 | typedef uint64_t u64; 53 | typedef uint32_t u32; 54 | typedef float f32; 55 | typedef double f64; 56 | 57 | using std::max; 58 | using std::min; 59 | using std::swap; 60 | 61 | template 62 | maths_inline T sgn(T val) 63 | { 64 | return (T)(T(0) < val) - (val < T(0)); 65 | } 66 | 67 | template 68 | maths_inline T sqr(const T& x) 69 | { 70 | return x * x; 71 | } 72 | 73 | template 74 | maths_inline T cube(const T& x) 75 | { 76 | return x * x * x; 77 | } 78 | 79 | template 80 | maths_inline T rsqrt(const T& x) 81 | { 82 | return (T)1 / sqrt(x); 83 | } 84 | 85 | template 86 | maths_inline T min(T a1, T a2, T a3) 87 | { 88 | return min(a1, min(a2, a3)); 89 | } 90 | 91 | template 92 | maths_inline T min(T a1, T a2, T a3, T a4) 93 | { 94 | return min(min(a1, a2), min(a3, a4)); 95 | } 96 | 97 | template 98 | maths_inline T min(T a1, T a2, T a3, T a4, T a5) 99 | { 100 | return min(min(a1, a2), min(a3, a4), a5); 101 | } 102 | 103 | template 104 | maths_inline T min(T a1, T a2, T a3, T a4, T a5, T a6) 105 | { 106 | return min(min(a1, a2), min(a3, a4), min(a5, a6)); 107 | } 108 | 109 | template 110 | maths_inline T max(T a1, T a2, T a3) 111 | { 112 | return max(a1, max(a2, a3)); 113 | } 114 | 115 | template 116 | maths_inline T max(T a1, T a2, T a3, T a4) 117 | { 118 | return max(max(a1, a2), max(a3, a4)); 119 | } 120 | 121 | template 122 | maths_inline T max(T a1, T a2, T a3, T a4, T a5) 123 | { 124 | return max(max(a1, a2), max(a3, a4), a5); 125 | } 126 | 127 | template 128 | maths_inline T max(T a1, T a2, T a3, T a4, T a5, T a6) 129 | { 130 | return max(max(a1, a2), max(a3, a4), max(a5, a6)); 131 | } 132 | 133 | template 134 | maths_inline void minmax(T a1, T a2, T& amin, T& amax) 135 | { 136 | if (a1 < a2) 137 | { 138 | amin = a1; 139 | amax = a2; 140 | } 141 | else 142 | { 143 | amin = a2; 144 | amax = a1; 145 | } 146 | } 147 | 148 | template 149 | inline void minmax(T a1, T a2, T a3, T& amin, T& amax) 150 | { 151 | if (a1 < a2) 152 | { 153 | if (a1 < a3) 154 | { 155 | amin = a1; 156 | if (a2 < a3) 157 | amax = a3; 158 | else 159 | amax = a2; 160 | } 161 | else 162 | { 163 | amin = a3; 164 | if (a1 < a2) 165 | amax = a2; 166 | else 167 | amax = a1; 168 | } 169 | } 170 | else 171 | { 172 | if (a2 < a3) 173 | { 174 | amin = a2; 175 | if (a1 < a3) 176 | amax = a3; 177 | else 178 | amax = a1; 179 | } 180 | else 181 | { 182 | amin = a3; 183 | amax = a1; 184 | } 185 | } 186 | } 187 | 188 | template 189 | inline void minmax(T a1, T a2, T a3, T a4, T& amin, T& amax) 190 | { 191 | if (a1 < a2) 192 | { 193 | if (a3 < a4) 194 | { 195 | amin = min(a1, a3); 196 | amax = max(a2, a4); 197 | } 198 | else 199 | { 200 | amin = min(a1, a4); 201 | amax = max(a2, a3); 202 | } 203 | } 204 | else 205 | { 206 | if (a3 < a4) 207 | { 208 | amin = min(a2, a3); 209 | amax = max(a1, a4); 210 | } 211 | else 212 | { 213 | amin = min(a2, a4); 214 | amax = max(a1, a3); 215 | } 216 | } 217 | } 218 | 219 | template 220 | maths_inline void minmax(T a1, T a2, T a3, T a4, T a5, T& amin, T& amax) 221 | { 222 | amin = min(a1, a2, a3, a4, a5); 223 | amax = max(a1, a2, a3, a4, a5); 224 | } 225 | 226 | template 227 | maths_inline void minmax(T a1, T a2, T a3, T a4, T a5, T a6, T& amin, T& amax) 228 | { 229 | amin = min(a1, a2, a3, a4, a5, a6); 230 | amax = max(a1, a2, a3, a4, a5, a6); 231 | } 232 | 233 | template 234 | maths_inline void update_minmax(T a1, T& amin, T& amax) 235 | { 236 | if (a1 < amin) 237 | amin = a1; 238 | else if (a1 > amax) 239 | amax = a1; 240 | } 241 | 242 | template 243 | inline void sort(T& a, T& b, T& c) 244 | { 245 | T temp; 246 | if (a < b) 247 | { 248 | if (a < c) 249 | { 250 | if (c < b) 251 | { // a 293 | maths_inline T clamp(T a, T lower, T upper) 294 | { 295 | if (a < lower) 296 | return lower; 297 | else if (a > upper) 298 | return upper; 299 | else 300 | return a; 301 | } 302 | 303 | // only makes sense with T=float or double 304 | template 305 | maths_inline T smooth_step(T r) 306 | { 307 | if (r < 0) 308 | return 0; 309 | else if (r > 1) 310 | return 1; 311 | return r * r * r * (10 + r * (-15 + r * 6)); 312 | } 313 | 314 | template 315 | maths_inline T smooth_step(T r, T r_lower, T r_upper, T value_lower, T value_upper) 316 | { 317 | return value_lower + smooth_step((r - r_lower) / (r_upper - r_lower)) * (value_upper - value_lower); 318 | } 319 | 320 | // its like smooth step but with linear interpolation 321 | template 322 | maths_inline T linear_step(T l, T r, T v) 323 | { 324 | if (v <= l) 325 | return 0.0; 326 | 327 | if (v >= r) 328 | return 1.0; 329 | 330 | return (v - l) / (r - l); 331 | } 332 | 333 | // only makes sense with T=float or double 334 | template 335 | maths_inline T ramp(T r) 336 | { 337 | return smooth_step((r + 1) / 2) * 2 - 1; 338 | } 339 | 340 | template 341 | maths_inline T saturate(T v) 342 | { 343 | v = max(v, (T)0); 344 | v = min(v, (T)1); 345 | return v; 346 | } 347 | 348 | #ifdef WIN32__ 349 | maths_inline int lround(double x) 350 | { 351 | if (x > 0) 352 | return (x - floor(x) < 0.5) ? (int)floor(x) : (int)ceil(x); 353 | else 354 | return (x - floor(x) <= 0.5) ? (int)floor(x) : (int)ceil(x); 355 | } 356 | 357 | maths_inline double remainder(double x, double y) 358 | { 359 | return x - std::floor(x / y + 0.5) * y; 360 | } 361 | #endif 362 | 363 | maths_inline unsigned int round_up_to_power_of_two(unsigned int n) 364 | { 365 | int exponent = 0; 366 | --n; 367 | while (n) 368 | { 369 | ++exponent; 370 | n >>= 1; 371 | } 372 | return 1 << exponent; 373 | } 374 | 375 | maths_inline unsigned int round_down_to_power_of_two(unsigned int n) 376 | { 377 | int exponent = 0; 378 | while (n > 1) 379 | { 380 | ++exponent; 381 | n >>= 1; 382 | } 383 | return 1 << exponent; 384 | } 385 | 386 | // return the morton index from x,y position 387 | inline uint64_t morton_xy(uint64_t x, uint64_t y) 388 | { 389 | x = (x | (x << 16)) & 0x0000FFFF0000FFFF; 390 | x = (x | (x << 8)) & 0x00FF00FF00FF00FF; 391 | x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0F; 392 | x = (x | (x << 2)) & 0x3333333333333333; 393 | x = (x | (x << 1)) & 0x5555555555555555; 394 | 395 | y = (y | (y << 16)) & 0x0000FFFF0000FFFF; 396 | y = (y | (y << 8)) & 0x00FF00FF00FF00FF; 397 | y = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0F; 398 | y = (y | (y << 2)) & 0x3333333333333333; 399 | y = (y | (y << 1)) & 0x5555555555555555; 400 | 401 | return x | (y << 1); 402 | } 403 | 404 | /// returns the morten order index from x,y,z position 405 | inline uint64_t morton_xyz(uint64_t x, uint64_t y, uint64_t z) 406 | { 407 | x = (x | (x << 16)) & 0xFFFF00000000FFFF; 408 | x = (x | (x << 8)) & 0xF00F00F00F00F; 409 | x = (x | (x << 4)) & 0x30C30C30C30C30C3; 410 | x = (x | (x << 2)) & 0x9249249249249249; 411 | 412 | y = (y | (y << 16)) & 0xFFFF00000000FFFF; 413 | y = (y | (y << 8)) & 0xF00F00F00F00F; 414 | y = (y | (y << 4)) & 0x30C30C30C30C30C3; 415 | y = (y | (y << 2)) & 0x9249249249249249; 416 | 417 | z = (z | (z << 16)) & 0xFFFF00000000FFFF; 418 | z = (z | (z << 8)) & 0xF00F00F00F00F; 419 | z = (z | (z << 4)) & 0x30C30C30C30C30C3; 420 | z = (z | (z << 2)) & 0x9249249249249249; 421 | 422 | return (x << 0) | (y << 1) | (z << 2); 423 | } 424 | 425 | 426 | // morton_1 - extract even bits value 0b010101 returns 0b111 427 | inline uint64_t morton_1(uint64_t x) 428 | { 429 | x = x & 0x5555555555555555; 430 | x = (x | (x >> 1)) & 0x3333333333333333; 431 | x = (x | (x >> 2)) & 0x0F0F0F0F0F0F0F0F; 432 | x = (x | (x >> 4)) & 0x00FF00FF00FF00FF; 433 | x = (x | (x >> 8)) & 0x0000FFFF0000FFFF; 434 | x = (x | (x >> 16)) & 0x00000000FFFFFFFF; 435 | return x; 436 | } 437 | 438 | // returns the number of bits divisible by 3. value 0b001001001 returns 0b111 439 | inline uint64_t morton_2(uint64_t x) { 440 | x = x & 0x9249249249249249; 441 | x = (x | (x >> 2)) & 0x30C30C30C30C30C3; 442 | x = (x | (x >> 4)) & 0xF00F00F00F00F; 443 | x = (x | (x >> 8)) & 0xFF0000FF0000FF; 444 | x = (x | (x >> 16)) & 0xFFFF00000000FFFF; 445 | x = (x | (x >> 32)) & 0x00000000FFFFFFFF; 446 | return x; 447 | } 448 | 449 | // returns the x,y grid position for morton order index d 450 | inline void morton_to_xy(uint64_t d, uint64_t &x, uint64_t &y) 451 | { 452 | x = morton_1(d); 453 | y = morton_1(d >> 1); 454 | } 455 | 456 | // returns the x,y,z grid position for morton order index d 457 | inline void morton_to_xyz(uint64_t d, uint64_t &x, uint64_t &y, uint64_t &z) 458 | { 459 | x = morton_2(d >> 0); 460 | y = morton_2(d >> 1); 461 | z = morton_2(d >> 2); 462 | } 463 | 464 | inline int intlog2(int x) 465 | { 466 | int exp = -1; 467 | while (x) 468 | { 469 | x >>= 1; 470 | ++exp; 471 | } 472 | return exp; 473 | } 474 | 475 | template 476 | inline void get_barycentric(T x, int& i, T& f, int i_low, int i_high) 477 | { 478 | T s = std::floor(x); 479 | i = (int)s; 480 | if (i < i_low) 481 | { 482 | i = i_low; 483 | f = 0; 484 | } 485 | else if (i > i_high - 2) 486 | { 487 | i = i_high - 2; 488 | f = 1; 489 | } 490 | else 491 | f = (T)(x - s); 492 | } 493 | 494 | template 495 | maths_inline S lerp(const S& value0, const S& value1, T f) 496 | { 497 | return (1 - f) * value0 + f * value1; 498 | } 499 | 500 | template 501 | maths_inline S bilerp(const S& v00, const S& v10, const S& v01, const S& v11, T fx, T fy) 502 | { 503 | return lerp(lerp(v00, v10, fx), lerp(v01, v11, fx), fy); 504 | } 505 | 506 | template 507 | inline S trilerp(const S& v000, const S& v100, const S& v010, const S& v110, const S& v001, const S& v101, const S& v011, 508 | const S& v111, T fx, T fy, T fz) 509 | { 510 | return lerp(bilerp(v000, v100, v010, v110, fx, fy), bilerp(v001, v101, v011, v111, fx, fy), fz); 511 | } 512 | 513 | template 514 | inline S quadlerp(const S& v0000, const S& v1000, const S& v0100, const S& v1100, const S& v0010, const S& v1010, 515 | const S& v0110, const S& v1110, const S& v0001, const S& v1001, const S& v0101, const S& v1101, 516 | const S& v0011, const S& v1011, const S& v0111, const S& v1111, T fx, T fy, T fz, T ft) 517 | { 518 | return lerp(trilerp(v0000, v1000, v0100, v1100, v0010, v1010, v0110, v1110, fx, fy, fz), 519 | trilerp(v0001, v1001, v0101, v1101, v0011, v1011, v0111, v1111, fx, fy, fz), ft); 520 | } 521 | 522 | // f should be between 0 and 1, with f=0.5 corresponding to balanced weighting between w0 and w2 523 | template 524 | inline void quadratic_bspline_weights(T f, T& w0, T& w1, T& w2) 525 | { 526 | w0 = T(0.5) * sqr(f - 1); 527 | w1 = T(0.75) - sqr(f - T(0.5)); 528 | w2 = T(0.5) * sqr(f); 529 | } 530 | 531 | // f should be between 0 and 1 532 | template 533 | inline void cubic_interp_weights(T f, T& wneg1, T& w0, T& w1, T& w2) 534 | { 535 | T f2(f * f), f3(f2 * f); 536 | wneg1 = -T(1. / 3) * f + T(1. / 2) * f2 - T(1. / 6) * f3; 537 | w0 = 1 - f2 + T(1. / 2) * (f3 - f); 538 | w1 = f + T(1. / 2) * (f2 - f3); 539 | w2 = T(1. / 6) * (f3 - f); 540 | } 541 | 542 | template 543 | inline S cubic_interp(const S& value_neg1, const S& value0, const S& value1, const S& value2, T f) 544 | { 545 | T wneg1, w0, w1, w2; 546 | cubic_interp_weights(f, wneg1, w0, w1, w2); 547 | return wneg1 * value_neg1 + w0 * value0 + w1 * value1 + w2 * value2; 548 | } 549 | 550 | template 551 | inline T map_to_range(T in_range_start, T in_range_end, T out_range_start, T out_range_end, T v) 552 | { 553 | T slope = 1.0f * (out_range_end - out_range_start) / (in_range_end - in_range_start); 554 | return out_range_start + slope * (v - in_range_start); 555 | } 556 | 557 | template 558 | inline T catmul_rom_spline(float t, T p0, T p1, T p2, T p3) 559 | { 560 | return 0.5f * ((2.0f * p1) + 561 | (p2 - p0) * t + 562 | (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * (t * t) + 563 | (-1.0f * p0 + 3.0f * p1 - 3.0f * p2 + p3) * (t * t * t)); 564 | } 565 | 566 | template 567 | inline T catmul_rom_spline(float t, T p0, T p1, T p2, T p3, float alpha) 568 | { 569 | auto getT = [](float t, T p0, T p1, float alpha) -> float 570 | { 571 | T square = sqr(p1-p0); 572 | 573 | float sum = 0.0f; 574 | size_t numComponents = sizeof(p1) / sizeof(((float*)&p1)[0]); 575 | for(size_t i = 0; i < numComponents; ++i) 576 | sum += ((float*)&square)[i]; 577 | 578 | float b = sqrt(sum); 579 | float c = pow(b, alpha); 580 | return c + t; 581 | }; 582 | 583 | float t0 = 0.0f; 584 | float t1 = getT(t0, p0, p1, alpha); 585 | float t2 = getT(t1, p1, p2, alpha); 586 | float t3 = getT(t2, p2, p3, alpha); 587 | 588 | t = lerp(t1, t2, t); 589 | 590 | T a1 = (t1-t)/(t1-t0)*p0 + (t-t0)/(t1-t0)*p1; 591 | T a2 = (t2-t)/(t2-t1)*p1 + (t-t1)/(t2-t1)*p2; 592 | T a3 = (t3-t)/(t3-t2)*p2 + (t-t2)/(t3-t2)*p3; 593 | 594 | T b1 = (t2-t)/(t2-t0)*a1 + (t-t0)/(t2-t0)*a2; 595 | T b2 = (t3-t)/(t3-t1)*a2 + (t-t1)/(t3-t1)*a3; 596 | 597 | return (t2-t)/(t2-t1)*b1 + (t-t1)/(t2-t1)*b2; 598 | } 599 | 600 | template 601 | inline T catmul_rom_spline_centripital(float t, T p0, T p1, T p2, T p3) 602 | { 603 | return catmul_rom_spline(t, p0, p1, p2, p3, 0.5f); 604 | } 605 | 606 | template 607 | maths_inline T smooth_start2(T t, T b = 0.0, T c = 1.0, T d = 1.0) 608 | { 609 | t /= d; 610 | return c * t*t + b; 611 | } 612 | 613 | template 614 | maths_inline T smooth_start3(T t, T b = 0.0, T c = 1.0, T d = 1.0) 615 | { 616 | t /= d; 617 | return c * t*t*t + b; 618 | } 619 | 620 | template 621 | maths_inline T smooth_start4(T t, T b = 0.0, T c = 1.0, T d = 1.0) 622 | { 623 | t /= d; 624 | return c * t*t*t*t + b; 625 | } 626 | 627 | template 628 | maths_inline T smooth_start5(T t, T b = 0.0, T c = 1.0, T d = 1.0) 629 | { 630 | t /= d; 631 | return c * t*t*t*t*t + b; 632 | } 633 | 634 | template 635 | maths_inline T smooth_stop2(T t, T b = 0.0, T c = 1.0, T d = 1.0) 636 | { 637 | t /= d; 638 | return -c * t * (t - 2) + b; 639 | } 640 | 641 | template 642 | maths_inline T smooth_stop3(T t, T b = 0.0, T c = 1.0, T d = 1.0) 643 | { 644 | t = t / d - 1; 645 | return c * (t*t*t + (T)1) + b; 646 | } 647 | 648 | template 649 | maths_inline T smooth_stop4(T t, T b = 0.0, T c = 1.0, T d = 1.0) 650 | { 651 | t = t / d - 1; 652 | return -c * (t*t*t*t - (T)1) + b; 653 | } 654 | 655 | template 656 | maths_inline T smooth_stop5(T t, T b = 0.0, T c = 1.0, T d = 1.0) 657 | { 658 | t = t / d - 1; 659 | return c * (t*t*t*t*t + (T)1) + b; 660 | } 661 | 662 | template 663 | maths_inline T soften_towards_edge(T c, T p, T e, T r) 664 | { 665 | T pd = abs(e - p); 666 | T cd = abs(e - c); 667 | if (cd < pd) 668 | { 669 | T s = smooth_step(cd, (T)0.0, r, (T)0.0, (T)1.0); 670 | return lerp(p, c, s); 671 | } 672 | return c; 673 | } 674 | 675 | template 676 | maths_inline T soften_towards_edges(T c, T p, T e0, T e1, T r) 677 | { 678 | c = soften_towards_edge(c, p, e0, r); 679 | c = soften_towards_edge(c, p, e1, r); 680 | return c; 681 | } 682 | 683 | // thanks to inigo quilez for the following functions: https://iquilezles.org/articles/functions/ 684 | // returns an exponential impulse (y position on a graph for x); k controls the stretching of the function 685 | template 686 | maths_inline T impulse(T k, T x) 687 | { 688 | const T h = k * x; 689 | return h * exp((T)1.0 - h); 690 | } 691 | 692 | // returns a cubic pulse (y position on a graph for x); equivalent to: smoothstep(c-w,c,x)-smoothstep(c,c+w,x) 693 | template 694 | maths_inline T cubic_pulse(T c, T w, T x) 695 | { 696 | x = fabs(x - c); 697 | if (x > w) return (T)0.0; 698 | x /= w; 699 | return (T)1.0 - x * x*((T)3.0 - (T)2.0*x); 700 | } 701 | 702 | // returns an exponential step (y position on a graph for x); k is control parameter, n is power which gives sharper curves. 703 | template 704 | maths_inline T exp_step(T x, T k, T n) 705 | { 706 | return exp(-k * pow(x, n)); 707 | } 708 | 709 | // returns a parabola (y position on a graph for x); use k to control its shape 710 | template 711 | maths_inline T parabola(T x, T k) 712 | { 713 | return pow((T)4.0 * x * ((T)1.0 - x), k); 714 | } 715 | 716 | // returns a power curve (y position on a graph for x); this is a generalziation of the parabola 717 | template 718 | maths_inline T pcurve(T x, T a, T b) 719 | { 720 | T k = pow(a + b, a + b) / (pow(a, a)*pow(b, b)); 721 | return k * pow(x, a) * pow((T)1.0 - x, b); 722 | } 723 | 724 | // returns an exponential sustained impulse (y position on a graph for x); control on the width of attack with k and release with f 725 | template 726 | maths_inline T exp_sustained_impulse(T x, T f, T k) 727 | { 728 | T s = max(x-f, (T)0); 729 | return min(x*x/(f*f), (T)1 + ((T)2/f)*s*(T)exp(-k*s)); 730 | } 731 | 732 | // returns a sin curve (y position on a graph for x); can be used for some bouncing behaviors. give k different integer values to tweak the amount of bounces 733 | template 734 | maths_inline T sinc(T x, T k) 735 | { 736 | auto a = (T)M_PI * (k*x-(T)1); 737 | return std::sin(a)/a; 738 | } 739 | 740 | // returns gain (y position on a graph for x); remapping the unit interval into the unit interval by expanding the sides and compressing the center 741 | template 742 | maths_inline T gain(T x, T k) 743 | { 744 | const T a = (T)0.5*pow((T)2.0*((x<(T)0.5)?x:(T)1.0-x), k); 745 | return (x<(T)0.5)?a:(T)1.0-a; 746 | } 747 | 748 | // returns soft clipping (in a cubic fashion) of x; let m be the threshold (anything above m stays unchanged), and n the value things will take when the signal is zero 749 | template 750 | maths_inline T almost_identity(T x, T m, T n) 751 | { 752 | if(x>m) 753 | return x; 754 | const T a = (T)2.0*n - m; 755 | const T b = (T)2.0*m - (T)3.0*n; 756 | const T t = x/m; 757 | return (a*t + b)*t*t + n; 758 | } 759 | 760 | // returns the integral smoothstep of x it's derivative is never larger than 1 761 | template 762 | maths_inline T integral_smoothstep(T x, T t) 763 | { 764 | if(x>t) 765 | return x - t/(T)2.0; 766 | return x*x*x*((T)1.0-x*(T)0.5/t)/t/t; 767 | } 768 | 769 | // returns an quadratic impulse (y position on a graph for x); k controls the stretching of the function 770 | template 771 | maths_inline T quad_impulse(T k, T x) 772 | { 773 | return (T)2.0*sqrt(k)*x/((T)1.0+k*x*x); 774 | } 775 | 776 | // returns a quadratic impulse (y position on a graph for x); n is the degree of the polynomial and k controls the stretching of the function 777 | template 778 | maths_inline T poly_impulse(T k, T n, T x) 779 | { 780 | return (n/(n-(T)1.0))*pow((n-(T)1.0)*k,(T)1.0/n)*x/((T)1.0+k*pow(x,n)); 781 | } 782 | -------------------------------------------------------------------------------- /mat.h: -------------------------------------------------------------------------------- 1 | // mat.h 2 | // Copyright 2014 - 2020 Alex Dixon. 3 | // License: https://github.com/polymonster/maths/blob/master/license.md 4 | 5 | #pragma once 6 | 7 | #include "vec.h" 8 | #include // memcpy linux 9 | 10 | template 11 | struct Mat 12 | { 13 | T m[R * C]; 14 | 15 | // Constructors 16 | Mat(){}; 17 | 18 | Mat(T* data) 19 | { 20 | for (size_t i = 0; i < R * C; ++i) 21 | m[i] = data[i]; 22 | } 23 | 24 | template 25 | Mat(const Mat& other) 26 | { 27 | for (size_t r = 0; r < R; ++r) 28 | { 29 | for (size_t c = 0; c < C; ++c) 30 | { 31 | m.at(r, c) = other.at(r, c); 32 | } 33 | } 34 | } 35 | 36 | // common ctrs for initializer lists 37 | template < size_t R2 = R, size_t C2 = C, typename = typename std::enable_if< R2 == 2 && C2 == 2 >::type > 38 | Mat(T v00, T v01, 39 | T v10, T v11) 40 | { 41 | m[0] = v00; 42 | m[1] = v01; 43 | m[2] = v10; 44 | m[3] = v11; 45 | } 46 | 47 | template < size_t R2 = R, size_t C2 = C, typename = typename std::enable_if< R2 == 3 && C2 == 3 >::type > 48 | Mat(T v00, T v01, T v02, 49 | T v10, T v11, T v12, 50 | T v20, T v21, T v22) 51 | { 52 | m[0] = v00; 53 | m[1] = v01; 54 | m[2] = v02; 55 | m[3] = v10; 56 | m[4] = v11; 57 | m[5] = v12; 58 | m[6] = v20; 59 | m[7] = v21; 60 | m[8] = v22; 61 | } 62 | 63 | template < size_t R2 = R, size_t C2 = C, typename = typename std::enable_if< R2 == 4 && C2 == 4 >::type > 64 | Mat(T v00, T v01, T v02, T v03, 65 | T v10, T v11, T v12, T v13, 66 | T v20, T v21, T v22, T v23, 67 | T v30, T v31, T v32, T v33) 68 | { 69 | m[0] = v00; 70 | m[1] = v01; 71 | m[2] = v02; 72 | m[3] = v03; 73 | m[4] = v10; 74 | m[5] = v11; 75 | m[6] = v12; 76 | m[7] = v13; 77 | m[8] = v20; 78 | m[9] = v21; 79 | m[10] = v22; 80 | m[11] = v23; 81 | m[12] = v30; 82 | m[13] = v31; 83 | m[14] = v32; 84 | m[15] = v33; 85 | } 86 | 87 | static Mat create_identity(); 88 | 89 | // Operators 90 | Mat operator*(T rhs) const; 91 | Mat& operator*=(T rhs); 92 | Mat operator*(const Mat& rhs) const; 93 | Mat& operator*=(const Mat& rhs); 94 | Vec operator*(const Vec& rhs) const; 95 | T& operator()(size_t r, size_t c); 96 | const T& operator()(size_t r, size_t c) const; 97 | 98 | // Accessors 99 | T& at(size_t r, size_t c); 100 | const T& at(size_t r, size_t c) const; 101 | Vec get_row(size_t index) const; 102 | Vec get_column(size_t index) const; 103 | Vec<3, T> get_translation() const; 104 | void set_row(size_t index, const Vec& row); 105 | void set_column(size_t index, const Vec& col); 106 | void set_translation(const Vec<3, T>& t); 107 | void set_vectors(const Vec<3, T>& right, const Vec<3, T>& up, const Vec<3, T>& at, const Vec<3, T>& pos); 108 | 109 | // Computation 110 | Mat multiply(T scalar) const; 111 | Mat multiply(const Mat& rhs) const; 112 | Vec multiply(const Vec& rhs) const; 113 | Vec<4, T> transform_vector(const Vec<4, T>& v) const; 114 | Vec<3, T> transform_vector(const Vec<3, T>& v, T& w) const; 115 | Vec<3, T> transform_vector(const Vec<3, T>& v) const; 116 | Mat transposed(); 117 | void transpose(); 118 | }; 119 | 120 | // Accessor Functions 121 | template 122 | maths_inline T& Mat::at(size_t r, size_t c) 123 | { 124 | return m[r * C + c]; 125 | } 126 | 127 | template 128 | maths_inline const T& Mat::at(size_t r, size_t c) const 129 | { 130 | return m[r * C + c]; 131 | } 132 | 133 | template 134 | maths_inline Vec Mat::get_row(size_t index) const 135 | { 136 | return Vec(&m[index * C]); 137 | } 138 | 139 | template 140 | maths_inline Vec Mat::get_column(size_t index) const 141 | { 142 | Vec col; 143 | for (size_t i = 0; i < R; ++i) 144 | col[i] = at(i, index); 145 | 146 | return col; 147 | } 148 | 149 | template 150 | maths_inline Vec<3, T> Mat::get_translation() const 151 | { 152 | return Vec<3, T>(m[3], m[7], m[11]); 153 | } 154 | 155 | template 156 | maths_inline void Mat::set_row(size_t index, const Vec& row) 157 | { 158 | size_t i = index * C; 159 | memcpy(&m[i], &row.v, sizeof(T) * C); 160 | } 161 | 162 | template 163 | maths_inline void Mat::set_column(size_t index, const Vec& col) 164 | { 165 | for (size_t r = 0; r < R; ++r) 166 | at(r, index) = col[r]; 167 | } 168 | 169 | template 170 | maths_inline void Mat::set_translation(const Vec<3, T>& t) 171 | { 172 | m[3] = t.x; 173 | m[7] = t.y; 174 | m[11] = t.z; 175 | } 176 | 177 | template 178 | void Mat::set_vectors(const Vec<3, T>& right, const Vec<3, T>& up, const Vec<3, T>& at, const Vec<3, T>& pos) 179 | { 180 | set_row(0, Vec<4, T>(right, pos.x)); 181 | set_row(1, Vec<4, T>(up, pos.y)); 182 | set_row(2, Vec<4, T>(at, pos.z)); 183 | set_row(3, Vec<4, T>(0.0f, 0.0f, 0.0f, 1.0f)); 184 | } 185 | 186 | // Operators 187 | template 188 | maths_inline Vec Mat::operator*(const Vec& rhs) const 189 | { 190 | return multiply(rhs); 191 | } 192 | 193 | template 194 | maths_inline Mat Mat::operator*(const Mat& rhs) const 195 | { 196 | return multiply(rhs); 197 | } 198 | 199 | template 200 | maths_inline Mat& Mat::operator*=(const Mat& rhs) 201 | { 202 | *this = multiply(rhs); 203 | return *this; 204 | } 205 | 206 | template 207 | maths_inline Mat Mat::operator*(T rhs) const 208 | { 209 | return multiply(rhs); 210 | } 211 | 212 | template 213 | maths_inline Mat& Mat::operator*=(T rhs) 214 | { 215 | *this = multiply(rhs); 216 | return *this; 217 | } 218 | 219 | template 220 | maths_inline T& Mat::operator()(size_t r, size_t c) 221 | { 222 | return at(r, c); 223 | } 224 | 225 | template 226 | maths_inline const T& Mat::operator()(size_t r, size_t c) const 227 | { 228 | return at(r, c); 229 | } 230 | 231 | // Computation functions 232 | template 233 | inline Mat Mat::multiply(const Mat& rhs) const 234 | { 235 | Mat result; 236 | 237 | for (size_t r = 0; r < R; ++r) 238 | { 239 | for (size_t c = 0; c < C; ++c) 240 | { 241 | T& element = result.at(r, c); 242 | 243 | Vec vr = get_row(r); 244 | Vec vc = rhs.get_column(c); 245 | 246 | element = dot(vr, vc); 247 | } 248 | } 249 | 250 | return result; 251 | } 252 | 253 | template 254 | maths_inline Mat Mat::multiply(T scalar) const 255 | { 256 | Mat result; 257 | for (size_t i = 0; i < R * C; ++i) 258 | { 259 | result.m[i] = m[i] * scalar; 260 | } 261 | 262 | return result; 263 | } 264 | 265 | template 266 | maths_inline Vec Mat::multiply(const Vec& v) const 267 | { 268 | Vec result; 269 | for (size_t r = 0; r < R; ++r) 270 | { 271 | result[r] = dot(v, get_row(r)); 272 | } 273 | 274 | return result; 275 | } 276 | 277 | template 278 | maths_inline Vec<4, T> Mat::transform_vector(const Vec<4, T>& v) const 279 | { 280 | Vec<4, T> result; 281 | for (size_t r = 0; r < R; ++r) 282 | { 283 | result[r] = dot(v, get_row(r)); 284 | } 285 | 286 | return result; 287 | } 288 | 289 | template 290 | maths_inline Vec<3, T> Mat::transform_vector(const Vec<3, T>& v, T& w) const 291 | { 292 | Vec<4, T> result = Vec<4, T>(v, w); 293 | Vec<4, T> v4 = Vec<4, T>(v, w); 294 | for (size_t r = 0; r < R; ++r) 295 | { 296 | result[r] = dot(v4, get_row(r)); 297 | } 298 | 299 | w = result.w; 300 | return result.xyz; 301 | } 302 | 303 | template 304 | maths_inline Vec<3, T> Mat::transform_vector(const Vec<3, T>& v) const 305 | { 306 | Vec<4, T> result = Vec<4, T>(v, 1.0); 307 | Vec<4, T> v4 = Vec<4, T>(v, 1.0); 308 | for (size_t r = 0; r < R; ++r) 309 | { 310 | result[r] = dot(v4, get_row(r)); 311 | } 312 | 313 | return result.xyz; 314 | } 315 | 316 | template 317 | maths_inline void Mat::transpose() 318 | { 319 | Mat t = this->transposed(); 320 | *this = t; 321 | } 322 | 323 | template 324 | maths_inline Mat Mat::transposed() 325 | { 326 | Mat t; 327 | 328 | for (size_t r = 0; r < R; ++r) 329 | for (size_t c = 0; c < C; ++c) 330 | t.at(c, r) = at(r, c); 331 | 332 | return t; 333 | } 334 | 335 | template 336 | maths_inline Mat Mat::create_identity() 337 | { 338 | Mat identity; 339 | memset(&identity, 0x0, sizeof(Mat)); 340 | 341 | for (size_t r = 0; r < R; ++r) 342 | for (size_t c = 0; c < C; ++c) 343 | if (r == c) 344 | identity.at(r, c) = 1; 345 | 346 | return identity; 347 | } 348 | 349 | template 350 | std::ostream& operator<<(std::ostream& out, const Mat& m) 351 | { 352 | out << m.m[0]; 353 | for (size_t i = 1; i < R * C; ++i) 354 | out << ", " << m.m[i]; 355 | return out; 356 | } 357 | 358 | template 359 | std::ostream& operator<<(std::ostream& out, const Mat& m) 360 | { 361 | out << "(float)" << m.m[0]; 362 | for (size_t i = 1; i < R * C; ++i) 363 | out << ", " << "(float)" << m.m[i]; 364 | return out; 365 | } 366 | 367 | namespace mat 368 | { 369 | template 370 | inline T compute_determinant(const Mat<2, 2, T>& matrix_) 371 | { 372 | const T& a = matrix_(0, 0); 373 | const T& b = matrix_(0, 1); 374 | const T& c = matrix_(1, 0); 375 | const T& d = matrix_(1, 1); 376 | return a * d - b * c; 377 | } 378 | 379 | template 380 | inline T compute_determinant(const Mat<3, 3, T>& m_) 381 | { 382 | return m_(0, 0) * (m_(1, 1) * m_(2, 2) - m_(1, 2) * m_(2, 1)) + 383 | m_(0, 1) * (m_(1, 2) * m_(2, 0) - m_(1, 0) * m_(2, 2)) + 384 | m_(0, 2) * (m_(1, 0) * m_(2, 1) - m_(1, 1) * m_(2, 0)); 385 | } 386 | 387 | template 388 | inline T compute_determinant(const Mat<4, 4, T>& m) 389 | { 390 | T m00 = m(0, 0); 391 | T m10 = m(1, 0); 392 | T m20 = m(2, 0); 393 | T m30 = m(3, 0); 394 | T m01 = m(0, 1); 395 | T m11 = m(1, 1); 396 | T m21 = m(2, 1); 397 | T m31 = m(3, 1); 398 | T m02 = m(0, 2); 399 | T m12 = m(1, 2); 400 | T m22 = m(2, 2); 401 | T m32 = m(3, 2); 402 | T m03 = m(0, 3); 403 | T m13 = m(1, 3); 404 | T m23 = m(2, 3); 405 | T m33 = m(3, 3); 406 | 407 | return m03 * m12 * m21 * m30 - m02 * m13 * m21 * m30 - m03 * m11 * m22 * m30 + m01 * m13 * m22 * m30 + 408 | m02 * m11 * m23 * m30 - m01 * m12 * m23 * m30 - m03 * m12 * m20 * m31 + m02 * m13 * m20 * m31 + 409 | m03 * m10 * m22 * m31 - m00 * m13 * m22 * m31 - m02 * m10 * m23 * m31 + m00 * m12 * m23 * m31 + 410 | m03 * m11 * m20 * m32 - m01 * m13 * m20 * m32 - m03 * m10 * m21 * m32 + m00 * m13 * m21 * m32 + 411 | m01 * m10 * m23 * m32 - m00 * m11 * m23 * m32 - m02 * m11 * m20 * m33 + m01 * m12 * m20 * m33 + 412 | m02 * m10 * m21 * m33 - m00 * m12 * m21 * m33 - m01 * m10 * m22 * m33 + m00 * m11 * m22 * m33; 413 | } 414 | 415 | template 416 | Mat<4, 4, T> inverse3x3(const Mat<4, 4, T>& mat) 417 | { 418 | const T* m = &mat.m[0]; 419 | 420 | // determinant 421 | T one_over_det = (T)1 / compute_determinant(mat); 422 | 423 | Mat<4, 4, T> inverse = Mat<4, 4, T>::create_identity(); 424 | 425 | // find the adjoint matrix (transposed) and multiply by 1/det to get the inverse 426 | inverse.m[0] = (m[5] * m[10] - m[6] * m[9]) * one_over_det; 427 | inverse.m[1] = -(m[1] * m[10] - m[2] * m[9]) * one_over_det; 428 | inverse.m[2] = (m[1] * m[6] - m[2] * m[5]) * one_over_det; 429 | 430 | inverse.m[4] = -(m[4] * m[10] - m[6] * m[8]) * one_over_det; 431 | inverse.m[5] = (m[0] * m[10] - m[2] * m[8]) * one_over_det; 432 | inverse.m[6] = -(m[0] * m[6] - m[2] * m[4]) * one_over_det; 433 | 434 | inverse.m[8] = (m[4] * m[9] - m[5] * m[8]) * one_over_det; 435 | inverse.m[9] = -(m[0] * m[9] - m[1] * m[8]) * one_over_det; 436 | inverse.m[10] = (m[0] * m[5] - m[1] * m[4]) * one_over_det; 437 | 438 | return inverse; 439 | } 440 | 441 | template 442 | Mat<4, 4, T> inverse3x4(const Mat<4, 4, T>& mat) 443 | { 444 | const T* m = &mat.m[0]; 445 | 446 | // determinant 447 | T det = (m[0] * (m[5] * m[10] - m[9] * m[6])) - (m[4] * (m[1] * m[10] - m[9] * m[2])) + 448 | (m[8] * (m[1] * m[6] - m[5] * m[2])); 449 | 450 | T one_over_det = (T)1 / det; 451 | 452 | Mat<4, 4, T> inverse = Mat<4, 4, T>::create_identity(); 453 | 454 | // find the adjoint matrix (transposed) and multiply by 1/det to get the inverse 455 | inverse.m[0] = (m[5] * m[10] - m[6] * m[9]) * one_over_det; 456 | inverse.m[1] = -(m[1] * m[10] - m[2] * m[9]) * one_over_det; 457 | inverse.m[2] = (m[1] * m[6] - m[2] * m[5]) * one_over_det; 458 | 459 | inverse.m[4] = -(m[4] * m[10] - m[6] * m[8]) * one_over_det; 460 | inverse.m[5] = (m[0] * m[10] - m[2] * m[8]) * one_over_det; 461 | inverse.m[6] = -(m[0] * m[6] - m[2] * m[4]) * one_over_det; 462 | 463 | inverse.m[8] = (m[4] * m[9] - m[5] * m[8]) * one_over_det; 464 | inverse.m[9] = -(m[0] * m[9] - m[1] * m[8]) * one_over_det; 465 | inverse.m[10] = (m[0] * m[5] - m[1] * m[4]) * one_over_det; 466 | 467 | // take into account inverse the translation portion (inverse translation portion * inverse rotation) 468 | Vec<3, T> t(-m[3], -m[7], -m[11]); 469 | inverse.m[3] = t.x * inverse.m[0] + t.y * inverse.m[1] + t.z * inverse.m[2]; 470 | inverse.m[7] = t.x * inverse.m[4] + t.y * inverse.m[5] + t.z * inverse.m[6]; 471 | inverse.m[11] = t.x * inverse.m[8] + t.y * inverse.m[9] + t.z * inverse.m[10]; 472 | 473 | return inverse; 474 | } 475 | 476 | template 477 | Mat<4, 4, T> inverse4x4(const Mat<4, 4, T>& mat) 478 | { 479 | const T* m = &mat.m[0]; 480 | 481 | // laplace expansion theorum 482 | T s0 = ((m[0] * m[5]) - (m[1] * m[4])); 483 | T s1 = ((m[0] * m[6]) - (m[2] * m[4])); 484 | T s2 = ((m[0] * m[7]) - (m[3] * m[4])); 485 | T s3 = ((m[1] * m[6]) - (m[2] * m[5])); 486 | T s4 = ((m[1] * m[7]) - (m[3] * m[5])); 487 | T s5 = ((m[2] * m[7]) - (m[3] * m[6])); 488 | 489 | T c5 = ((m[10] * m[15]) - (m[11] * m[14])); 490 | T c4 = ((m[9] * m[15]) - (m[11] * m[13])); 491 | T c3 = ((m[9] * m[14]) - (m[10] * m[13])); 492 | T c2 = ((m[8] * m[15]) - (m[11] * m[12])); 493 | T c1 = ((m[8] * m[14]) - (m[10] * m[12])); 494 | T c0 = ((m[8] * m[13]) - (m[9] * m[12])); 495 | 496 | T det = (s0 * c5) - (s1 * c4) + (s2 * c3) + (s3 * c2) - (s4 * c1) + (s5 * c0); 497 | 498 | T one_over_det = (T)1 / det; 499 | 500 | Mat<4, 4, T> inverse; 501 | 502 | inverse.m[0] = +(m[5] * c5 - m[6] * c4 + m[7] * c3) * one_over_det; 503 | inverse.m[1] = -(m[1] * c5 - m[2] * c4 + m[3] * c3) * one_over_det; 504 | inverse.m[2] = +(m[13] * s5 - m[14] * s4 + m[15] * s3) * one_over_det; 505 | inverse.m[3] = -(m[9] * s5 - m[10] * s4 + m[11] * s3) * one_over_det; 506 | 507 | inverse.m[4] = -(m[4] * c5 - m[6] * c2 + m[7] * c1) * one_over_det; 508 | inverse.m[5] = +(m[0] * c5 - m[2] * c2 + m[3] * c1) * one_over_det; 509 | inverse.m[6] = -(m[12] * s5 - m[14] * s2 + m[15] * s1) * one_over_det; 510 | inverse.m[7] = +(m[8] * s5 - m[10] * s2 + m[11] * s1) * one_over_det; 511 | 512 | inverse.m[8] = +(m[4] * c4 - m[5] * c2 + m[7] * c0) * one_over_det; 513 | inverse.m[9] = -(m[0] * c4 - m[1] * c2 + m[3] * c0) * one_over_det; 514 | inverse.m[10] = +(m[12] * s4 - m[13] * s2 + m[15] * s0) * one_over_det; 515 | inverse.m[11] = -(m[8] * s4 - m[9] * s2 + m[11] * s0) * one_over_det; 516 | 517 | inverse.m[12] = -(m[4] * c3 - m[5] * c1 + m[6] * c0) * one_over_det; 518 | inverse.m[13] = +(m[0] * c3 - m[1] * c1 + m[2] * c0) * one_over_det; 519 | inverse.m[14] = -(m[12] * s3 - m[13] * s1 + m[14] * s0) * one_over_det; 520 | inverse.m[15] = +(m[8] * s3 - m[9] * s1 + m[10] * s0) * one_over_det; 521 | 522 | return inverse; 523 | } 524 | 525 | template 526 | inline Mat<4, 4, T> create_translation(const Vec<3, T>& t) 527 | { 528 | Mat<4, 4, T> m; 529 | 530 | m.m[0] = 1; 531 | m.m[1] = 0; 532 | m.m[2] = 0; 533 | m.m[3] = t.x; 534 | m.m[4] = 0; 535 | m.m[5] = 1; 536 | m.m[6] = 0; 537 | m.m[7] = t.y; 538 | m.m[8] = 0; 539 | m.m[9] = 0; 540 | m.m[10] = 1; 541 | m.m[11] = t.z; 542 | m.m[12] = 0; 543 | m.m[13] = 0; 544 | m.m[14] = 0; 545 | m.m[15] = 1; 546 | 547 | return m; 548 | } 549 | 550 | template 551 | inline Mat<4, 4, T> create_x_rotation(T theta) 552 | { 553 | Mat<4, 4, T> m; 554 | 555 | // get sin / cos theta once 556 | T theta_rad = theta; 557 | T sin_theta = sin(theta_rad); 558 | T cos_theta = cos(theta_rad); 559 | 560 | m.m[0] = 1; 561 | m.m[1] = 0; 562 | m.m[2] = 0; 563 | m.m[3] = 0; 564 | m.m[4] = 0; 565 | m.m[5] = cos_theta; 566 | m.m[6] = -sin_theta; 567 | m.m[7] = 0; 568 | m.m[8] = 0; 569 | m.m[9] = sin_theta; 570 | m.m[10] = cos_theta; 571 | m.m[11] = 0; 572 | m.m[12] = 0; 573 | m.m[13] = 0; 574 | m.m[14] = 0; 575 | m.m[15] = 1; 576 | 577 | return m; 578 | } 579 | 580 | template 581 | inline Mat<4, 4, T> create_y_rotation(T theta) 582 | { 583 | Mat<4, 4, T> m; 584 | 585 | // get sin / cos theta once 586 | T theta_rad = theta; 587 | T sin_theta = sin(theta_rad); 588 | T cos_theta = cos(theta_rad); 589 | 590 | m.m[0] = cos_theta; 591 | m.m[1] = 0; 592 | m.m[2] = sin_theta; 593 | m.m[3] = 0; 594 | m.m[4] = 0; 595 | m.m[5] = 1; 596 | m.m[6] = 0; 597 | m.m[7] = 0; 598 | m.m[8] = -sin_theta; 599 | m.m[9] = 0; 600 | m.m[10] = cos_theta; 601 | m.m[11] = 0; 602 | m.m[12] = 0; 603 | m.m[13] = 0; 604 | m.m[14] = 0; 605 | m.m[15] = 1; 606 | 607 | return m; 608 | } 609 | 610 | template 611 | inline Mat<4, 4, T> create_z_rotation(T theta) 612 | { 613 | Mat<4, 4, T> m; 614 | 615 | // get sin / cos theta once 616 | T theta_rad = theta; 617 | T sin_theta = sin(theta_rad); 618 | T cos_theta = cos(theta_rad); 619 | 620 | m.m[0] = cos_theta; 621 | m.m[1] = -sin_theta; 622 | m.m[2] = 0; 623 | m.m[3] = 0; 624 | m.m[4] = sin_theta; 625 | m.m[5] = cos_theta; 626 | m.m[6] = 0.0f; 627 | m.m[7] = 0; 628 | m.m[8] = 0; 629 | m.m[9] = 0; 630 | m.m[10] = 1; 631 | m.m[11] = 0; 632 | m.m[12] = 0; 633 | m.m[13] = 0; 634 | m.m[14] = 0; 635 | m.m[15] = 1; 636 | 637 | return m; 638 | } 639 | 640 | template 641 | inline Mat<4, 4, T> create_rotation(const Vec<3, T>& axis, T theta) 642 | { 643 | Mat<4, 4, T> m; 644 | 645 | T theta_rad = theta; 646 | T sin_theta = sin(theta_rad); 647 | T cos_theta = cos(theta_rad); 648 | T inv_cos_theta = 1 - cos(theta_rad); 649 | 650 | m.m[0] = inv_cos_theta * axis.x * axis.x + cos_theta; 651 | m.m[1] = inv_cos_theta * axis.x * axis.y - sin_theta * axis.z; 652 | m.m[2] = inv_cos_theta * axis.x * axis.z + sin_theta * axis.y; 653 | m.m[3] = 0; 654 | 655 | m.m[4] = inv_cos_theta * axis.x * axis.y + sin_theta * axis.z; 656 | m.m[5] = inv_cos_theta * axis.y * axis.y + cos_theta; 657 | m.m[6] = inv_cos_theta * axis.y * axis.z - sin_theta * axis.x; 658 | m.m[7] = 0; 659 | 660 | m.m[8] = inv_cos_theta * axis.x * axis.z - sin_theta * axis.y; 661 | m.m[9] = inv_cos_theta * axis.y * axis.z + sin_theta * axis.x; 662 | m.m[10] = inv_cos_theta * axis.z * axis.z + cos_theta; 663 | m.m[11] = 0; 664 | 665 | m.m[12] = 0; 666 | m.m[13] = 0; 667 | m.m[14] = 0; 668 | m.m[15] = 1; 669 | 670 | return m; 671 | } 672 | 673 | template 674 | inline Mat<4, 4, T> create_scale(const Vec<3, T>& s) 675 | { 676 | Mat<4, 4, T> m; 677 | 678 | m.m[0] = s.x; 679 | m.m[1] = 0; 680 | m.m[2] = 0; 681 | m.m[3] = 0; 682 | m.m[4] = 0; 683 | m.m[5] = s.y; 684 | m.m[6] = 0; 685 | m.m[7] = 0; 686 | m.m[8] = 0; 687 | m.m[9] = 0; 688 | m.m[10] = s.z; 689 | m.m[11] = 0; 690 | m.m[12] = 0; 691 | m.m[13] = 0; 692 | m.m[14] = 0; 693 | m.m[15] = 1; 694 | 695 | return m; 696 | } 697 | 698 | template 699 | inline Mat<4, 4, T> create_bias() 700 | { 701 | Mat<4, 4, T> m; 702 | 703 | static T half = 1 / 2; 704 | 705 | m.m[0] = half; 706 | m.m[1] = 0; 707 | m.m[2] = 0; 708 | m.m[3] = half; 709 | m.m[4] = 0; 710 | m.m[5] = half; 711 | m.m[6] = 0; 712 | m.m[7] = half; 713 | m.m[8] = 0; 714 | m.m[9] = 0; 715 | m.m[10] = half; 716 | m.m[11] = half; 717 | m.m[12] = 0; 718 | m.m[13] = 0; 719 | m.m[14] = 0; 720 | m.m[15] = 1; 721 | 722 | return m; 723 | } 724 | 725 | template 726 | inline Mat<4, 4, T> create_axis_swap(const Vec<3, T>& x, const Vec<3, T>& y, const Vec<3, T>& z) 727 | { 728 | Mat<4, 4, T> m; 729 | 730 | m.m[0] = x.x; 731 | m.m[1] = y.x; 732 | m.m[2] = z.x; 733 | m.m[3] = 0.0f; 734 | m.m[4] = x.y; 735 | m.m[5] = y.y; 736 | m.m[6] = z.y; 737 | m.m[7] = 0.0f; 738 | m.m[8] = x.z; 739 | m.m[9] = y.z; 740 | m.m[10] = z.z; 741 | m.m[11] = 0.0f; 742 | m.m[12] = 0.0f; 743 | m.m[13] = 0.0f; 744 | m.m[14] = 0.0f; 745 | m.m[15] = 1.0f; 746 | 747 | return m; 748 | } 749 | 750 | template 751 | inline Mat<4, 4, T> create_perspective_projection(T left, T right, T bottom, T top, T znear, T zfar) 752 | { 753 | Mat<4, 4, T> m; 754 | 755 | m.m[0] = ((T)2 * znear) / (right - left); 756 | m.m[1] = 0; 757 | m.m[2] = (right + left) / (right - left); 758 | m.m[3] = 0; 759 | m.m[4] = 0; 760 | m.m[5] = (2 * znear) / (top - bottom); 761 | m.m[6] = (top + bottom) / (top - bottom); 762 | m.m[7] = 0; 763 | m.m[8] = 0; 764 | m.m[9] = 0; 765 | m.m[10] = (-zfar - znear) / (zfar - znear); 766 | m.m[11] = (-(2 * znear) * zfar) / (zfar - znear); 767 | m.m[12] = 0; 768 | m.m[13] = 0; 769 | m.m[14] = -1; 770 | m.m[15] = 0; 771 | 772 | return m; 773 | } 774 | 775 | template 776 | inline Mat<4, 4, T> create_perspective_projection_yup(T fov, T aspect, T znear, T zfar) 777 | { 778 | T tfov = (T)tan(fov * 0.5); 779 | T right = tfov * aspect * znear; 780 | T left = -right; 781 | 782 | T top = tfov * znear; 783 | T bottom = -top; 784 | 785 | return create_perspective_projection(left, right, bottom, top, znear, zfar); 786 | } 787 | 788 | // y-is down 789 | template 790 | inline Mat<4, 4, T> create_perspective_projection(T fov, T aspect, T znear, T zfar) 791 | { 792 | T tfov = (T)tan(fov * 0.5); 793 | T right = tfov * aspect * znear; 794 | T left = -right; 795 | 796 | T bottom = tfov * znear; 797 | T top = -bottom; 798 | 799 | return create_perspective_projection(left, right, bottom, top, znear, zfar); 800 | } 801 | 802 | template 803 | inline Mat<4, 4, T> create_perspective_projection_inverse_depth(T left, T right, T bottom, T top, T znear, T zfar) 804 | { 805 | Mat<4, 4, T> m; 806 | 807 | m.m[0] = ((T)2 * znear) / (right - left); 808 | m.m[1] = 0; 809 | m.m[2] = (right + left) / (right - left); 810 | m.m[3] = 0; 811 | m.m[4] = 0; 812 | m.m[5] = (2 * znear) / (top - bottom); 813 | m.m[6] = (top + bottom) / (top - bottom); 814 | m.m[7] = 0; 815 | m.m[8] = 0; 816 | m.m[9] = 0; 817 | m.m[10] = (-znear) / (znear - zfar); 818 | m.m[11] = (-znear * zfar) / (znear - zfar); 819 | m.m[12] = 0; 820 | m.m[13] = 0; 821 | m.m[14] = -1; 822 | m.m[15] = 0; 823 | 824 | return m; 825 | } 826 | 827 | template 828 | inline Mat<4, 4, T> create_perspective_projection_yup_inverse_depth(T fov, T aspect, T znear, T zfar) 829 | { 830 | Mat<4, 4, T> m; 831 | 832 | T tfov = (T)tan(fov * 0.5); 833 | T right = tfov * aspect * znear; 834 | T left = -right; 835 | 836 | T top = tfov * znear; 837 | T bottom = -top; 838 | 839 | return create_perspective_projection_inverse_depth(left, right, bottom, top, znear, zfar); 840 | } 841 | 842 | template 843 | inline Mat<4, 4, T> create_perspective_projection_inverse_depth(T fov, T aspect, T znear, T zfar) 844 | { 845 | T tfov = (T)tan(fov * 0.5); 846 | T right = tfov * aspect * znear; 847 | T left = -right; 848 | 849 | T bottom = tfov * znear; 850 | T top = -bottom; 851 | 852 | return create_perspective_projection_inverse_depth(left, right, bottom, top, znear, zfar); 853 | } 854 | 855 | template 856 | inline Mat<4, 4, T> create_orthographic_projection(T left, T right, T bottom, T top, T znear, T zfar) 857 | { 858 | T& l = left; 859 | T& r = right; 860 | 861 | T& t = top; 862 | T& b = bottom; 863 | 864 | T& n = znear; 865 | T& f = zfar; 866 | 867 | Mat<4, 4, T> m = Mat<4, 4, T>::create_identity(); 868 | 869 | m.m[0] = (T)2 / (r - l); 870 | m.m[5] = (T)2 / (t - b); 871 | m.m[10] = (T)1 / (n - f); 872 | 873 | m.m[3] = (l + r) / (l - r); 874 | m.m[7] = (t + b) / (b - t); 875 | m.m[11] = n / (n - f); 876 | 877 | return m; 878 | } 879 | 880 | template 881 | Mat<3, 3, T> to3x3(const Mat<4, 4, T>& rhs) 882 | { 883 | Mat<3, 3, T> mm; 884 | for(size_t r = 0; r < 3; ++r) 885 | { 886 | for(size_t c = 0; c < 3; ++c) 887 | { 888 | mm.at(r, c) = rhs.at(r, c); 889 | } 890 | } 891 | return mm; 892 | } 893 | } // namespace mat 894 | 895 | // abbreviations 896 | typedef Mat<3, 3, float> Mat3f; 897 | typedef Mat<4, 4, float> Mat4f; 898 | typedef Mat<3, 4, float> Mat34f; 899 | typedef Mat<3, 3, float> mat3; 900 | typedef Mat<4, 4, float> mat4; 901 | typedef Mat<2, 2, float> mat2; 902 | typedef Mat<3, 3, float> mat3f; 903 | typedef Mat<4, 4, float> mat4f; 904 | typedef Mat<2, 2, float> mat2f; 905 | typedef Mat<4, 4, float> float4x4; 906 | typedef Mat<3, 4, float> float3x4; 907 | typedef Mat<4, 3, float> float4x3; 908 | typedef Mat<3, 3, float> float3x3; 909 | typedef Mat<2, 2, float> float2x2; 910 | -------------------------------------------------------------------------------- /swizzle.h: -------------------------------------------------------------------------------- 1 | // swizzle.h 2 | // Copyright 2014 - 2020 Alex Dixon. 3 | // License: https://github.com/polymonster/maths/blob/master/license.md 4 | 5 | #pragma once 6 | 7 | #if __cplusplus >= 201402L 8 | // c++14 onwards 9 | template 10 | constexpr size_t max_elem() { 11 | std::initializer_list l = {SW...}; 12 | return std::max(l)+1; 13 | } 14 | #define SW_TYPE_SIZE max_elem() 15 | #define SW_ASSIGN(v) v 16 | #else 17 | // c++11.. 18 | // W is the width of the swizzle, not the width of the vector, the swizzle of W=2 (.zw) would need to write to 19 | // index 2 and 3, this is ok because the swizzle is backed by a union with a vec containing T v[4] 20 | // we know the pointer to v[0] and the sizeof the struct is such that a v4 is 16 bytes... 21 | // this is undefined behaviour so proceed with caution, but has been tested and works on clang, gcc and msvc. 22 | // when writing to a swizzle in operator=, requires a cast to a T* (ie. float*) and the write, to avoid UB sanitizer warning 23 | #define SW_ASSIGN(v) &v[0] 24 | #define SW_TYPE_SIZE W 25 | #endif 26 | 27 | template 28 | struct Swizzle 29 | { 30 | // in c++14 we know the min size of the array we require for a swizzle at compile time. 31 | // in c++11 it might be possible, but I havent figured it out! so it requires a hack to work around UBSan 32 | T v[SW_TYPE_SIZE]; 33 | 34 | template 35 | Swizzle& operator=(const Swizzle& lhs) 36 | { 37 | static_assert(W == W2, "error: assigning swizzle of different dimensions"); 38 | size_t i1[] = {SW...}; 39 | size_t i2[] = {SW2...}; 40 | 41 | auto vw = SW_ASSIGN(v); 42 | auto vr = SW_ASSIGN(lhs.v); 43 | for(size_t x = 0; x < W; ++x) 44 | vw[i1[x]] = vr[i2[x]]; 45 | 46 | return *this; 47 | } 48 | 49 | template 50 | Swizzle& operator=(const Vec& lhs) 51 | { 52 | static_assert(W == N, "error: assigning vector to swizzle of different dimensions"); 53 | size_t i1[] = {SW...}; 54 | 55 | auto vw = SW_ASSIGN(v); 56 | for(size_t x = 0; x < W; ++x) 57 | vw[i1[x]] = (T)lhs.v[x]; 58 | 59 | return *this; 60 | } 61 | 62 | operator Vec () const 63 | { 64 | Vec vec; 65 | size_t i1[] = {SW...}; 66 | for(size_t x = 0; x < W; ++x) 67 | vec[x] = v[i1[x]]; 68 | return vec; 69 | } 70 | 71 | // vec 72 | template 73 | Vec operator+(const Swizzle& rhs) const 74 | { 75 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 76 | return Vec(Vec(*this) + Vec(rhs)); 77 | } 78 | 79 | template 80 | Vec operator-(const Swizzle& rhs) const 81 | { 82 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 83 | return Vec(Vec(*this) - Vec(rhs)); 84 | } 85 | 86 | template 87 | Vec operator/(const Swizzle& rhs) const 88 | { 89 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 90 | return Vec(Vec(*this) / Vec(rhs)); 91 | } 92 | 93 | template 94 | Vec operator*(const Swizzle& rhs) const 95 | { 96 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 97 | return Vec(Vec(*this) * Vec(rhs)); 98 | } 99 | 100 | // compund swizzle 101 | template 102 | Swizzle& operator+=(const Swizzle& rhs) 103 | { 104 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 105 | *this = Vec(*this) + Vec(rhs); 106 | return *this; 107 | } 108 | 109 | template 110 | Swizzle& operator-=(const Swizzle& rhs) 111 | { 112 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 113 | *this = Vec(*this) - Vec(rhs); 114 | return *this; 115 | } 116 | 117 | template 118 | Swizzle& operator/=(const Swizzle& rhs) 119 | { 120 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 121 | *this = Vec(*this) / Vec(rhs); 122 | return *this; 123 | } 124 | 125 | template 126 | Swizzle& operator*=(const Swizzle& rhs) 127 | { 128 | static_assert(W == W2, "error: performing arithmetic on swizzles of different sizes"); 129 | *this = Vec(*this) * Vec(rhs); 130 | return *this; 131 | } 132 | 133 | // scalar 134 | Vec operator+(T rhs) const 135 | { 136 | return Vec(Vec(*this) + (rhs)); 137 | } 138 | 139 | Vec operator-(T rhs) const 140 | { 141 | return Vec(Vec(*this) - (rhs)); 142 | } 143 | 144 | Vec operator/(T rhs) const 145 | { 146 | return Vec(Vec(*this) / (rhs)); 147 | } 148 | 149 | Vec operator*(T rhs) const 150 | { 151 | return Vec(Vec(*this) * (rhs)); 152 | } 153 | }; 154 | 155 | // unary minus 156 | template 157 | maths_inline Vec operator-(const Swizzle& lhs) 158 | { 159 | return -Vec(lhs); 160 | } 161 | 162 | // add 163 | template 164 | maths_inline Vec operator+(const Vec& rhs, const Swizzle& lhs) 165 | { 166 | return Vec(lhs) + Vec(rhs); 167 | } 168 | 169 | template 170 | maths_inline Vec operator+(const Swizzle& lhs, const Vec& rhs) 171 | { 172 | return Vec(lhs) + Vec(rhs); 173 | } 174 | 175 | template 176 | maths_inline Vec operator+(const Swizzle& lhs, const T& rhs) 177 | { 178 | return Vec(lhs) + rhs; 179 | } 180 | 181 | // compound add 182 | template 183 | maths_inline Swizzle& operator+=(Swizzle& lhs, const Vec& rhs) 184 | { 185 | lhs = Vec(lhs) + Vec(rhs); 186 | return lhs; 187 | } 188 | 189 | template 190 | maths_inline Vec& operator+=(Vec& lhs, const Swizzle& rhs) 191 | { 192 | lhs = Vec(lhs) + Vec(rhs); 193 | return lhs; 194 | } 195 | 196 | template 197 | maths_inline Swizzle& operator+=(Swizzle& lhs, const T& rhs) 198 | { 199 | lhs = Vec(lhs) + rhs; 200 | return lhs; 201 | } 202 | 203 | // subtract 204 | template 205 | maths_inline Vec operator-(const Swizzle& lhs, const Vec& rhs) 206 | { 207 | return Vec(lhs) - Vec(rhs); 208 | } 209 | 210 | template 211 | maths_inline Vec operator-(const Vec& lhs, const Swizzle& rhs) 212 | { 213 | return Vec(lhs) - Vec(rhs); 214 | } 215 | 216 | template 217 | maths_inline Vec operator-(const Swizzle& lhs, const T& rhs) 218 | { 219 | return Vec(lhs) - rhs; 220 | } 221 | 222 | // compound subtract 223 | template 224 | maths_inline Swizzle& operator-=(Swizzle& lhs, const Vec& rhs) 225 | { 226 | lhs = Vec(lhs) - Vec(rhs); 227 | return lhs; 228 | } 229 | 230 | template 231 | maths_inline Vec& operator-=(Vec& lhs, const Swizzle& rhs) 232 | { 233 | lhs = Vec(lhs) - Vec(rhs); 234 | return lhs; 235 | } 236 | 237 | template 238 | maths_inline Swizzle& operator-=(Swizzle& lhs, const T& rhs) 239 | { 240 | lhs = Vec(lhs) - rhs; 241 | return lhs; 242 | } 243 | 244 | // divide 245 | template 246 | maths_inline Vec operator/(const Swizzle& lhs, const Vec& rhs) 247 | { 248 | return Vec(lhs) / Vec(rhs); 249 | } 250 | 251 | template 252 | maths_inline Vec operator/(const Vec& lhs, const Swizzle& rhs) 253 | { 254 | return Vec(lhs) / Vec(rhs); 255 | } 256 | 257 | template 258 | maths_inline Vec operator/(const Swizzle& lhs, const T& rhs) 259 | { 260 | return Vec(lhs) / rhs; 261 | } 262 | 263 | // compound divide 264 | template 265 | maths_inline Swizzle& operator/=(Swizzle& lhs, const Vec& rhs) 266 | { 267 | lhs = Vec(lhs) / Vec(rhs); 268 | return lhs; 269 | } 270 | 271 | template 272 | maths_inline Vec& operator/=(Vec& lhs, const Swizzle& rhs) 273 | { 274 | lhs = Vec(lhs) / Vec(rhs); 275 | return lhs; 276 | } 277 | 278 | template 279 | maths_inline Swizzle& operator/=(Swizzle& lhs, const T& rhs) 280 | { 281 | lhs = Vec(lhs) / rhs; 282 | return lhs; 283 | } 284 | 285 | // multiply 286 | template 287 | maths_inline Vec operator*(const Swizzle& lhs, const Vec& rhs) 288 | { 289 | return Vec(lhs) * Vec(rhs); 290 | } 291 | 292 | template 293 | maths_inline Vec operator*(const Vec& lhs, const Swizzle& rhs) 294 | { 295 | return Vec(lhs) * Vec(rhs); 296 | } 297 | 298 | template 299 | maths_inline Vec operator*(const Swizzle& lhs, const T& rhs) 300 | { 301 | return Vec(lhs) * rhs; 302 | } 303 | 304 | // compound multiply 305 | template 306 | maths_inline Swizzle& operator*=(Swizzle& lhs, const Vec& rhs) 307 | { 308 | lhs = Vec(lhs) * Vec(rhs); 309 | return lhs; 310 | } 311 | 312 | template 313 | maths_inline Vec& operator*=(Vec& lhs, const Swizzle& rhs) 314 | { 315 | lhs = Vec(lhs) * Vec(rhs); 316 | return lhs; 317 | } 318 | 319 | template 320 | maths_inline Swizzle& operator*=(Swizzle& lhs, const T& rhs) 321 | { 322 | lhs = Vec(lhs) * rhs; 323 | return lhs; 324 | } 325 | 326 | // 327 | // swizzle functions for horizontal ops, swizzle -> T 328 | // 329 | 330 | #define SWIZZLE_HORIZ_FUNC(VEC_FUNC) \ 331 | template \ 332 | T VEC_FUNC(const Swizzle& s) \ 333 | { \ 334 | return VEC_FUNC((Vec)s); \ 335 | } 336 | 337 | SWIZZLE_HORIZ_FUNC(mag); 338 | SWIZZLE_HORIZ_FUNC(mag2); 339 | 340 | // 341 | // swizzle functions performing component wise operations 342 | // 343 | 344 | #define SWIZZLE_FUNC(SCALAR_FUNC) \ 345 | template \ 346 | Vec SCALAR_FUNC(const Swizzle& s) \ 347 | { \ 348 | return SCALAR_FUNC((Vec)s); \ 349 | } 350 | 351 | // cmath 352 | SWIZZLE_FUNC(perp); 353 | SWIZZLE_FUNC(sin); 354 | SWIZZLE_FUNC(asin); 355 | SWIZZLE_FUNC(cos); 356 | SWIZZLE_FUNC(acos); 357 | SWIZZLE_FUNC(tan); 358 | SWIZZLE_FUNC(tanh); 359 | SWIZZLE_FUNC(floor); 360 | SWIZZLE_FUNC(ceil); 361 | SWIZZLE_FUNC(abs); 362 | SWIZZLE_FUNC(fabs); 363 | SWIZZLE_FUNC(exp); 364 | SWIZZLE_FUNC(exp2); 365 | SWIZZLE_FUNC(frac); 366 | SWIZZLE_FUNC(trunc); 367 | SWIZZLE_FUNC(sqrt); 368 | SWIZZLE_FUNC(log); 369 | SWIZZLE_FUNC(log10); 370 | SWIZZLE_FUNC(log2); 371 | SWIZZLE_FUNC(sgn); 372 | SWIZZLE_FUNC(round); 373 | SWIZZLE_FUNC(normalize); 374 | SWIZZLE_FUNC(saturate); 375 | 376 | // 377 | // return vec, with different combination of swiz and vec 378 | // 379 | 380 | #define VEC_X_X(VEC_FUNC) \ 381 | template \ 382 | Vec VEC_FUNC(const Vec& v, Swizzle& s1) \ 383 | { \ 384 | return VEC_FUNC(v, (Vec)s1); \ 385 | } \ 386 | template \ 387 | Vec VEC_FUNC(Swizzle& s1, const Vec& v) \ 388 | { \ 389 | return VEC_FUNC((Vec)s1, v); \ 390 | } 391 | 392 | VEC_X_X(cross); 393 | VEC_X_X(min_union); 394 | VEC_X_X(max_union); 395 | VEC_X_X(pow); 396 | 397 | #define T_X_X(VEC_FUNC) \ 398 | template \ 399 | T VEC_FUNC(const Vec& v, Swizzle& s1) \ 400 | { \ 401 | return VEC_FUNC(v, (Vec)s1); \ 402 | } \ 403 | template \ 404 | T VEC_FUNC(Swizzle& s1, const Vec& v) \ 405 | { \ 406 | return VEC_FUNC((Vec)s1, v); \ 407 | } 408 | 409 | T_X_X(dot); 410 | T_X_X(dist); 411 | T_X_X(dist2); 412 | 413 | // swizzle permutations 414 | 415 | // v2 swizzles 416 | #define swizzle_v2 \ 417 | Swizzle xx; \ 418 | Swizzle xy; \ 419 | Swizzle yx; \ 420 | Swizzle yy; \ 421 | Swizzle xxx; \ 422 | Swizzle xxy; \ 423 | Swizzle xyx; \ 424 | Swizzle xyy; \ 425 | Swizzle yxx; \ 426 | Swizzle yxy; \ 427 | Swizzle yyx; \ 428 | Swizzle yyy; \ 429 | Swizzle xxxx; \ 430 | Swizzle xxxy; \ 431 | Swizzle xxyx; \ 432 | Swizzle xxyy; \ 433 | Swizzle xyxx; \ 434 | Swizzle xyxy; \ 435 | Swizzle xyyx; \ 436 | Swizzle xyyy; \ 437 | Swizzle yxxx; \ 438 | Swizzle yxxy; \ 439 | Swizzle yxyx; \ 440 | Swizzle yxyy; \ 441 | Swizzle yyxx; \ 442 | Swizzle yyxy; \ 443 | Swizzle yyyx; \ 444 | Swizzle yyyy; 445 | 446 | // v3 swizzles 447 | #define swizzle_v3 \ 448 | Swizzle xx; \ 449 | Swizzle xy; \ 450 | Swizzle xz; \ 451 | Swizzle yx; \ 452 | Swizzle yy; \ 453 | Swizzle yz; \ 454 | Swizzle zx; \ 455 | Swizzle zy; \ 456 | Swizzle zz; \ 457 | Swizzle xxx; \ 458 | Swizzle xxy; \ 459 | Swizzle xxz; \ 460 | Swizzle xyx; \ 461 | Swizzle xyy; \ 462 | Swizzle xyz; \ 463 | Swizzle xzx; \ 464 | Swizzle xzy; \ 465 | Swizzle xzz; \ 466 | Swizzle yxx; \ 467 | Swizzle yxy; \ 468 | Swizzle yxz; \ 469 | Swizzle yyx; \ 470 | Swizzle yyy; \ 471 | Swizzle yyz; \ 472 | Swizzle yzx; \ 473 | Swizzle yzy; \ 474 | Swizzle yzz; \ 475 | Swizzle zxx; \ 476 | Swizzle zxy; \ 477 | Swizzle zxz; \ 478 | Swizzle zyx; \ 479 | Swizzle zyy; \ 480 | Swizzle zyz; \ 481 | Swizzle zzx; \ 482 | Swizzle zzy; \ 483 | Swizzle zzz; \ 484 | Swizzle xxxx; \ 485 | Swizzle xxxy; \ 486 | Swizzle xxxz; \ 487 | Swizzle xxyx; \ 488 | Swizzle xxyy; \ 489 | Swizzle xxyz; \ 490 | Swizzle xxzx; \ 491 | Swizzle xxzy; \ 492 | Swizzle xxzz; \ 493 | Swizzle xyxx; \ 494 | Swizzle xyxy; \ 495 | Swizzle xyxz; \ 496 | Swizzle xyyx; \ 497 | Swizzle xyyy; \ 498 | Swizzle xyyz; \ 499 | Swizzle xyzx; \ 500 | Swizzle xyzy; \ 501 | Swizzle xyzz; \ 502 | Swizzle xzxx; \ 503 | Swizzle xzxy; \ 504 | Swizzle xzxz; \ 505 | Swizzle xzyx; \ 506 | Swizzle xzyy; \ 507 | Swizzle xzyz; \ 508 | Swizzle xzzx; \ 509 | Swizzle xzzy; \ 510 | Swizzle xzzz; \ 511 | Swizzle yxxx; \ 512 | Swizzle yxxy; \ 513 | Swizzle yxxz; \ 514 | Swizzle yxyx; \ 515 | Swizzle yxyy; \ 516 | Swizzle yxyz; \ 517 | Swizzle yxzx; \ 518 | Swizzle yxzy; \ 519 | Swizzle yxzz; \ 520 | Swizzle yyxx; \ 521 | Swizzle yyxy; \ 522 | Swizzle yyxz; \ 523 | Swizzle yyyx; \ 524 | Swizzle yyyy; \ 525 | Swizzle yyyz; \ 526 | Swizzle yyzx; \ 527 | Swizzle yyzy; \ 528 | Swizzle yyzz; \ 529 | Swizzle yzxx; \ 530 | Swizzle yzxy; \ 531 | Swizzle yzxz; \ 532 | Swizzle yzyx; \ 533 | Swizzle yzyy; \ 534 | Swizzle yzyz; \ 535 | Swizzle yzzx; \ 536 | Swizzle yzzy; \ 537 | Swizzle yzzz; \ 538 | Swizzle zxxx; \ 539 | Swizzle zxxy; \ 540 | Swizzle zxxz; \ 541 | Swizzle zxyx; \ 542 | Swizzle zxyy; \ 543 | Swizzle zxyz; \ 544 | Swizzle zxzx; \ 545 | Swizzle zxzy; \ 546 | Swizzle zxzz; \ 547 | Swizzle zyxx; \ 548 | Swizzle zyxy; \ 549 | Swizzle zyxz; \ 550 | Swizzle zyyx; \ 551 | Swizzle zyyy; \ 552 | Swizzle zyyz; \ 553 | Swizzle zyzx; \ 554 | Swizzle zyzy; \ 555 | Swizzle zyzz; \ 556 | Swizzle zzxx; \ 557 | Swizzle zzxy; \ 558 | Swizzle zzxz; \ 559 | Swizzle zzyx; \ 560 | Swizzle zzyy; \ 561 | Swizzle zzyz; \ 562 | Swizzle zzzx; \ 563 | Swizzle zzzy; \ 564 | Swizzle zzzz; 565 | 566 | // v4 swizzles 567 | #define swizzle_v4 \ 568 | Swizzle xx; \ 569 | Swizzle xy; \ 570 | Swizzle xz; \ 571 | Swizzle xw; \ 572 | Swizzle yx; \ 573 | Swizzle yy; \ 574 | Swizzle yz; \ 575 | Swizzle yw; \ 576 | Swizzle zx; \ 577 | Swizzle zy; \ 578 | Swizzle zz; \ 579 | Swizzle zw; \ 580 | Swizzle wx; \ 581 | Swizzle wy; \ 582 | Swizzle wz; \ 583 | Swizzle ww; \ 584 | Swizzle xxx; \ 585 | Swizzle xxy; \ 586 | Swizzle xxz; \ 587 | Swizzle xxw; \ 588 | Swizzle xyx; \ 589 | Swizzle xyy; \ 590 | Swizzle xyz; \ 591 | Swizzle xyw; \ 592 | Swizzle xzx; \ 593 | Swizzle xzy; \ 594 | Swizzle xzz; \ 595 | Swizzle xzw; \ 596 | Swizzle xwx; \ 597 | Swizzle xwy; \ 598 | Swizzle xwz; \ 599 | Swizzle xww; \ 600 | Swizzle yxx; \ 601 | Swizzle yxy; \ 602 | Swizzle yxz; \ 603 | Swizzle yxw; \ 604 | Swizzle yyx; \ 605 | Swizzle yyy; \ 606 | Swizzle yyz; \ 607 | Swizzle yyw; \ 608 | Swizzle yzx; \ 609 | Swizzle yzy; \ 610 | Swizzle yzz; \ 611 | Swizzle yzw; \ 612 | Swizzle ywx; \ 613 | Swizzle ywy; \ 614 | Swizzle ywz; \ 615 | Swizzle yww; \ 616 | Swizzle zxx; \ 617 | Swizzle zxy; \ 618 | Swizzle zxz; \ 619 | Swizzle zxw; \ 620 | Swizzle zyx; \ 621 | Swizzle zyy; \ 622 | Swizzle zyz; \ 623 | Swizzle zyw; \ 624 | Swizzle zzx; \ 625 | Swizzle zzy; \ 626 | Swizzle zzz; \ 627 | Swizzle zzw; \ 628 | Swizzle zwx; \ 629 | Swizzle zwy; \ 630 | Swizzle zwz; \ 631 | Swizzle zww; \ 632 | Swizzle wxx; \ 633 | Swizzle wxy; \ 634 | Swizzle wxz; \ 635 | Swizzle wxw; \ 636 | Swizzle wyx; \ 637 | Swizzle wyy; \ 638 | Swizzle wyz; \ 639 | Swizzle wyw; \ 640 | Swizzle wzx; \ 641 | Swizzle wzy; \ 642 | Swizzle wzz; \ 643 | Swizzle wzw; \ 644 | Swizzle wwx; \ 645 | Swizzle wwy; \ 646 | Swizzle wwz; \ 647 | Swizzle www; \ 648 | Swizzle xxxx; \ 649 | Swizzle xxxy; \ 650 | Swizzle xxxz; \ 651 | Swizzle xxxw; \ 652 | Swizzle xxyx; \ 653 | Swizzle xxyy; \ 654 | Swizzle xxyz; \ 655 | Swizzle xxyw; \ 656 | Swizzle xxzx; \ 657 | Swizzle xxzy; \ 658 | Swizzle xxzz; \ 659 | Swizzle xxzw; \ 660 | Swizzle xxwx; \ 661 | Swizzle xxwy; \ 662 | Swizzle xxwz; \ 663 | Swizzle xxww; \ 664 | Swizzle xyxx; \ 665 | Swizzle xyxy; \ 666 | Swizzle xyxz; \ 667 | Swizzle xyxw; \ 668 | Swizzle xyyx; \ 669 | Swizzle xyyy; \ 670 | Swizzle xyyz; \ 671 | Swizzle xyyw; \ 672 | Swizzle xyzx; \ 673 | Swizzle xyzy; \ 674 | Swizzle xyzz; \ 675 | Swizzle xyzw; \ 676 | Swizzle xywx; \ 677 | Swizzle xywy; \ 678 | Swizzle xywz; \ 679 | Swizzle xyww; \ 680 | Swizzle xzxx; \ 681 | Swizzle xzxy; \ 682 | Swizzle xzxz; \ 683 | Swizzle xzxw; \ 684 | Swizzle xzyx; \ 685 | Swizzle xzyy; \ 686 | Swizzle xzyz; \ 687 | Swizzle xzyw; \ 688 | Swizzle xzzx; \ 689 | Swizzle xzzy; \ 690 | Swizzle xzzz; \ 691 | Swizzle xzzw; \ 692 | Swizzle xzwx; \ 693 | Swizzle xzwy; \ 694 | Swizzle xzwz; \ 695 | Swizzle xzww; \ 696 | Swizzle xwxx; \ 697 | Swizzle xwxy; \ 698 | Swizzle xwxz; \ 699 | Swizzle xwxw; \ 700 | Swizzle xwyx; \ 701 | Swizzle xwyy; \ 702 | Swizzle xwyz; \ 703 | Swizzle xwyw; \ 704 | Swizzle xwzx; \ 705 | Swizzle xwzy; \ 706 | Swizzle xwzz; \ 707 | Swizzle xwzw; \ 708 | Swizzle xwwx; \ 709 | Swizzle xwwy; \ 710 | Swizzle xwwz; \ 711 | Swizzle xwww; \ 712 | Swizzle yxxx; \ 713 | Swizzle yxxy; \ 714 | Swizzle yxxz; \ 715 | Swizzle yxxw; \ 716 | Swizzle yxyx; \ 717 | Swizzle yxyy; \ 718 | Swizzle yxyz; \ 719 | Swizzle yxyw; \ 720 | Swizzle yxzx; \ 721 | Swizzle yxzy; \ 722 | Swizzle yxzz; \ 723 | Swizzle yxzw; \ 724 | Swizzle yxwx; \ 725 | Swizzle yxwy; \ 726 | Swizzle yxwz; \ 727 | Swizzle yxww; \ 728 | Swizzle yyxx; \ 729 | Swizzle yyxy; \ 730 | Swizzle yyxz; \ 731 | Swizzle yyxw; \ 732 | Swizzle yyyx; \ 733 | Swizzle yyyy; \ 734 | Swizzle yyyz; \ 735 | Swizzle yyyw; \ 736 | Swizzle yyzx; \ 737 | Swizzle yyzy; \ 738 | Swizzle yyzz; \ 739 | Swizzle yyzw; \ 740 | Swizzle yywx; \ 741 | Swizzle yywy; \ 742 | Swizzle yywz; \ 743 | Swizzle yyww; \ 744 | Swizzle yzxx; \ 745 | Swizzle yzxy; \ 746 | Swizzle yzxz; \ 747 | Swizzle yzxw; \ 748 | Swizzle yzyx; \ 749 | Swizzle yzyy; \ 750 | Swizzle yzyz; \ 751 | Swizzle yzyw; \ 752 | Swizzle yzzx; \ 753 | Swizzle yzzy; \ 754 | Swizzle yzzz; \ 755 | Swizzle yzzw; \ 756 | Swizzle yzwx; \ 757 | Swizzle yzwy; \ 758 | Swizzle yzwz; \ 759 | Swizzle yzww; \ 760 | Swizzle ywxx; \ 761 | Swizzle ywxy; \ 762 | Swizzle ywxz; \ 763 | Swizzle ywxw; \ 764 | Swizzle ywyx; \ 765 | Swizzle ywyy; \ 766 | Swizzle ywyz; \ 767 | Swizzle ywyw; \ 768 | Swizzle ywzx; \ 769 | Swizzle ywzy; \ 770 | Swizzle ywzz; \ 771 | Swizzle ywzw; \ 772 | Swizzle ywwx; \ 773 | Swizzle ywwy; \ 774 | Swizzle ywwz; \ 775 | Swizzle ywww; \ 776 | Swizzle zxxx; \ 777 | Swizzle zxxy; \ 778 | Swizzle zxxz; \ 779 | Swizzle zxxw; \ 780 | Swizzle zxyx; \ 781 | Swizzle zxyy; \ 782 | Swizzle zxyz; \ 783 | Swizzle zxyw; \ 784 | Swizzle zxzx; \ 785 | Swizzle zxzy; \ 786 | Swizzle zxzz; \ 787 | Swizzle zxzw; \ 788 | Swizzle zxwx; \ 789 | Swizzle zxwy; \ 790 | Swizzle zxwz; \ 791 | Swizzle zxww; \ 792 | Swizzle zyxx; \ 793 | Swizzle zyxy; \ 794 | Swizzle zyxz; \ 795 | Swizzle zyxw; \ 796 | Swizzle zyyx; \ 797 | Swizzle zyyy; \ 798 | Swizzle zyyz; \ 799 | Swizzle zyyw; \ 800 | Swizzle zyzx; \ 801 | Swizzle zyzy; \ 802 | Swizzle zyzz; \ 803 | Swizzle zyzw; \ 804 | Swizzle zywx; \ 805 | Swizzle zywy; \ 806 | Swizzle zywz; \ 807 | Swizzle zyww; \ 808 | Swizzle zzxx; \ 809 | Swizzle zzxy; \ 810 | Swizzle zzxz; \ 811 | Swizzle zzxw; \ 812 | Swizzle zzyx; \ 813 | Swizzle zzyy; \ 814 | Swizzle zzyz; \ 815 | Swizzle zzyw; \ 816 | Swizzle zzzx; \ 817 | Swizzle zzzy; \ 818 | Swizzle zzzz; \ 819 | Swizzle zzzw; \ 820 | Swizzle zzwx; \ 821 | Swizzle zzwy; \ 822 | Swizzle zzwz; \ 823 | Swizzle zzww; \ 824 | Swizzle zwxx; \ 825 | Swizzle zwxy; \ 826 | Swizzle zwxz; \ 827 | Swizzle zwxw; \ 828 | Swizzle zwyx; \ 829 | Swizzle zwyy; \ 830 | Swizzle zwyz; \ 831 | Swizzle zwyw; \ 832 | Swizzle zwzx; \ 833 | Swizzle zwzy; \ 834 | Swizzle zwzz; \ 835 | Swizzle zwzw; \ 836 | Swizzle zwwx; \ 837 | Swizzle zwwy; \ 838 | Swizzle zwwz; \ 839 | Swizzle zwww; \ 840 | Swizzle wxxx; \ 841 | Swizzle wxxy; \ 842 | Swizzle wxxz; \ 843 | Swizzle wxxw; \ 844 | Swizzle wxyx; \ 845 | Swizzle wxyy; \ 846 | Swizzle wxyz; \ 847 | Swizzle wxyw; \ 848 | Swizzle wxzx; \ 849 | Swizzle wxzy; \ 850 | Swizzle wxzz; \ 851 | Swizzle wxzw; \ 852 | Swizzle wxwx; \ 853 | Swizzle wxwy; \ 854 | Swizzle wxwz; \ 855 | Swizzle wxww; \ 856 | Swizzle wyxx; \ 857 | Swizzle wyxy; \ 858 | Swizzle wyxz; \ 859 | Swizzle wyxw; \ 860 | Swizzle wyyx; \ 861 | Swizzle wyyy; \ 862 | Swizzle wyyz; \ 863 | Swizzle wyyw; \ 864 | Swizzle wyzx; \ 865 | Swizzle wyzy; \ 866 | Swizzle wyzz; \ 867 | Swizzle wyzw; \ 868 | Swizzle wywx; \ 869 | Swizzle wywy; \ 870 | Swizzle wywz; \ 871 | Swizzle wyww; \ 872 | Swizzle wzxx; \ 873 | Swizzle wzxy; \ 874 | Swizzle wzxz; \ 875 | Swizzle wzxw; \ 876 | Swizzle wzyx; \ 877 | Swizzle wzyy; \ 878 | Swizzle wzyz; \ 879 | Swizzle wzyw; \ 880 | Swizzle wzzx; \ 881 | Swizzle wzzy; \ 882 | Swizzle wzzz; \ 883 | Swizzle wzzw; \ 884 | Swizzle wzwx; \ 885 | Swizzle wzwy; \ 886 | Swizzle wzwz; \ 887 | Swizzle wzww; \ 888 | Swizzle wwxx; \ 889 | Swizzle wwxy; \ 890 | Swizzle wwxz; \ 891 | Swizzle wwxw; \ 892 | Swizzle wwyx; \ 893 | Swizzle wwyy; \ 894 | Swizzle wwyz; \ 895 | Swizzle wwyw; \ 896 | Swizzle wwzx; \ 897 | Swizzle wwzy; \ 898 | Swizzle wwzz; \ 899 | Swizzle wwzw; \ 900 | Swizzle wwwx; \ 901 | Swizzle wwwy; \ 902 | Swizzle wwwz; \ 903 | Swizzle wwww; 904 | -------------------------------------------------------------------------------- /vec.h: -------------------------------------------------------------------------------- 1 | // vec.h 2 | // Copyright 2014 - 2020 Alex Dixon. 3 | // License: https://github.com/polymonster/maths/blob/master/license.md 4 | // The initial implementation and some functions started from https://github.com/christopherbatty/SDFGen 5 | 6 | #pragma once 7 | 8 | #include "util.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef WIN32 15 | #undef min 16 | #undef max 17 | #endif 18 | 19 | template 20 | struct Vec 21 | { 22 | T v[N]; 23 | 24 | Vec(void) 25 | { 26 | } 27 | 28 | Vec(T value_for_all) 29 | { 30 | for (size_t i = 0; i < N; ++i) 31 | v[i] = value_for_all; 32 | } 33 | 34 | template 35 | explicit Vec(const S* source) 36 | { 37 | for (size_t i = 0; i < N; ++i) 38 | v[i] = (T)source[i]; 39 | } 40 | 41 | template 42 | explicit Vec(const Vec& source) 43 | { 44 | for (size_t i = 0; i < N; ++i) 45 | v[i] = (T)source[i]; 46 | } 47 | 48 | Vec(T v0, T v1) 49 | { 50 | static_assert(N == 2, "error: trying to construct vec of incorrect dimension"); 51 | v[0] = v0; 52 | v[1] = v1; 53 | } 54 | 55 | Vec(T v0, T v1, T v2) 56 | { 57 | static_assert(N == 3, "error: trying to construct vec of incorrect dimension"); 58 | v[0] = v0; 59 | v[1] = v1; 60 | v[2] = v2; 61 | } 62 | 63 | Vec(T v0, T v1, T v2, T v3) 64 | { 65 | static_assert(N == 4, "error: trying to construct vec of incorrect dimension"); 66 | v[0] = v0; 67 | v[1] = v1; 68 | v[2] = v2; 69 | v[3] = v3; 70 | } 71 | 72 | Vec(T v0, T v1, T v2, T v3, T v4) 73 | { 74 | static_assert(N == 5, "error: trying to construct vec of incorrect dimension"); 75 | v[0] = v0; 76 | v[1] = v1; 77 | v[2] = v2; 78 | v[3] = v3; 79 | v[4] = v4; 80 | } 81 | 82 | Vec(T v0, T v1, T v2, T v3, T v4, T v5) 83 | { 84 | static_assert(N == 6, "error: trying to construct vec of incorrect dimension"); 85 | v[0] = v0; 86 | v[1] = v1; 87 | v[2] = v2; 88 | v[3] = v3; 89 | v[4] = v4; 90 | v[5] = v5; 91 | } 92 | 93 | T& operator[](size_t index) 94 | { 95 | return v[index]; 96 | } 97 | 98 | const T& operator[](size_t index) const 99 | { 100 | return v[index]; 101 | } 102 | }; 103 | 104 | #include "swizzle.h" 105 | 106 | // Template specialisations for 2, 3, 4 107 | 108 | // INFO about possible undefined behaviour on swizzles and the struct.x/y/z/w members: 109 | // The union of T[] and T x, y, z is considered by some as undefined behaviour. 110 | // This is a possible loophole: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0120r0.html 111 | // All of the types inside the union are the same, the size of the structs containing unions is consistent and expected. 112 | // This code has been used on a number of different compiler versions of GCC, Clang and MSVC an it exhibits no 113 | // undefined behaviour or undefined behaviour sanitization issues. 114 | 115 | template 116 | struct Vec<2, T> 117 | { 118 | union { 119 | T v[2]; 120 | struct { 121 | T x, y; 122 | }; 123 | swizzle_v2; 124 | }; 125 | 126 | Vec<2, T>(void) 127 | { 128 | } 129 | 130 | Vec<2, T>(T value_for_all) 131 | { 132 | for (size_t i = 0; i < 2; ++i) 133 | v[i] = value_for_all; 134 | } 135 | 136 | template 137 | explicit Vec<2, T>(const S* source) 138 | { 139 | for (size_t i = 0; i < 2; ++i) 140 | v[i] = (T)source[i]; 141 | } 142 | 143 | template 144 | Vec<2, T>(const Vec<2, S>& source) 145 | { 146 | for (size_t i = 0; i < 2; ++i) 147 | v[i] = (T)source[i]; 148 | } 149 | 150 | template 151 | Vec<2, T>(const Swizzle& lhs) 152 | { 153 | size_t ii[] = {SW...}; 154 | for(size_t i = 0; i < sizeof...(SW); ++i) 155 | if(ii[i] != -1) 156 | v[i] = lhs.v[ii[i]]; 157 | } 158 | 159 | template 160 | const Vec<2, T>& operator=(const Swizzle& lhs) 161 | { 162 | size_t ii[] = {SW...}; 163 | for(size_t i = 0; i < sizeof...(SW); ++i) 164 | if(ii[i] != -1) 165 | v[i] = lhs.v[ii[i]]; 166 | return *this; 167 | } 168 | 169 | Vec<2, T>(T v0, T v1) 170 | { 171 | v[0] = v0; 172 | v[1] = v1; 173 | } 174 | 175 | T& operator[](size_t index) 176 | { 177 | return v[index]; 178 | } 179 | 180 | const T& operator[](size_t index) const 181 | { 182 | return v[index]; 183 | } 184 | 185 | inline static Vec<2, T> one() 186 | { 187 | return Vec<2, T>(1, 1); 188 | } 189 | 190 | inline static Vec<2, T> zero() 191 | { 192 | return Vec<2, T>(0, 0); 193 | } 194 | 195 | inline static Vec<2, T> flt_max() 196 | { 197 | return Vec<2, T>(FLT_MAX, FLT_MAX); 198 | } 199 | 200 | inline static Vec<2, T> unit_x() 201 | { 202 | return Vec<2, T>(1, 0); 203 | } 204 | 205 | inline static Vec<2, T> unit_y() 206 | { 207 | return Vec<2, T>(0, 1); 208 | } 209 | }; 210 | 211 | template 212 | struct Vec<3, T> 213 | { 214 | union { 215 | T v[3]; 216 | struct 217 | { 218 | T x, y, z; 219 | }; 220 | struct 221 | { 222 | T r, g, b; 223 | }; 224 | swizzle_v3; 225 | }; 226 | 227 | Vec<3, T>(void) 228 | { 229 | } 230 | 231 | Vec<3, T>(T value_for_all) 232 | { 233 | for (size_t i = 0; i < 3; ++i) 234 | v[i] = value_for_all; 235 | } 236 | 237 | template 238 | explicit Vec<3, T>(const S* source) 239 | { 240 | for (size_t i = 0; i < 3; ++i) 241 | v[i] = (T)source[i]; 242 | } 243 | 244 | template 245 | explicit Vec<3, T>(const Vec<3, S>& source) 246 | { 247 | for (size_t i = 0; i < 3; ++i) 248 | v[i] = (T)source[i]; 249 | } 250 | 251 | Vec<3, T>(T v0, T v1, T v2) 252 | { 253 | v[0] = v0; 254 | v[1] = v1; 255 | v[2] = v2; 256 | } 257 | 258 | Vec<3, T>(const Vec<2, T>& v2, T _z) 259 | { 260 | for (size_t i = 0; i < 2; ++i) 261 | v[i] = (T)v2[i]; 262 | 263 | v[2] = _z; 264 | } 265 | 266 | template 267 | Vec<3, T>(const Swizzle& lhs) 268 | { 269 | size_t ii[] = {SW...}; 270 | for(size_t i = 0; i < sizeof...(SW); ++i) 271 | v[i] = lhs.v[ii[i]]; 272 | } 273 | 274 | template 275 | Vec<3, T>& operator=(const Swizzle& lhs) 276 | { 277 | size_t ii[] = {SW...}; 278 | for(size_t i = 0; i < sizeof...(SW); ++i) 279 | if(ii[i] != -1) 280 | v[i] = lhs.v[ii[i]]; 281 | 282 | return *this; 283 | } 284 | 285 | T& operator[](size_t index) 286 | { 287 | return v[index]; 288 | } 289 | 290 | const T& operator[](size_t index) const 291 | { 292 | return v[index]; 293 | } 294 | 295 | inline static Vec<3, T> one() 296 | { 297 | return Vec<3, T>(1, 1, 1); 298 | } 299 | 300 | inline static Vec<3, T> zero() 301 | { 302 | return Vec<3, T>(0, 0, 0); 303 | } 304 | 305 | inline static Vec<3, T> flt_max() 306 | { 307 | return Vec<3, T>(FLT_MAX, FLT_MAX, FLT_MAX); 308 | } 309 | 310 | inline static Vec<3, T> unit_x() 311 | { 312 | return Vec<3, T>(1, 0, 0); 313 | } 314 | 315 | inline static Vec<3, T> unit_y() 316 | { 317 | return Vec<3, T>(0, 1, 0); 318 | } 319 | 320 | inline static Vec<3, T> unit_z() 321 | { 322 | return Vec<3, T>(0, 0, 1); 323 | } 324 | 325 | inline static Vec<3, T> white() 326 | { 327 | return Vec<3, T>(1, 1, 1); 328 | } 329 | 330 | inline static Vec<3, T> black() 331 | { 332 | return Vec<3, T>(0, 0, 0); 333 | } 334 | 335 | inline static Vec<3, T> red() 336 | { 337 | return Vec<3, T>(1, 0, 0); 338 | } 339 | 340 | inline static Vec<3, T> green() 341 | { 342 | return Vec<3, T>(0, 1, 0); 343 | } 344 | 345 | inline static Vec<3, T> blue() 346 | { 347 | return Vec<3, T>(0, 0, 1); 348 | } 349 | 350 | inline static Vec<3, T> yellow() 351 | { 352 | return Vec<3, T>(1, 1, 0); 353 | } 354 | 355 | inline static Vec<3, T> cyan() 356 | { 357 | return Vec<3, T>(0, 1, 1); 358 | } 359 | 360 | inline static Vec<3, T> magenta() 361 | { 362 | return Vec<3, T>(1, 0, 1); 363 | } 364 | 365 | inline static Vec<3, T> orange() 366 | { 367 | return Vec<3, T>(1, 0.5, 0); 368 | } 369 | }; 370 | 371 | template 372 | struct Vec<4, T> 373 | { 374 | union { 375 | T v[4]; 376 | struct { 377 | T x, y, z, w; 378 | }; 379 | struct { 380 | T r, g, b, a; 381 | }; 382 | swizzle_v4; 383 | }; 384 | 385 | Vec<4, T>(void) 386 | { 387 | } 388 | 389 | Vec<4, T>& operator=(const Vec<4, T>& lhs) 390 | { 391 | for(size_t i = 0; i < 4; ++i) 392 | v[i] = lhs.v[i]; 393 | 394 | return *this; 395 | } 396 | 397 | template 398 | Vec<4, T>(const Swizzle& lhs) 399 | { 400 | size_t ii[] = {SW...}; 401 | for(size_t i = 0; i < sizeof...(SW); ++i) 402 | if(ii[i] != -1) 403 | v[i] = lhs.v[ii[i]]; 404 | } 405 | 406 | template 407 | Vec<4, T>& operator=(const Swizzle& lhs) 408 | { 409 | size_t ii[] = {SW...}; 410 | for(size_t i = 0; i < sizeof...(SW); ++i) 411 | if(ii[i] != -1) 412 | v[i] = lhs.v[ii[i]]; 413 | return *this; 414 | } 415 | 416 | Vec<4, T>(T value_for_all) 417 | { 418 | for (size_t i = 0; i < 4; ++i) 419 | v[i] = value_for_all; 420 | } 421 | 422 | template 423 | explicit Vec<4, T>(const S* source) 424 | { 425 | for (size_t i = 0; i < 4; ++i) 426 | v[i] = (T)source[i]; 427 | } 428 | 429 | template 430 | explicit Vec<4, T>(const Vec<4, S>& source) 431 | { 432 | for (size_t i = 0; i < 4; ++i) 433 | v[i] = (T)source[i]; 434 | } 435 | 436 | Vec<4, T>(T v0, T v1, T v2, T v3) 437 | { 438 | v[0] = v0; 439 | v[1] = v1; 440 | v[2] = v2; 441 | v[3] = v3; 442 | } 443 | 444 | Vec<4, T>(const Vec<2, T>& v2, T _z, T _w) 445 | { 446 | for (size_t i = 0; i < 2; ++i) 447 | v[i] = (T)v2[i]; 448 | 449 | v[2] = _z; 450 | v[3] = _w; 451 | } 452 | 453 | Vec<4, T>(const Vec<2, T>& v2, const Vec<2, T>& v3) 454 | { 455 | for (size_t i = 0; i < 2; ++i) 456 | v[i] = (T)v2[i]; 457 | 458 | for (size_t i = 0; i < 2; ++i) 459 | v[i+2] = (T)v3[i]; 460 | } 461 | 462 | Vec<4, T>(const Vec<3, T>& v3, T _w) 463 | { 464 | for (size_t i = 0; i < 3; ++i) 465 | v[i] = (T)v3[i]; 466 | 467 | v[3] = _w; 468 | } 469 | 470 | T& operator[](size_t index) 471 | { 472 | return v[index]; 473 | } 474 | 475 | const T& operator[](size_t index) const 476 | { 477 | return v[index]; 478 | } 479 | 480 | inline static Vec<4, T> one() 481 | { 482 | return Vec<4, T>(1, 1, 1, 1); 483 | } 484 | 485 | inline static Vec<4, T> zero() 486 | { 487 | return Vec<4, T>(0, 0, 0, 0); 488 | } 489 | 490 | inline static Vec<4, T> flt_max() 491 | { 492 | return Vec<4, T>(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX); 493 | } 494 | 495 | inline static Vec<4, T> unit_x() 496 | { 497 | return Vec<4, T>(1, 0, 0, 0); 498 | } 499 | 500 | inline static Vec<4, T> unit_y() 501 | { 502 | return Vec<4, T>(0, 1, 0, 0); 503 | } 504 | 505 | inline static Vec<4, T> unit_z() 506 | { 507 | return Vec<4, T>(0, 0, 1, 0); 508 | } 509 | 510 | inline static Vec<4, T> white() 511 | { 512 | return Vec<4, T>(1, 1, 1, 1); 513 | } 514 | 515 | inline static Vec<4, T> black() 516 | { 517 | return Vec<4, T>(0, 0, 0, 1); 518 | } 519 | 520 | inline static Vec<4, T> red() 521 | { 522 | return Vec<4, T>(1, 0, 0, 1); 523 | } 524 | 525 | inline static Vec<4, T> green() 526 | { 527 | return Vec<4, T>(0, 1, 0, 1); 528 | } 529 | 530 | inline static Vec<4, T> blue() 531 | { 532 | return Vec<4, T>(0, 0, 1, 1); 533 | } 534 | 535 | inline static Vec<4, T> yellow() 536 | { 537 | return Vec<4, T>(1, 1, 0, 1); 538 | } 539 | 540 | inline static Vec<4, T> cyan() 541 | { 542 | return Vec<4, T>(0, 1, 1, 1); 543 | } 544 | 545 | inline static Vec<4, T> magenta() 546 | { 547 | return Vec<4, T>(1, 0, 1, 1); 548 | } 549 | 550 | inline static Vec<4, T> orange() 551 | { 552 | return Vec<4, T>(1, 0.5, 0, 1); 553 | } 554 | }; 555 | 556 | // 557 | // operators 558 | // 559 | 560 | template 561 | maths_inline Vec& operator+=(Vec& lhs, const Vec& rhs) 562 | { 563 | for (size_t i = 0; i < N; ++i) 564 | lhs[i] += rhs[i]; 565 | return lhs; 566 | } 567 | 568 | template 569 | maths_inline Vec operator+(const Vec& lhs, const Vec& rhs) 570 | { 571 | Vec sum(lhs); 572 | sum += rhs; 573 | return sum; 574 | } 575 | 576 | template 577 | maths_inline Vec& operator-=(Vec& lhs, const Vec& rhs) 578 | { 579 | for (size_t i = 0; i < N; ++i) 580 | lhs[i] -= rhs[i]; 581 | return lhs; 582 | } 583 | 584 | template 585 | maths_inline Vec operator-(const Vec& rhs) // unary minus 586 | { 587 | Vec negative; 588 | for (size_t i = 0; i < N; ++i) 589 | negative.v[i] = -rhs.v[i]; 590 | return negative; 591 | } 592 | 593 | template 594 | maths_inline Vec operator-(const Vec& lhs, const Vec& rhs) // subtraction 595 | { 596 | Vec diff(lhs); 597 | diff -= rhs; 598 | return diff; 599 | } 600 | 601 | template 602 | maths_inline Vec& operator*=(Vec& lhs, T a) 603 | { 604 | for (size_t i = 0; i < N; ++i) 605 | lhs.v[i] *= a; 606 | return lhs; 607 | } 608 | 609 | template 610 | maths_inline Vec operator*(const Vec& lhs, T a) 611 | { 612 | Vec w(lhs); 613 | w *= a; 614 | return w; 615 | } 616 | 617 | template 618 | maths_inline Vec& operator*=(Vec& lhs, const Vec& rhs) 619 | { 620 | for (size_t i = 0; i < N; ++i) 621 | lhs.v[i] *= rhs.v[i]; 622 | return lhs; 623 | } 624 | 625 | template 626 | maths_inline Vec operator*(const Vec& lhs, const Vec& rhs) 627 | { 628 | Vec componentwise_product; 629 | for (size_t i = 0; i < N; ++i) 630 | componentwise_product[i] = lhs.v[i] * rhs.v[i]; 631 | return componentwise_product; 632 | } 633 | 634 | template 635 | maths_inline Vec& operator/=(Vec& lhs, T a) 636 | { 637 | for (size_t i = 0; i < N; ++i) 638 | lhs.v[i] /= a; 639 | return lhs; 640 | } 641 | 642 | template 643 | maths_inline Vec operator/(const Vec& lhs, T a) 644 | { 645 | Vec w(lhs); 646 | w /= a; 647 | return w; 648 | } 649 | 650 | template 651 | maths_inline Vec& operator/=(Vec& lhs, const Vec& rhs) 652 | { 653 | for (size_t i = 0; i < N; ++i) 654 | lhs.v[i] /= rhs.v[i]; 655 | return lhs; 656 | } 657 | 658 | template 659 | maths_inline Vec operator/(const Vec& lhs, const Vec& rhs) 660 | { 661 | Vec componentwise_divide; 662 | for (size_t i = 0; i < N; ++i) 663 | componentwise_divide[i] = lhs.v[i] / rhs.v[i]; 664 | return componentwise_divide; 665 | } 666 | 667 | template 668 | maths_inline bool operator==(Vec& lhs, const Vec& rhs) 669 | { 670 | return (equals(lhs, rhs)); 671 | } 672 | 673 | template 674 | maths_inline bool operator!=(Vec& lhs, const Vec& rhs) 675 | { 676 | return (!equals(lhs, rhs)); 677 | } 678 | 679 | template 680 | maths_inline Vec& operator+=(Vec& lhs, T a) 681 | { 682 | for (size_t i = 0; i < N; ++i) 683 | lhs[i] += a; 684 | return lhs; 685 | } 686 | 687 | template 688 | maths_inline Vec operator+(const Vec& lhs, T a) 689 | { 690 | Vec sum(lhs); 691 | sum += a; 692 | return sum; 693 | } 694 | 695 | template 696 | maths_inline Vec& operator-=(Vec& lhs, T a) 697 | { 698 | for (size_t i = 0; i < N; ++i) 699 | lhs[i] -= a; 700 | return lhs; 701 | } 702 | 703 | template 704 | maths_inline Vec operator-(const Vec& lhs, T a) 705 | { 706 | Vec sum(lhs); 707 | sum -= a; 708 | return sum; 709 | } 710 | 711 | template 712 | maths_inline std::ostream& operator<<(std::ostream& out, const Vec& v) 713 | { 714 | out << v.v[0]; 715 | for (size_t i = 1; i < N; ++i) 716 | out << ", " << v.v[i]; 717 | return out; 718 | } 719 | 720 | template 721 | maths_inline std::ostream& operator<<(std::ostream& out, const Vec& v) 722 | { 723 | out << v.v[0]; 724 | for (size_t i = 1; i < N; ++i) 725 | out << ", " << v.v[i]; 726 | return out; 727 | } 728 | 729 | template 730 | maths_inline std::istream& operator>>(std::istream& in, Vec& v) 731 | { 732 | in >> v.v[0]; 733 | for (size_t i = 1; i < N; ++i) 734 | in >> v.v[i]; 735 | return in; 736 | } 737 | 738 | template 739 | maths_inline bool operator==(const Vec& a, const Vec& b) 740 | { 741 | bool t = (a.v[0] == b.v[0]); 742 | size_t i = 1; 743 | while (i < N && t) 744 | { 745 | t = t && (a.v[i] == b.v[i]); 746 | ++i; 747 | } 748 | return t; 749 | } 750 | 751 | template 752 | maths_inline bool operator!=(const Vec& a, const Vec& b) 753 | { 754 | bool t = (a.v[0] != b.v[0]); 755 | size_t i = 1; 756 | while (i < N && !t) 757 | { 758 | t = t || (a.v[i] != b.v[i]); 759 | ++i; 760 | } 761 | return t; 762 | } 763 | 764 | template 765 | maths_inline Vec operator*(T a, const Vec& v) 766 | { 767 | Vec w(v); 768 | w *= a; 769 | return w; 770 | } 771 | 772 | // 773 | // free functions 774 | // 775 | 776 | template 777 | maths_inline T component_wise_min(const Vec& v) 778 | { 779 | T _min = v.v[0]; 780 | for (size_t i = 1; i < N; ++i) 781 | _min = v.v[i] < _min ? v.v[i] : _min; 782 | 783 | return _min; 784 | } 785 | 786 | template 787 | maths_inline T component_wise_max(const Vec& v) 788 | { 789 | T _max = v.v[0]; 790 | for (size_t i = 1; i < N; ++i) 791 | _max = v.v[i] > _max ? v.v[i] : _max; 792 | 793 | return _max; 794 | } 795 | 796 | template 797 | maths_inline Vec lerp(const Vec& value0, const Vec& value1, T f) 798 | { 799 | return value0 * (1 - f) + value1 * f; 800 | } 801 | 802 | template 803 | maths_inline Vec nlerp(const Vec& value0, const Vec& value1, T f) 804 | { 805 | return normalize(lerp(value0, value1, f)); 806 | } 807 | 808 | template 809 | maths_inline Vec slerp(const Vec& e0, const Vec& e1, T t) 810 | { 811 | // https://blog.demofox.org/2016/02/19/normalized-vector-interpolation-tldr/ 812 | T dot = dot(e0, e1); 813 | dot = clamp(dot, (T)-1, (T)1); 814 | T theta = std::acos(dot) * t; 815 | Vec v = normalize(e1 - e0 * dot); 816 | return ((e0 * std::cos(theta)) + (v * std::sin(theta))); 817 | } 818 | 819 | template 820 | maths_inline Vec lerp(const Vec& value0, const Vec& value1, const Vec& f) 821 | { 822 | return value0 * (1 - f) + value1 * f; 823 | } 824 | 825 | template 826 | maths_inline Vec clamp(const Vec& a, T lower, T upper) 827 | { 828 | Vec res = a; 829 | for (size_t i = 0; i < N; ++i) 830 | { 831 | if (a[i] < lower) 832 | res[i] = lower; 833 | else if (a[i] > upper) 834 | res[i] = upper; 835 | } 836 | 837 | return res; 838 | } 839 | 840 | template 841 | maths_inline Vec clamp(const Vec& a, const Vec& lower, const Vec& upper) 842 | { 843 | Vec res = a; 844 | for (size_t i = 0; i < N; ++i) 845 | { 846 | if (a[i] < lower[i]) 847 | res[i] = lower[i]; 848 | else if (a[i] > upper[i]) 849 | res[i] = upper[i]; 850 | } 851 | 852 | return res; 853 | } 854 | 855 | template 856 | maths_inline Vec saturate(const Vec& a) 857 | { 858 | Vec res = a; 859 | for (size_t i = 0; i < N; ++i) 860 | { 861 | if (a[i] < 0) 862 | res[i] = 0; 863 | else if (a[i] > 1) 864 | res[i] = 1; 865 | } 866 | 867 | return res; 868 | } 869 | 870 | template 871 | maths_inline bool all(const Vec& a) 872 | { 873 | for (size_t i = 0; i < N; ++i) 874 | if (a[i] == 0) 875 | return false; 876 | 877 | return true; 878 | } 879 | 880 | template 881 | maths_inline bool any(const Vec& a) 882 | { 883 | for (size_t i = 0; i < N; ++i) 884 | if (a[i] != 0) 885 | return true; 886 | 887 | return false; 888 | } 889 | 890 | template 891 | maths_inline Vec smooth_step(T r, const Vec& edge0, const Vec& edge1) 892 | { 893 | Vec res; 894 | for (size_t i = 0; i < N; ++i) 895 | res[i] = smooth_step(r, edge0[i], edge1[i], 0, 1); 896 | 897 | return res; 898 | } 899 | 900 | template 901 | maths_inline Vec smoothstep(T r, const Vec& edge0, const Vec& edge1) 902 | { 903 | Vec res; 904 | for (size_t i = 0; i < N; ++i) 905 | res[i] = smooth_step(r, edge0[i], edge1[i], 0, 1); 906 | 907 | return res; 908 | } 909 | 910 | template 911 | maths_inline Vec step(const Vec& value0, const Vec& value1) 912 | { 913 | Vec res; 914 | for (size_t i = 0; i < N; ++i) 915 | res[i] = value0[i] > value1[i] ? 1 : 0; 916 | 917 | return res; 918 | } 919 | 920 | template 921 | maths_inline bool equals(const Vec& lhs, const Vec& rhs) 922 | { 923 | for (size_t i = 0; i < N; ++i) 924 | if (lhs[i] != rhs[i]) 925 | return false; 926 | 927 | return true; 928 | } 929 | 930 | template 931 | maths_inline bool almost_equal(const Vec& lhs, const Vec& rhs, const T& epsilon) 932 | { 933 | if (dist(lhs, rhs) < epsilon) 934 | return true; 935 | 936 | return false; 937 | } 938 | 939 | template 940 | maths_inline bool nonzero(const Vec& v) 941 | { 942 | for (size_t i = 0; i < N; ++i) 943 | if (v[i]) 944 | return true; 945 | return false; 946 | } 947 | 948 | template 949 | maths_inline T mag2(const Vec& a) 950 | { 951 | T l = sqr(a.v[0]); 952 | for (size_t i = 1; i < N; ++i) 953 | l += sqr(a.v[i]); 954 | return l; 955 | } 956 | 957 | template 958 | maths_inline T mag(const Vec& a) 959 | { 960 | return sqrt(mag2(a)); 961 | } 962 | 963 | template 964 | maths_inline T dist2(const Vec& a, const Vec& b) 965 | { 966 | T d = sqr(a.v[0] - b.v[0]); 967 | for (size_t i = 1; i < N; ++i) 968 | d += sqr(a.v[i] - b.v[i]); 969 | return d; 970 | } 971 | 972 | template 973 | maths_inline T dist(const Vec& a, const Vec& b) 974 | { 975 | return std::sqrt(dist2(a, b)); 976 | } 977 | 978 | template 979 | maths_inline Vec normalize(const Vec& a) 980 | { 981 | return a / mag(a); 982 | } 983 | 984 | template 985 | maths_inline Vec chebyshev_normalize(const Vec& a) 986 | { 987 | return a / max(fabs(a)); 988 | } 989 | 990 | template 991 | maths_inline T infnorm(const Vec& a) 992 | { 993 | T d = std::fabs(a.v[0]); 994 | for (size_t i = 1; i < N; ++i) 995 | d = max(std::fabs(a.v[i]), d); 996 | return d; 997 | } 998 | 999 | template 1000 | maths_inline void zero(Vec& a) 1001 | { 1002 | for (size_t i = 0; i < N; ++i) 1003 | a.v[i] = 0; 1004 | } 1005 | 1006 | // returns the minimum scalar component of the vector, for hlsl style min use min_union 1007 | template 1008 | maths_inline T min(const Vec& a) 1009 | { 1010 | T m = a.v[0]; 1011 | for (size_t i = 1; i < N; ++i) 1012 | if (a.v[i] < m) 1013 | m = a.v[i]; 1014 | return m; 1015 | } 1016 | 1017 | // returns the maximum scalar component of the vector, for hlsl style min use max_union 1018 | template 1019 | maths_inline T max(const Vec& a) 1020 | { 1021 | T m = a.v[0]; 1022 | for (size_t i = 1; i < N; ++i) 1023 | if (a.v[i] > m) 1024 | m = a.v[i]; 1025 | return m; 1026 | } 1027 | 1028 | // component wise min 1029 | template 1030 | maths_inline Vec min_union(const Vec& a, const Vec& b) 1031 | { 1032 | Vec m; 1033 | for (size_t i = 0; i < N; ++i) 1034 | (a.v[i] < b.v[i]) ? m.v[i] = a.v[i] : m.v[i] = b.v[i]; 1035 | return m; 1036 | } 1037 | 1038 | // component wise max 1039 | template 1040 | maths_inline Vec max_union(const Vec& a, const Vec& b) 1041 | { 1042 | Vec m; 1043 | for (size_t i = 0; i < N; ++i) 1044 | (a.v[i] > b.v[i]) ? m.v[i] = a.v[i] : m.v[i] = b.v[i]; 1045 | return m; 1046 | } 1047 | 1048 | template 1049 | maths_inline Vec fmod(const Vec& a, T mod) 1050 | { 1051 | Vec modded; 1052 | for (size_t i = 0; i < N; ++i) 1053 | modded.v[i] = (T)fmod(a.v[i], mod); 1054 | return modded; 1055 | } 1056 | 1057 | template 1058 | maths_inline Vec fmod(const Vec& a, Vec mod) 1059 | { 1060 | Vec modded; 1061 | for (size_t i = 0; i < N; ++i) 1062 | modded.v[i] = (T)fmod(a.v[i], mod[i]); 1063 | return modded; 1064 | } 1065 | 1066 | template 1067 | maths_inline T dot(const Vec& a, const Vec& b) 1068 | { 1069 | T d = a.v[0] * b.v[0]; 1070 | for (size_t i = 1; i < N; ++i) 1071 | d += a.v[i] * b.v[i]; 1072 | return d; 1073 | } 1074 | 1075 | template 1076 | maths_inline Vec<2, T> rotate(const Vec<2, T>& a, float angle) 1077 | { 1078 | T c = cos(angle); 1079 | T s = sin(angle); 1080 | return Vec<2, T>(c * a[0] - s * a[1], s * a[0] + c * a[1]); // anti-clockwise rotation 1081 | } 1082 | 1083 | template 1084 | maths_inline Vec<2, T> rotate(const Vec<2, T>& a, float angle, const Vec<2, T>& pivot) 1085 | { 1086 | return Vec<2, T>( 1087 | ((a[0] - pivot[0]) * cos(angle) - (a[1] - pivot[1]) * sin(angle)) + pivot[0], 1088 | ((a[0]-pivot[0]) * sin(angle) + (a[1]-pivot[1]) * cos(angle)) + pivot[1] 1089 | ); 1090 | } 1091 | 1092 | template 1093 | maths_inline Vec<2, T> perp(const Vec<2, T>& a) 1094 | { 1095 | return Vec<2, T>(-a.v[1], a.v[0]); 1096 | } // anti-clockwise rotation by 90 degrees 1097 | 1098 | template 1099 | maths_inline T cross(const Vec<2, T>& a, const Vec<2, T>& b) 1100 | { 1101 | return a.v[0] * b.v[1] - a.v[1] * b.v[0]; 1102 | } 1103 | 1104 | template 1105 | maths_inline Vec<3, T> cross(const Vec<3, T>& a, const Vec<3, T>& b) 1106 | { 1107 | return Vec<3, T>(a.v[1] * b.v[2] - a.v[2] * b.v[1], a.v[2] * b.v[0] - a.v[0] * b.v[2], a.v[0] * b.v[1] - a.v[1] * b.v[0]); 1108 | } 1109 | 1110 | // scalar triple product 1111 | template 1112 | maths_inline T triple(const Vec<3, T>& a, const Vec<3, T>& b, const Vec<3, T>& c) 1113 | { 1114 | return a.v[0] * (b.v[1] * c.v[2] - b.v[2] * c.v[1]) + a.v[1] * (b.v[2] * c.v[0] - b.v[0] * c.v[2]) + 1115 | a.v[2] * (b.v[0] * c.v[1] - b.v[1] * c.v[0]); 1116 | } 1117 | 1118 | // vector triple product 1119 | template 1120 | maths_inline Vec<3, T> vector_triple(const Vec<3, T>& a, const Vec<3, T>& b, const Vec<3, T>& c) 1121 | { 1122 | return cross(cross(a, b), c); 1123 | } 1124 | 1125 | template 1126 | maths_inline Vec<2, T> vector_triple(const Vec<2, T>& a, const Vec<2, T>& b, const Vec<2, T>& c) 1127 | { 1128 | return cross(Vec<3, T>(0.0, 0.0, cross(a, b)), Vec<3, T>(c.x, c.y, 0.0)).xy; 1129 | } 1130 | 1131 | template 1132 | maths_inline size_t hash(const Vec& a) 1133 | { 1134 | size_t h = a.v[0]; 1135 | for (size_t i = 1; i < N; ++i) 1136 | h = hash(h ^ a.v[i]); 1137 | return h; 1138 | } 1139 | 1140 | template 1141 | maths_inline void assign(const Vec& a, T& a0, T& a1) 1142 | { 1143 | assert(N == 2); 1144 | a0 = a.v[0]; 1145 | a1 = a.v[1]; 1146 | } 1147 | 1148 | template 1149 | maths_inline void assign(const Vec& a, T& a0, T& a1, T& a2) 1150 | { 1151 | assert(N == 3); 1152 | a0 = a.v[0]; 1153 | a1 = a.v[1]; 1154 | a2 = a.v[2]; 1155 | } 1156 | 1157 | template 1158 | maths_inline void assign(const Vec& a, T& a0, T& a1, T& a2, T& a3) 1159 | { 1160 | assert(N == 4); 1161 | a0 = a.v[0]; 1162 | a1 = a.v[1]; 1163 | a2 = a.v[2]; 1164 | a3 = a.v[3]; 1165 | } 1166 | 1167 | template 1168 | maths_inline void assign(const Vec& a, T& a0, T& a1, T& a2, T& a3, T& a4, T& a5) 1169 | { 1170 | assert(N == 6); 1171 | a0 = a.v[0]; 1172 | a1 = a.v[1]; 1173 | a2 = a.v[2]; 1174 | a3 = a.v[3]; 1175 | a4 = a.v[4]; 1176 | a5 = a.v[5]; 1177 | } 1178 | 1179 | template 1180 | maths_inline Vec round(const Vec& a) 1181 | { 1182 | Vec rounded; 1183 | for (size_t i = 0; i < N; ++i) 1184 | rounded.v[i] = (T)lround(a.v[i]); 1185 | return rounded; 1186 | } 1187 | 1188 | template 1189 | maths_inline void minmax(const Vec& x0, const Vec& x1, Vec& xmin, Vec& xmax) 1190 | { 1191 | for (size_t i = 0; i < N; ++i) 1192 | minmax(x0.v[i], x1.v[i], xmin.v[i], xmax.v[i]); 1193 | } 1194 | 1195 | template 1196 | maths_inline void minmax(const Vec& x0, const Vec& x1, const Vec& x2, Vec& xmin, Vec& xmax) 1197 | { 1198 | for (size_t i = 0; i < N; ++i) 1199 | minmax(x0.v[i], x1.v[i], x2.v[i], xmin.v[i], xmax.v[i]); 1200 | } 1201 | 1202 | template 1203 | maths_inline void minmax(const Vec& x0, const Vec& x1, const Vec& x2, const Vec& x3, Vec& xmin, 1204 | Vec& xmax) 1205 | { 1206 | for (size_t i = 0; i < N; ++i) 1207 | minmax(x0.v[i], x1.v[i], x2.v[i], x3.v[i], xmin.v[i], xmax.v[i]); 1208 | } 1209 | 1210 | template 1211 | maths_inline void minmax(const Vec& x0, const Vec& x1, const Vec& x2, const Vec& x3, const Vec& x4, 1212 | Vec& xmin, Vec& xmax) 1213 | { 1214 | for (size_t i = 0; i < N; ++i) 1215 | minmax(x0.v[i], x1.v[i], x2.v[i], x3.v[i], x4.v[i], xmin.v[i], xmax.v[i]); 1216 | } 1217 | 1218 | template 1219 | maths_inline void minmax(const Vec& x0, const Vec& x1, const Vec& x2, const Vec& x3, const Vec& x4, 1220 | const Vec& x5, Vec& xmin, Vec& xmax) 1221 | { 1222 | for (size_t i = 0; i < N; ++i) 1223 | minmax(x0.v[i], x1.v[i], x2.v[i], x3.v[i], x4.v[i], x5.v[i], xmin.v[i], xmax.v[i]); 1224 | } 1225 | 1226 | template 1227 | maths_inline void update_minmax(const Vec& x, Vec& xmin, Vec& xmax) 1228 | { 1229 | for (size_t i = 0; i < N; ++i) 1230 | update_minmax(x[i], xmin[i], xmax[i]); 1231 | } 1232 | 1233 | // 1234 | // vec functions of cmath, performing component wise op 1235 | // 1236 | 1237 | // component wise ops on single vec 1238 | #define VEC_FUNC(SCALAR_FUNC) \ 1239 | template \ 1240 | Vec SCALAR_FUNC(const Vec v) \ 1241 | { \ 1242 | Vec r; \ 1243 | for(size_t i = 0; i < N; ++i) \ 1244 | r[i] = (T)SCALAR_FUNC(v[i]); \ 1245 | return r; \ 1246 | } 1247 | 1248 | // component wise ops on 2 vecs 1249 | #define VEC_FUNC_X_X(SCALAR_FUNC) \ 1250 | template \ 1251 | Vec SCALAR_FUNC(const Vec& v, const Vec& v2) \ 1252 | { \ 1253 | Vec r; \ 1254 | for(size_t i = 0; i < N; ++i) \ 1255 | r[i] = (T)SCALAR_FUNC(v[i], v2[i]); \ 1256 | return r; \ 1257 | } 1258 | 1259 | VEC_FUNC(sgn); 1260 | VEC_FUNC(sin); 1261 | VEC_FUNC(asin); 1262 | VEC_FUNC(cos); 1263 | VEC_FUNC(acos); 1264 | VEC_FUNC(tan); 1265 | VEC_FUNC(tanh); 1266 | VEC_FUNC(floor); 1267 | VEC_FUNC(ceil); 1268 | VEC_FUNC(abs); 1269 | VEC_FUNC(fabs); 1270 | VEC_FUNC(exp); 1271 | VEC_FUNC(exp2); 1272 | VEC_FUNC(frac); 1273 | VEC_FUNC(trunc); 1274 | VEC_FUNC(sqrt); 1275 | VEC_FUNC(log); 1276 | VEC_FUNC(log10); 1277 | VEC_FUNC(log2); 1278 | 1279 | VEC_FUNC_X_X(pow); 1280 | 1281 | // 1282 | // abbreviations 1283 | // 1284 | 1285 | typedef Vec<2, double> Vec2d; 1286 | typedef Vec<2, float> Vec2f; 1287 | typedef Vec<2, int> Vec2i; 1288 | typedef Vec<2, unsigned int> Vec2ui; 1289 | typedef Vec<2, short> Vec2s; 1290 | typedef Vec<2, unsigned short> Vec2us; 1291 | typedef Vec<2, char> Vec2c; 1292 | typedef Vec<2, unsigned char> Vec2uc; 1293 | 1294 | typedef Vec<3, double> Vec3d; 1295 | typedef Vec<3, float> Vec3f; 1296 | typedef Vec<3, int> Vec3i; 1297 | typedef Vec<3, unsigned int> Vec3ui; 1298 | typedef Vec<3, short> Vec3s; 1299 | typedef Vec<3, unsigned short> Vec3us; 1300 | typedef Vec<3, char> Vec3c; 1301 | typedef Vec<3, unsigned char> Vec3uc; 1302 | 1303 | typedef Vec<4, double> Vec4d; 1304 | typedef Vec<4, float> Vec4f; 1305 | typedef Vec<4, int> Vec4i; 1306 | typedef Vec<4, unsigned int> Vec4ui; 1307 | typedef Vec<4, short> Vec4s; 1308 | typedef Vec<4, unsigned short> Vec4us; 1309 | typedef Vec<4, char> Vec4c; 1310 | typedef Vec<4, unsigned char> Vec4uc; 1311 | 1312 | typedef Vec2i vec2i; 1313 | typedef Vec2f vec2f; 1314 | typedef Vec2d vec2d; 1315 | typedef Vec3f vec3f; 1316 | typedef Vec3d vec3d; 1317 | typedef Vec3ui vec3ui; 1318 | typedef Vec3i vec3i; 1319 | typedef Vec4f vec4f; 1320 | typedef Vec4i vec4i; 1321 | typedef Vec4f float4; 1322 | typedef Vec3f float3; 1323 | typedef Vec2f float2; 1324 | -------------------------------------------------------------------------------- /maths.h: -------------------------------------------------------------------------------- 1 | // maths.h 2 | // Copyright 2014 - 2020 Alex Dixon. 3 | // License: https://github.com/polymonster/maths/blob/master/license.md 4 | 5 | #pragma once 6 | 7 | #include "mat.h" 8 | #include "quat.h" 9 | #include "util.h" 10 | #include "vec.h" 11 | 12 | constexpr double M_PI_OVER_180 = 3.1415926535897932384626433832795 / 180.0; 13 | constexpr double M_180_OVER_PI = 180.0 / 3.1415926535897932384626433832795; 14 | constexpr double M_TWO_PI = M_PI * 2.0; 15 | constexpr double M_PHI = 1.61803398875; 16 | constexpr double M_INV_PHI = 0.61803398875; 17 | 18 | namespace maths 19 | { 20 | enum e_classifications 21 | { 22 | INTERSECTS = 0, 23 | BEHIND = 1, 24 | INFRONT = 2, 25 | }; 26 | typedef u32 classification; 27 | 28 | struct transform 29 | { 30 | vec3f translation = vec3f::zero(); 31 | quat rotation = quat(); 32 | vec3f scale = vec3f::one(); 33 | }; 34 | 35 | // a collection of tests and useful maths functions 36 | // see inline implementation below file for explanation of args and return values. 37 | // .. consider moving large functions into a cpp instead of keeping them inline, just leaving them inline here for 38 | // convenience and to keep the library header only 39 | 40 | // Basis 41 | vec3f get_normal(const vec3f& v1, const vec3f& v2, const vec3f& v3); 42 | void get_orthonormal_basis_hughes_moeller(const vec3f& n, vec3f& b1, vec3f& b2); 43 | void get_orthonormal_basis_frisvad(const vec3f& n, vec3f& b1, vec3f& b2); 44 | void get_frustum_planes_from_matrix(const mat4f& view_projection, vec4f* planes_out); 45 | void get_frustum_corners_from_matrix(const mat4f& view_projection, vec3f* corners); 46 | transform get_transform_from_matrix(const mat4f& mat); 47 | 48 | template 49 | Vec<3, T> barycentric(const Vec& p, const Vec& a, const Vec& b, const Vec& c); 50 | 51 | // Angles 52 | f32 deg_to_rad(f32 degree_angle); 53 | f32 rad_to_deg(f32 radian_angle); 54 | vec3f azimuth_altitude_to_xyz(f32 azimuth, f32 altitude); 55 | void xyz_to_azimuth_altitude(vec3f v, f32& azimuth, f32& altitude); 56 | f32 focal_length_to_fov(f32 focal_length, f32 aperture_width); 57 | f32 fov_to_focal_length(f32 fov, f32 aperture_width); 58 | 59 | // Colours 60 | vec3f rgb_to_hsv(vec3f rgb); 61 | vec3f hsv_to_rgb(vec3f hsv); 62 | vec4f rgba8_to_vec4f(u32 rgba); 63 | u32 vec4f_to_rgba8(vec4f); 64 | 65 | // Projection 66 | // ndc = normalized device coordinates (-1 to 1) 67 | // sc = screen coordinates (viewport (0,0) to (width, height) 68 | // vdown = y.0 = top, y.height = bottom 69 | // vup (no suffix) = y.0 bottom, y.height = top 70 | vec3f project_to_ndc(const vec3f& p, const mat4f& view_projection); 71 | vec3f project_to_sc(const vec3f& p, const mat4f& view_projection, const vec2i& viewport); 72 | vec3f project_to_sc_vdown(const vec3f& p, const mat4f& view_projection, const vec2i& viewport); 73 | vec3f unproject_ndc(const vec3f& p, const mat4f& view_projection); 74 | vec3f unproject_sc(const vec3f& p, const mat4f& view_projection, const vec2i& viewport); 75 | vec3f unproject_sc_vdown(const vec3f& p, const mat4f& view_projection, const vec2i& viewport); 76 | 77 | // Plane Classification 78 | classification point_vs_plane(const vec3f& p, const vec3f& x, const vec3f& n); 79 | classification aabb_vs_plane(const vec3f& aabb_min, const vec3f& aabb_max, const vec3f& x0, const vec3f& xN); 80 | classification sphere_vs_plane(const vec3f& s, f32 r, const vec3f& x0, const vec3f& xN); 81 | classification capsule_vs_plane(const vec3f& c1, const vec3f& c2, f32 r, const vec3f& x, const vec3f& n); 82 | classification cone_vs_plane(const vec3f& cp, const vec3f& cv, f32 h, f32 r, const vec3f& x, const vec3f& n); 83 | 84 | // Overlaps 85 | bool sphere_vs_sphere(const vec3f& s0, f32 r0, const vec3f& s1, f32 r1); 86 | bool sphere_vs_aabb(const vec3f& s0, f32 r0, const vec3f& aabb_min, const vec3f& aabb_max); 87 | bool sphere_vs_obb(const vec3f& s0, f32 r0, const mat4f& obb); 88 | bool aabb_vs_aabb(const vec3f& min0, const vec3f& max0, const vec3f& min1, const vec3f& max1); 89 | bool aabb_vs_obb(const vec3f& aabb_min, const vec3f& aabb_max, const mat4f& obb); 90 | bool aabb_vs_frustum(const vec3f& aabb_pos, const vec3f& aabb_extent, vec4f* planes); 91 | bool sphere_vs_frustum(const vec3f& pos, f32 radius, vec4f* planes); 92 | bool sphere_vs_capsule(const vec3f s0, f32 sr, const vec3f& cp0, const vec3f& cp1, f32 cr); 93 | bool capsule_vs_capsule(const vec3f& cp0, const vec3f& cp1, f32 cr0, const vec3f& cp2, const vec3f& cp3, f32 cr1); 94 | bool obb_vs_obb(const mat4f& obb0, const mat4f& obb1); 95 | bool convex_hull_vs_convex_hull(const std::vector& hull0, const std::vector& hull1); 96 | bool gjk_2d(const std::vector& convex0, const std::vector& convex1); 97 | bool gjk_3d(const std::vector& convex0, const std::vector& convex1); 98 | 99 | // Point Test 100 | template 101 | bool point_inside_aabb(const Vec& min, const Vec& max, const Vec& p0); 102 | bool point_inside_sphere(const vec3f& s0, f32 r0, const vec3f& p0); 103 | bool point_inside_obb(const mat4f& mat, const vec3f& p); 104 | bool point_inside_triangle(const vec3f& p, const vec3f& v1, const vec3f& v2, const vec3f& v3); 105 | bool point_inside_cone(const vec3f& p, const vec3f& cp, const vec3f& cv, f32 h, f32 r); 106 | bool point_inside_convex_hull(const vec2f& p, const std::vector& hull); 107 | bool point_inside_poly(const vec2f& p, const std::vector& poly); 108 | bool point_inside_frustum(const vec3f& p, vec4f* planes); 109 | 110 | // Closest Point 111 | template 112 | Vec closest_point_on_aabb(const Vec& p0, const Vec& aabb_min, const Vec& aabb_max); 113 | template 114 | Vec closest_point_on_line(const Vec& l1, const Vec& l2, const Vec& p); 115 | vec3f closest_point_on_plane(const vec3f& p, const vec3f& x, const vec3f& n); 116 | vec3f closest_point_on_obb(const mat4f& mat, const vec3f& p); 117 | vec3f closest_point_on_sphere(const vec3f& s0, f32 r0, const vec3f& p0); 118 | vec3f closest_point_on_ray(const vec3f& r0, const vec3f& rV, const vec3f& p); 119 | vec3f closest_point_on_triangle(const vec3f& p, const vec3f& v1, const vec3f& v2, const vec3f& v3, f32& side); 120 | vec2f closest_point_on_polygon(const vec2f& p, std::vector poly); 121 | vec2f closest_point_on_convex_hull(const vec2f& p, std::vector hull); 122 | vec3f closest_point_on_cone(const vec3f& p, const vec3f& cp, const vec3f& cv, f32 h, f32 r); 123 | 124 | // Point Distance 125 | template 126 | T point_aabb_distance(const Vec& p0, const Vec& aabb_min, const Vec& aabb_max); 127 | template 128 | T point_segment_distance(const Vec& x0, const Vec& x1, const Vec& x2); 129 | f32 point_triangle_distance(const vec3f& x0, const vec3f& x1, const vec3f& x2, const vec3f& x3); 130 | template 131 | T distance_on_line(const Vec & l1, const Vec & l2, const Vec & p); 132 | f32 point_plane_distance(const vec3f& p0, const vec3f& x0, const vec3f& xN); 133 | f32 plane_distance(const vec3f& x0, const vec3f& xN); 134 | f32 point_sphere_distance(const vec3f& p0, const vec3f& s0, f32 r); 135 | f32 point_polygon_distance(const vec2f& p, std::vector poly); 136 | f32 point_convex_hull_distance(const vec2f& p, std::vector hull); 137 | f32 point_cone_distance(const vec3f& p, const vec3f& cp, const vec3f& cv, f32 h, f32 r); 138 | f32 point_obb_distance(const vec3f& p, const mat4f& obb); 139 | 140 | // Ray / Line 141 | vec3f ray_vs_plane(const vec3f& r0, const vec3f& rV, const vec3f& x0, const vec3f& xN); 142 | bool ray_vs_triangle(const vec3f& r0, const vec3f& rv, const vec3f& t0, const vec3f& t1, const vec3f& t2, vec3f& ip); 143 | bool ray_vs_sphere(const vec3f& r0, const vec3f& rv, const vec3f& s0, f32 r, vec3f& ip); 144 | bool ray_vs_line_segment(const vec3f& l1, const vec3f& l2, const vec3f& r0, const vec3f& rV, vec3f& ip); 145 | bool ray_vs_aabb(const vec3f& min, const vec3f& max, const vec3f& r1, const vec3f& rv, vec3f& ip); 146 | bool ray_vs_obb(const mat4f& mat, const vec3f& r1, const vec3f& rv, vec3f& ip); 147 | bool ray_vs_capsule(const vec3f& r0, const vec3f& rv, const vec3f& c1, const vec3f& c2, f32 r, vec3f& ip); 148 | bool ray_vs_cylinder(const vec3f& r0, const vec3f& rv, const vec3f& c0, const vec3f& c1, f32 cr, vec3f& ip); 149 | bool line_vs_line(const vec3f& l1, const vec3f& l2, const vec3f& s1, const vec3f& s2, vec3f& ip); 150 | bool line_vs_poly(const vec2f& l1, const vec2f& l2, const std::vector& poly, std::vector& ips); 151 | bool shortest_line_segment_between_lines(const vec3f& p1, const vec3f& p2, const vec3f& p3, const vec3f& p4, vec3f& r0, vec3f& r1); 152 | bool shortest_line_segment_between_line_segments(const vec3f& p1, const vec3f& p2, const vec3f& p3, const vec3f& p4, vec3f& r0, vec3f& r1); 153 | 154 | // Convex Hull 155 | void convex_hull_from_points(std::vector& hull, const std::vector& p); 156 | 157 | template 158 | Vec get_convex_hull_centre(const std::vector>& hull); 159 | 160 | // 161 | // Deprecated Functions (they still exist but have been renamed for api consitency) 162 | // 163 | 164 | maths_deprecated vec3f ray_plane_intersect(const vec3f& r0, const vec3f& rV, const vec3f& x0, const vec3f& xN); 165 | maths_deprecated bool ray_triangle_intersect(const vec3f& r0, const vec3f& rv, const vec3f& t0, const vec3f& t1, const vec3f& t2, vec3f& ip); 166 | maths_deprecated bool line_vs_ray(const vec3f& l1, const vec3f& l2, const vec3f& r0, const vec3f& rV, vec3f& ip); 167 | 168 | // 169 | // Implementation 170 | // 171 | 172 | maths_inline f32 deg_to_rad(f32 degree_angle) 173 | { 174 | return (degree_angle * (f32)M_PI_OVER_180); 175 | } 176 | 177 | maths_inline f32 rad_to_deg(f32 radian_angle) 178 | { 179 | return (radian_angle * (f32)M_180_OVER_PI); 180 | } 181 | 182 | // Convert rgb [0-1] to hsv [0-1] 183 | inline vec3f rgb_to_hsv(vec3f rgb) 184 | { 185 | // from Foley & van Dam p592 186 | // optimized: http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv 187 | f32 r = rgb.r; 188 | f32 g = rgb.g; 189 | f32 b = rgb.b; 190 | 191 | vec3f out_hsv; 192 | 193 | f32 K = 0.f; 194 | if (g < b) 195 | { 196 | std::swap(g, b); 197 | K = -1.f; 198 | } 199 | if (r < g) 200 | { 201 | std::swap(r, g); 202 | K = -2.f / 6.f - K; 203 | } 204 | 205 | const f32 chroma = r - (g < b ? g : b); 206 | out_hsv.r = fabsf(K + (g - b) / (6.f * chroma + 1e-20f)); 207 | out_hsv.g = chroma / (r + 1e-20f); 208 | out_hsv.b = r; 209 | 210 | return out_hsv; 211 | } 212 | 213 | // Convert hsv [0-1] to rgb [0-1] 214 | inline vec3f hsv_to_rgb(vec3f hsv) 215 | { 216 | // from Foley & van Dam p593: http://en.wikipedia.org/wiki/HSL_and_HSV 217 | f32 h = hsv.r; 218 | f32 s = hsv.g; 219 | f32 v = hsv.b; 220 | 221 | vec3f out_rgb; 222 | 223 | if (s == 0.0f) 224 | { 225 | // gray 226 | out_rgb.r = out_rgb.g = out_rgb.b = v; 227 | return out_rgb; 228 | } 229 | 230 | h = fmodf(h, 1.0f) / (60.0f/360.0f); 231 | int i = (int)h; 232 | f32 f = h - (f32)i; 233 | f32 p = v * (1.0f - s); 234 | f32 q = v * (1.0f - s * f); 235 | f32 t = v * (1.0f - s * (1.0f - f)); 236 | 237 | switch (i) 238 | { 239 | case 0: out_rgb.r = v; out_rgb.g = t; out_rgb.b = p; break; 240 | case 1: out_rgb.r = q; out_rgb.g = v; out_rgb.b = p; break; 241 | case 2: out_rgb.r = p; out_rgb.g = v; out_rgb.b = t; break; 242 | case 3: out_rgb.r = p; out_rgb.g = q; out_rgb.b = v; break; 243 | case 4: out_rgb.r = t; out_rgb.g = p; out_rgb.b = v; break; 244 | case 5: default: out_rgb.r = v; out_rgb.g = p; out_rgb.b = q; break; 245 | } 246 | 247 | return out_rgb; 248 | } 249 | 250 | // convert rgb8 packed in u32 to vec4 (f32) rgba 251 | inline vec4f rgba8_to_vec4f(u32 rgba) 252 | { 253 | constexpr f32 k_one_over_255 = 1.0f/255.0f; 254 | return vec4f( 255 | ((rgba >> 0) & 0xff) * k_one_over_255, 256 | ((rgba >> 8) & 0xff) * k_one_over_255, 257 | ((rgba >> 16) & 0xff) * k_one_over_255, 258 | ((rgba >> 24) & 0xff) * k_one_over_255 259 | ); 260 | } 261 | 262 | // convert vec4 (f32) rgba into a packed u32 containing rgba8 263 | inline u32 vec4f_to_rgba8(vec4f v) 264 | { 265 | u32 rgba = 0; 266 | rgba |= ((u32)(v[0] * 255.0f)); 267 | rgba |= ((u32)(v[1] * 255.0f)) << 8; 268 | rgba |= ((u32)(v[2] * 255.0f)) << 16; 269 | rgba |= ((u32)(v[3] * 255.0f)) << 24; 270 | return rgba; 271 | } 272 | 273 | // given the normalized vector n, constructs an orthonormal basis returned in n, b1, b2 274 | inline void get_orthonormal_basis_hughes_moeller(const vec3f& n, vec3f& b1, vec3f& b2) 275 | { 276 | // choose a vector orthogonal to n as the direction of b2. 277 | if(fabs(n.x) > fabs(n.z)) 278 | { 279 | b2 = vec3f(-n.y, n.x, 0.0f); 280 | } 281 | else 282 | { 283 | b2 = vec3f(0.0f, -n.z, n.y); 284 | } 285 | 286 | // normalize b2 287 | b2 *= rsqrt(dot(b2, b2)); 288 | 289 | // construct b1 using cross product 290 | b1 = cross(b2, n); 291 | } 292 | 293 | // given the normalized vector n construct an orthonormal basis without sqrt.. 294 | inline void get_orthonormal_basis_frisvad(const vec3f& n, vec3f& b1, vec3f& b2) 295 | { 296 | constexpr f32 k_singularity = -0.99999999f; 297 | if(n.z < k_singularity) 298 | { 299 | b1 = vec3f(0.0f, -1.0f, 0.0f); 300 | b2 = vec3f(-1.0f, 0.0f, 0.0f); 301 | return; 302 | } 303 | 304 | f32 a = 1.0f/(1.0f + n.z); 305 | f32 b = -n.x * n.y * a; 306 | b1 = vec3f(1.0f - n.x * n.x * a, b, -n.x); 307 | b2 = vec3f(b, 1.0f - n.y * n.y * a, -n.y); 308 | } 309 | 310 | // returns the barycentric coordinates of point p within triangle t0-t1-t2 311 | // with the result packed into a vec (u = x, v = y, w = z) 312 | template 313 | Vec<3, T> barycentric(const Vec& p, const Vec& t0, const Vec& t1, const Vec& t2) 314 | { 315 | Vec v0 = t1 - t0, v1 = t2 - t0, v2 = p - t0; 316 | T d00 = dot(v0, v0); 317 | T d01 = dot(v0, v1); 318 | T d11 = dot(v1, v1); 319 | T d20 = dot(v2, v0); 320 | T d21 = dot(v2, v1); 321 | T denom = d00 * d11 - d01 * d01; 322 | 323 | T v = (d11 * d20 - d01 * d21) / denom; 324 | T w = (d00 * d21 - d01 * d20) / denom; 325 | T u = 1.0f - v - w; 326 | 327 | return {u, v, w}; 328 | } 329 | 330 | // project point p by view_projection to normalized device coordinates, perfroming homogeneous divide 331 | inline vec3f project_to_ndc(const vec3f& p, const mat4f& view_projection) 332 | { 333 | vec4f ndc = view_projection.transform_vector(vec4f(p, 1.0f)); 334 | ndc /= ndc.w; 335 | return ndc.xyz; 336 | } 337 | 338 | // project point p to screen coordinates of viewport after projecting to normalized device coordinates first 339 | // coordinates are vup in the y-axis y.0 = bottom y.height = top 340 | inline vec3f project_to_sc(const vec3f& p, const mat4f& view_projection, const vec2i& viewport) 341 | { 342 | vec3f ndc = project_to_ndc(p, view_projection); 343 | vec3f sc = ndc * 0.5f + 0.5f; 344 | sc.xy *= vec2f((f32)viewport.x, (f32)viewport.y); 345 | return sc; 346 | } 347 | 348 | // project point p to screen coordinates of viewport after projecting to normalized device coordinates first 349 | // coordinates are vdown in the y-axis vdown = y.0 = top y.height = bottom 350 | inline vec3f project_to_sc_vdown(const vec3f& p, const mat4f& view_projection, const vec2i& viewport) 351 | { 352 | vec3f ndc = project_to_ndc(p, view_projection); 353 | ndc.y *= -1.0f; 354 | vec3f sc = ndc * 0.5f + 0.5f; 355 | sc.xy *= vec2f((f32)viewport.x, (f32)viewport.y); 356 | return sc; 357 | } 358 | 359 | // unproject normalized device coordinate p with viewport using inverse view_projection 360 | inline vec3f unproject_ndc(const vec3f& p, const mat4f& view_projection) 361 | { 362 | mat4f inv = mat::inverse4x4(view_projection); 363 | vec4f ppc = inv.transform_vector(vec4f(p, 1.0f)); 364 | return ppc.xyz / ppc.w; 365 | } 366 | 367 | // unproject screen coordinate p with viewport using inverse view_projection 368 | // coordinates are vup in the y-axis y.0 = bottom y.height = top 369 | // this function expects z in -1 to 1 range 370 | inline vec3f unproject_sc(const vec3f& p, const mat4f& view_projection, const vec2i& viewport) 371 | { 372 | vec2f ndc_xy = (p.xy / (vec2f)viewport) * vec2f(2.0) - vec2f(1.0); 373 | vec3f ndc = vec3f(ndc_xy, p.z); 374 | return unproject_ndc(ndc, view_projection); 375 | } 376 | 377 | // unproject screen coordinate p with viewport using inverse view_projection 378 | // coordinates are vdown in the y-axis vdown = y.0 = top y.height = bottom 379 | inline vec3f unproject_sc_vdown(const vec3f& p, const mat4f& view_projection, const vec2i& viewport) 380 | { 381 | vec2f ndc_xy = (p.xy / (vec2f)viewport) * vec2f(2.0) - vec2f(1.0); 382 | ndc_xy.y *= -1.0f; 383 | vec3f ndc = vec3f(ndc_xy, p.z); 384 | return unproject_ndc(ndc, view_projection); 385 | } 386 | 387 | // convert azimuth / altitude to vec3f xyz 388 | inline vec3f azimuth_altitude_to_xyz(f32 azimuth, f32 altitude) 389 | { 390 | f32 z = sin(altitude); 391 | f32 hyp = cos(altitude); 392 | f32 y = hyp * cos(azimuth); 393 | f32 x = hyp * sin(azimuth); 394 | return vec3f(x, z, y); 395 | } 396 | 397 | // convert vector xyz to azimuth, altitude 398 | inline void xyz_to_azimuth_altitude(vec3f v, f32& azimuth, f32& altitude) 399 | { 400 | azimuth = atan2(v.y, v.x); 401 | altitude = atan2(v.z, sqrt(v.x * v.x + v.y * v.y)); 402 | } 403 | 404 | // convert focal length to field of view with the specified aperture_width 405 | inline f32 focal_length_to_fov(f32 focal_length, f32 aperture_width) 406 | { 407 | return (2.0f * rad_to_deg(atan((aperture_width * 25.4f) / (2.0f * focal_length)))); 408 | } 409 | 410 | // convert field of view to focal length with the specified aperture_width 411 | inline f32 fov_to_focal_length(f32 fov, f32 aperture_width) 412 | { 413 | return (aperture_width * 25.4f) / (2.0f * tan(maths::deg_to_rad(fov / 2.0f))); 414 | } 415 | 416 | // get distance to plane x defined by point on plane x0 and normal of plane xN 417 | maths_inline f32 plane_distance(const vec3f& x0, const vec3f& xN) 418 | { 419 | return dot(xN, x0) * -1.0f; 420 | } 421 | 422 | // get distance from point p0 to plane defined by point x0 and normal xN 423 | maths_inline f32 point_plane_distance(const vec3f& p0, const vec3f& x0, const vec3f& xN) 424 | { 425 | f32 d = plane_distance(x0, xN); 426 | return dot(p0, xN) + d; 427 | } 428 | 429 | // returns the unsigned distance from point p0 to the sphere centred at s0 with radius r 430 | maths_inline f32 point_sphere_distance(const vec3f& p0, const vec3f& s0, f32 r) 431 | { 432 | vec3f cp = closest_point_on_sphere(s0, r, p0); 433 | return dist(p0, cp); 434 | } 435 | 436 | // returns the unsigned distance from point p to polygon defined by pairs of points which define the polygons edges 437 | maths_inline f32 point_polygon_distance(const vec2f& p, std::vector poly) 438 | { 439 | return dist(p, closest_point_on_polygon(p, poly)); 440 | } 441 | 442 | // returns the unsigned distance from point p to convex hull defined by pairs of points which define the hulls edges 443 | maths_inline f32 point_convex_hull_distance(const vec2f& p, std::vector hull) 444 | { 445 | return dist(p, closest_point_on_polygon(p, hull)); 446 | } 447 | 448 | // returns the unsigned distance from point p to the edge of the cone defined start position cp, direction cv with height h and radius r 449 | maths_inline f32 point_cone_distance(const vec3f& p, const vec3f& cp, const vec3f& cv, f32 h, f32 r) 450 | { 451 | return dist(closest_point_on_cone(p, cp, cv, h, r), p); 452 | } 453 | 454 | // returns the unsigned distance from point p to the obb defined by matrix obb, where the matrix transforms a unit cube 455 | // from -1 to 1 into an obb 456 | inline f32 point_obb_distance(const vec3f& p, const mat4f& obb) 457 | { 458 | return dist(p, closest_point_on_obb(obb, p)); 459 | } 460 | 461 | // returns the intersection point of ray defined by origin r0 and direction rV, 462 | // with plane defined by point on plane x0 normal of plane xN 463 | inline vec3f ray_vs_plane(const vec3f& r0, const vec3f& rV, const vec3f& x0, const vec3f& xN) 464 | { 465 | f32 d = plane_distance(x0, xN); 466 | f32 t = -(dot(r0, xN) + d) / dot(rV, xN); 467 | return r0 + (rV * t); 468 | } 469 | 470 | // deprecated: use ray_vs_plane 471 | inline vec3f ray_plane_intersect(const vec3f& r0, const vec3f& rV, const vec3f& x0, const vec3f& xN) 472 | { 473 | return ray_vs_plane(r0, rV, x0, xN); 474 | } 475 | 476 | // returns true if the ray (origin r0, direction rv) intersects with the triangle (t0,t1,t2) 477 | // if it does intersect, ip is set to the intersection point 478 | inline bool ray_vs_triangle(const vec3f& r0, const vec3f& rv, const vec3f& t0, const vec3f& t1, const vec3f& t2, vec3f& ip) 479 | { 480 | vec3f n = get_normal(t0, t1, t2); 481 | vec3f p = ray_vs_plane(r0, rv, t0, n); 482 | bool hit = point_inside_triangle(p, t0, t1, t2); 483 | if(hit) 484 | ip = p; 485 | return hit; 486 | } 487 | 488 | // deprecated: use ray_vs_triangle 489 | inline bool ray_triangle_intersect(const vec3f& r0, const vec3f& rv, const vec3f& t0, const vec3f& t1, const vec3f& t2, vec3f& ip) 490 | { 491 | return ray_vs_triangle(r0, rv, t0, t1, t2, ip); 492 | } 493 | 494 | // returns true if the ray (origin r0, direction rv) intersects with the sphere at s0 with radius r 495 | // if it does intersect, ip is set to the intersection point 496 | inline bool ray_vs_sphere(const vec3f& r0, const vec3f& rv, const vec3f& s0, f32 r, vec3f& ip) 497 | { 498 | vec3f oc = r0 - s0; 499 | f32 a = dot(rv, rv); 500 | f32 b = 2.0f * dot(oc, rv); 501 | f32 c = dot(oc,oc) - r*r; 502 | f32 discriminant = b*b - 4*a*c; 503 | bool hit = discriminant > 0.0f; 504 | if(hit) 505 | { 506 | f32 t1 = (-b - sqrt(discriminant)) / (2.0f*a); 507 | f32 t2 = (-b + sqrt(discriminant)) / (2.0f*a); 508 | f32 t; 509 | if (t1 > 0.0f && t2 > 0.0f) 510 | { 511 | // shooting from outside 512 | // get nearest 513 | t = std::min(t1, t2); 514 | } 515 | else 516 | { 517 | // shooting from inside 518 | // get hit in ray dir 519 | t = (t1 > 0.0f ? t1 : t2); 520 | } 521 | ip = r0 + rv * t; 522 | } 523 | return hit; 524 | } 525 | 526 | // returns the classification of the point p vs the plane defined by point on plane x and normal n 527 | inline classification point_vs_plane(const vec3f& p, const vec3f& x, const vec3f& n) 528 | { 529 | f32 d = point_plane_distance(p, x, n); 530 | if(d < 0.0f) 531 | { 532 | return BEHIND; 533 | } 534 | else if(d > 0.0f) 535 | { 536 | return INFRONT; 537 | } 538 | 539 | // point is on the plane 540 | return INTERSECTS; 541 | } 542 | 543 | // returns the classification of an aabb vs a plane aabb defined by min and max 544 | // plane defined by point on plane x0 and normal of plane xN 545 | inline classification aabb_vs_plane(const vec3f& aabb_min, const vec3f& aabb_max, const vec3f& x0, const vec3f& xN) 546 | { 547 | vec3f e = (aabb_max - aabb_min) / 2.0f; 548 | vec3f centre = aabb_min + e; 549 | f32 radius = fabs(xN.x * e.x) + fabs(xN.y * e.y) + fabs(xN.z * e.z); 550 | f32 pd = plane_distance(x0, xN); 551 | f32 d = dot(xN, centre) + pd; 552 | 553 | if (d > radius) 554 | return INFRONT; 555 | 556 | if (d < -radius) 557 | return BEHIND; 558 | 559 | return INTERSECTS; 560 | } 561 | 562 | // returns the classification of a sphere vs a plane 563 | // sphere defined by centre s, and radius r 564 | // plane defined by point on plane x0 and normal of plane xN 565 | inline classification sphere_vs_plane(const vec3f& s, f32 r, const vec3f& x0, const vec3f& xN) 566 | { 567 | f32 pd = plane_distance(x0, xN); 568 | f32 d = dot(xN, s) + pd; 569 | 570 | if (d > r) 571 | return INFRONT; 572 | 573 | if (d < -r) 574 | return BEHIND; 575 | 576 | return INTERSECTS; 577 | } 578 | 579 | // returns the classification of a capsule defined by line c1-c2 with radius r vs a plane defined by point on plane x and normal n 580 | inline classification capsule_vs_plane(const vec3f& c1, const vec3f& c2, f32 r, const vec3f& x, const vec3f& n) 581 | { 582 | auto pd = plane_distance(x, n); 583 | // classify both spheres at the ends of the capsule 584 | // sphere 1 585 | auto d1 = dot(n, c1) + pd; 586 | auto r1 = INTERSECTS; 587 | if(d1 > r) 588 | { 589 | r1 = INFRONT; 590 | } 591 | else if (d1 < -r) 592 | { 593 | r1 = BEHIND; 594 | } 595 | 596 | // sphere 2 597 | auto d2 = dot(n, c2) + pd; 598 | auto r2 = INTERSECTS; 599 | if(d2 > r) 600 | { 601 | r2 = INFRONT; 602 | } 603 | else if (d2 < -r) 604 | { 605 | r2 = BEHIND; 606 | } 607 | 608 | // .. 609 | if(r1 == r2) { 610 | // if both speheres are the same, we return their classification this could give us infront, behind or intersects 611 | return r1; 612 | } 613 | else { 614 | // the else case means r1 != r2 and this means we are on opposite side of the plane or one of them intersects 615 | return INTERSECTS; 616 | } 617 | } 618 | 619 | // return the classification of cone defined by position cp, direction cv with height h and radius at the base of r. vs the plane defined by point x and normal n 620 | inline classification cone_vs_plane(const vec3f& cp, const vec3f& cv, f32 h, f32 r, const vec3f& x, const vec3f& n) 621 | { 622 | auto l2 = cp + cv * h; 623 | auto pd = maths::plane_distance(x, n); 624 | // check if the tip and cones extent are on different sides of the plane 625 | auto d1 = dot(n, cp) + pd; 626 | // extent from the tip is at the base centre point perp of cv at the radius edge 627 | auto perp = normalize(cross(cross(n, cv), cv)); 628 | auto extent = l2 + perp * r; 629 | auto extent2 = l2 + perp * r * -1.0f; 630 | // take left and right extent. 631 | auto d2 = dot(n, extent) + pd; 632 | auto d3 = dot(n, extent2) + pd; 633 | // if tip and both extents lie on the same side, we are either infront or behind 634 | if(d1 < 0.0f && d2 < 0.0f && d3 < 0.0f) 635 | { 636 | return maths::BEHIND; 637 | } 638 | else if(d1 > 0.0f && d2 > 0.0f && d3 > 0.0f) 639 | { 640 | return maths::INFRONT; 641 | } 642 | // otherwise we have points on either side of the plane meaning we intersect 643 | return maths::INTERSECTS; 644 | } 645 | 646 | // returns true if sphere with centre s0 and radius r0 overlaps 647 | // sphere with centre s1 and radius r1 648 | inline bool sphere_vs_sphere(const vec3f& s0, f32 r0, const vec3f& s1, f32 r1) 649 | { 650 | f32 rr = r0 + r1; 651 | f32 d = dist(s0, s1); 652 | 653 | if (d < rr) 654 | return true; 655 | 656 | return false; 657 | } 658 | 659 | // returns true if sphere with centre s0 and radius r0 overlaps 660 | // AABB defined by aabb_min and aabb_max extents 661 | inline bool sphere_vs_aabb(const vec3f& s0, f32 r0, const vec3f& aabb_min, const vec3f& aabb_max) 662 | { 663 | vec3f cp = closest_point_on_aabb(s0, aabb_min, aabb_max); 664 | f32 d = dist(cp, s0); 665 | 666 | return d < r0; 667 | } 668 | 669 | // returns true if the sphere with centre s0 and radius r0 overlaps obb defined by matrix obb, where the matrix 670 | // transforms a unit cube with extents -1 to 1 into an obb 671 | inline bool sphere_vs_obb(const vec3f& s0, f32 r0, const mat4f& obb) 672 | { 673 | // test the distance to the closest point on the obb 674 | vec3f cp = closest_point_on_obb(obb, s0); 675 | if(dist2(s0, cp) < r0 * r0) 676 | { 677 | return true; 678 | } 679 | 680 | return false; 681 | } 682 | 683 | // returns true if the aabb's defined by min0,max0 and min1,max1 overlap 684 | inline bool aabb_vs_aabb(const vec3f& min0, const vec3f& max0, const vec3f& min1, const vec3f& max1) 685 | { 686 | // discard non overlaps quickly 687 | for (u32 i = 0; i < 3; ++i) 688 | { 689 | if (min0[i] > max1[i]) 690 | return false; 691 | 692 | if (max0[i] < min1[i]) 693 | return false; 694 | } 695 | 696 | return true; 697 | } 698 | 699 | // returns true if the 3d aabb defined by aabb_min-aabb_max overlaps with the obb defined by matrix obb, where 700 | // the matrix transforms a unit aabb with extents -1 to 1 into an obb 701 | inline bool aabb_vs_obb(const vec3f& aabb_min, const vec3f& aabb_max, const mat4f& obb) 702 | { 703 | // this function is for convenience, you can extract vertices and pass to gjk_3d yourself 704 | static const vec3f corners[8] = { 705 | vec3f(-1.0f, -1.0f, -1.0f), 706 | vec3f( 1.0f, -1.0f, -1.0f), 707 | vec3f( 1.0f, 1.0f, -1.0f), 708 | vec3f(-1.0f, 1.0f, -1.0f), 709 | vec3f(-1.0f, -1.0f, 1.0f), 710 | vec3f( 1.0f, -1.0f, 1.0f), 711 | vec3f( 1.0f, 1.0f, 1.0f), 712 | vec3f(-1.0f, 1.0f, 1.0f), 713 | }; 714 | 715 | std::vector verts0 = { 716 | aabb_min, 717 | vec3f(aabb_min.x, aabb_min.y, aabb_max.z), 718 | vec3f(aabb_max.x, aabb_min.y, aabb_min.z), 719 | vec3f(aabb_max.x, aabb_min.y, aabb_max.z), 720 | vec3f(aabb_min.x, aabb_max.y, aabb_min.z), 721 | vec3f(aabb_min.x, aabb_max.y, aabb_max.z), 722 | vec3f(aabb_max.x, aabb_max.y, aabb_min.z), 723 | aabb_max 724 | }; 725 | 726 | std::vector verts1; 727 | for(u32 i = 0; i < 8; ++i) 728 | { 729 | verts1.push_back(obb.transform_vector(corners[i])); 730 | } 731 | 732 | return gjk_3d(verts0, verts1); 733 | } 734 | 735 | // returns true if the 3d obb defined by matrix obb0 and obb defined by matrix obb1 overlap, matrices will transform 736 | // unit aabb with extents -1 to 1 into an obb 737 | inline bool obb_vs_obb(const mat4f& obb0, const mat4f& obb1) 738 | { 739 | // this function is for convenience, you can extract vertices and pass to gjk_3d yourself 740 | static const vec3f corners[8] = { 741 | vec3f(-1.0f, -1.0f, -1.0f), 742 | vec3f( 1.0f, -1.0f, -1.0f), 743 | vec3f( 1.0f, 1.0f, -1.0f), 744 | vec3f(-1.0f, 1.0f, -1.0f), 745 | vec3f(-1.0f, -1.0f, 1.0f), 746 | vec3f( 1.0f, -1.0f, 1.0f), 747 | vec3f( 1.0f, 1.0f, 1.0f), 748 | vec3f(-1.0f, 1.0f, 1.0f), 749 | }; 750 | 751 | std::vector verts0; 752 | std::vector verts1; 753 | 754 | for(u32 i = 0; i < 8; ++i) 755 | { 756 | verts0.push_back(obb0.transform_vector(corners[i])); 757 | verts1.push_back(obb1.transform_vector(corners[i])); 758 | } 759 | 760 | return gjk_3d(verts0, verts1); 761 | } 762 | 763 | // returns true if an aabb defined by aabb_pos (centre) and aabb_extent (half extent) is inside or intersecting the frustum 764 | // defined by 6 planes (xyz = plane normal, w = plane constant / distance from origin) 765 | // implemented via info detailed in this insightful blog post: https://fgiesen.wordpress.com/2010/10/17/view-frustum-culling 766 | // sse/avx simd optimised variations can be found here: https://github.com/polymonster/pmtech/blob/master/core/put/source/ecs/ecs_cull.cpp 767 | inline bool aabb_vs_frustum(const vec3f& aabb_pos, const vec3f& aabb_extent, vec4f* planes) 768 | { 769 | bool inside = true; 770 | for (size_t p = 0; p < 6; ++p) 771 | { 772 | vec3f sign_flip = sgn(planes[p].xyz) * -1.0f; 773 | f32 pd = planes[p].w; 774 | f32 d2 = dot(aabb_pos + aabb_extent * sign_flip, planes[p].xyz); 775 | 776 | if (d2 > -pd) 777 | { 778 | inside = false; 779 | } 780 | } 781 | return inside; 782 | } 783 | 784 | // returns true if the sphere defined by pos and radius is inside or intersecting frustum 785 | // defined by 6 planes (xyz = plane normal, w = plane constant / distance) 786 | // sse/avx simd optimised variations can be found here: https://github.com/polymonster/pmtech/blob/master/core/put/source/ecs/ecs_cull.cpp 787 | inline bool sphere_vs_frustum(const vec3f& pos, f32 radius, vec4f* planes) 788 | { 789 | for (size_t p = 0; p < 6; ++p) 790 | { 791 | f32 d = dot(pos, planes[p].xyz) + planes[p].w; 792 | if (d > radius) 793 | { 794 | return false; 795 | } 796 | } 797 | return true; 798 | } 799 | 800 | // returns true if the sphere with centre s0 and radius sr overlaps the capsule with line c0-c1 and radius cr 801 | inline bool sphere_vs_capsule(const vec3f s0, f32 sr, const vec3f& cp0, const vec3f& cp1, f32 cr) 802 | { 803 | auto cp = closest_point_on_line(cp0, cp1, s0); 804 | auto r2 = sqr(sr + cr); 805 | return dist2(s0, cp) < r2; 806 | } 807 | 808 | // returns true if the capsule cp0-cp1 with radius cr0 overlaps the capsule cp2-cp3 with radius cr1 809 | inline bool capsule_vs_capsule(const vec3f& cp0, const vec3f& cp1, f32 cr0, const vec3f& cp2, const vec3f& cp3, f32 cr1) 810 | { 811 | f32 r2 = (cr0 + cr1) * (cr0 + cr1); 812 | 813 | // check shortest distance between the 2 capsule line segments, if less than the sq radius we overlap 814 | vec3f start, end; 815 | if(shortest_line_segment_between_line_segments(cp0, cp1, cp2, cp3, start, end)) 816 | { 817 | f32 m = dist2(start, end); 818 | if(m < r2) 819 | { 820 | return true; 821 | } 822 | } 823 | else 824 | { 825 | // we must be orthogonal, which means there is no one single closest point between the 2 lines 826 | 827 | // find the distance between the 2 axes 828 | vec3f l0 = normalize(cp1 - cp0); 829 | f32 t0 = dot(cp2 - cp0, l0); 830 | vec3f ip0 = cp0 + l0 * t0; 831 | f32 m0 = dist2(cp2, ip0); 832 | 833 | // check axes afre within distance 834 | if(m0 < r2) 835 | { 836 | vec3f l1 = normalize(cp3 - cp2); 837 | f32 t1 = dot(cp0 - cp2, l1); 838 | 839 | // now check if the axes overlap 840 | if(t0 >= 0.0f && t0*t0 < dist2(cp1, cp0)) 841 | { 842 | return true; 843 | } 844 | else if(t1 > 0.0f && t1*t1 < dist2(cp2, cp3)) 845 | { 846 | return true; 847 | } 848 | } 849 | } 850 | 851 | return false; 852 | } 853 | 854 | // returns true if the convex hull hull0 overlaps hull1 855 | inline bool convex_hull_vs_convex_hull(const std::vector& hull0, const std::vector& hull1) 856 | { 857 | return gjk_2d(hull0, hull1); 858 | } 859 | 860 | // returns true if sphere with centre s0 and radius r0 contains point p0 861 | inline bool point_inside_sphere(const vec3f& s0, f32 r0, const vec3f& p0) 862 | { 863 | return dist2(p0, s0) < r0 * r0; 864 | } 865 | 866 | // returns true if point p0 is inside aabb defined by min and max extents 867 | template 868 | inline bool point_inside_aabb(const Vec& min, const Vec& max, const Vec& p0) 869 | { 870 | for(size_t i = 0; i < N; ++i) 871 | if(p0.v[i] < min.v[i] || p0.v[i] > max.v[i]) 872 | return false; 873 | 874 | return true; 875 | } 876 | 877 | // returns if the point p is inside the obb defined by mat 878 | // mat will transform an aabb centred at 0 with extents -1 to 1 into an obb 879 | inline bool point_inside_obb(const mat4f& mat, const vec3f& p) 880 | { 881 | mat4f invm = mat::inverse4x4(mat); 882 | vec3f tp = invm.transform_vector(vec4f(p, 1.0f)).xyz; 883 | return point_inside_aabb(-vec3f::one(), vec3f::one(), tp); 884 | } 885 | 886 | // return true if point p is inside cone defined by position cp facing direction cv with height h and radius r 887 | inline bool point_inside_cone(const vec3f& p, const vec3f& cp, const vec3f& cv, f32 h, f32 r) 888 | { 889 | vec3f l2 = cp + cv * h; 890 | f32 dh = distance_on_line(cp, l2, p) / h; 891 | vec3f x0 = closest_point_on_line(cp, l2, p); 892 | 893 | f32 d = dist(x0, p); 894 | if (d < dh * r && dh < 1.0f) 895 | return true; 896 | 897 | return false; 898 | } 899 | 900 | // return true if point p is inside convex hull defined by point list 'hull', with clockwise winding 901 | // ... use convex_hull_from_points to generate a compatible convex hull from point cloud. 902 | inline bool point_inside_convex_hull(const vec2f& p, const std::vector& hull) 903 | { 904 | vec3f p0 = vec3f(p.xy, 0.0f); 905 | 906 | size_t ncp = hull.size(); 907 | for(size_t i = 0; i < ncp; ++i) 908 | { 909 | size_t i2 = (i+1)%ncp; 910 | 911 | vec3f p1 = vec3f(hull[i].xy, 0.0f); 912 | vec3f p2 = vec3f(hull[i2].xy, 0.0f); 913 | 914 | vec3f v1 = p2 - p1; 915 | vec3f v2 = p0 - p1; 916 | 917 | if(cross(v2,v1).z > 0.0f) 918 | return false; 919 | } 920 | 921 | return true; 922 | } 923 | 924 | // returns true if the point p is inside the polygon, which may be concave 925 | // it even supports self intersections! 926 | inline bool point_inside_poly(const vec2f& p, const std::vector& poly) 927 | { 928 | // copyright (c) 1970-2003, Wm. Randolph Franklin 929 | // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html 930 | intptr_t npol = (intptr_t)poly.size(); 931 | intptr_t i, j; 932 | bool c = false; 933 | for (i = 0, j = npol-1; i < npol; j = i++) { 934 | if ((((poly[i].y <= p.y) && (p.y < poly[j].y)) || 935 | ((poly[j].y <= p.y) && (p.y < poly[i].y))) && 936 | (p.x < (poly[j].x - poly[i].x) * (p.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x)) 937 | c = !c; 938 | } 939 | return c; 940 | } 941 | 942 | // returns true if point pos inside the frustum defined by 6 planes, xyz = normal, w = plane constant (distance) 943 | inline bool point_inside_frustum(const vec3f& pos, vec4f* planes) 944 | { 945 | for (size_t p = 0; p < 6; ++p) 946 | { 947 | f32 d = dot(pos, planes[p].xyz) + planes[p].w; 948 | if (d > 0.0f) 949 | { 950 | return false; 951 | } 952 | } 953 | return true; 954 | } 955 | 956 | // returns the closest point from p0 on sphere s0 with radius r0 957 | inline vec3f closest_point_on_sphere(const vec3f& s0, f32 r0, const vec3f& p0) 958 | { 959 | vec3f v = normalize(p0 - s0); 960 | vec3f cp = s0 + v * r0; 961 | 962 | return cp; 963 | } 964 | 965 | // returns closest point on aabb defined by aabb_min -> aabb_max to the point p0 966 | template 967 | inline Vec closest_point_on_aabb(const Vec& p0, const Vec& aabb_min, const Vec& aabb_max) 968 | { 969 | Vec t1 = max_union(p0, aabb_min); 970 | return min_union(t1, aabb_max); 971 | } 972 | 973 | // returns the closest point to p on the line segment l1-l2 974 | template 975 | inline Vec closest_point_on_line(const Vec& l1, const Vec& l2, const Vec& p) 976 | { 977 | Vec v1 = p - l1; 978 | Vec v2 = normalize(l2 - l1); 979 | 980 | T d = dist(l1, l2); 981 | T t = dot(v2, v1); 982 | 983 | if (t <= 0) 984 | return l1; 985 | 986 | if (t >= d) 987 | return l2; 988 | 989 | return l1 + v2 * t; 990 | } 991 | 992 | // returns the distance (t) of p along the line l1-l2 993 | template 994 | inline T distance_on_line(const Vec & l1, const Vec & l2, const Vec & p) 995 | { 996 | Vec v1 = p - l1; 997 | Vec v2 = normalize(l2 - l1); 998 | return dot(v2, v1); 999 | } 1000 | 1001 | // returns true if the line segment and ray intersect and stores the intersection point in ip 1002 | inline bool ray_vs_line_segment(const vec3f& l1, const vec3f& l2, const vec3f& r0, const vec3f& rV, vec3f& ip) 1003 | { 1004 | vec3f da = l2 - l1; 1005 | vec3f db = rV; 1006 | vec3f dc = r0 - l1; 1007 | 1008 | if (dot(dc, cross(da, db)) != 0.0) // lines are not coplanar 1009 | return false; 1010 | 1011 | f32 s = dot(cross(dc, db), cross(da, db)) / mag2(cross(da, db)); 1012 | if (s >= 0.0 && s <= 1.0) 1013 | { 1014 | ip = l1 + da * s; 1015 | f32 t = distance_on_line(r0, r0 + rV, ip); 1016 | if(t < 0.0f) 1017 | { 1018 | return false; 1019 | } 1020 | return true; 1021 | } 1022 | return false; 1023 | } 1024 | 1025 | // deprecated: use ray_vs_line_segment 1026 | inline bool line_vs_ray(const vec3f& l1, const vec3f& l2, const vec3f& r0, const vec3f& rV, vec3f& ip) 1027 | { 1028 | return ray_vs_line_segment(l1, l2, r0, rV, ip); 1029 | } 1030 | 1031 | // returns true if the line segment l1-l2 intersects with s1-s2 and stores the intersection point in ip 1032 | inline bool line_vs_line(const vec3f& l1, const vec3f& l2, const vec3f& s1, const vec3f& s2, vec3f& ip) 1033 | { 1034 | vec3f da = l2 - l1; 1035 | vec3f db = s2 - s1; 1036 | vec3f dc = s1 - l1; 1037 | 1038 | if (dot(dc, cross(da, db)) != 0.0) // lines are not coplanar 1039 | return false; 1040 | 1041 | f32 s = dot(cross(dc, db), cross(da, db)) / mag2(cross(da, db)); 1042 | if (s >= 0.0 && s <= 1.0) 1043 | { 1044 | ip = l1 + da * s; 1045 | f32 t = distance_on_line(s1, s2, ip) / dist(s1, s2); 1046 | if (t >= 0.0f && t <= 1.0f) 1047 | return true; 1048 | } 1049 | 1050 | return false; 1051 | } 1052 | 1053 | // returns true if the line l1-l2 intersects with the polygon, and stores an the intersection points in ips in an unspecified order 1054 | // (if you need them to be sorted some way you have to do it yourself) 1055 | inline bool line_vs_poly(const vec2f& l1, const vec2f& l2, const std::vector& poly, std::vector& ips) 1056 | { 1057 | ips.clear(); 1058 | for(size_t i = 0, n = poly.size(); i < n; ++i) 1059 | { 1060 | size_t next = (i + 1) % n; 1061 | vec3f ip; 1062 | if(line_vs_line(vec3f(l1, 0), vec3f(l2, 0), vec3f(poly[i], 0), vec3f(poly[next], 0), ip)) 1063 | ips.push_back(ip.xy); 1064 | } 1065 | return ips.empty() ? false : true; 1066 | } 1067 | 1068 | // returns the closest point to p on the line the ray r0 with direction rV 1069 | inline vec3f closest_point_on_ray(const vec3f& r0, const vec3f& rV, const vec3f& p) 1070 | { 1071 | vec3f v1 = p - r0; 1072 | f32 t = dot(v1, rV); 1073 | return r0 + rV * t; 1074 | } 1075 | 1076 | // find distance p0 is from aabb defined by aabb_min -> aabb_max 1077 | template 1078 | inline T point_aabb_distance(const Vec& p0, const Vec& aabb_min, const Vec& aabb_max) 1079 | { 1080 | Vec cp = closest_point_on_aabb(p0, aabb_min, aabb_max); 1081 | return dist(cp, p0); 1082 | } 1083 | 1084 | // find distance x0 is from segment x1-x2 1085 | template 1086 | inline T point_segment_distance(const Vec& x0, const Vec& x1, const Vec& x2) 1087 | { 1088 | Vec dx(x2 - x1); 1089 | T m2 = mag2(dx); 1090 | // find parameter value of closest point on segment 1091 | T s12 = (T)(dot(x2 - x0, dx) / m2); 1092 | if (s12 < 0) 1093 | { 1094 | s12 = 0; 1095 | } 1096 | else if (s12 > 1) 1097 | { 1098 | s12 = 1; 1099 | } 1100 | // and find the distance 1101 | return dist(x0, s12 * x1 + (1 - s12) * x2); 1102 | } 1103 | 1104 | // find distance x0 is from triangle x1-x2-x3 1105 | inline f32 point_triangle_distance(const vec3f& x0, const vec3f& x1, const vec3f& x2, const vec3f& x3) 1106 | { 1107 | // first find barycentric coordinates of closest point on infinite plane 1108 | vec3f x13(x1 - x3), x23(x2 - x3), x03(x0 - x3); 1109 | f32 m13 = mag2(x13), m23 = mag2(x23), d = dot(x13, x23); 1110 | f32 invdet = 1.f / max(m13 * m23 - d * d, 1e-30f); 1111 | f32 a = dot(x13, x03), b = dot(x23, x03); 1112 | // the barycentric coordinates themselves 1113 | f32 w23 = invdet * (m23 * a - d * b); 1114 | f32 w31 = invdet * (m13 * b - d * a); 1115 | f32 w12 = 1 - w23 - w31; 1116 | if (w23 >= 0 && w31 >= 0 && w12 >= 0) 1117 | { 1118 | // if we're inside the triangle 1119 | return dist(x0, w23 * x1 + w31 * x2 + w12 * x3); 1120 | } 1121 | else 1122 | { 1123 | // we have to clamp to one of the edges 1124 | if (w23 > 0) // this rules out edge 2-3 for us 1125 | return min(point_segment_distance(x0, x1, x2), point_segment_distance(x0, x1, x3)); 1126 | else if (w31 > 0) // this rules out edge 1-3 1127 | return min(point_segment_distance(x0, x1, x2), point_segment_distance(x0, x2, x3)); 1128 | else // w12 must be >0, ruling out edge 1-2 1129 | return min(point_segment_distance(x0, x1, x3), point_segment_distance(x0, x2, x3)); 1130 | } 1131 | } 1132 | 1133 | // returns true if p is inside the triangle v1-v2-v3 1134 | inline bool point_inside_triangle(const vec3f& p, const vec3f& v1, const vec3f& v2, const vec3f& v3) 1135 | { 1136 | vec3f w = barycentric<3, f32>(p, v1, v2, v3); 1137 | return w.x >= 0.0f && w.y >= 0.0f && w.z >= 0.0f; 1138 | } 1139 | 1140 | // returns the closest point on triangle v1-v2-v3 to point p 1141 | // side is 1 or -1 depending on whether the point is infront or behind the triangle 1142 | inline vec3f closest_point_on_triangle(const vec3f& p, const vec3f& v1, const vec3f& v2, const vec3f& v3, f32& side) 1143 | { 1144 | vec3f n = normalize(cross(v3 - v1, v2 - v1)); 1145 | f32 d = point_plane_distance(p, v1, n); 1146 | side = d <= 0.0f ? -1.0f : 1.0f; 1147 | 1148 | vec3f cp = p - n * d; 1149 | if (maths::point_inside_triangle(v1, v2, v3, cp)) 1150 | return cp; 1151 | 1152 | vec3f cl[] = {closest_point_on_line(v1, v2, cp), closest_point_on_line(v2, v3, cp), 1153 | closest_point_on_line(v1, v3, cp)}; 1154 | 1155 | f32 ld = dist(p, cl[0]); 1156 | cp = cl[0]; 1157 | 1158 | for (size_t l = 1; l < 3; ++l) 1159 | { 1160 | f32 ldd = dist(p, cl[l]); 1161 | 1162 | if (ldd < ld) 1163 | { 1164 | cp = cl[l]; 1165 | ld = ldd; 1166 | } 1167 | } 1168 | 1169 | return cp; 1170 | } 1171 | 1172 | // returns the closest point from p to the polygon defined by array of points 1173 | // where each point pair defines an edge of the polygon 1174 | inline vec2f closest_point_on_polygon(const vec2f& p, std::vector poly) 1175 | { 1176 | f32 cd = FLT_MAX; 1177 | vec2f cp = vec2f::flt_max(); 1178 | for(size_t i = 0, n = poly.size(); i < n; ++i) 1179 | { 1180 | size_t j = (i + 1) % n; 1181 | 1182 | // distance to closest point 1183 | vec2f cp2 = closest_point_on_line(poly[i], poly[j], p); 1184 | f32 d2 = dist2(cp2, p); 1185 | if(d2 < cd) 1186 | { 1187 | cd = d2; 1188 | cp = cp2; 1189 | } 1190 | } 1191 | 1192 | return cp; 1193 | } 1194 | 1195 | // returns the closest point from p to the convex hull defined by array of points 1196 | // where each point pair defines an edge of the convex_hull 1197 | inline vec2f closest_point_on_convex_hull(const vec2f& p, std::vector hull) 1198 | { 1199 | return closest_point_on_polygon(p, hull); 1200 | } 1201 | 1202 | // returns the closest point from p to the cone defined by cone defined by position cp facing direction cv with height h and radius r 1203 | inline vec3f closest_point_on_cone(const vec3f& p, const vec3f& cp, const vec3f& cv, f32 h, f32 r) 1204 | { 1205 | // centre point of the cones base (where radius is largest) 1206 | auto l2 = cp + cv * h; 1207 | 1208 | // find point on base plane and clamp to the extent 1209 | vec3f cplane = closest_point_on_plane(p, l2, cv); 1210 | vec3f extent = l2 + normalize(cplane - l2) * r; 1211 | 1212 | // test closest point on line with the axis along the side and bottom of the cone 1213 | vec3f e1 = closest_point_on_line(cp, extent, p); 1214 | vec3f e2 = closest_point_on_line(l2, extent, p); 1215 | 1216 | if(dist2(p, e1) < dist2(p, e2)) 1217 | { 1218 | return e1; 1219 | } 1220 | 1221 | return e2; 1222 | } 1223 | 1224 | // return the shortest line segment between 2 infinite lines defined by points on the lines p1-p2 and p3-p4 1225 | // storing the result in r0-r1 if any exists, returns false if the lines are orthogonal 1226 | inline bool shortest_line_segment_between_lines( 1227 | const vec3f& p1, const vec3f& p2, const vec3f& p3, const vec3f& p4, vec3f& r0, vec3f& r1) 1228 | { 1229 | // https://web.archive.org/web/20120404121511/http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/lineline.c 1230 | auto p13 = p1 - p3; 1231 | auto p43 = p4 - p3; 1232 | 1233 | constexpr f32 k_epsilon = 0.00001f; 1234 | if(mag2(p43) < k_epsilon) 1235 | { 1236 | return false; 1237 | } 1238 | 1239 | auto p21 = p2 - p1; 1240 | if(mag2(p21) < k_epsilon) 1241 | { 1242 | return false; 1243 | } 1244 | 1245 | auto d1343 = dot(p13, p43); 1246 | auto d4321 = dot(p43, p21); 1247 | auto d1321 = dot(p13, p21); 1248 | auto d4343 = dot(p43, p43); 1249 | auto d2121 = dot(p21, p21); 1250 | 1251 | auto denom = d2121 * d4343 - d4321 * d4321; 1252 | if(abs(denom) < k_epsilon) 1253 | { 1254 | return false; 1255 | } 1256 | 1257 | auto numer = d1343 * d4321 - d1321 * d4343; 1258 | auto mua = numer / denom; 1259 | auto mub = (d1343 + d4321 * mua) / d4343; 1260 | 1261 | r0 = p1 + (p21 * mua); 1262 | r1 = p3 + (p43 * mub); 1263 | return true; 1264 | } 1265 | 1266 | // return the shortest line segment between 2 line segments defined by points on the lines p1-p2 and p3-p4 1267 | // storing the result in r0-r1 if any exists, returns false if the lines are orthogonal 1268 | inline bool shortest_line_segment_between_line_segments( 1269 | const vec3f& p1, const vec3f& p2, const vec3f& p3, const vec3f& p4, vec3f& r0, vec3f& r1) 1270 | { 1271 | // https://web.archive.org/web/20120404121511/http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/lineline.c 1272 | auto p13 = p1 - p3; 1273 | auto p43 = p4 - p3; 1274 | 1275 | constexpr f32 k_epsilon = 0.00001f; 1276 | if(mag2(p43) < k_epsilon) 1277 | { 1278 | return false; 1279 | } 1280 | 1281 | auto p21 = p2 - p1; 1282 | if(mag2(p21) < k_epsilon) 1283 | { 1284 | return false; 1285 | } 1286 | 1287 | auto d1343 = dot(p13, p43); 1288 | auto d4321 = dot(p43, p21); 1289 | auto d1321 = dot(p13, p21); 1290 | auto d4343 = dot(p43, p43); 1291 | auto d2121 = dot(p21, p21); 1292 | 1293 | auto denom = d2121 * d4343 - d4321 * d4321; 1294 | if(abs(denom) < k_epsilon) 1295 | { 1296 | return false; 1297 | } 1298 | 1299 | auto numer = d1343 * d4321 - d1321 * d4343; 1300 | auto mua = saturate(numer / denom); 1301 | auto mub = saturate((d1343 + d4321 * mua) / d4343); 1302 | 1303 | r0 = p1 + (p21 * mua); 1304 | r1 = p3 + (p43 * mub); 1305 | return true; 1306 | } 1307 | 1308 | inline bool shortest_line_segment_between_line_and_line_segment( 1309 | const vec3f& p1, const vec3f& p2, const vec3f& p3, const vec3f& p4, vec3f& r0, vec3f& r1) 1310 | { 1311 | // https://web.archive.org/web/20120404121511/http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/lineline.c 1312 | auto p13 = p1 - p3; 1313 | auto p43 = p4 - p3; 1314 | 1315 | constexpr f32 k_epsilon = 0.00001f; 1316 | if(mag2(p43) < k_epsilon) 1317 | { 1318 | return false; 1319 | } 1320 | 1321 | auto p21 = p2 - p1; 1322 | if(mag2(p21) < k_epsilon) 1323 | { 1324 | return false; 1325 | } 1326 | 1327 | auto d1343 = dot(p13, p43); 1328 | auto d4321 = dot(p43, p21); 1329 | auto d1321 = dot(p13, p21); 1330 | auto d4343 = dot(p43, p43); 1331 | auto d2121 = dot(p21, p21); 1332 | 1333 | auto denom = d2121 * d4343 - d4321 * d4321; 1334 | if(abs(denom) < k_epsilon) 1335 | { 1336 | return false; 1337 | } 1338 | 1339 | auto numer = d1343 * d4321 - d1321 * d4343; 1340 | auto mua = numer / denom; 1341 | auto mub = saturate((d1343 + d4321 * mua) / d4343); 1342 | 1343 | r0 = p1 + (p21 * mua); 1344 | r1 = p3 + (p43 * mub); 1345 | return true; 1346 | } 1347 | 1348 | // get normal of triangle v1-v2-v3 with left handed winding 1349 | inline vec3f get_normal(const vec3f& v1, const vec3f& v2, const vec3f& v3) 1350 | { 1351 | vec3f vA = v3 - v1; 1352 | vec3f vB = v2 - v1; 1353 | 1354 | return normalize(cross(vB, vA)); 1355 | } 1356 | 1357 | // extracts frustum planes in the form of (xyz = planes normal, w = plane constant / distance from origin) 1358 | // planes must be a pointer to an array of 6 vec4f's 1359 | inline void get_frustum_planes_from_matrix(const mat4f& view_projection, vec4f* planes_out) 1360 | { 1361 | // unproject matrix to get frustum corners grouped as 4 near, 4 far. 1362 | static vec2f ndc_coords[] = { 1363 | vec2f(0.0f, 1.0f), 1364 | vec2f(1.0f, 1.0f), 1365 | vec2f(0.0f, 0.0f), 1366 | vec2f(1.0f, 0.0f), 1367 | }; 1368 | static vec2i vpi = vec2i(1, 1); 1369 | vec3f corners[2][4]; 1370 | for (size_t i = 0; i < 4; ++i) 1371 | { 1372 | corners[0][i] = maths::unproject_sc(vec3f(ndc_coords[i], 0.0f), view_projection, vpi); 1373 | corners[1][i] = maths::unproject_sc(vec3f(ndc_coords[i], 1.0f), view_projection, vpi); 1374 | } 1375 | 1376 | // construct vectors to obtain normals 1377 | vec3f plane_vectors[] = { 1378 | corners[0][0], corners[1][0], corners[0][2], // left 1379 | corners[0][0], corners[0][1], corners[1][0], // top 1380 | corners[0][1], corners[0][3], corners[1][1], // right 1381 | corners[0][2], corners[1][2], corners[0][3], // bottom 1382 | corners[0][0], corners[0][2], corners[0][1], // near 1383 | corners[1][0], corners[1][1], corners[1][2] // far 1384 | }; 1385 | 1386 | 1387 | // extract normals and distance 1388 | for (size_t i = 0; i < 6; ++i) 1389 | { 1390 | size_t offset = i * 3; 1391 | vec3f v1 = normalize(plane_vectors[offset + 1] - plane_vectors[offset + 0]); 1392 | vec3f v2 = normalize(plane_vectors[offset + 2] - plane_vectors[offset + 0]); 1393 | 1394 | planes_out[i].xyz = cross(v1, v2); 1395 | planes_out[i].w = maths::plane_distance(plane_vectors[offset], planes_out[i].xyz); 1396 | } 1397 | } 1398 | 1399 | // gets frustum corners sorted as 4 near, 4 far into an array of vec3f corners[8]; 1400 | inline void get_frustum_corners_from_matrix(const mat4f& view_projection, vec3f* corners) 1401 | { 1402 | // unproject matrix to get frustum corners grouped as 4 near, 4 far. 1403 | static vec2f ndc_coords[] = { 1404 | vec2f(0.0f, 1.0f), 1405 | vec2f(1.0f, 1.0f), 1406 | vec2f(0.0f, 0.0f), 1407 | vec2f(1.0f, 0.0f), 1408 | }; 1409 | static vec2i vpi = vec2i(1, 1); 1410 | for (size_t i = 0; i < 4; ++i) 1411 | { 1412 | corners[i] = maths::unproject_sc(vec3f(ndc_coords[i], 0.0f), view_projection, vpi); 1413 | corners[i+4] = maths::unproject_sc(vec3f(ndc_coords[i], 1.0f), view_projection, vpi); 1414 | } 1415 | } 1416 | 1417 | // returns a transform extracting translation, scale and quaternion rotation from a 4x4 matrix 1418 | inline transform get_transform_from_matrix(const mat4f& mat) 1419 | { 1420 | transform t; 1421 | t.translation = mat.get_translation(); 1422 | t.rotation.from_matrix(mat); 1423 | t.scale.x = mag((vec3f)mat.get_row(0).xyz); 1424 | t.scale.y = mag((vec3f)mat.get_row(1).xyz); 1425 | t.scale.z = mag((vec3f)mat.get_row(2).xyz); 1426 | return t; 1427 | } 1428 | 1429 | // returns true if ray with origin r1 and direction rv intersects the aabb defined by emin and emax 1430 | // Intersection point is stored in ip 1431 | inline bool ray_vs_aabb(const vec3f& emin, const vec3f& emax, const vec3f& r1, const vec3f& rv, vec3f& ip) 1432 | { 1433 | // thanks: http://gamedev.stackexchange.com/a/18459 1434 | vec3f dirfrac = vec3f(1.0f) / rv; 1435 | 1436 | f32 t1 = (emin.x - r1.x) * dirfrac.x; 1437 | f32 t2 = (emax.x - r1.x) * dirfrac.x; 1438 | f32 t3 = (emin.y - r1.y) * dirfrac.y; 1439 | f32 t4 = (emax.y - r1.y) * dirfrac.y; 1440 | f32 t5 = (emin.z - r1.z) * dirfrac.z; 1441 | f32 t6 = (emax.z - r1.z) * dirfrac.z; 1442 | 1443 | f32 tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6)); 1444 | f32 tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6)); 1445 | 1446 | f32 t = 0.0f; 1447 | 1448 | // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us 1449 | if (tmax < 0) 1450 | return false; 1451 | 1452 | // if tmin > tmax, ray doesn't intersect AABB 1453 | if (tmin > tmax) 1454 | return false; 1455 | 1456 | t = tmin; 1457 | ip = r1 + rv * t; 1458 | return true; 1459 | } 1460 | 1461 | // returns true if there is an intersection bewteen ray with origin r1 and direction rv and obb defined by matrix mat 1462 | // mat will transform an aabb centred at 0 with extents -1 to 1 into an obb 1463 | inline bool ray_vs_obb(const mat4f& mat, const vec3f& r1, const vec3f& rv, vec3f& ip) 1464 | { 1465 | mat4f invm = mat::inverse4x4(mat); 1466 | vec3f tr1 = invm.transform_vector(vec4f(r1, 1.0f)).xyz; 1467 | 1468 | invm.set_translation(vec3f::zero()); 1469 | vec3f trv = invm.transform_vector(vec4f(rv, 1.0f)).xyz; 1470 | 1471 | bool ii = ray_vs_aabb(-vec3f::one(), vec3f::one(), tr1, normalize(trv), ip); 1472 | 1473 | ip = mat.transform_vector(vec4f(ip, 1.0f)).xyz; 1474 | return ii; 1475 | } 1476 | 1477 | // returns true if there is an intersection between ray with origin r0 and direction rv against the capsule with line c0 - c1 and radius cr 1478 | // the intersection point is stored in ip if one exists 1479 | inline bool ray_vs_capsule(const vec3f& r0, const vec3f& rv, const vec3f& c0, const vec3f& c1, f32 cr, vec3f& ip) 1480 | { 1481 | // shortest line seg within radius will indicate we intersect an infinite cylinder about an axis 1482 | vec3f l0, l1; 1483 | bool nonartho = shortest_line_segment_between_line_and_line_segment(r0, r0 + rv, c0, c1, l0, l1); 1484 | 1485 | // check we intesect the cylinder 1486 | vec3f ipc = vec3f::flt_max(); 1487 | bool bc = false; 1488 | if(nonartho) 1489 | { 1490 | if(dist2(l0, l1) < sqr(cr)) 1491 | { 1492 | // intesection of ray and infinite cylinder about axis 1493 | // https://stackoverflow.com/questions/4078401/trying-to-optimize-line-vs-cylinder-intersection 1494 | vec3f a = c0; 1495 | vec3f b = c1; 1496 | vec3f v = rv; 1497 | f32 r = cr; 1498 | 1499 | vec3f ab = b - a; 1500 | vec3f ao = r0 - a; 1501 | vec3f aoxab = cross(ao, ab); 1502 | vec3f vxab = cross(v, ab); 1503 | f32 ab2 = dot(ab, ab); 1504 | 1505 | f32 aa = dot(vxab, vxab); 1506 | f32 bb = 2.0f * dot(vxab, aoxab); 1507 | f32 cc = dot(aoxab, aoxab) - (r*r * ab2); 1508 | f32 dd = bb * bb - 4.0f * aa * cc; 1509 | 1510 | if(dd >= 0.0f) 1511 | { 1512 | f32 t = (-bb - sqrt(dd)) / (2.0f * aa); 1513 | 1514 | if(t >= 0.0f) 1515 | { 1516 | ipc = r0 + rv * t; 1517 | 1518 | // clamps to finite cylinder extents 1519 | f32 ipd = maths::distance_on_line(a, b, ipc); 1520 | if(ipd >= 0.0f && ipd <= dist(a, b)) 1521 | { 1522 | bc = true; 1523 | } 1524 | } 1525 | } 1526 | } 1527 | } 1528 | 1529 | // if our line does not intersect the cylinder, we might still intersect the top / bottom sphere 1530 | // test intersections with the end spheres 1531 | vec3f ips1 = vec3f::flt_max(); 1532 | bool bs1 = maths::ray_vs_sphere(r0, rv, c0, cr, ips1); 1533 | 1534 | vec3f ips2 = vec3f::flt_max(); 1535 | bool bs2 = maths::ray_vs_sphere(r0, rv, c1, cr, ips2); 1536 | 1537 | // we need to choose the closes intersection if we have multiple 1538 | vec3f ips[3] = {ips1, ips2, ipc}; 1539 | bool bips[3] = {bs1, bs2, bc}; 1540 | 1541 | u32 iclosest = -1; 1542 | f32 dclosest = FLT_MAX; 1543 | for(u32 i = 0; i < 3; ++i) 1544 | { 1545 | if(bips[i]) 1546 | { 1547 | f32 dd = distance_on_line(r0, r0 + rv, ips[i]); 1548 | if(dd < dclosest) 1549 | { 1550 | iclosest = i; 1551 | dclosest = dd; 1552 | } 1553 | } 1554 | } 1555 | 1556 | // if we have a valid closest point 1557 | if(iclosest != -1) 1558 | { 1559 | ip = ips[iclosest]; 1560 | return true; 1561 | } 1562 | 1563 | // no intersection is found 1564 | return false; 1565 | } 1566 | 1567 | // returns true if there is an intersection between ray with origin r0 and direction rv against the cylinder with line c0 - c1 and radius cr 1568 | // the intersection point is stored in ip if one exists 1569 | inline bool ray_vs_cylinder(const vec3f& r0, const vec3f& rv, const vec3f& c0, const vec3f& c1, f32 cr, vec3f& ip) 1570 | { 1571 | // intersection of ray and infinite cylinder about axis 1572 | // https://stackoverflow.com/questions/4078401/trying-to-optimize-line-vs-cylinder-intersection 1573 | vec3f a = c0; 1574 | vec3f b = c1; 1575 | vec3f v = rv; 1576 | f32 r = cr; 1577 | 1578 | vec3f ab = b - a; 1579 | vec3f ao = r0 - a; 1580 | vec3f aoxab = cross(ao, ab); 1581 | vec3f vxab = cross(v, ab); 1582 | f32 ab2 = dot(ab, ab); 1583 | 1584 | f32 aa = dot(vxab, vxab); 1585 | f32 bb = 2.0f * dot(vxab, aoxab); 1586 | f32 cc = dot(aoxab, aoxab) - (r*r * ab2); 1587 | f32 dd = bb * bb - 4.0f * aa * cc; 1588 | 1589 | vec3f ipc; 1590 | if(dd >= 0.0f) 1591 | { 1592 | f32 t = (-bb - sqrt(dd)) / (2.0f * aa); 1593 | 1594 | if(t >= 0.0f) 1595 | { 1596 | ipc = r0 + rv * t; 1597 | 1598 | // clamps to finite cylinder extents 1599 | f32 ipd = maths::distance_on_line(a, b, ipc); 1600 | if(ipd >= 0.0f && ipd <= dist(a, b)) 1601 | { 1602 | ip = ipc; 1603 | return true; 1604 | } 1605 | } 1606 | } 1607 | 1608 | // intersect with the top and bottom circles 1609 | vec3f ip_top = ray_vs_plane(r0, rv, c0, normalize(c0 - c1)); 1610 | vec3f ip_bottom = ray_vs_plane(r0, rv, c1, normalize(c1 - c0)); 1611 | 1612 | bool btop = false; 1613 | f32 r2 = r*r; 1614 | if(dist2(ip_top, c0) < r2) 1615 | { 1616 | ip = ip_top; 1617 | btop = true; 1618 | } 1619 | 1620 | if(dist2(ip_bottom, c1) < r2) 1621 | { 1622 | if(btop) 1623 | { 1624 | f32 d1 = distance_on_line(r0, r0 + rv, ip_top); 1625 | f32 d2 = distance_on_line(r0, r0 + rv, ip_bottom); 1626 | 1627 | if(d2 < d1) 1628 | { 1629 | ip = ip_bottom; 1630 | return true; 1631 | } 1632 | } 1633 | else 1634 | { 1635 | ip = ip_bottom; 1636 | return true; 1637 | } 1638 | } 1639 | 1640 | if(btop) 1641 | { 1642 | return true; 1643 | } 1644 | 1645 | return false; 1646 | } 1647 | 1648 | // returns the closest point to p on the plane defined by point on plane x and normal n 1649 | maths_inline vec3f closest_point_on_plane(const vec3f& p, const vec3f& x, const vec3f& n) 1650 | { 1651 | return p - n * (dot(p, n) - dot(x, n)); 1652 | } 1653 | 1654 | // returns the closest point to point p on the obb defined by mat 1655 | // mat will transform an aabb centred at 0 with extents -1 to 1 into an obb 1656 | inline vec3f closest_point_on_obb(const mat4f& mat, const vec3f& p) 1657 | { 1658 | mat4f invm = mat::inverse4x4(mat); 1659 | vec3f tp = invm.transform_vector(vec4f(p, 1.0f)).xyz; 1660 | vec3f cp = closest_point_on_aabb(tp, -vec3f::one(), vec3f::one()); 1661 | 1662 | vec3f tcp = mat.transform_vector(vec4f(cp, 1.0f)).xyz; 1663 | return tcp; 1664 | } 1665 | 1666 | // returns a convex hull wound clockwise from point cloud "points" 1667 | inline void convex_hull_from_points(std::vector& hull, const std::vector& points) 1668 | { 1669 | std::vector to_sort; 1670 | 1671 | for (auto& p : points) 1672 | to_sort.push_back({p, 0.0f}); 1673 | 1674 | //find right most 1675 | vec3f cur = to_sort[0]; 1676 | size_t curi = 0; 1677 | for (size_t i = 1; i < to_sort.size(); ++i) 1678 | { 1679 | // sort by x 1680 | if(to_sort[i].x > cur.x) 1681 | { 1682 | cur = to_sort[i]; 1683 | curi = i; 1684 | } 1685 | else if(to_sort[i].x == cur.x && to_sort[i].y > cur.y) 1686 | { 1687 | // if we share same x, sort by y 1688 | cur = to_sort[i]; 1689 | curi = i; 1690 | } 1691 | } 1692 | 1693 | // wind 1694 | hull.push_back(cur.xy); 1695 | for(;;) 1696 | { 1697 | size_t rm = (curi+1)%to_sort.size(); 1698 | vec3f x1 = to_sort[rm]; 1699 | for (size_t i = 0; i < to_sort.size(); ++i) 1700 | { 1701 | if(i == curi) 1702 | continue; 1703 | 1704 | vec3f x2 = to_sort[i]; 1705 | vec3f v1 = x1 - cur; 1706 | vec3f v2 = x2 - cur; 1707 | vec3f cp = cross(v2, v1); 1708 | if (cp.z > 0.0f) 1709 | { 1710 | x1 = to_sort[i]; 1711 | rm = i; 1712 | } 1713 | } 1714 | if(almost_equal((vec2f)x1.xy, hull[0], 0.0001f)) 1715 | break; 1716 | 1717 | cur = x1; 1718 | curi = rm; 1719 | hull.push_back(x1.xy); 1720 | } 1721 | } 1722 | 1723 | // return the centre point of a convex hull 2D or 3D 1724 | template 1725 | Vec get_convex_hull_centre(const std::vector>& hull) 1726 | { 1727 | Vec cp = Vec::zero(); 1728 | for(auto& p : hull) 1729 | cp += p; 1730 | return cp / (f32)hull.size(); 1731 | } 1732 | 1733 | // finds support vertices for gjk based on convex meshses where convex0 and convex1 are an array of vertices that form a convex hull 1734 | template 1735 | inline Vec gjk_mesh_support_function(const std::vector>& convex0, const std::vector>& convex1, Vec dir) 1736 | { 1737 | auto furthest_point = [](const Vec& dir, const std::vector>& vertices) -> Vec 1738 | { 1739 | T fd = -FLT_MAX; 1740 | Vec fv = vertices[0]; 1741 | 1742 | for(auto& v : vertices) 1743 | { 1744 | T d = dot(dir, v); 1745 | if(d > fd) 1746 | { 1747 | fv = v; 1748 | fd = d; 1749 | } 1750 | } 1751 | 1752 | return fv; 1753 | }; 1754 | 1755 | Vec fp0 = furthest_point(dir, convex0); 1756 | Vec fp1 = furthest_point(-dir, convex1); 1757 | Vec s = fp0 - fp1; 1758 | return s; 1759 | } 1760 | 1761 | // simplex evolution for 2d mesh overlaps 1762 | template 1763 | inline bool handle_simplex_2d(std::vector>& simplex, Vec<2, T>& dir) 1764 | { 1765 | if(simplex.size() == 2) 1766 | { 1767 | Vec<2, T> a = simplex[1]; 1768 | Vec<2, T> b = simplex[0]; 1769 | 1770 | Vec<2, T> ab = b - a; 1771 | Vec<2, T> ao = -a; 1772 | 1773 | dir = vector_triple(ab, ao, ab); 1774 | 1775 | return false; 1776 | } 1777 | else if(simplex.size() == 3) 1778 | { 1779 | Vec<2, T> a = simplex[2]; 1780 | Vec<2, T> b = simplex[1]; 1781 | Vec<2, T> c = simplex[0]; 1782 | 1783 | Vec<2, T> ab = b - a; 1784 | Vec<2, T> ac = c - a; 1785 | Vec<2, T> ao = -a; 1786 | 1787 | Vec<2, T> abperp = vector_triple(ac, ab, ab); 1788 | Vec<2, T> acperp = vector_triple(ab, ac, ac); 1789 | 1790 | if(dot(abperp, ao) > 0.0f) 1791 | { 1792 | simplex.erase(simplex.begin() + 0); 1793 | dir = abperp; 1794 | return false; 1795 | } 1796 | else if (dot(acperp, ao) > 0.0f) 1797 | { 1798 | simplex.erase(simplex.begin() + 1); 1799 | dir = acperp; 1800 | return false; 1801 | } 1802 | return true; 1803 | } 1804 | 1805 | // we shouldnt hit this case, we should always have 2 or 3 points in the simplex 1806 | assert(0); 1807 | return false; 1808 | } 1809 | 1810 | // returns true if the 2d convex hull convex0 overlaps with convex1 using the gjk algorithm 1811 | inline bool gjk_2d(const std::vector& convex0, const std::vector& convex1) 1812 | { 1813 | // implemented following details in this insightful video: https://www.youtube.com/watch?v=ajv46BSqcK4 1814 | 1815 | // start with arbitrary direction 1816 | vec2f dir = vec2f::unit_x(); 1817 | vec2f support = gjk_mesh_support_function(convex0, convex1, dir); 1818 | dir = normalize(-support); 1819 | 1820 | // iterative build and test simplex 1821 | std::vector simplex; 1822 | simplex.push_back(support); 1823 | 1824 | constexpr size_t max_iters = 32; 1825 | for(size_t i = 0; i < max_iters; ++i) 1826 | { 1827 | vec2f a = gjk_mesh_support_function(convex0, convex1, dir); 1828 | if(dot(a, dir) < 0.0f) 1829 | { 1830 | return false; 1831 | } 1832 | simplex.push_back(a); 1833 | 1834 | if(handle_simplex_2d(simplex, dir)) 1835 | { 1836 | return true; 1837 | } 1838 | } 1839 | 1840 | // if we reach here we likely have got stuck in a simplex building loop, we assume the shapes are touching but not intersecting 1841 | return false; 1842 | } 1843 | 1844 | // simplex evolution for 3d mesh overlaps 1845 | inline bool handle_simplex_3d(std::vector& simplex, vec3f& dir) 1846 | { 1847 | if(simplex.size() == 2) 1848 | { 1849 | vec3f a = simplex[1]; 1850 | vec3f b = simplex[0]; 1851 | 1852 | vec3f ab = b - a; 1853 | vec3f ao = -a; 1854 | 1855 | dir = vector_triple(ab, ao, ab); 1856 | 1857 | return false; 1858 | } 1859 | else if(simplex.size() == 3) 1860 | { 1861 | vec3f a = simplex[2]; 1862 | vec3f b = simplex[1]; 1863 | vec3f c = simplex[0]; 1864 | 1865 | vec3f ab = b - a; 1866 | vec3f ac = c - a; 1867 | vec3f ao = -a; 1868 | 1869 | dir = cross(ac, ab); 1870 | 1871 | // flip normal so it points toward the origin 1872 | if(dot(dir, ao) < 0.0f) 1873 | { 1874 | dir *= -1.0f; 1875 | } 1876 | 1877 | return false; 1878 | } 1879 | else if(simplex.size() == 4) 1880 | { 1881 | vec3f a = simplex[3]; 1882 | vec3f b = simplex[2]; 1883 | vec3f c = simplex[1]; 1884 | vec3f d = simplex[0]; 1885 | 1886 | vec3f centre = (a+b+c+d) * 0.25f; 1887 | 1888 | vec3f ab = b - a; 1889 | vec3f ac = c - a; 1890 | vec3f ad = d - a; 1891 | vec3f ao = -a; 1892 | 1893 | vec3f abac = cross(ab, ac); 1894 | vec3f acad = cross(ac, ad); 1895 | vec3f adab = cross(ad, ab); 1896 | 1897 | // flip the normals so they always face outward 1898 | vec3f centre_abc = (a + b + c) / 3.0f; 1899 | vec3f centre_acd = (a + c + d) / 3.0f; 1900 | vec3f centre_adb = (a + d + b) / 3.0f; 1901 | 1902 | if(dot(centre - centre_abc, abac) > 0.0f) 1903 | { 1904 | abac *= -1.0f; 1905 | } 1906 | 1907 | if(dot(centre - centre_acd, acad) > 0.0f) 1908 | { 1909 | acad *= -1.0f; 1910 | } 1911 | 1912 | if(dot(centre - centre_adb, adab) > 0.0f) 1913 | { 1914 | adab *= -1.0f; 1915 | } 1916 | 1917 | if(dot(abac, ao) > 0.0f) // orange 1918 | { 1919 | // erase c 1920 | simplex.erase(simplex.begin() + 0); 1921 | dir = abac; 1922 | 1923 | return false; 1924 | } 1925 | else if(dot(acad, ao) > 0.0f) // yellow 1926 | { 1927 | // erase a 1928 | simplex.erase(simplex.begin() + 1); 1929 | dir = acad; 1930 | return false; 1931 | } 1932 | else if(dot(adab, ao) > 0.0f) // red 1933 | { 1934 | // erase b 1935 | simplex.erase(simplex.begin() + 2); 1936 | dir = adab; 1937 | return false; 1938 | } 1939 | 1940 | return true; 1941 | } 1942 | 1943 | // we shouldnt hit this case, we should always have 2 or 3 points in the simplex 1944 | assert(0); 1945 | return false; 1946 | } 1947 | 1948 | // returns true if the 3d convex mesh convex0 overlaps with convex1 using the gjk algorithm 1949 | inline bool gjk_3d(const std::vector& convex0, const std::vector& convex1) 1950 | { 1951 | // implemented following details in this insightful video: https://www.youtube.com/watch?v=ajv46BSqcK4 1952 | 1953 | // start with arbitrary direction 1954 | vec3f dir = get_convex_hull_centre(convex0) - get_convex_hull_centre(convex1); 1955 | vec3f support = gjk_mesh_support_function(convex0, convex1, dir); 1956 | dir = normalize(-support); 1957 | 1958 | // iterative build and test simplex 1959 | std::vector simplex; 1960 | simplex.push_back(support); 1961 | 1962 | constexpr size_t max_iters = 32; 1963 | for(size_t i = 0; i < max_iters; ++i) 1964 | { 1965 | vec3f a = gjk_mesh_support_function(convex0, convex1, dir); 1966 | if(dot(a, dir) < 0.0f) 1967 | { 1968 | return false; 1969 | } 1970 | simplex.push_back(a); 1971 | 1972 | if(handle_simplex_3d(simplex, dir)) 1973 | { 1974 | return true; 1975 | } 1976 | } 1977 | 1978 | // if we reach here we likely have got stuck in a simplex building loop, we assume the shapes are touching but not intersecting 1979 | return false; 1980 | } 1981 | } // namespace maths 1982 | --------------------------------------------------------------------------------