1 /** 2 3x3 matrix datatype. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.core.basis; 14 15 import godot.core.defs; 16 import godot.core.vector3; 17 import godot.core.quat; 18 19 import std.math; 20 import std.algorithm.mutation : swap; 21 22 /** 23 3x3 matrix used for 3D rotation and scale. Contains 3 vector fields x, y, and z as its columns, which can be interpreted as the local basis vectors of a transformation. Can also be accessed as array of 3D vectors. These vectors are orthogonal to each other, but are not necessarily normalized. Almost always used as orthogonal basis for a $(D Transform). 24 25 For such use, it is composed of a scaling and a rotation matrix, in that order (M = R.S). 26 */ 27 struct Basis 28 { 29 @nogc nothrow: 30 31 Vector3[3] elements = 32 [ 33 Vector3(1.0,0.0,0.0), 34 Vector3(0.0,1.0,0.0), 35 Vector3(0.0,0.0,1.0), 36 ]; 37 38 this(in Vector3 row0, in Vector3 row1, in Vector3 row2) 39 { 40 elements[0]=row0; 41 elements[1]=row1; 42 elements[2]=row2; 43 } 44 45 this(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) 46 { 47 set(xx, xy, xz, yx, yy, yz, zx, zy, zz); 48 } 49 50 const(Vector3) opIndex(int axis) const 51 { 52 return elements[axis]; 53 } 54 ref Vector3 opIndex(int axis) return 55 { 56 return elements[axis]; 57 } 58 59 private pragma(inline, true) 60 real_t cofac(int row1, int col1, int row2, int col2) const 61 { 62 return (elements[row1][col1] * elements[row2][col2] - 63 elements[row1][col2] * elements[row2][col1]); 64 } 65 66 void invert() 67 { 68 real_t[3] co=[ 69 cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1) 70 ]; 71 real_t det = elements[0][0] * co[0] + 72 elements[0][1] * co[1] + 73 elements[0][2] * co[2]; 74 75 /// ERR_FAIL_COND(det != 0); 76 /// TODO: implement errors; D assert/exceptions won't work! 77 78 real_t s = 1.0/det; 79 80 set(co[0]*s, cofac(0, 2, 2, 1) * s, cofac(0, 1, 1, 2) * s, 81 co[1]*s, cofac(0, 0, 2, 2) * s, cofac(0, 2, 1, 0) * s, 82 co[2]*s, cofac(0, 1, 2, 0) * s, cofac(0, 0, 1, 1) * s ); 83 } 84 85 bool isEqualApprox(in Basis a, in Basis b) const 86 { 87 for (int i=0;i<3;i++) { 88 for (int j=0;j<3;j++) { 89 if ((fabs(a.elements[i][j]-b.elements[i][j]) < CMP_EPSILON) == false) 90 return false; 91 } 92 } 93 94 return true; 95 } 96 97 98 bool isOrthogonal() const 99 { 100 Basis id; 101 Basis m = (this)*transposed(); 102 103 return isEqualApprox(id,m); 104 } 105 106 bool isRotation() const 107 { 108 return fabs(determinant()-1) < CMP_EPSILON && isOrthogonal(); 109 } 110 111 void transpose() 112 { 113 swap(elements[0][1],elements[1][0]); 114 swap(elements[0][2],elements[2][0]); 115 swap(elements[1][2],elements[2][1]); 116 } 117 118 Basis inverse() const 119 { 120 Basis b = this; 121 b.invert(); 122 return b; 123 } 124 125 Basis transposed() const 126 { 127 Basis b = this; 128 b.transpose(); 129 return b; 130 } 131 132 real_t determinant() const 133 { 134 return elements[0][0]*(elements[1][1]*elements[2][2] - elements[2][1]*elements[1][2]) - 135 elements[1][0]*(elements[0][1]*elements[2][2] - elements[2][1]*elements[0][2]) + 136 elements[2][0]*(elements[0][1]*elements[1][2] - elements[1][1]*elements[0][2]); 137 } 138 139 Vector3 getAxis(int p_axis) const 140 { 141 // get actual basis axis (elements is transposed for performance) 142 return Vector3( elements[0][p_axis], elements[1][p_axis], elements[2][p_axis] ); 143 } 144 void setAxis(int p_axis, in Vector3 p_value) 145 { 146 // get actual basis axis (elements is transposed for performance) 147 elements[0][p_axis]=p_value.x; 148 elements[1][p_axis]=p_value.y; 149 elements[2][p_axis]=p_value.z; 150 } 151 152 void rotate(in Vector3 p_axis, real_t p_phi) 153 { 154 this = rotated(p_axis, p_phi); 155 } 156 157 Basis rotated(in Vector3 p_axis, real_t p_phi) const 158 { 159 return Basis(p_axis, p_phi) * (this); 160 } 161 162 void scale( in Vector3 p_scale ) 163 { 164 elements[0][0]*=p_scale.x; 165 elements[0][1]*=p_scale.x; 166 elements[0][2]*=p_scale.x; 167 elements[1][0]*=p_scale.y; 168 elements[1][1]*=p_scale.y; 169 elements[1][2]*=p_scale.y; 170 elements[2][0]*=p_scale.z; 171 elements[2][1]*=p_scale.z; 172 elements[2][2]*=p_scale.z; 173 } 174 175 Basis scaled( in Vector3 p_scale ) const 176 { 177 Basis b = this; 178 b.scale(p_scale); 179 return b; 180 } 181 182 Vector3 getScale() const 183 { 184 // We are assuming M = R.S, and performing a polar decomposition to extract R and S. 185 // FIXME: We eventually need a proper polar decomposition. 186 // As a cheap workaround until then, to ensure that R is a proper rotation matrix with determinant +1 187 // (such that it can be represented by a Quat or Euler angles), we absorb the sign flip into the scaling matrix. 188 // As such, it works in conjuction with get_rotation(). 189 real_t det_sign = determinant() > 0 ? 1 : -1; 190 return det_sign*Vector3( 191 Vector3(elements[0][0],elements[1][0],elements[2][0]).length, 192 Vector3(elements[0][1],elements[1][1],elements[2][1]).length, 193 Vector3(elements[0][2],elements[1][2],elements[2][2]).length 194 ); 195 } 196 197 Vector3 getEuler() const 198 { 199 // Euler angles in XYZ convention. 200 // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix 201 // 202 // rot = cy*cz -cy*sz sy 203 // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx 204 // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy 205 206 Vector3 euler; 207 208 if (isRotation() == false) 209 return euler; 210 211 euler.y = asin(elements[0][2]); 212 if( euler.y < PI*0.5) 213 { 214 if( euler.y > -PI*0.5) 215 { 216 euler.x = atan2(-elements[1][2],elements[2][2]); 217 euler.z = atan2(-elements[0][1],elements[0][0]); 218 } 219 else 220 { 221 real_t r = atan2(elements[1][0],elements[1][1]); 222 euler.z = 0.0; 223 euler.x = euler.z - r; 224 } 225 } 226 else 227 { 228 real_t r = atan2(elements[0][1],elements[1][1]); 229 euler.z = 0; 230 euler.x = r - euler.z; 231 } 232 return euler; 233 } 234 235 void setEuler(in Vector3 p_euler) 236 { 237 real_t c, s; 238 239 c = cos(p_euler.x); 240 s = sin(p_euler.x); 241 Basis xmat = Basis(1.0,0.0,0.0,0.0,c,-s,0.0,s,c); 242 243 c = cos(p_euler.y); 244 s = sin(p_euler.y); 245 Basis ymat = Basis(c,0.0,s,0.0,1.0,0.0,-s,0.0,c); 246 247 c = cos(p_euler.z); 248 s = sin(p_euler.z); 249 Basis zmat = Basis(c,-s,0.0,s,c,0.0,0.0,0.0,1.0); 250 251 //optimizer will optimize away all this anyway 252 this = xmat*(ymat*zmat); 253 } 254 255 // transposed dot products 256 real_t tdotx(in Vector3 v) const 257 { 258 return elements[0][0] * v[0] + elements[1][0] * v[1] + elements[2][0] * v[2]; 259 } 260 real_t tdoty(in Vector3 v) const 261 { 262 return elements[0][1] * v[0] + elements[1][1] * v[1] + elements[2][1] * v[2]; 263 } 264 real_t tdotz(in Vector3 v) const 265 { 266 return elements[0][2] * v[0] + elements[1][2] * v[1] + elements[2][2] * v[2]; 267 } 268 269 int opCmp(in Basis other) const 270 { 271 for (int i=0;i<3;i++) 272 { 273 for (int j=0;j<3;j++) 274 { 275 if (elements[i][j] != other.elements[i][j]) 276 return (elements[i][j] > other.elements[i][j])?1:-1; 277 } 278 } 279 return 0; 280 } 281 282 Vector3 xform(in Vector3 p_vector) const 283 { 284 return Vector3( 285 elements[0].dot(p_vector), 286 elements[1].dot(p_vector), 287 elements[2].dot(p_vector) 288 ); 289 } 290 291 Vector3 xformInv(in Vector3 p_vector) const 292 { 293 return Vector3( 294 (elements[0][0]*p_vector.x ) + ( elements[1][0]*p_vector.y ) + ( elements[2][0]*p_vector.z ), 295 (elements[0][1]*p_vector.x ) + ( elements[1][1]*p_vector.y ) + ( elements[2][1]*p_vector.z ), 296 (elements[0][2]*p_vector.x ) + ( elements[1][2]*p_vector.y ) + ( elements[2][2]*p_vector.z ) 297 ); 298 } 299 void opOpAssign(string op : "*")(in Basis p_matrix) 300 { 301 set( 302 p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), 303 p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), 304 p_matrix.tdotx(elements[2]), p_matrix.tdoty(elements[2]), p_matrix.tdotz(elements[2])); 305 306 } 307 308 Basis opBinary(string op : "*")(in Basis p_matrix) const 309 { 310 return Basis( 311 p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), 312 p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), 313 p_matrix.tdotx(elements[2]), p_matrix.tdoty(elements[2]), p_matrix.tdotz(elements[2]) ); 314 315 } 316 317 318 void opOpAssign(string op : "+")(in Basis p_matrix) 319 { 320 elements[0] += p_matrix.elements[0]; 321 elements[1] += p_matrix.elements[1]; 322 elements[2] += p_matrix.elements[2]; 323 } 324 325 Basis opBinary(string op : "+")(in Basis p_matrix) const 326 { 327 Basis ret = this; 328 ret += p_matrix; 329 return ret; 330 } 331 332 void opOpAssign(string op : "-")(in Basis p_matrix) 333 { 334 elements[0] -= p_matrix.elements[0]; 335 elements[1] -= p_matrix.elements[1]; 336 elements[2] -= p_matrix.elements[2]; 337 } 338 339 Basis opBinary(string op : "-")(in Basis p_matrix) const 340 { 341 Basis ret = this; 342 ret -= p_matrix; 343 return ret; 344 } 345 346 void opOpAssign(string op : "*")(real_t p_val) { 347 348 elements[0]*=p_val; 349 elements[1]*=p_val; 350 elements[2]*=p_val; 351 } 352 353 Basis opBinary(string op : "*")(real_t p_val) const { 354 355 Basis ret = this; 356 ret *= p_val; 357 return ret; 358 } 359 360 361 /++String opCast(T : String)() const 362 { 363 String s; 364 // @Todo 365 return s; 366 }+/ 367 368 /* create / set */ 369 370 371 void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) 372 { 373 elements[0][0]=xx; 374 elements[0][1]=xy; 375 elements[0][2]=xz; 376 elements[1][0]=yx; 377 elements[1][1]=yy; 378 elements[1][2]=yz; 379 elements[2][0]=zx; 380 elements[2][1]=zy; 381 elements[2][2]=zz; 382 } 383 Vector3 getColumn(int i) const 384 { 385 return Vector3(elements[0][i],elements[1][i],elements[2][i]); 386 } 387 388 Vector3 getRow(int i) const 389 { 390 return Vector3(elements[i][0],elements[i][1],elements[i][2]); 391 } 392 Vector3 getMainDiagonal() const 393 { 394 return Vector3(elements[0][0],elements[1][1],elements[2][2]); 395 } 396 397 void setRow(int i, in Vector3 p_row) 398 { 399 elements[i][0]=p_row.x; 400 elements[i][1]=p_row.y; 401 elements[i][2]=p_row.z; 402 } 403 404 Basis transposeXform(in Basis m) const 405 { 406 return Basis( 407 elements[0].x * m[0].x + elements[1].x * m[1].x + elements[2].x * m[2].x, 408 elements[0].x * m[0].y + elements[1].x * m[1].y + elements[2].x * m[2].y, 409 elements[0].x * m[0].z + elements[1].x * m[1].z + elements[2].x * m[2].z, 410 elements[0].y * m[0].x + elements[1].y * m[1].x + elements[2].y * m[2].x, 411 elements[0].y * m[0].y + elements[1].y * m[1].y + elements[2].y * m[2].y, 412 elements[0].y * m[0].z + elements[1].y * m[1].z + elements[2].y * m[2].z, 413 elements[0].z * m[0].x + elements[1].z * m[1].x + elements[2].z * m[2].x, 414 elements[0].z * m[0].y + elements[1].z * m[1].y + elements[2].z * m[2].y, 415 elements[0].z * m[0].z + elements[1].z * m[1].z + elements[2].z * m[2].z); 416 } 417 418 void orthonormalize() 419 { 420 ///ERR_FAIL_COND(determinant() != 0); 421 422 // Gram-Schmidt Process 423 424 Vector3 x=getAxis(0); 425 Vector3 y=getAxis(1); 426 Vector3 z=getAxis(2); 427 428 x.normalize(); 429 y = (y-x*(x.dot(y))); 430 y.normalize(); 431 z = (z-x*(x.dot(z))-y*(y.dot(z))); 432 z.normalize(); 433 434 setAxis(0,x); 435 setAxis(1,y); 436 setAxis(2,z); 437 } 438 439 Basis orthonormalized() const 440 { 441 Basis b = this; 442 b.orthonormalize(); 443 return b; 444 } 445 446 bool isSymmetric() const 447 { 448 if (fabs(elements[0][1] - elements[1][0]) > CMP_EPSILON) 449 return false; 450 if (fabs(elements[0][2] - elements[2][0]) > CMP_EPSILON) 451 return false; 452 if (fabs(elements[1][2] - elements[2][1]) > CMP_EPSILON) 453 return false; 454 455 return true; 456 } 457 458 Basis diagonalize() 459 { 460 // much copy paste, WOW 461 if (!isSymmetric()) 462 return Basis(); 463 464 const int ite_max = 1024; 465 466 real_t off_matrix_norm_2 = elements[0][1] * elements[0][1] + elements[0][2] * elements[0][2] + elements[1][2] * elements[1][2]; 467 468 int ite = 0; 469 Basis acc_rot; 470 while (off_matrix_norm_2 > CMP_EPSILON2 && ite++ < ite_max ) 471 { 472 real_t el01_2 = elements[0][1] * elements[0][1]; 473 real_t el02_2 = elements[0][2] * elements[0][2]; 474 real_t el12_2 = elements[1][2] * elements[1][2]; 475 // Find the pivot element 476 int i, j; 477 if (el01_2 > el02_2) { 478 if (el12_2 > el01_2) { 479 i = 1; 480 j = 2; 481 } else { 482 i = 0; 483 j = 1; 484 } 485 } else { 486 if (el12_2 > el02_2) { 487 i = 1; 488 j = 2; 489 } else { 490 i = 0; 491 j = 2; 492 } 493 } 494 495 // Compute the rotation angle 496 real_t angle; 497 if (fabs(elements[j][j] - elements[i][i]) < CMP_EPSILON) 498 { 499 angle = PI / 4; 500 } 501 else 502 { 503 angle = 0.5 * atan(2 * elements[i][j] / (elements[j][j] - elements[i][i])); 504 } 505 506 // Compute the rotation matrix 507 Basis rot; 508 rot.elements[i][i] = rot.elements[j][j] = cos(angle); 509 rot.elements[i][j] = - (rot.elements[j][i] = sin(angle)); 510 511 // Update the off matrix norm 512 off_matrix_norm_2 -= elements[i][j] * elements[i][j]; 513 514 // Apply the rotation 515 this = rot * this * rot.transposed(); 516 acc_rot = rot * acc_rot; 517 } 518 519 return acc_rot; 520 } 521 522 523 static immutable Basis[24] _ortho_bases = 524 [ 525 Basis(1, 0, 0, 0, 1, 0, 0, 0, 1), 526 Basis(0, -1, 0, 1, 0, 0, 0, 0, 1), 527 Basis(-1, 0, 0, 0, -1, 0, 0, 0, 1), 528 Basis(0, 1, 0, -1, 0, 0, 0, 0, 1), 529 Basis(1, 0, 0, 0, 0, -1, 0, 1, 0), 530 Basis(0, 0, 1, 1, 0, 0, 0, 1, 0), 531 Basis(-1, 0, 0, 0, 0, 1, 0, 1, 0), 532 Basis(0, 0, -1, -1, 0, 0, 0, 1, 0), 533 Basis(1, 0, 0, 0, -1, 0, 0, 0, -1), 534 Basis(0, 1, 0, 1, 0, 0, 0, 0, -1), 535 Basis(-1, 0, 0, 0, 1, 0, 0, 0, -1), 536 Basis(0, -1, 0, -1, 0, 0, 0, 0, -1), 537 Basis(1, 0, 0, 0, 0, 1, 0, -1, 0), 538 Basis(0, 0, -1, 1, 0, 0, 0, -1, 0), 539 Basis(-1, 0, 0, 0, 0, -1, 0, -1, 0), 540 Basis(0, 0, 1, -1, 0, 0, 0, -1, 0), 541 Basis(0, 0, 1, 0, 1, 0, -1, 0, 0), 542 Basis(0, -1, 0, 0, 0, 1, -1, 0, 0), 543 Basis(0, 0, -1, 0, -1, 0, -1, 0, 0), 544 Basis(0, 1, 0, 0, 0, -1, -1, 0, 0), 545 Basis(0, 0, 1, 0, -1, 0, 1, 0, 0), 546 Basis(0, 1, 0, 0, 0, 1, 1, 0, 0), 547 Basis(0, 0, -1, 0, 1, 0, 1, 0, 0), 548 Basis(0, -1, 0, 0, 0, -1, 1, 0, 0) 549 ]; 550 551 552 int getOrthogonalIndex() const 553 { 554 //could be sped up if i come up with a way 555 Basis orth=this; 556 for(int i=0;i<3;i++) { 557 for(int j=0;j<3;j++) { 558 559 real_t v = orth[i][j]; 560 if (v>0.5) 561 v=1.0; 562 else if (v<-0.5) 563 v=-1.0; 564 else 565 v=0; 566 567 orth[i][j]=v; 568 } 569 } 570 571 for(int i=0;i<24;i++) { 572 573 if (_ortho_bases[i]==orth) 574 return i; 575 576 577 } 578 579 return 0; 580 } 581 582 583 void setOrthogonalIndex(int p_index) 584 { 585 //there only exist 24 orthogonal bases in r3 586 ///ERR_FAIL_COND(p_index >= 24); 587 this=_ortho_bases[p_index]; 588 } 589 590 591 592 this(in Vector3 p_euler) 593 { 594 setEuler( p_euler ); 595 } 596 597 this(in Quat p_quat) 598 { 599 600 real_t d = p_quat.lengthSquared(); 601 real_t s = 2.0 / d; 602 real_t xs = p_quat.x * s, ys = p_quat.y * s, zs = p_quat.z * s; 603 real_t wx = p_quat.w * xs, wy = p_quat.w * ys, wz = p_quat.w * zs; 604 real_t xx = p_quat.x * xs, xy = p_quat.x * ys, xz = p_quat.x * zs; 605 real_t yy = p_quat.y * ys, yz = p_quat.y * zs, zz = p_quat.z * zs; 606 set( 1.0 - (yy + zz), xy - wz, xz + wy, 607 xy + wz, 1.0 - (xx + zz), yz - wx, 608 xz - wy, yz + wx, 1.0 - (xx + yy)) ; 609 610 } 611 612 this(in Vector3 p_axis, real_t p_phi) 613 { 614 // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle 615 616 Vector3 axis_sq = Vector3(p_axis.x*p_axis.x,p_axis.y*p_axis.y,p_axis.z*p_axis.z); 617 618 real_t cosine= cos(p_phi); 619 real_t sine= sin(p_phi); 620 621 elements[0][0] = axis_sq.x + cosine * ( 1.0 - axis_sq.x ); 622 elements[0][1] = p_axis.x * p_axis.y * ( 1.0 - cosine ) - p_axis.z * sine; 623 elements[0][2] = p_axis.z * p_axis.x * ( 1.0 - cosine ) + p_axis.y * sine; 624 625 elements[1][0] = p_axis.x * p_axis.y * ( 1.0 - cosine ) + p_axis.z * sine; 626 elements[1][1] = axis_sq.y + cosine * ( 1.0 - axis_sq.y ); 627 elements[1][2] = p_axis.y * p_axis.z * ( 1.0 - cosine ) - p_axis.x * sine; 628 629 elements[2][0] = p_axis.z * p_axis.x * ( 1.0 - cosine ) - p_axis.y * sine; 630 elements[2][1] = p_axis.y * p_axis.z * ( 1.0 - cosine ) + p_axis.x * sine; 631 elements[2][2] = axis_sq.z + cosine * ( 1.0 - axis_sq.z ); 632 633 } 634 635 Quat quat() const 636 { 637 ///ERR_FAIL_COND_V(is_rotation() == false, Quat()); 638 639 real_t trace = elements[0][0] + elements[1][1] + elements[2][2]; 640 real_t[4] temp; 641 642 if (trace > 0.0) 643 { 644 real_t s = sqrt(trace + 1.0); 645 temp[3]=(s * 0.5); 646 s = 0.5 / s; 647 648 temp[0]=((elements[2][1] - elements[1][2]) * s); 649 temp[1]=((elements[0][2] - elements[2][0]) * s); 650 temp[2]=((elements[1][0] - elements[0][1]) * s); 651 } 652 else 653 { 654 int i = elements[0][0] < elements[1][1] ? 655 (elements[1][1] < elements[2][2] ? 2 : 1) : 656 (elements[0][0] < elements[2][2] ? 2 : 0); 657 int j = (i + 1) % 3; 658 int k = (i + 2) % 3; 659 660 real_t s = sqrt(elements[i][i] - elements[j][j] - elements[k][k] + 1.0); 661 temp[i] = s * 0.5; 662 s = 0.5 / s; 663 664 temp[3] = (elements[k][j] - elements[j][k]) * s; 665 temp[j] = (elements[j][i] + elements[i][j]) * s; 666 temp[k] = (elements[k][i] + elements[i][k]) * s; 667 } 668 return Quat(temp[0],temp[1],temp[2],temp[3]); 669 } 670 } 671 672 673