Matrix3.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. using System;
  2. using System.Runtime.InteropServices;
  3. namespace BansheeEngine
  4. {
  5. [StructLayout(LayoutKind.Sequential), SerializeObject]
  6. public struct Matrix3
  7. {
  8. private struct EulerAngleOrderData
  9. {
  10. public EulerAngleOrderData(int a, int b, int c, float sign)
  11. {
  12. this.a = a;
  13. this.b = b;
  14. this.c = c;
  15. this.sign = sign;
  16. }
  17. public int a, b, c;
  18. public float sign;
  19. };
  20. private static EulerAngleOrderData[] EA_LOOKUP =
  21. { new EulerAngleOrderData(0, 1, 2, 1.0f), new EulerAngleOrderData(0, 2, 1, -1.0f), new EulerAngleOrderData(1, 0, 2, -1.0f),
  22. new EulerAngleOrderData(1, 2, 0, 1.0f), new EulerAngleOrderData(2, 0, 1, 1.0f), new EulerAngleOrderData(2, 1, 0, -1.0f) };
  23. public static readonly Matrix3 zero = new Matrix3();
  24. public static readonly Matrix3 identity = new Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1);
  25. public float m00;
  26. public float m01;
  27. public float m02;
  28. public float m10;
  29. public float m11;
  30. public float m12;
  31. public float m20;
  32. public float m21;
  33. public float m22;
  34. public Matrix3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)
  35. {
  36. this.m00 = m00;
  37. this.m01 = m01;
  38. this.m02 = m02;
  39. this.m10 = m10;
  40. this.m11 = m11;
  41. this.m12 = m12;
  42. this.m20 = m20;
  43. this.m21 = m21;
  44. this.m22 = m22;
  45. }
  46. public float this[int row, int column]
  47. {
  48. get
  49. {
  50. return this[row * 3 + column];
  51. }
  52. set
  53. {
  54. this[row * 3 + column] = value;
  55. }
  56. }
  57. public float this[int index]
  58. {
  59. get
  60. {
  61. switch (index)
  62. {
  63. case 0:
  64. return m00;
  65. case 1:
  66. return m01;
  67. case 2:
  68. return m02;
  69. case 3:
  70. return m10;
  71. case 4:
  72. return m11;
  73. case 5:
  74. return m12;
  75. case 6:
  76. return m20;
  77. case 7:
  78. return m21;
  79. case 8:
  80. return m22;
  81. default:
  82. throw new IndexOutOfRangeException("Invalid matrix index.");
  83. }
  84. }
  85. set
  86. {
  87. switch (index)
  88. {
  89. case 0:
  90. m00 = value;
  91. break;
  92. case 1:
  93. m01 = value;
  94. break;
  95. case 2:
  96. m02 = value;
  97. break;
  98. case 3:
  99. m10 = value;
  100. break;
  101. case 4:
  102. m11 = value;
  103. break;
  104. case 5:
  105. m12 = value;
  106. break;
  107. case 6:
  108. m20 = value;
  109. break;
  110. case 7:
  111. m21 = value;
  112. break;
  113. case 8:
  114. m22 = value;
  115. break;
  116. default:
  117. throw new IndexOutOfRangeException("Invalid matrix index.");
  118. }
  119. }
  120. }
  121. public static Matrix3 operator *(Matrix3 lhs, Matrix3 rhs)
  122. {
  123. return new Matrix3()
  124. {
  125. m00 = lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10 + lhs.m02 * rhs.m20,
  126. m01 = lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11 + lhs.m02 * rhs.m21,
  127. m02 = lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02 * rhs.m22,
  128. m10 = lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10 + lhs.m12 * rhs.m20,
  129. m11 = lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21,
  130. m12 = lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22,
  131. m20 = lhs.m20 * rhs.m00 + lhs.m21 * rhs.m10 + lhs.m22 * rhs.m20,
  132. m21 = lhs.m20 * rhs.m01 + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21,
  133. m22 = lhs.m20 * rhs.m02 + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22,
  134. };
  135. }
  136. public static bool operator== (Matrix3 lhs, Matrix3 rhs)
  137. {
  138. if (lhs.m00 == rhs.m00 && lhs.m01 == rhs.m01 && lhs.m02 == rhs.m02 &&
  139. lhs.m10 == rhs.m10 && lhs.m11 == rhs.m11 && lhs.m12 == rhs.m12 &&
  140. lhs.m20 == rhs.m20 && lhs.m21 == rhs.m21 && lhs.m22 == rhs.m22)
  141. return true;
  142. else
  143. return false;
  144. }
  145. public static bool operator !=(Matrix3 lhs, Matrix3 rhs)
  146. {
  147. return !(lhs == rhs);
  148. }
  149. public override int GetHashCode()
  150. {
  151. float hash1 = m00.GetHashCode() ^ m10.GetHashCode() << 2 ^ m20.GetHashCode() >> 2;
  152. float hash2 = m01.GetHashCode() ^ m11.GetHashCode() << 2 ^ m21.GetHashCode() >> 2;
  153. float hash3 = m02.GetHashCode() ^ m12.GetHashCode() << 2 ^ m22.GetHashCode() >> 2;
  154. return hash1.GetHashCode() ^ hash2.GetHashCode() << 2 ^ hash3.GetHashCode() >> 2;
  155. }
  156. public override bool Equals(object other)
  157. {
  158. if (!(other is Matrix3))
  159. return false;
  160. Matrix3 mat = (Matrix3)other;
  161. if (m00 == mat.m00 && m01 == mat.m01 && m02 == mat.m02 &&
  162. m10 == mat.m10 && m11 == mat.m11 && m12 == mat.m12 &&
  163. m20 == mat.m20 && m21 == mat.m21 && m22 == mat.m22)
  164. return true;
  165. else
  166. return false;
  167. }
  168. public void Invert()
  169. {
  170. float[,] invVals = new float[3,3];
  171. invVals[0, 0] = m11 * m22 - m12 * m21;
  172. invVals[1, 0] = m12 * m20 - m10 * m22;
  173. invVals[2, 0] = m10 * m21 - m11 * m20;
  174. float det = m00 * invVals[0, 0] + m01 * invVals[1, 0] + m02 * invVals[2, 0];
  175. if (MathEx.Abs(det) <= 1e-06f)
  176. throw new DivideByZeroException("Matrix determinant is zero. Cannot invert.");
  177. invVals[0, 1] = m02 * m21 - m01 * m22;
  178. invVals[0, 2] = m01 * m12 - m02 * m11;
  179. invVals[1, 1] = m00 * m22 - m02 * m20;
  180. invVals[1, 2] = m02 * m10 - m00 * m12;
  181. invVals[2, 1] = m01 * m20 - m00 * m21;
  182. invVals[2, 2] = m00 * m11 - m01 * m10;
  183. float invDet = 1.0f/det;
  184. for (int row = 0; row < 3; row++)
  185. {
  186. for (int col = 0; col < 3; col++)
  187. invVals[row, col] *= invDet;
  188. }
  189. }
  190. public void Transpose()
  191. {
  192. float tmp = m10;
  193. m10 = m01;
  194. m01 = tmp;
  195. tmp = m20;
  196. m20 = m02;
  197. m02 = tmp;
  198. tmp = m12;
  199. m12 = m21;
  200. m21 = tmp;
  201. }
  202. public float Determinant()
  203. {
  204. float cofactor00 = m11 * m22 - m12 * m21;
  205. float cofactor10 = m12 * m20 - m10 * m22;
  206. float cofactor20 = m10 * m21 - m11 * m20;
  207. float det = m00 * cofactor00 + m01 * cofactor10 + m02 * cofactor20;
  208. return det;
  209. }
  210. public Vector3 Transform(Vector3 vec)
  211. {
  212. Vector3 outVec;
  213. outVec.x = m00 * vec.x + m01 * vec.y + m02 * vec.z;
  214. outVec.y = m10 * vec.x + m11 * vec.y + m12 * vec.z;
  215. outVec.z = m20 * vec.x + m21 * vec.y + m22 * vec.z;
  216. return outVec;
  217. }
  218. public void QDUDecomposition(out Matrix3 matQ, out Vector3 vecD, out Vector3 vecU)
  219. {
  220. matQ = new Matrix3();
  221. vecD = new Vector3();
  222. vecU = new Vector3();
  223. // Build orthogonal matrix Q
  224. float invLength = MathEx.InvSqrt(m00*m00 + m10*m10 + m20*m20);
  225. matQ.m00 = m00*invLength;
  226. matQ.m10 = m10*invLength;
  227. matQ.m20 = m20*invLength;
  228. float dot = matQ.m00*m01 + matQ.m10*m11 + matQ.m20*m21;
  229. matQ.m01 = m01-dot*matQ.m00;
  230. matQ.m11 = m11-dot*matQ.m10;
  231. matQ.m21 = m21-dot*matQ.m20;
  232. invLength = MathEx.InvSqrt(matQ.m01*matQ.m01 + matQ.m11*matQ.m11 + matQ.m21*matQ.m21);
  233. matQ.m01 *= invLength;
  234. matQ.m11 *= invLength;
  235. matQ.m21 *= invLength;
  236. dot = matQ.m00*m02 + matQ.m10*m12 + matQ.m20*m22;
  237. matQ.m02 = m02-dot*matQ.m00;
  238. matQ.m12 = m12-dot*matQ.m10;
  239. matQ.m22 = m22-dot*matQ.m20;
  240. dot = matQ.m01*m02 + matQ.m11*m12 + matQ.m21*m22;
  241. matQ.m02 -= dot*matQ.m01;
  242. matQ.m12 -= dot*matQ.m11;
  243. matQ.m22 -= dot*matQ.m21;
  244. invLength = MathEx.InvSqrt(matQ.m02*matQ.m02 + matQ.m12*matQ.m12 + matQ.m22*matQ.m22);
  245. matQ.m02 *= invLength;
  246. matQ.m12 *= invLength;
  247. matQ.m22 *= invLength;
  248. // Guarantee that orthogonal matrix has determinant 1 (no reflections)
  249. float fDet = matQ.m00*matQ.m11*matQ.m22 + matQ.m01*matQ.m12*matQ.m20 +
  250. matQ.m02*matQ.m10*matQ.m21 - matQ.m02*matQ.m11*matQ.m20 -
  251. matQ.m01*matQ.m10*matQ.m22 - matQ.m00*matQ.m12*matQ.m21;
  252. if (fDet < 0.0f)
  253. {
  254. matQ.m00 = -matQ.m00;
  255. matQ.m01 = -matQ.m01;
  256. matQ.m02 = -matQ.m02;
  257. matQ.m10 = -matQ.m10;
  258. matQ.m11 = -matQ.m11;
  259. matQ.m12 = -matQ.m12;
  260. matQ.m20 = -matQ.m20;
  261. matQ.m21 = -matQ.m21;
  262. matQ.m21 = -matQ.m22;
  263. }
  264. // Build "right" matrix R
  265. Matrix3 matRight = new Matrix3();
  266. matRight.m00 = matQ.m00 * m00 + matQ.m10 * m10 + matQ.m20 * m20;
  267. matRight.m01 = matQ.m00 * m01 + matQ.m10 * m11 + matQ.m20 * m21;
  268. matRight.m11 = matQ.m01 * m01 + matQ.m11 * m11 + matQ.m21 * m21;
  269. matRight.m02 = matQ.m00 * m02 + matQ.m10 * m12 + matQ.m20 * m22;
  270. matRight.m12 = matQ.m01 * m02 + matQ.m11 * m12 + matQ.m21 * m22;
  271. matRight.m22 = matQ.m02 * m02 + matQ.m12 * m12 + matQ.m22 * m22;
  272. // The scaling component
  273. vecD[0] = matRight.m00;
  274. vecD[1] = matRight.m11;
  275. vecD[2] = matRight.m22;
  276. // The shear component
  277. float invD0 = 1.0f/vecD[0];
  278. vecU[0] = matRight.m01 * invD0;
  279. vecU[1] = matRight.m02 * invD0;
  280. vecU[2] = matRight.m12 / vecD[1];
  281. }
  282. /**
  283. * @note Returns angles in degrees.
  284. */
  285. public Vector3 ToEulerAngles()
  286. {
  287. float xAngle = -MathEx.Asin(this[1, 2]);
  288. if (xAngle < MathEx.HalfPi)
  289. {
  290. if (xAngle > -MathEx.HalfPi)
  291. {
  292. float yAngle = MathEx.Atan2(this[0, 2], this[2, 2]);
  293. float zAngle = MathEx.Atan2(this[1, 0], this[1, 1]);
  294. return new Vector3(xAngle * MathEx.Rad2Deg, yAngle * MathEx.Rad2Deg, zAngle * MathEx.Rad2Deg);
  295. }
  296. else
  297. {
  298. // Note: Not an unique solution.
  299. xAngle = -MathEx.HalfPi;
  300. float yAngle = MathEx.Atan2(-this[0, 1], this[0, 0]);
  301. float zAngle = 0.0f;
  302. return new Vector3(xAngle * MathEx.Rad2Deg, yAngle * MathEx.Rad2Deg, zAngle * MathEx.Rad2Deg);
  303. }
  304. }
  305. else
  306. {
  307. // Note: Not an unique solution.
  308. xAngle = MathEx.HalfPi;
  309. float yAngle = MathEx.Atan2(this[0, 1], this[0, 0]);
  310. float zAngle = 0.0f;
  311. return new Vector3(xAngle * MathEx.Rad2Deg, yAngle * MathEx.Rad2Deg, zAngle * MathEx.Rad2Deg);
  312. }
  313. }
  314. public Quaternion ToQuaternion()
  315. {
  316. return Quaternion.FromRotationMatrix(this);
  317. }
  318. public void ToAxisAngle(out Vector3 axis, out Degree angle)
  319. {
  320. float trace = m00 + m11 + m22;
  321. float cos = 0.5f*(trace-1.0f);
  322. Radian radians = MathEx.Acos(cos); // In [0, PI]
  323. angle = radians.Degrees;
  324. if (radians > 0.0f)
  325. {
  326. if (radians < MathEx.Pi)
  327. {
  328. axis.x = m21 - m12;
  329. axis.y = m02 - m20;
  330. axis.z = m10 - m01;
  331. axis.Normalize();
  332. }
  333. else
  334. {
  335. // Angle is PI
  336. float halfInverse;
  337. if (m00 >= m11)
  338. {
  339. // r00 >= r11
  340. if (m00 >= m22)
  341. {
  342. // r00 is maximum diagonal term
  343. axis.x = 0.5f*MathEx.Sqrt(m00 - m11 - m22 + 1.0f);
  344. halfInverse = 0.5f/axis.x;
  345. axis.y = halfInverse*m01;
  346. axis.z = halfInverse*m02;
  347. }
  348. else
  349. {
  350. // r22 is maximum diagonal term
  351. axis.z = 0.5f*MathEx.Sqrt(m22 - m00 - m11 + 1.0f);
  352. halfInverse = 0.5f/axis.z;
  353. axis.x = halfInverse*m02;
  354. axis.y = halfInverse*m12;
  355. }
  356. }
  357. else
  358. {
  359. // r11 > r00
  360. if (m11 >= m22)
  361. {
  362. // r11 is maximum diagonal term
  363. axis.y = 0.5f*MathEx.Sqrt(m11 - m00 - m22 + 1.0f);
  364. halfInverse = 0.5f/axis.y;
  365. axis.x = halfInverse*m01;
  366. axis.z = halfInverse*m12;
  367. }
  368. else
  369. {
  370. // r22 is maximum diagonal term
  371. axis.z = 0.5f*MathEx.Sqrt(m22 - m00 - m11 + 1.0f);
  372. halfInverse = 0.5f/axis.z;
  373. axis.x = halfInverse*m02;
  374. axis.y = halfInverse*m12;
  375. }
  376. }
  377. }
  378. }
  379. else
  380. {
  381. // The angle is 0 and the matrix is the identity. Any axis will
  382. // work, so just use the x-axis.
  383. axis.x = 1.0f;
  384. axis.y = 0.0f;
  385. axis.z = 0.0f;
  386. }
  387. }
  388. public static Matrix3 Inverse(Matrix3 mat)
  389. {
  390. Matrix3 copy = mat;
  391. copy.Invert();
  392. return copy;
  393. }
  394. public static Matrix3 Transpose(Matrix3 mat)
  395. {
  396. Matrix3 copy = mat;
  397. copy.Transpose();
  398. return copy;
  399. }
  400. public static Matrix3 FromEuler(Vector3 eulerDeg, EulerAngleOrder order)
  401. {
  402. return FromEuler(new Degree(eulerDeg.x), new Degree(eulerDeg.y), new Degree(eulerDeg.y), order);
  403. }
  404. public static Matrix3 FromEuler(Radian xAngle, Radian yAngle, Radian zAngle, EulerAngleOrder order)
  405. {
  406. EulerAngleOrderData l = EA_LOOKUP[(int)order];
  407. Matrix3[] mats = new Matrix3[3];
  408. float cx = MathEx.Cos(xAngle);
  409. float sx = MathEx.Sin(xAngle);
  410. mats[0] = new Matrix3(
  411. 1.0f, 0.0f, 0.0f,
  412. 0.0f, cx, -sx,
  413. 0.0f, sx, cx);
  414. float cy = MathEx.Cos(yAngle);
  415. float sy = MathEx.Sin(yAngle);
  416. mats[1] = new Matrix3(
  417. cy, 0.0f, sy,
  418. 0.0f, 1.0f, 0.0f,
  419. -sy, 0.0f, cy);
  420. float cz = MathEx.Cos(zAngle);
  421. float sz = MathEx.Sin(zAngle);
  422. mats[2] = new Matrix3(
  423. cz, -sz, 0.0f,
  424. sz, cz, 0.0f,
  425. 0.0f, 0.0f, 1.0f);
  426. return mats[l.a]*(mats[l.b]*mats[l.c]);
  427. }
  428. public static Matrix3 FromEuler(Vector3 eulerDeg)
  429. {
  430. return FromEuler(new Degree(eulerDeg.x), new Degree(eulerDeg.y), new Degree(eulerDeg.y));
  431. }
  432. public static Matrix3 FromEuler(Radian xAngle, Radian yAngle, Radian zAngle)
  433. {
  434. Matrix3 m = new Matrix3();
  435. float cx = MathEx.Cos(xAngle);
  436. float sx = MathEx.Sin(xAngle);
  437. float cy = MathEx.Cos(yAngle);
  438. float sy = MathEx.Sin(yAngle);
  439. float cz = MathEx.Cos(zAngle);
  440. float sz = MathEx.Sin(zAngle);
  441. m[0, 0] = cy * cz + sx * sy * sz;
  442. m[0, 1] = cz * sx * sy - cy * sz;
  443. m[0, 2] = cx * sy;
  444. m[1, 0] = cx * sz;
  445. m[1, 1] = cx * cz;
  446. m[1, 2] = -sx;
  447. m[2, 0] = -cz * sy + cy * sx * sz;
  448. m[2, 1] = cy * cz * sx + sy * sz;
  449. m[2, 2] = cx * cy;
  450. return m;
  451. }
  452. public static Matrix3 FromAxisAngle(Vector3 axis, Degree angle)
  453. {
  454. Matrix3 mat;
  455. float cos = MathEx.Cos(angle);
  456. float sin = MathEx.Sin(angle);
  457. float oneMinusCos = 1.0f - cos;
  458. float x2 = axis.x * axis.x;
  459. float y2 = axis.y * axis.y;
  460. float z2 = axis.z * axis.z;
  461. float xym = axis.x * axis.y * oneMinusCos;
  462. float xzm = axis.x * axis.z * oneMinusCos;
  463. float yzm = axis.y * axis.z * oneMinusCos;
  464. float xSin = axis.x * sin;
  465. float ySin = axis.y * sin;
  466. float zSin = axis.z * sin;
  467. mat.m00 = x2 * oneMinusCos + cos;
  468. mat.m01 = xym - zSin;
  469. mat.m02 = xzm + ySin;
  470. mat.m10 = xym + zSin;
  471. mat.m11 = y2 * oneMinusCos + cos;
  472. mat.m12 = yzm - xSin;
  473. mat.m20 = xzm - ySin;
  474. mat.m21 = yzm + xSin;
  475. mat.m22 = z2 * oneMinusCos + cos;
  476. return mat;
  477. }
  478. public static Matrix3 FromQuaternion(Quaternion quat)
  479. {
  480. return quat.ToRotationMatrix();
  481. }
  482. public override string ToString()
  483. {
  484. return String.Format("({0}, {1}, {2},\n{3}, {4}, {5}\n{6}, {7}, {8})",
  485. m00, m01, m02, m10, m11, m12, m20, m21, m22);
  486. }
  487. }
  488. }