1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source: $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/math/matrix.d) 6 * Documentation: 7 * Coverage: 8 **/ 9 module liberty.math.matrix; 10 11 import std.traits : isFloatingPoint; 12 13 import liberty.math.traits; 14 import liberty.math.functions; 15 import liberty.math.vector; 16 17 /// T = Type of elements. 18 /// R = Number of rows. 19 /// C = Number of columns. 20 /// O = Matrix order. It can be RowMajor or ColumnMajor. 21 struct Matrix(T, ubyte R, ubyte C = R) if (R >= 2 && R <= 4 && C >= 2 && C <= 4) { 22 /// 23 alias type = T; 24 /// 25 enum rowCount = R; 26 /// 27 enum columnCount = C; 28 /// 29 enum order = 0; 30 /// 31 enum bool isSquare = (R == C); 32 /// 33 alias RowType = Vector!(T, C) ; 34 /// 35 alias ColumnType = Vector!(T, R); 36 /// Fields definition. 37 union { 38 /// All elements. 39 T[C * R] v; 40 /// All rows. 41 RowType[R] row; 42 /// Components. 43 T[C][R] c; 44 } 45 46 static if (R == 4 && C == 4) { 47 void setTranslation(string op = "=")(Vector3!T translation) 48 if (op == "=" || op == "+=" || op == "-=") 49 do { 50 mixin("c[0][3] " ~ op ~ " translation.x;"); 51 mixin("c[1][3] " ~ op ~ " translation.y;"); 52 mixin("c[2][3] " ~ op ~ " translation.z;"); 53 } 54 55 void setScale(string op = "=")(Vector3!T scale) 56 if (op == "=" || op == "+=" || op == "-=") 57 do { 58 mixin("c[0][0] " ~ op ~ " scale.x;"); 59 mixin("c[1][1] " ~ op ~ " scale.y;"); 60 mixin("c[2][2] " ~ op ~ " scale.z;"); 61 } 62 } 63 64 65 /// 66 this(U...)(U values) { 67 import std.meta : allSatisfy; 68 import std.traits : isAssignable; 69 enum bool isAsgn(U) = isAssignable!(T, U); 70 static if ((U.length == C * R) && allSatisfy!(isAsgn, U)) { 71 static foreach (i, x; values) { 72 v[i] = x; 73 } 74 } else static if ((U.length == 1) && isAsgn!U && (!is(U[0] : Matrix))) { 75 v[] = values[0]; 76 } else static if (U.length == 0) { 77 v[] = cast(T)0; 78 } else { 79 static assert(false, "Cannot create a matrix from given arguments!"); 80 } 81 } 82 /// 83 unittest { 84 // Create a matrix using all values. 85 const matrix1 = Matrix!(int, 2)(1, 2, 3, 4); 86 assert(matrix1.v == [1, 2, /**/ 3, 4]); 87 // Create a matrix from one value. 88 const matrix2 = Matrix!(float, 2)(5.2f); 89 assert(matrix2.v == [5.2f, 5.2f, /**/ 5.2f, 5.2f]); 90 // Create a matrix with no value (implicit 0). 91 const matrix3 = Matrix!(short, 2)(); 92 assert(matrix3.v == [0, 0, /**/ 0, 0]); 93 } 94 /// Construct a matrix from columns. 95 static Matrix fromColumns(ColumnType[] columns) 96 in { 97 assert(columns.length == C); 98 } do { 99 Matrix ret = void; 100 // todo 101 return ret; 102 } 103 /// 104 unittest { 105 // todo 106 } 107 /// Construct a matrix from rows. 108 static Matrix fromRows(RowType[] rows) 109 in { 110 assert(rows.length == R); 111 } do { 112 Matrix ret = void; 113 ret.row[] = rows[]; 114 return ret; 115 } 116 /// 117 unittest { 118 auto row = Vector!(double, 2)(2.0, 4.5); 119 const matrix = Matrix!(double, 2).fromRows([row, row]); 120 assert(matrix.v == [2.0, 4.5, /**/ 2.0, 4.5]); 121 } 122 /// Returns an identity matrix. 123 static Matrix identity() { 124 Matrix ret = void; 125 static foreach (i; 0 .. R) { 126 static foreach (j; 0 .. C) { 127 static if (i == j) { 128 ret.c[i][j] = 1; 129 } else { 130 ret.c[i][j] = 0; 131 } 132 } 133 } 134 return ret; 135 } 136 /// 137 unittest { 138 const matrix = Matrix!(uint, 3).identity(); 139 assert(matrix.v == [1, 0, 0, /**/ 0, 1, 0, /**/ 0, 0, 1]); 140 } 141 /// Returns a constant matrix. 142 static Matrix constant(U)(U x) { 143 Matrix ret = void; 144 static foreach (i; 0 .. R * C) { 145 ret.v[i] = cast(T)x; 146 } 147 return ret; 148 } 149 /// 150 unittest { 151 const matrix = Matrix!(int, 2, 3).constant(7); 152 assert(matrix.v == [7, 7, 7, /**/ 7, 7, 7]); 153 } 154 /// 155 ColumnType column(int j) const { 156 ColumnType ret = void; 157 static foreach (i; 0..R) { 158 ret.v[i] = c[i][j]; 159 } 160 return ret; 161 } 162 /// 163 Matrix opBinary(string op)(T fprimitive) const if (op == "*") { 164 Matrix ret = void; 165 static foreach (i; 0..R) { 166 static foreach (j; 0..C) { 167 ret.c[i][j] = c[i][j] * fprimitive; 168 } 169 } 170 return ret; 171 } 172 /// 173 unittest { 174 const m1 = Matrix2I(3); 175 const m2 = m1 * 4; 176 assert(m2.v == [12, 12, /**/ 12, 12]); 177 } 178 /// 179 ColumnType opBinary(string op)(RowType x) const if (op == "*") { 180 ColumnType ret = void; 181 static foreach (i; 0..R) { 182 T sum = 0; 183 static foreach (j; 0..C) { 184 sum += c[i][j] * x.v[j]; 185 } 186 res.v[i] = sum; 187 } 188 return ret; 189 } 190 /// 191 auto opBinary(string op, U)(U x) const if (isMatrixInstance!U && (U.rowCount == C) && (op == "*")) { 192 Matrix!(T, R, U.columnCount) ret = void; 193 T sum = 0; 194 static foreach (i; 0..R) { 195 static foreach (j; 0..U.columnCount) { 196 static foreach (k; 0..C) { 197 sum += c[i][k] * x.c[k][j]; 198 } 199 ret.c[i][j] = sum; 200 sum = 0; 201 } 202 } 203 return ret; 204 } 205 /// 206 unittest { 207 auto m1 = Matrix2I(2, 5, /**/ -1, 2); 208 auto m2 = Matrix2x3I(3, 3, -2, /**/ 3, 4, 3); 209 auto m3 = m1 * m2; 210 assert(m3.v == [21, 26, 11, /**/ 3, 5, 8]); 211 } 212 /// 213 Matrix opBinary(string op, U)(U rhs) const if (op == "+" || op == "-") { 214 Matrix ret = void; 215 static if (is (U : Matrix)) { 216 static foreach (i; 0..R) { 217 static foreach (j; 0..C) { 218 mixin("ret.c[i][j] = c[i][j] " ~ op ~ " rhs.c[i][j];"); 219 } 220 } 221 } else static if (is (U : T)) { 222 mixin("ret.v[] = this.v[] " ~ op ~ " rhs;"); 223 } else { 224 static assert(0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Matrix.stringof); 225 } 226 return ret; 227 } 228 /// 229 unittest { 230 const m1 = Matrix2I(1, 3, /**/ -1, 4); 231 const m2 = Matrix2I(5, 2, /**/ 1, 0); 232 const m3 = m1 + m2; 233 assert(m3.v == [6, 5, /**/ 0, 4]); 234 const m4 = m1 - 7; 235 assert(m4.v == [-6, -4, /**/ -8, -3]); 236 } 237 /// 238 ref Matrix opOpAssign(string op, U)(U rhs) { 239 static if (is(U : Matrix) || is(U : T)) { 240 mixin("this = this " ~ op ~ " rhs;"); 241 } else { 242 static assert(0, "Cannot assign a variable of type " ~ U.stringof ~ 243 " within a variable of type " ~ Matrix.stringof 244 ); 245 } 246 return this; 247 } 248 /// 249 unittest { 250 auto m1 = Matrix2I(1, 3, /**/ -1, 4); 251 const m2 = Matrix2I(5, 2, /**/ 1, 0); 252 m1 += m2; 253 assert(m1.v == [6, 5, /**/ 0, 4]); 254 m1 -= 2; 255 assert(m1.v == [4, 3, /**/ -2, 2]); 256 } 257 /// 258 Matrix opUnary(string op)() const if (op == "+" || op == "-" || op == "~" || op == "!") { 259 Matrix ret = void; 260 static foreach (i; 0..R * C) { 261 mixin("ret.v[i] = " ~ op ~ "v[i];"); 262 } 263 return ret; 264 } 265 /// 266 unittest { 267 const m1 = Matrix2I(1, 3, /**/ -1, 0); 268 assert((+m1).v == [1, 3, /**/ -1, 0]); 269 assert((-m1).v == [-1, -3, /**/ 1, 0]); 270 assert((~m1).v == [-2, -4, /**/ 0, -1]); 271 // *** BUG *** 272 //auto m2 = Matrix!(bool, 2)(true, true, /**/ false, false); 273 //assert((!m2).v == [false, false, /**/ true, true]); 274 } 275 /// 276 U opCast(U)() const if (isMatrixInstance!U && U.rowCount == rowCount && U.columnCount == columnCount) { 277 U ret = U.identity(); 278 enum min_r = R < U.rowCount ? R : U.rowCount; 279 enum min_c = C < U.columnCount ? C : U.columnCount; 280 static foreach (i; 0..min_r) { 281 static foreach (j; 0..min_c) { 282 ret.c[i][j] = cast(U.type)(c[i][j]); 283 } 284 } 285 return ret; 286 } 287 /// 288 unittest { 289 auto m1 = Matrix2F(3.2f, 4.5f, /**/ 3.8f, -7.2f); 290 Matrix2I m2 = cast(Matrix2I)m1; 291 assert(m2.v == [3, 4, /**/ 3, -7]); 292 } 293 /// 294 bool opEquals(U)(U rhs) const { 295 static if (is(U : Matrix)) { 296 static foreach (i; 0..R * C) { 297 if (v[i] != rhs.v[i]) { 298 return false; 299 } 300 } 301 } else static if (is(U : T)) { 302 static foreach (i; 0..R * C) { 303 if (v[i] != rhs) { 304 return false; 305 } 306 } 307 } else { 308 static assert(0, "Cannot compare a variable of type " ~ 309 U.stringof ~ " with a variable of type " ~ Matrix.stringof 310 ); 311 } 312 return true; 313 } 314 /// 315 unittest { 316 const m1 = Matrix2I(6, 3, /**/ -1, 0); 317 const m2 = Matrix2I(6, 3, /**/ -1, 0); 318 const m3 = Matrix2I(1, 4, /**/ -1, 7); 319 assert(m1 == m2); 320 assert(m1 != m3); 321 const m4 = Matrix2I(3, 3, /**/ 3, 3); 322 assert(m4 == 3); 323 assert(m4 != -3); 324 } 325 /// Returns a pointer to content. 326 inout(T)* ptr() inout { 327 return v.ptr; 328 } 329 static if (isFloatingPoint!T && R == 2 && C == 2) { 330 /// Returns inverse of matrix 2x2. 331 Matrix inverse() const { 332 T invDet = 1 / (c[0][0] * c[1][1] - c[0][1] * c[1][0]); 333 return Matrix(c[1][1] * invDet, -c[0][1] * invDet, -c[1][0] * invDet, c[0][0] * invDet); 334 } 335 /// 336 unittest { 337 import std.math : approxEqual; 338 auto m1 = Matrix2D(2.0, 3.0, /**/ -1.0, 5.0); 339 auto m2 = m1.inverse(); 340 assert(m2.v[0].approxEqual(0.384615)); 341 assert(m2.v[1].approxEqual(-0.230769)); 342 assert(m2.v[2].approxEqual(0.0769231)); 343 assert(m2.v[3].approxEqual(0.153846)); 344 } 345 } 346 static if (isFloatingPoint!T && R == 3 && C == 3) { 347 /// Returns inverse of matrix 3x3. 348 Matrix inverse() const { 349 const T det = c[0][0] * (c[1][1] * c[2][2] - c[2][1] * c[1][2]) 350 - c[0][1] * (c[1][0] * c[2][2] - c[1][2] * c[2][0]) 351 + c[0][2] * (c[1][0] * c[2][1] - c[1][1] * c[2][0]); 352 const T invDet = 1 / det; 353 Matrix ret = void; 354 ret.c[0][0] = (c[1][1] * c[2][2] - c[2][1] * c[1][2]) * invDet; 355 ret.c[0][1] = -(c[0][1] * c[2][2] - c[0][2] * c[2][1]) * invDet; 356 ret.c[0][2] = (c[0][1] * c[1][2] - c[0][2] * c[1][1]) * invDet; 357 ret.c[1][0] = -(c[1][0] * c[2][2] - c[1][2] * c[2][0]) * invDet; 358 ret.c[1][1] = (c[0][0] * c[2][2] - c[0][2] * c[2][0]) * invDet; 359 ret.c[1][2] = -(c[0][0] * c[1][2] - c[1][0] * c[0][2]) * invDet; 360 ret.c[2][0] = (c[1][0] * c[2][1] - c[2][0] * c[1][1]) * invDet; 361 ret.c[2][1] = -(c[0][0] * c[2][1] - c[2][0] * c[0][1]) * invDet; 362 ret.c[2][2] = (c[0][0] * c[1][1] - c[1][0] * c[0][1]) * invDet; 363 return ret; 364 } 365 /// 366 unittest { 367 } 368 } 369 static if (isFloatingPoint!T && R == 4 && C == 4) { 370 /// Returns inverse of matrix 4x4. 371 Matrix inverse() const { 372 const T det2_01_01 = c[0][0] * c[1][1] - c[0][1] * c[1][0]; 373 const T det2_01_02 = c[0][0] * c[1][2] - c[0][2] * c[1][0]; 374 const T det2_01_03 = c[0][0] * c[1][3] - c[0][3] * c[1][0]; 375 const T det2_01_12 = c[0][1] * c[1][2] - c[0][2] * c[1][1]; 376 const T det2_01_13 = c[0][1] * c[1][3] - c[0][3] * c[1][1]; 377 const T det2_01_23 = c[0][2] * c[1][3] - c[0][3] * c[1][2]; 378 const T det3_201_012 = c[2][0] * det2_01_12 - c[2][1] * det2_01_02 + c[2][2] * det2_01_01; 379 const T det3_201_013 = c[2][0] * det2_01_13 - c[2][1] * det2_01_03 + c[2][3] * det2_01_01; 380 const T det3_201_023 = c[2][0] * det2_01_23 - c[2][2] * det2_01_03 + c[2][3] * det2_01_02; 381 const T det3_201_123 = c[2][1] * det2_01_23 - c[2][2] * det2_01_13 + c[2][3] * det2_01_12; 382 const T det = - det3_201_123 * c[3][0] + det3_201_023 * c[3][1] - det3_201_013 * c[3][2] + det3_201_012 * c[3][3]; 383 const T invDet = 1 / det; 384 const T det2_03_01 = c[0][0] * c[3][1] - c[0][1] * c[3][0]; 385 const T det2_03_02 = c[0][0] * c[3][2] - c[0][2] * c[3][0]; 386 const T det2_03_03 = c[0][0] * c[3][3] - c[0][3] * c[3][0]; 387 const T det2_03_12 = c[0][1] * c[3][2] - c[0][2] * c[3][1]; 388 const T det2_03_13 = c[0][1] * c[3][3] - c[0][3] * c[3][1]; 389 const T det2_03_23 = c[0][2] * c[3][3] - c[0][3] * c[3][2]; 390 const T det2_13_01 = c[1][0] * c[3][1] - c[1][1] * c[3][0]; 391 const T det2_13_02 = c[1][0] * c[3][2] - c[1][2] * c[3][0]; 392 const T det2_13_03 = c[1][0] * c[3][3] - c[1][3] * c[3][0]; 393 const T det2_13_12 = c[1][1] * c[3][2] - c[1][2] * c[3][1]; 394 const T det2_13_13 = c[1][1] * c[3][3] - c[1][3] * c[3][1]; 395 const T det2_13_23 = c[1][2] * c[3][3] - c[1][3] * c[3][2]; 396 const T det3_203_012 = c[2][0] * det2_03_12 - c[2][1] * det2_03_02 + c[2][2] * det2_03_01; 397 const T det3_203_013 = c[2][0] * det2_03_13 - c[2][1] * det2_03_03 + c[2][3] * det2_03_01; 398 const T det3_203_023 = c[2][0] * det2_03_23 - c[2][2] * det2_03_03 + c[2][3] * det2_03_02; 399 const T det3_203_123 = c[2][1] * det2_03_23 - c[2][2] * det2_03_13 + c[2][3] * det2_03_12; 400 const T det3_213_012 = c[2][0] * det2_13_12 - c[2][1] * det2_13_02 + c[2][2] * det2_13_01; 401 const T det3_213_013 = c[2][0] * det2_13_13 - c[2][1] * det2_13_03 + c[2][3] * det2_13_01; 402 const T det3_213_023 = c[2][0] * det2_13_23 - c[2][2] * det2_13_03 + c[2][3] * det2_13_02; 403 const T det3_213_123 = c[2][1] * det2_13_23 - c[2][2] * det2_13_13 + c[2][3] * det2_13_12; 404 const T det3_301_012 = c[3][0] * det2_01_12 - c[3][1] * det2_01_02 + c[3][2] * det2_01_01; 405 const T det3_301_013 = c[3][0] * det2_01_13 - c[3][1] * det2_01_03 + c[3][3] * det2_01_01; 406 const T det3_301_023 = c[3][0] * det2_01_23 - c[3][2] * det2_01_03 + c[3][3] * det2_01_02; 407 const T det3_301_123 = c[3][1] * det2_01_23 - c[3][2] * det2_01_13 + c[3][3] * det2_01_12; 408 Matrix ret = void; 409 ret.c[0][0] = - det3_213_123 * invDet; 410 ret.c[1][0] = + det3_213_023 * invDet; 411 ret.c[2][0] = - det3_213_013 * invDet; 412 ret.c[3][0] = + det3_213_012 * invDet; 413 ret.c[0][1] = + det3_203_123 * invDet; 414 ret.c[1][1] = - det3_203_023 * invDet; 415 ret.c[2][1] = + det3_203_013 * invDet; 416 ret.c[3][1] = - det3_203_012 * invDet; 417 ret.c[0][2] = + det3_301_123 * invDet; 418 ret.c[1][2] = - det3_301_023 * invDet; 419 ret.c[2][2] = + det3_301_013 * invDet; 420 ret.c[3][2] = - det3_301_012 * invDet; 421 ret.c[0][3] = - det3_201_123 * invDet; 422 ret.c[1][3] = + det3_201_023 * invDet; 423 ret.c[2][3] = - det3_201_013 * invDet; 424 ret.c[3][3] = + det3_201_012 * invDet; 425 return ret; 426 } 427 /// 428 unittest { 429 } 430 } 431 /// Returns transposed matrix. 432 Matrix!(T, C, R) transposed() const { 433 Matrix!(T, C, R) ret = void; 434 static foreach (i; 0..C) { 435 static foreach (j; 0..R) { 436 ret.c[i][j] = c[j][i]; 437 } 438 } 439 return ret; 440 } 441 /// 442 unittest { 443 auto m1 = Matrix2I(4, 5, /**/ -1, 0); 444 assert(m1.transposed().v == [4, -1, /**/ 5, 0]); 445 } 446 static if (isSquare && R > 2) { 447 /// Makes a diagonal matrix from a vector. 448 static Matrix diag(Vector!(T, R) v) { 449 Matrix ret = void; 450 static foreach (i; 0..R) { 451 static foreach (j; 0..C) { 452 ret.c[i][j] = (i == j) ? v.v[i] : 0; 453 } 454 } 455 return ret; 456 } 457 /// 458 unittest { 459 const v1 = Vector!(int, 3)(4, 5, -1); 460 const m1 = Matrix!(int, 3).diag(v1); 461 assert(m1.v == [4, 0, 0, /**/ 0, 5, 0, /**/ 0, 0, -1]); 462 } 463 /// 464 unittest { 465 } 466 /// Make a translation matrix. 467 static Matrix translation(Vector!(T, R-1) v) { 468 Matrix ret = identity(); 469 static foreach (i; 0..R - 1) { 470 ret.c[i][C - 1] += v.v[i]; 471 } 472 return ret; 473 } 474 /// 475 unittest { 476 } 477 /// Make a scaling matrix. 478 static Matrix scaling(Vector!(T, R-1) v) { 479 Matrix ret = identity(); 480 static foreach (i; 0..R - 1) { 481 ret.c[i][i] = v.v[i]; 482 } 483 return ret; 484 } 485 /// 486 unittest { 487 } 488 } 489 static if (isSquare && (R == 3 || R == 4) && isFloatingPoint!T) { 490 /// 491 static Matrix rotateAxis(int i, int j)(T angle) { 492 import liberty.math.functions : sin, cos; 493 Matrix ret = identity(); 494 const T cosa = cos(angle); 495 const T sina = sin(angle); 496 ret.c[i][i] = cosa; 497 ret.c[i][j] = -sina; 498 ret.c[j][i] = sina; 499 ret.c[j][j] = cosa; 500 return ret; 501 } 502 /// 503 unittest { 504 } 505 /// Returns rotation matrix along axis X. 506 alias rotateXAxix = rotateAxis!(1, 2); 507 /// Returns rotation matrix along axis Y. 508 alias rotateYAxis = rotateAxis!(2, 0); 509 /// Returns rotation matrix along axis Z. 510 alias rotateZAxis = rotateAxis!(0, 1); 511 /// In-place rotation by (v, 1) 512 void rotate(T angle, Vector!(T, 3) axis) { 513 this = rotation(angle, axis, this); 514 } 515 /// 516 unittest { 517 } 518 /// 519 void rotateX(T angle) { 520 this = rotation(angle, Vector!(T, 3)(1, 0, 0), this); 521 } 522 /// 523 unittest { 524 } 525 /// 526 void rotateY(T angle) { 527 this = rotation(angle, Vector!(T, 3)(0, 1, 0), this); 528 } 529 /// 530 unittest { 531 } 532 /// 533 void rotateZ(T angle) { 534 this = rotation(angle, Vector!(T, 3)(0, 0, 1), this); 535 } 536 /// 537 unittest { 538 } 539 /// 540 static Matrix rotation(T angle, Vector!(T, 3) axis, Matrix m = identity()) { 541 import liberty.math.functions : sin, cos; 542 Matrix ret = m; 543 const T c = cos(angle); 544 const oneMinusC = 1 - c; 545 const T s = sin(angle); 546 axis = axis.normalized(); 547 const T x = axis.x, 548 y = axis.y, 549 z = axis.z; 550 const T xy = x * y, 551 yz = y * z, 552 xz = x * z; 553 ret.c[0][0] = x * x * oneMinusC + c; 554 ret.c[0][1] = x * y * oneMinusC - z * s; 555 ret.c[0][2] = x * z * oneMinusC + y * s; 556 ret.c[1][0] = y * x * oneMinusC + z * s; 557 ret.c[1][1] = y * y * oneMinusC + c; 558 ret.c[1][2] = y * z * oneMinusC - x * s; 559 ret.c[2][0] = z * x * oneMinusC - y * s; 560 ret.c[2][1] = z * y * oneMinusC + x * s; 561 ret.c[2][2] = z * z * oneMinusC + c; 562 return ret; 563 } 564 /// 565 unittest { 566 } 567 } 568 static if (isSquare && R == 4 && isFloatingPoint!T) { 569 /** 570 * Transform a Vector4 by a Matrix4. 571 **/ 572 static Vector4!T transformation(Matrix lhs, Vector4!T rhs) { 573 const T x = lhs.c[0][0] * rhs.x + lhs.c[1][0] * rhs.y + lhs.c[2][0] * rhs.z + lhs.c[3][0] * rhs.w; 574 const T y = lhs.c[0][1] * rhs.x + lhs.c[1][1] * rhs.y + lhs.c[2][1] * rhs.z + lhs.c[3][1] * rhs.w; 575 const T z = lhs.c[0][2] * rhs.x + lhs.c[1][2] * rhs.y + lhs.c[2][2] * rhs.z + lhs.c[3][2] * rhs.w; 576 const T w = lhs.c[0][3] * rhs.x + lhs.c[1][3] * rhs.y + lhs.c[2][3] * rhs.z + lhs.c[3][3] * rhs.w; 577 578 return Vector4!T(x, y, z, w); 579 } 580 /// 581 //static Matrix orthographic(T left, T right, T bottom, T top) { 582 // return Matrix( 583 // (2) / (right - left), 0, 0, 0, 584 // 0, (2) / (top - bottom), 0, 0, 585 // 0, 0, -1, 0, 586 // -(right + left) / (right - left), -(top + bottom) / (top - bottom), 0, 0 587 // ); 588 //} 589 590 /// 591 unittest { 592 } 593 /// Returns perspective projection. 594 static Matrix perspective(T FOVInRadians, T width, T height, T zNear, T zFar) { 595 import liberty.math.functions : tan; 596 T f = 1 / tan(FOVInRadians / 2); 597 T d = 1 / (zNear - zFar); 598 return Matrix( 599 f / (width / height), 0, 0, 0, 600 0, f, 0, 0, 601 0, 0, (zFar + zNear) * d, 2 * d * zFar * zNear, 602 0, 0, -1, 0 603 ); 604 } 605 /// 606 unittest { 607 } 608 /// Returns lookAt projection. 609 static Matrix lookAt(Vector!(T, 3) eye, Vector!(T, 3) target, Vector!(T, 3) up) { 610 import liberty.math.vector : dot, cross; 611 Vector!(T, 3) Z = (eye - target).normalized(); 612 Vector!(T, 3) X = cross(-up, Z).normalized(); 613 Vector!(T, 3) Y = cross(Z, -X); 614 return Matrix(-X.x, -X.y, -X.z, dot(X, eye), Y.x, Y.y, Y.z, -dot(Y, eye), Z.x, Z.y, Z.z, -dot(Z, eye), 0, 0, 0, 1); 615 } 616 /// Returns camera projection 617 static Matrix camera(Vector!(T, 3) forward, Vector!(T, 3) up) { 618 Vector!(T, 3) f = forward; 619 f.normalize(); 620 621 Vector!(T, 3) r = up; 622 r.normalize(); 623 r = r.cross(f); 624 625 Vector!(T, 3) u = f.cross(r); 626 627 return Matrix( 628 r.x, r.y, r.z, 0.0f, 629 u.x, u.y, u.z, 0.0f, 630 f.x, f.y, f.z, 0.0f, 631 0.0f, 0.0f, 0.0f, 1.0f 632 ); 633 } 634 /// 635 unittest { 636 } 637 } 638 } 639 /// 640 template Matrix2(T) { 641 alias Matrix2 = Matrix!(T, 2); 642 } 643 /// 644 template Matrix3(T) { 645 alias Matrix3 = Matrix!(T, 3); 646 } 647 /// 648 template Matrix4(T) { 649 alias Matrix4 = Matrix!(T, 4); 650 } 651 /// 652 template Matrix2x3(T) { 653 alias Matrix2x3 = Matrix!(T, 2, 3); 654 } 655 /// 656 template Matrix2x4(T) { 657 alias Matrix2x4 = Matrix!(T, 2, 4); 658 } 659 /// 660 template Matrix3x2(T) { 661 alias Matrix3x2 = Matrix!(T, 3, 2); 662 } 663 /// 664 template Matrix3x4(T) { 665 alias Matrix3x4 = Matrix!(T, 3, 4); 666 } 667 /// 668 template Matrix4x2(T) { 669 alias Matrix4x2 = Matrix!(T, 4, 2); 670 } 671 /// 672 template Matrix4x3(T) { 673 alias Matrix4x3 = Matrix!(T, 4, 3); 674 } 675 /// 676 alias Matrix2I = Matrix2!int; 677 /// 678 alias Matrix3I = Matrix3!int; 679 /// 680 alias Matrix4I = Matrix4!int; 681 /// 682 alias Matrix2F = Matrix2!float; 683 /// 684 alias Matrix3F = Matrix3!float; 685 /// 686 alias Matrix4F = Matrix4!float; 687 /// 688 alias Matrix2D = Matrix2!double; 689 /// 690 alias Matrix3D = Matrix3!double; 691 /// 692 alias Matrix4D = Matrix4!double; 693 /// 694 alias Matrix2x3I = Matrix2x3!int; 695 /// 696 alias Matrix2x4I = Matrix2x4!int; 697 /// 698 alias Matrix3x2I = Matrix3x2!int; 699 /// 700 alias Matrix2x3F = Matrix2x3!float; 701 /// 702 alias Matrix2x4F = Matrix2x4!float; 703 /// 704 alias Matrix3x2F = Matrix3x2!float; 705 /// 706 alias Matrix2x3D = Matrix2x3!double; 707 /// 708 alias Matrix2x4D = Matrix2x4!double; 709 /// 710 alias Matrix3x2D = Matrix3x2!double; 711 /// 712 alias Matrix3x4I = Matrix3x4!int; 713 /// 714 alias Matrix4x2I = Matrix4x2!int; 715 /// 716 alias Matrix4x3I = Matrix4x3!int; 717 /// 718 alias Matrix3x4F = Matrix3x4!float; 719 /// 720 alias Matrix4x2F = Matrix4x2!float; 721 /// 722 alias Matrix4x3F = Matrix4x3!float; 723 /// 724 alias Matrix3x4D = Matrix3x4!double; 725 /// 726 alias Matrix4x2D = Matrix4x2!double; 727 /// 728 alias Matrix4x3D = Matrix4x3!double; 729 /// 730 final class MatrixStack(int R, T) if (R == 3 || R == 4) { 731 private { 732 size_t _top; 733 size_t _depth; 734 MatrixType* _matrices; 735 MatrixType* _invMatrices; 736 } 737 /// 738 alias MatrixType = Matrix!(T, R, R); 739 /// Creates a matrix stack. 740 this(size_t depth = 32) 741 in { 742 assert(depth > 0); 743 } do { 744 import core.stdc.stdlib : malloc; 745 size_t memory_needed = MatrixType.sizeof * depth * 2; 746 const void* data = malloc(memory_needed * 2); 747 _matrices = cast(MatrixType*)data; 748 _invMatrices = cast(MatrixType*)(data + memory_needed); 749 _top = 0; 750 _depth = depth; 751 loadIdentity(); 752 } 753 /// Relases the matrix stack memory. 754 ~this() { 755 if (_matrices !is null) { 756 import core.stdc.stdlib : free; 757 free(_matrices); 758 _matrices = null; 759 } 760 } 761 /// 762 void loadIdentity() { 763 _matrices[_top] = MatrixType.identity(); 764 _invMatrices[_top] = MatrixType.identity(); 765 } 766 /// 767 void push() { 768 if(_top + 1 >= _depth) { 769 assert(false, "Matrix stack is full!"); 770 } 771 _matrices[_top + 1] = _matrices[_top]; 772 _invMatrices[_top + 1] = _invMatrices[_top]; 773 _top++; 774 } 775 /// Replacement for $(D glPopMatrix). 776 void pop() { 777 if (_top <= 0) { 778 assert(false, "Matrix stack is empty!"); 779 } 780 _top--; 781 } 782 /// 783 MatrixType top() const { 784 return _matrices[_top]; 785 } 786 /// 787 MatrixType invTop() const { 788 return _invMatrices[_top]; 789 } 790 /// 791 void top(MatrixType m) { 792 _matrices[_top] = m; 793 _invMatrices[_top] = m.inverse(); 794 } 795 /// 796 void mult(MatrixType m) { 797 mult(m, m.inverse()); 798 } 799 /// 800 void mult(MatrixType m, MatrixType invM) { 801 _matrices[_top] = _matrices[_top] * m; 802 _invMatrices[_top] = invM *_invMatrices[_top]; 803 } 804 /// 805 void translate(Vector!(T, R-1) v) { 806 mult(MatrixType.translation(v), MatrixType.translation(-v)); 807 } 808 /// 809 void scale(Vector!(T, R-1) v) { 810 mult(MatrixType.scaling(v), MatrixType.scaling(1 / v)); 811 } 812 static if (R == 4) { 813 /// 814 void rotate(T angle, Vector!(T, 3) axis) { 815 MatrixType rot = MatrixType.rotation(angle, axis); 816 mult(rot, rot.transposed()); 817 } 818 /// 819 void perspective(T FOVInRadians, T width, T height, T zNear, T zFar) { 820 mult(MatrixType.perspective(FOVInRadians, width, height, zNear, zFar)); 821 } 822 /// 823 //void orthographic(T left, T right, T bottom, T top, T near, T far) { 824 // mult(MatrixType.orthographic(left, right, bottom, top, near, far)); 825 //} 826 /// 827 void lookAt(Vector!(T, 3) eye, Vector!(T, 3) target, Vector!(T, 3) up) { 828 mult(MatrixType.lookAt(eye, target, up)); 829 } 830 } 831 } 832 /// 833 unittest { 834 auto m = new MatrixStack!(4, double)(); 835 scope(exit) destroy(m); 836 m.loadIdentity(); 837 m.push(); 838 m.pop(); 839 m.translate(Vector!(double, 3)(2.4, 3.3, 7.5)); 840 m.scale(Vector!(double, 3)(0.7)); 841 auto t = new MatrixStack!(3, float)(); 842 scope(exit) destroy(t); 843 t.loadIdentity(); 844 t.push(); 845 t.pop(); 846 t.translate(Vector!(float, 2)(1, -8)); 847 t.scale(Vector!(float, 2)(0.2)); 848 }