1 //------------------------------------------------------------------------------ 2 // math.d 3 // 4 // minimal vector math helper functions, just the stuff needed for 5 // the sokol-samples 6 // 7 // Ported from HandmadeMath.h 8 //------------------------------------------------------------------------------ 9 10 module handmade.math; 11 12 extern (C): 13 @safe: 14 15 version (WebAssembly) { 16 // zig stdlib no-libc math functions 17 enum PI = 3.14159265358979323846264338327950288419716939937510; 18 double zig_cos(double value) @nogc nothrow @trusted; 19 double zig_floor(double value) @nogc nothrow @trusted; 20 double zig_sin(double value) @nogc nothrow @trusted; 21 double zig_sqrt(size_t value) @nogc nothrow @trusted; 22 double zig_sqrtf(double value) @nogc nothrow @trusted; 23 double zig_tan(double value) @nogc nothrow @trusted; 24 alias cos = zig_cos; 25 alias floor = zig_floor; 26 alias sin = zig_sin; 27 alias tan = zig_tan; 28 29 auto sqrt(T)(T value) { 30 static if (is(T == double) || is(T == float)) { 31 return zig_sqrtf(value); 32 } else { 33 return zig_sqrt(value); 34 } 35 } 36 } else { 37 public import core.stdc.math : sqrt, cos, sin, tan, floor; 38 public import std.math : PI; 39 } 40 41 struct Vec2 { 42 float x = 0.0, y = 0.0; 43 44 static Vec2 zero() @nogc nothrow { 45 return Vec2(0, 0); 46 } 47 48 this(float x, float y) @nogc nothrow { 49 this.x = x; 50 this.y = y; 51 } 52 } 53 54 struct Vec3 { 55 float x = 0.0, y = 0.0, z = 0.0; 56 57 static Vec3 zero() @nogc nothrow { 58 return Vec3(0, 0, 0); 59 } 60 61 this(float x, float y, float z) @nogc nothrow { 62 this.x = x; 63 this.y = y; 64 this.z = z; 65 } 66 67 static Vec3 up() @nogc nothrow { 68 return Vec3(0, 1, 0); 69 } 70 71 float len() const @nogc nothrow { 72 return sqrt(dot(this, this)); 73 } 74 75 static Vec3 add(Vec3 left, Vec3 right) @nogc nothrow { 76 return Vec3(left.x + right.x, left.y + right.y, left.z + right.z); 77 } 78 79 static Vec3 sub(Vec3 left, Vec3 right) @nogc nothrow { 80 return Vec3(left.x - right.x, left.y - right.y, left.z - right.z); 81 } 82 83 static Vec3 mul(Vec3 v, float s) @nogc nothrow { 84 return Vec3(v.x * s, v.y * s, v.z * s); 85 } 86 87 static Vec3 norm(Vec3 v) @nogc nothrow { 88 auto l = v.len(); 89 if (l != 0) { 90 return Vec3(v.x / l, v.y / l, v.z / l); 91 } else { 92 return Vec3.zero; 93 } 94 } 95 96 static Vec3 cross(Vec3 v0, Vec3 v1) @nogc nothrow { 97 return Vec3( 98 (v0.y * v1.z) - (v0.z * v1.y), 99 (v0.z * v1.x) - (v0.x * v1.z), 100 (v0.x * v1.y) - (v0.y * v1.x) 101 ); 102 } 103 104 static float dot(Vec3 v0, Vec3 v1) @nogc nothrow { 105 return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z; 106 } 107 } 108 109 struct Mat4 { 110 float[4][4] m; 111 112 static Mat4 identity() @nogc nothrow { 113 return Mat4([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]); 114 } 115 116 static Mat4 zero() @nogc nothrow { 117 return Mat4([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]); 118 } 119 120 static Mat4 mul(Mat4 left, Mat4 right) @nogc nothrow { 121 Mat4 result = Mat4.zero; 122 foreach (col; 0 .. 4) { 123 foreach (row; 0 .. 4) { 124 result.m[col][row] = 125 left.m[0][row] * right.m[col][0] 126 + left.m[1][row] * right.m[col][1] 127 + left.m[2][row] * right.m[col][2] 128 + left.m[3][row] * right.m[col][3]; 129 } 130 } 131 return result; 132 } 133 134 static Mat4 perspective(float fov, float aspect, float near, float far) @nogc nothrow { 135 Mat4 result = Mat4.identity; 136 137 float t = tan(fov * (PI / 360.0)); 138 result.m[0][0] = 1.0 / t; 139 result.m[1][1] = aspect / t; 140 result.m[2][3] = -1.0; 141 result.m[2][2] = (near + far) / (near - far); 142 result.m[3][2] = (2.0 * near * far) / (near - far); 143 result.m[3][3] = 0.0; 144 145 return result; 146 } 147 148 static Mat4 lookAt(Vec3 eye, Vec3 center, Vec3 up) @nogc nothrow { 149 Mat4 result = Mat4.zero(); 150 151 Vec3 f = Vec3.norm(Vec3.sub(center, eye)); 152 Vec3 s = Vec3.norm(Vec3.cross(f, up)); 153 Vec3 u = Vec3.cross(s, f); 154 155 result.m[0][0] = s.x; 156 result.m[0][1] = u.x; 157 result.m[0][2] = -f.x; 158 159 result.m[1][0] = s.y; 160 result.m[1][1] = u.y; 161 result.m[1][2] = -f.y; 162 163 result.m[2][0] = s.z; 164 result.m[2][1] = u.z; 165 result.m[2][2] = -f.z; 166 167 result.m[3][0] = -Vec3.dot(s, eye); 168 result.m[3][1] = -Vec3.dot(u, eye); 169 result.m[3][2] = Vec3.dot(f, eye); 170 result.m[3][3] = 1; 171 172 return result; 173 } 174 175 static Mat4 rotate(float angle, Vec3 axis) @nogc nothrow { 176 Mat4 result = Mat4.identity; 177 178 axis = Vec3.norm(axis); 179 float sinTheta = sin(radians(angle)); 180 float cosTheta = cos(radians(angle)); 181 float cosValue = 1.0 - cosTheta; 182 183 result.m[0][0] = (axis.x * axis.x * cosValue) + cosTheta; 184 result.m[0][1] = (axis.x * axis.y * cosValue) + (axis.z * sinTheta); 185 result.m[0][2] = (axis.x * axis.z * cosValue) - (axis.y * sinTheta); 186 187 result.m[1][0] = (axis.y * axis.x * cosValue) - (axis.z * sinTheta); 188 result.m[1][1] = (axis.y * axis.y * cosValue) + cosTheta; 189 result.m[1][2] = (axis.y * axis.z * cosValue) + (axis.x * sinTheta); 190 191 result.m[2][0] = (axis.z * axis.x * cosValue) + (axis.y * sinTheta); 192 result.m[2][1] = (axis.z * axis.y * cosValue) - (axis.x * sinTheta); 193 result.m[2][2] = (axis.z * axis.z * cosValue) + cosTheta; 194 195 return result; 196 } 197 198 static Mat4 translate(Vec3 translation) @nogc nothrow { 199 Mat4 result = Mat4.identity; 200 201 result.m[3][0] = translation.x; 202 result.m[3][1] = translation.y; 203 result.m[3][2] = translation.z; 204 205 return result; 206 } 207 } 208 209 float radians(float deg) @nogc nothrow { 210 return deg * (PI / 180.0); 211 } 212 213 unittest { 214 import std.math : isClose; 215 216 // Vec3.zero test 217 { 218 auto v = Vec3.zero(); 219 assert(v.x.isClose(0.0)); 220 assert(v.y.isClose(0.0)); 221 assert(v.z.isClose(0.0)); 222 } 223 224 } 225 226 unittest { 227 import std.math : isClose; 228 229 // Vec3.new test 230 { 231 auto v = Vec3(1.0, 2.0, 3.0); 232 assert(v.x.isClose(1.0)); 233 assert(v.y.isClose(2.0)); 234 assert(v.z.isClose(3.0)); 235 } 236 237 } 238 239 unittest { 240 import std.math : isClose; 241 242 // Mat4.identity test 243 { 244 auto m = Mat4.identity(); 245 foreach (i; 0 .. 4) { 246 foreach (j; 0 .. 4) { 247 if (i == j) { 248 assert(m.m[i][j].isClose(1.0)); 249 } else { 250 assert(m.m[i][j].isClose(0.0)); 251 } 252 } 253 } 254 } 255 }