PageRenderTime 60ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Datatypes/Matrix.cs

#
C# | 1820 lines | 1114 code | 118 blank | 588 comment | 86 complexity | 02a0f05e85a992c0b9fa7d8b4153d0c5 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.IO;
  3. using System.Runtime.InteropServices;
  4. using Delta.Utilities.Helpers;
  5. using Delta.Utilities.Profiling;
  6. using NUnit.Framework;
  7. // Disable warnings for some tests where we don't use created values
  8. #pragma warning disable 219
  9. // Disable the obsolete warnings for Matrix.Multiply here, it is optimized
  10. // already here and when it is not in some unit test, its ok too.
  11. #pragma warning disable 618
  12. namespace Delta.Utilities.Datatypes
  13. {
  14. /// <summary>
  15. /// Matrix struct, used mostly for 3D calculations. If possible this shares
  16. /// all the functionality of the XNA Matrix struct (if we are compiling
  17. /// only). In case we do not compile with XNA or SlimDX enabled (like on
  18. /// this class still provides all the basic functionality as a fallback. It
  19. /// might not be as optimized as some specialized xna code paths for the
  20. /// Xbox 360, but those only work on the XNA define and platform anyway.
  21. /// </summary>
  22. /// <remarks>
  23. /// Represents a row based matrix. Good wiki page about the coordinate
  24. /// system (right handed):
  25. /// http://en.wikipedia.org/wiki/Cartesian_coordinate_system
  26. /// <para />
  27. /// Note: This Matrix class was heavily profiled and optimized with help of
  28. /// the MatrixPerformance helper class included here. Many code paths
  29. /// are platform and framework dependant, this is why this class is so huge,
  30. /// but since this is very low level and highly performance critical, it is
  31. /// well worth the complexity and effort (the class is still easy to use).
  32. /// On the Xbox we will always try to use XNA implementation for performance
  33. /// critical methods, all other platforms are mostly implemented by us
  34. /// after testing performance. You can also checkout the SlimDX math classes,
  35. /// which mostly are just C#, only certain heavy methods like multiplying
  36. /// an array of matrices uses PInvoke to D3DXMatrixMultiply for example.
  37. /// <para />
  38. /// There are still performance improvements possible for this class
  39. /// like using ref for most performance critical methods like Invert,
  40. /// Multiply and most other operators, but that makes this class much harder
  41. /// to use and we can still do code-optimizations directly with the exposed
  42. /// data when needed (e.g. Bone Matrix code should be heavily optimized if
  43. /// it has to run on CPU). Currently this is fixed by marking slow methods
  44. /// as obsolete, which will give you compiler warnings, use the ref
  45. /// overload methods instead.
  46. /// <para />
  47. /// Also note that many methods are not faster by using the native XNA,
  48. /// OpenTK or SharpDX or SlimDX code paths, especially when just assigning
  49. /// values (where our code is almost always faster). But remember when any
  50. /// framework is using special tricks like low level assembly code or native
  51. /// code to calculate complex math operations, it can be faster and will be
  52. /// replaced by the build system automatically. If you notice any method
  53. /// that should be faster, contact us immediately so we can investigate!
  54. /// </remarks>
  55. [Serializable]
  56. [StructLayout(LayoutKind.Explicit)]
  57. public struct Matrix : ISaveLoadBinary, IEquatable<Matrix>
  58. {
  59. #region Constants
  60. /// <summary>
  61. /// Represents the number of values of that Matrix struct (here 4x4 = 16).
  62. /// </summary>
  63. public const int ValueCount = 16;
  64. /// <summary>
  65. /// Returns a zeroed matrix.
  66. /// </summary>
  67. public static readonly Matrix Zero = new Matrix(
  68. 0, 0, 0, 0,
  69. 0, 0, 0, 0,
  70. 0, 0, 0, 0,
  71. 0, 0, 0, 0);
  72. #endregion
  73. #region Invert (Static)
  74. /// <summary>
  75. /// Invert matrix, this is only slightly faster on the xna platform
  76. /// specific implementation, for performance critical code use the ref
  77. /// version!
  78. /// </summary>
  79. /// <param name="a">Matrix to invert</param>
  80. /// <returns>Inverted Matrix</returns>
  81. public static Matrix Invert(Matrix a)
  82. {
  83. //Check performance difference again:
  84. //Note: Performance results from 10 million calls:
  85. //Delta: 1962ms
  86. //XNA: 1076ms
  87. //OpenTK: 8611ms
  88. //SlimDX: 981ms
  89. // Ugly code that is actually lots faster than our clean implementation
  90. // below (see link on how it works basically). This is basically getting
  91. // the Determinant and then also calculating the inverse matrix in one
  92. // big ugly swoop.
  93. float m11 = a.M11;
  94. float m12 = a.M12;
  95. float m13 = a.M13;
  96. float m14 = a.M14;
  97. float m21 = a.M21;
  98. float m22 = a.M22;
  99. float m23 = a.M23;
  100. float m24 = a.M24;
  101. float m31 = a.M31;
  102. float m32 = a.M32;
  103. float m33 = a.M33;
  104. float m34 = a.M34;
  105. float m41 = a.M41;
  106. float m42 = a.M42;
  107. float m43 = a.M43;
  108. float m44 = a.M44;
  109. float m33x44m34x43 = (m33 * m44) - (m34 * m43);
  110. float m32x44m34x42 = (m32 * m44) - (m34 * m42);
  111. float m32x43m33x42 = (m32 * m43) - (m33 * m42);
  112. float m31x44m34x41 = (m31 * m44) - (m34 * m41);
  113. float m31x43m33x41 = (m31 * m43) - (m33 * m41);
  114. float m31x42m32x41 = (m31 * m42) - (m32 * m41);
  115. float m22m23m24 = ((m22 * m33x44m34x43) - (m23 * m32x44m34x42)) +
  116. (m24 * m32x43m33x42);
  117. float m21m23m24 = -(((m21 * m33x44m34x43) - (m23 * m31x44m34x41)) +
  118. (m24 * m31x43m33x41));
  119. float m21m22m24 = ((m21 * m32x44m34x42) - (m22 * m31x44m34x41)) +
  120. (m24 * m31x42m32x41);
  121. float m21m22m23 = -(((m21 * m32x43m33x42) - (m22 * m31x43m33x41)) +
  122. (m23 * m31x42m32x41));
  123. // Compute the the determinant
  124. float det =
  125. (m11 * m22m23m24) + (m12 * m21m23m24) + (m13 * m21m22m24) +
  126. (m14 * m21m22m23);
  127. if (det == 0.0f)
  128. {
  129. det = 0.000001f;
  130. }
  131. // Inverse(A) = 1/det(A) * B
  132. float inverseDet = 1f / det;
  133. float m23x44m24x43 = (m23 * m44) - (m24 * m43);
  134. float m22x44m24x42 = (m22 * m44) - (m24 * m42);
  135. float m22x43m23x42 = (m22 * m43) - (m23 * m42);
  136. float m21x44m24x41 = (m21 * m44) - (m24 * m41);
  137. float m21x43m23x41 = (m21 * m43) - (m23 * m41);
  138. float m21x42m22x41 = (m21 * m42) - (m22 * m41);
  139. float m23x34m24x33 = (m23 * m34) - (m24 * m33);
  140. float m22x34m24x32 = (m22 * m34) - (m24 * m32);
  141. float m22x33m23x32 = (m22 * m33) - (m23 * m32);
  142. float m21x34m23x31 = (m21 * m34) - (m24 * m31);
  143. float m21x33m23x31 = (m21 * m33) - (m23 * m31);
  144. float m21x32m22x31 = (m21 * m32) - (m22 * m31);
  145. return new Matrix(
  146. m22m23m24 * inverseDet,
  147. -(((m12 * m33x44m34x43) - (m13 * m32x44m34x42)) +
  148. (m14 * m32x43m33x42)) * inverseDet,
  149. (((m12 * m23x44m24x43) - (m13 * m22x44m24x42)) +
  150. (m14 * m22x43m23x42)) * inverseDet,
  151. -(((m12 * m23x34m24x33) - (m13 * m22x34m24x32)) +
  152. (m14 * m22x33m23x32)) * inverseDet,
  153. m21m23m24 * inverseDet,
  154. (((m11 * m33x44m34x43) - (m13 * m31x44m34x41)) +
  155. (m14 * m31x43m33x41)) * inverseDet,
  156. -(((m11 * m23x44m24x43) - (m13 * m21x44m24x41)) +
  157. (m14 * m21x43m23x41)) * inverseDet,
  158. (((m11 * m23x34m24x33) - (m13 * m21x34m23x31)) +
  159. (m14 * m21x33m23x31)) * inverseDet,
  160. m21m22m24 * inverseDet,
  161. -(((m11 * m32x44m34x42) - (m12 * m31x44m34x41)) +
  162. (m14 * m31x42m32x41)) * inverseDet,
  163. (((m11 * m22x44m24x42) - (m12 * m21x44m24x41)) +
  164. (m14 * m21x42m22x41)) * inverseDet,
  165. -(((m11 * m22x34m24x32) - (m12 * m21x34m23x31)) +
  166. (m14 * m21x32m22x31)) * inverseDet,
  167. m21m22m23 * inverseDet,
  168. (((m11 * m32x43m33x42) - (m12 * m31x43m33x41)) +
  169. (m13 * m31x42m32x41)) * inverseDet,
  170. -(((m11 * m22x43m23x42) - (m12 * m21x43m23x41)) +
  171. (m13 * m21x42m22x41)) * inverseDet,
  172. (((m11 * m22x33m23x32) - (m12 * m21x33m23x31)) +
  173. (m13 * m21x32m22x31)) * inverseDet);
  174. }
  175. /// <summary>
  176. /// Invert matrix ref version, this is the fastest one, especially if
  177. /// you use matrix and result for the same value, which will copy values
  178. /// over quickly.
  179. /// </summary>
  180. /// <param name="matrix">Matrix</param>
  181. /// <param name="result">Result</param>
  182. public static void Invert(ref Matrix matrix, ref Matrix result)
  183. {
  184. //Check performance difference again:
  185. // Ugly code that is actually lots faster than our clean implementation
  186. // below (see link on how it works basically). This is basically getting
  187. // the Determinant and then also calculating the inverse matrix in one
  188. // big ugly swoop.
  189. float m11 = matrix.M11;
  190. float m12 = matrix.M12;
  191. float m13 = matrix.M13;
  192. float m14 = matrix.M14;
  193. float m21 = matrix.M21;
  194. float m22 = matrix.M22;
  195. float m23 = matrix.M23;
  196. float m24 = matrix.M24;
  197. float m31 = matrix.M31;
  198. float m32 = matrix.M32;
  199. float m33 = matrix.M33;
  200. float m34 = matrix.M34;
  201. float m41 = matrix.M41;
  202. float m42 = matrix.M42;
  203. float m43 = matrix.M43;
  204. float m44 = matrix.M44;
  205. float m33x44m34x43 = (m33 * m44) - (m34 * m43);
  206. float m32x44m34x42 = (m32 * m44) - (m34 * m42);
  207. float m32x43m33x42 = (m32 * m43) - (m33 * m42);
  208. float m31x44m34x41 = (m31 * m44) - (m34 * m41);
  209. float m31x43m33x41 = (m31 * m43) - (m33 * m41);
  210. float m31x42m32x41 = (m31 * m42) - (m32 * m41);
  211. float m22m23m24 = ((m22 * m33x44m34x43) - (m23 * m32x44m34x42)) +
  212. (m24 * m32x43m33x42);
  213. float m21m23m24 = -(((m21 * m33x44m34x43) - (m23 * m31x44m34x41)) +
  214. (m24 * m31x43m33x41));
  215. float m21m22m24 = ((m21 * m32x44m34x42) - (m22 * m31x44m34x41)) +
  216. (m24 * m31x42m32x41);
  217. float m21m22m23 = -(((m21 * m32x43m33x42) - (m22 * m31x43m33x41)) +
  218. (m23 * m31x42m32x41));
  219. // Computing the determinant
  220. float det = (m11 * m22m23m24) + (m12 * m21m23m24) + (m13 * m21m22m24) +
  221. (m14 * m21m22m23);
  222. if (det == 0.0f)
  223. {
  224. det = 0.000001f;
  225. }
  226. // Inverse(A) = 1/det(A) * B
  227. float inverseDet = 1f / det;
  228. float m23x44m24x43 = (m23 * m44) - (m24 * m43);
  229. float m22x44m24x42 = (m22 * m44) - (m24 * m42);
  230. float m22x43m23x42 = (m22 * m43) - (m23 * m42);
  231. float m21x44m24x41 = (m21 * m44) - (m24 * m41);
  232. float m21x43m23x41 = (m21 * m43) - (m23 * m41);
  233. float m21x42m22x41 = (m21 * m42) - (m22 * m41);
  234. float m23x34m24x33 = (m23 * m34) - (m24 * m33);
  235. float m22x34m24x32 = (m22 * m34) - (m24 * m32);
  236. float m22x33m23x32 = (m22 * m33) - (m23 * m32);
  237. float m21x34m23x31 = (m21 * m34) - (m24 * m31);
  238. float m21x33m23x31 = (m21 * m33) - (m23 * m31);
  239. float m21x32m22x31 = (m21 * m32) - (m22 * m31);
  240. // Now just set everything to the output result matrix, which even can
  241. // be the same as the input matrix (for best performance).
  242. result.M11 = m22m23m24 * inverseDet;
  243. result.M12 = -(((m12 * m33x44m34x43) - (m13 * m32x44m34x42)) +
  244. (m14 * m32x43m33x42)) * inverseDet;
  245. result.M13 = (((m12 * m23x44m24x43) - (m13 * m22x44m24x42)) +
  246. (m14 * m22x43m23x42)) * inverseDet;
  247. result.M14 = -(((m12 * m23x34m24x33) - (m13 * m22x34m24x32)) +
  248. (m14 * m22x33m23x32)) * inverseDet;
  249. result.M21 = m21m23m24 * inverseDet;
  250. result.M22 = (((m11 * m33x44m34x43) - (m13 * m31x44m34x41)) +
  251. (m14 * m31x43m33x41)) * inverseDet;
  252. result.M23 = -(((m11 * m23x44m24x43) - (m13 * m21x44m24x41)) +
  253. (m14 * m21x43m23x41)) * inverseDet;
  254. result.M24 = (((m11 * m23x34m24x33) - (m13 * m21x34m23x31)) +
  255. (m14 * m21x33m23x31)) * inverseDet;
  256. result.M31 = m21m22m24 * inverseDet;
  257. result.M32 = -(((m11 * m32x44m34x42) - (m12 * m31x44m34x41)) +
  258. (m14 * m31x42m32x41)) * inverseDet;
  259. result.M33 = (((m11 * m22x44m24x42) - (m12 * m21x44m24x41)) +
  260. (m14 * m21x42m22x41)) * inverseDet;
  261. result.M34 = -(((m11 * m22x34m24x32) - (m12 * m21x34m23x31)) +
  262. (m14 * m21x32m22x31)) * inverseDet;
  263. result.M41 = m21m22m23 * inverseDet;
  264. result.M42 = (((m11 * m32x43m33x42) - (m12 * m31x43m33x41)) +
  265. (m13 * m31x42m32x41)) * inverseDet;
  266. result.M43 = -(((m11 * m22x43m23x42) - (m12 * m21x43m23x41)) +
  267. (m13 * m21x42m22x41)) * inverseDet;
  268. result.M44 = (((m11 * m22x33m23x32) - (m12 * m21x33m23x31)) +
  269. (m13 * m21x32m22x31)) * inverseDet;
  270. }
  271. #endregion
  272. #region Transpose (Static)
  273. /// <summary>
  274. /// Transpose, which basically just means we switch from a row to a column
  275. /// based matrix.
  276. /// </summary>
  277. /// <param name="matrix">Matrix</param>
  278. /// <returns>Transposed matrix (row/columns switched)</returns>
  279. public static Matrix Transpose(Matrix matrix)
  280. {
  281. //Check performance difference again:
  282. // Note: In all cases our implementation is slighly faster/or a lot faster
  283. //Delta: 569ms (10 mio calls)
  284. //XNA: 760ms (10 mio calls)
  285. //OpenTK: 904ms (10 mio calls)
  286. //SlimDX: 658ms (10 mio calls)
  287. return new Matrix(
  288. // First row
  289. matrix.M11, matrix.M21, matrix.M31, matrix.M41,
  290. // Second row
  291. matrix.M12, matrix.M22, matrix.M32, matrix.M42,
  292. // Third row
  293. matrix.M13, matrix.M23, matrix.M33, matrix.M43,
  294. // Forth row
  295. matrix.M14, matrix.M24, matrix.M34, matrix.M44);
  296. }
  297. /// <summary>
  298. /// Transpose, which basically just means we switch from a row to a column
  299. /// based matrix. Faster version using ref input and output values.
  300. /// </summary>
  301. /// <param name="matrix">Matrix</param>
  302. /// <param name="result">result</param>
  303. public static void Transpose(ref Matrix matrix, ref Matrix result)
  304. {
  305. result.M11 = matrix.M11;
  306. result.M12 = matrix.M21;
  307. result.M13 = matrix.M31;
  308. result.M14 = matrix.M41;
  309. result.M21 = matrix.M12;
  310. result.M22 = matrix.M22;
  311. result.M23 = matrix.M32;
  312. result.M24 = matrix.M42;
  313. result.M31 = matrix.M13;
  314. result.M32 = matrix.M23;
  315. result.M33 = matrix.M33;
  316. result.M34 = matrix.M43;
  317. result.M41 = matrix.M14;
  318. result.M42 = matrix.M24;
  319. result.M43 = matrix.M34;
  320. result.M44 = matrix.M44;
  321. }
  322. #endregion
  323. #region CreateColumnBased (Static)
  324. /// <summary>
  325. /// Create column based matrix, also used for creating an exported collada
  326. /// matrix, because the normal XNA/OpenTK/whatever matrix is row based.
  327. /// </summary>
  328. /// <param name="floatValues">Float values</param>
  329. /// <returns>Matrix</returns>
  330. public static Matrix CreateColumnBased(float[] floatValues)
  331. {
  332. // If we get no valid or an incorrect number of float values, then we
  333. // return only the identity matrix
  334. if (floatValues == null ||
  335. floatValues.Length != 16)
  336. {
  337. return Identity;
  338. }
  339. return new Matrix(
  340. floatValues[0], floatValues[4], floatValues[8], floatValues[12],
  341. floatValues[1], floatValues[5], floatValues[9], floatValues[13],
  342. floatValues[2], floatValues[6], floatValues[10], floatValues[14],
  343. floatValues[3], floatValues[7], floatValues[11], floatValues[15]);
  344. }
  345. #endregion
  346. #region CreateScale (Static)
  347. /// <summary>
  348. /// Create a scale matrix with the specified value for X, Y and Z.
  349. /// </summary>
  350. /// <param name="scale">Scale</param>
  351. /// <returns>New Matrix with the given scale</returns>
  352. public static Matrix CreateScale(float scale)
  353. {
  354. return new Matrix(
  355. scale, 0f, 0f, 0f,
  356. 0f, scale, 0f, 0f,
  357. 0f, 0f, scale, 0f,
  358. 0f, 0f, 0f, 1.0f);
  359. }
  360. /// <summary>
  361. /// Create scale
  362. /// </summary>
  363. /// <param name="scale">Scale</param>
  364. /// <returns>New Matrix with the given scale</returns>
  365. public static Matrix CreateScale(Vector scale)
  366. {
  367. return CreateScale(scale.X, scale.Y, scale.Z);
  368. }
  369. /// <summary>
  370. /// Create a scale matrix with the specified X, Y and Z scaling values.
  371. /// </summary>
  372. /// <param name="scaleX">The amount of scaling in X dimension.</param>
  373. /// <param name="scaleY">The amount of scaling in Y dimension.</param>
  374. /// <param name="scaleZ">The amount of scaling in Z dimension.</param>
  375. /// <returns>New Matrix with the given scale</returns>
  376. public static Matrix CreateScale(float scaleX, float scaleY, float scaleZ)
  377. {
  378. return new Matrix(
  379. scaleX, 0f, 0f, 0f,
  380. 0f, scaleY, 0f, 0f,
  381. 0f, 0f, scaleZ, 0f,
  382. 0f, 0f, 0f, 1f);
  383. }
  384. #endregion
  385. #region CreateRotationX (Static)
  386. /// <summary>
  387. /// Create rotation around x-axis (pitch)
  388. /// <para />
  389. /// It is important to know which axis is meant with Yaw, Pitch and Roll:
  390. /// http://www.spotimage.com/dimap/spec/dictionary/Spot_Scene/Illustrations/YAW.gif
  391. /// </summary>
  392. public static Matrix CreateRotationX(float degrees)
  393. {
  394. //Check performance difference again:
  395. // Note: All CreateRotation implementations are slow, there are slightly
  396. // faster on XNA, but overall not much of a difference between all
  397. // platforms and frameworks (see below in MatrixPerformance class).
  398. float cosValue = MathHelper.Cos(degrees);
  399. float sinValue = MathHelper.Sin(degrees);
  400. return new Matrix(
  401. 1f, 0f, 0f, 0f,
  402. 0f, cosValue, sinValue, 0f,
  403. 0f, -sinValue, cosValue, 0f,
  404. 0f, 0f, 0f, 1f);
  405. }
  406. #endregion
  407. #region CreateRotationY (Static)
  408. /// <summary>
  409. /// Create rotation around y-axis (yaw)
  410. /// <para />
  411. /// It is important to know which axis is meant with Yaw, Pitch and Roll:
  412. /// http://www.spotimage.com/dimap/spec/dictionary/Spot_Scene/Illustrations/YAW.gif
  413. /// </summary>
  414. /// <param name="degrees">Degrees to rotate around the Y axis</param>
  415. /// <returns>New matrix with the given rotation</returns>
  416. public static Matrix CreateRotationY(float degrees)
  417. {
  418. //Check performance difference again:
  419. // Note: All CreateRotation implementations are slow, there are slightly
  420. // faster on XNA, but overall not much of a difference between all
  421. // platforms and frameworks (see below in MatrixPerformance class).
  422. float cosValue = MathHelper.Cos(degrees);
  423. float sinValue = MathHelper.Sin(degrees);
  424. return new Matrix(
  425. cosValue, 0f, -sinValue, 0f,
  426. 0f, 1f, 0f, 0f,
  427. sinValue, 0f, cosValue, 0f,
  428. 0f, 0f, 0f, 1f);
  429. }
  430. #endregion
  431. #region CreateRotationZ (Static)
  432. /// <summary>
  433. /// Create rotation around z-axis (roll)
  434. /// <para />
  435. /// It is important to know which axis is meant with Yaw, Pitch and Roll:
  436. /// http://www.spotimage.com/dimap/spec/dictionary/Spot_Scene/Illustrations/YAW.gif
  437. /// </summary>
  438. /// <param name="degrees">Degrees to rotate around the Z axis</param>
  439. /// <returns>New matrix with the given rotation</returns>
  440. public static Matrix CreateRotationZ(float degrees)
  441. {
  442. //Check performance difference again:
  443. // Note: All CreateRotation implementations are slow, there are slightly
  444. // faster on XNA, but overall not much of a difference between all
  445. // platforms and frameworks (see below in MatrixPerformance class).
  446. float cosValue = MathHelper.Cos(degrees);
  447. float sinValue = MathHelper.Sin(degrees);
  448. return new Matrix(
  449. cosValue, sinValue, 0f, 0f,
  450. -sinValue, cosValue, 0f, 0f,
  451. 0f, 0f, 1f, 0f,
  452. 0f, 0f, 0f, 1f);
  453. }
  454. #endregion
  455. #region CreateRotationZYX (Static)
  456. /// <summary>
  457. /// Rotate around the Z axis, then the Y axis, and finally the X axis
  458. /// (rotX * rotY * rotZ). Please note that this is not the same as
  459. /// FromYawPitchRoll, which uses Z (yaw), then X (pitch), then Y (roll).
  460. /// <para />
  461. /// Note: This is the same as calling the CreateRotationX, Y, Z methods,
  462. /// just combined all together without having to multiply matrices).
  463. /// </summary>
  464. /// <param name="x">Degrees to rotate around the X axis</param>
  465. /// <param name="y">Degrees to rotate around the Y axis</param>
  466. /// <param name="z">Degrees to rotate around the Z axis</param>
  467. /// <returns>New matrix with the given rotation</returns>
  468. public static Matrix CreateRotationZYX(float x, float y, float z)
  469. {
  470. float cx = 1;
  471. float sx = 0;
  472. float cy = 1;
  473. float sy = 0;
  474. float cz = 1;
  475. float sz = 0;
  476. if (x != 0.0f)
  477. {
  478. cx = MathHelper.Cos(x);
  479. sx = MathHelper.Sin(x);
  480. }
  481. if (y != 0.0f)
  482. {
  483. cy = MathHelper.Cos(y);
  484. sy = MathHelper.Sin(y);
  485. }
  486. if (z != 0.0f)
  487. {
  488. cz = MathHelper.Cos(z);
  489. sz = MathHelper.Sin(z);
  490. }
  491. return new Matrix(
  492. cy * cz, cy * sz, -sy, 0.0f,
  493. (sx * sy * cz) + (cx * -sz), (sx * sy * sz) + (cx * cz), sx * cy, 0.0f,
  494. (cx * sy * cz) + (sx * sz), (cx * sy * sz) + (-sx * cz), cx * cy, 0.0f,
  495. 0.0f, 0.0f, 0.0f, 1.0f);
  496. }
  497. #endregion
  498. #region CreateRotationZY (Static)
  499. /// <summary>
  500. /// Rotate around Z axis, then around Y axis.
  501. /// (equals rotY * rotZ)
  502. /// </summary>
  503. /// <param name="y">Degrees to rotate around the Y axis</param>
  504. /// <param name="z">Degrees to rotate around the Z axis</param>
  505. /// <returns>New matrix with the given rotation</returns>
  506. public static Matrix CreateRotationZY(float y, float z)
  507. {
  508. float cy = 1;
  509. float sy = 0;
  510. float cz = 1;
  511. float sz = 0;
  512. // We only calculate sin/cos if given input is not zero
  513. if (y != 0.0f)
  514. {
  515. cy = MathHelper.Cos(y);
  516. sy = MathHelper.Sin(y);
  517. }
  518. if (z != 0.0f)
  519. {
  520. cz = MathHelper.Cos(z);
  521. sz = MathHelper.Sin(z);
  522. }
  523. return new Matrix(
  524. cy * cz, cy * sz, -sy, 0.0f,
  525. -sz, cz, 0.0f, 0.0f,
  526. sy * cz, sy * sz, cy, 0.0f,
  527. 0.0f, 0.0f, 0.0f, 1.0f);
  528. }
  529. #endregion
  530. #region FromYawPitchRoll (Static)
  531. /// <summary>
  532. /// From yaw pitch roll (yaw = Y, pitch = X, roll = Z)
  533. /// http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm
  534. /// It is important to know which axis is meant with Yaw, Pitch and Roll:
  535. /// http://www.spotimage.com/dimap/spec/dictionary/Spot_Scene/Illustrations/YAW.gif
  536. /// <para />
  537. /// Note: This method calculates Y * X * Z where
  538. /// Y = value.X = yaw
  539. /// X = value.Y = pitch
  540. /// Z = value.Z = roll
  541. /// </summary>
  542. public static Matrix FromYawPitchRoll(Vector value)
  543. {
  544. return FromYawPitchRoll(value.X, value.Y, value.Z);
  545. }
  546. /// <summary>
  547. /// From yaw pitch roll (yaw = Y, pitch = X, roll = Z)
  548. /// It is important to know which axis is meant with Yaw, Pitch and Roll:
  549. /// http://www.spotimage.com/dimap/spec/dictionary/Spot_Scene/Illustrations/YAW.gif
  550. /// <para />
  551. /// Note: This method calculates Y * X * Z where
  552. /// Y = yaw
  553. /// X = pitch
  554. /// Z = roll
  555. /// </summary>
  556. public static Matrix FromYawPitchRoll(float yaw, float pitch, float roll)
  557. {
  558. //Check performance difference again:
  559. float cy = MathHelper.Cos(yaw);
  560. float sy = MathHelper.Sin(yaw);
  561. float cp = MathHelper.Cos(pitch);
  562. float sp = MathHelper.Sin(pitch);
  563. float cr = MathHelper.Cos(roll);
  564. float sr = MathHelper.Sin(roll);
  565. return new Matrix(
  566. cy * cr - sr * sy * sp, cy * sr + sy * sp * cr, -sy * cp, 0.0f,
  567. -sr * cp, cp * cr, sp, 0.0f,
  568. sy * cr + sp * cy * sr, sy * sr - sp * cy * cr, cy * cp, 0.0f,
  569. 0.0f, 0.0f, 0.0f, 1.0f);
  570. }
  571. #endregion
  572. #region CreateTranslation (Static)
  573. /// <summary>
  574. /// Create translation matrix
  575. /// </summary>
  576. /// <param name="position">Position</param>
  577. /// <returns>Identity matrix with the translation (M41, M42, M43)</returns>
  578. public static Matrix CreateTranslation(Vector position)
  579. {
  580. return new Matrix(
  581. 1f, 0f, 0f, 0f,
  582. 0f, 1f, 0f, 0f,
  583. 0f, 0f, 1f, 0f,
  584. position.X, position.Y, position.Z, 1.0f);
  585. }
  586. /// <summary>
  587. /// Create translation matrix
  588. /// </summary>
  589. /// <param name="x">x</param>
  590. /// <param name="y">y</param>
  591. /// <param name="z">z</param>
  592. /// <returns>Identity matrix with the translation (M41, M42, M43)</returns>
  593. public static Matrix CreateTranslation(float x, float y, float z)
  594. {
  595. return new Matrix(
  596. 1f, 0f, 0f, 0f,
  597. 0f, 1f, 0f, 0f,
  598. 0f, 0f, 1f, 0f,
  599. x, y, z, 1.0f);
  600. }
  601. #endregion
  602. #region CreateScaleAndTranslation (Static)
  603. /// <summary>
  604. /// Create scale and translation, basically just does CreateScale and
  605. /// CreateTranslation, but much more efficient.
  606. /// </summary>
  607. /// <param name="scale">Scale for the new matrix</param>
  608. /// <param name="position">Position for the matrix translation.</param>
  609. /// <returns>New matrix with the given scale and translation.</returns>
  610. public static Matrix CreateScaleAndTranslation(float scale,
  611. Vector position)
  612. {
  613. return new Matrix(
  614. scale, 0f, 0f, 0f,
  615. 0f, scale, 0f, 0f,
  616. 0f, 0f, scale, 0f,
  617. position.X, position.Y, position.Z, 1f);
  618. }
  619. #endregion
  620. #region CreateLookAt (Static)
  621. /// <summary>
  622. /// Creates a right-handed look at matrix for a camera.
  623. /// </summary>
  624. /// <param name="cameraPosition">camera position</param>
  625. /// <param name="cameraTarget">camera target</param>
  626. /// <param name="cameraUpVector">camera up vector</param>
  627. /// <returns>New look at matrix</returns>
  628. public static Matrix CreateLookAt(Vector cameraPosition,
  629. Vector cameraTarget, Vector cameraUpVector)
  630. {
  631. //Check performance difference again:
  632. // Slightly faster in XNA (5%), but overall all these implementations
  633. // are roughly the same.
  634. // Note: All Create view implementations are slow, there are slighly
  635. // faster on XNA, but overall not much of a difference between all
  636. // platforms and frameworks (see below in MatrixPerformance class).
  637. // Simplified and optimized formula to create look matrix
  638. Vector dir = Vector.Normalize(cameraPosition - cameraTarget);
  639. Vector up = Vector.Normalize(Vector.Cross(cameraUpVector, dir));
  640. // Switch up around if cameraUpVector is the same as dir!
  641. if (up.LengthSquared == 0)
  642. {
  643. up = Vector.UnitY;
  644. }
  645. Vector right = Vector.Cross(dir, up);
  646. return new Matrix(
  647. up.X, right.X, dir.X, 0f,
  648. up.Y, right.Y, dir.Y, 0f,
  649. up.Z, right.Z, dir.Z, 0f,
  650. -Vector.Dot(up, cameraPosition),
  651. -Vector.Dot(right, cameraPosition),
  652. -Vector.Dot(dir, cameraPosition), 1f);
  653. }
  654. #endregion
  655. #region CreateFromAxisAngle (Static)
  656. /// <summary>
  657. /// Create from axis angle (in degrees)
  658. /// </summary>
  659. /// <param name="angle">Angle in degrees</param>
  660. /// <param name="axis">Axis to rotate around</param>
  661. /// <returns>Rotated matrix around axis for rotations</returns>
  662. public static Matrix CreateFromAxisAngle(Vector axis, float angle)
  663. {
  664. // Note: Variables in this method need to be beautifulized!
  665. //Check performance difference again:
  666. Matrix matrix = new Matrix();
  667. float x = axis.X;
  668. float y = axis.Y;
  669. float z = axis.Z;
  670. float num2 = MathHelper.Sin(angle);
  671. float num = MathHelper.Cos(angle);
  672. float num11 = x * x;
  673. float num10 = y * y;
  674. float num9 = z * z;
  675. float num8 = x * y;
  676. float num7 = x * z;
  677. float num6 = y * z;
  678. matrix.M11 = num11 + (num * (1f - num11));
  679. matrix.M12 = (num8 - (num * num8)) + (num2 * z);
  680. matrix.M13 = (num7 - (num * num7)) - (num2 * y);
  681. matrix.M14 = 0f;
  682. matrix.M21 = (num8 - (num * num8)) - (num2 * z);
  683. matrix.M22 = num10 + (num * (1f - num10));
  684. matrix.M23 = (num6 - (num * num6)) + (num2 * x);
  685. matrix.M24 = 0f;
  686. matrix.M31 = (num7 - (num * num7)) + (num2 * y);
  687. matrix.M32 = (num6 - (num * num6)) - (num2 * x);
  688. matrix.M33 = num9 + (num * (1f - num9));
  689. matrix.M34 = 0f;
  690. matrix.M41 = 0f;
  691. matrix.M42 = 0f;
  692. matrix.M43 = 0f;
  693. matrix.M44 = 1f;
  694. return matrix;
  695. }
  696. /// <summary>
  697. /// Create from axis angle (in degrees)
  698. /// </summary>
  699. /// <param name="angle">Angle in degrees</param>
  700. /// <param name="axis">Axis to rotate around</param>
  701. /// <param name="result">Rotated matrix around axis for rotations</param>
  702. public static void CreateFromAxisAngle(ref Vector axis, float angle,
  703. ref Matrix result)
  704. {
  705. float x = axis.X;
  706. float y = axis.Y;
  707. float z = axis.Z;
  708. float num2 = MathHelper.Sin(angle);
  709. float num = MathHelper.Cos(angle);
  710. float num11 = x * x;
  711. float num10 = y * y;
  712. float num9 = z * z;
  713. float num8 = x * y;
  714. float num7 = x * z;
  715. float num6 = y * z;
  716. result.M11 = num11 + (num * (1f - num11));
  717. result.M12 = (num8 - (num * num8)) + (num2 * z);
  718. result.M13 = (num7 - (num * num7)) - (num2 * y);
  719. result.M14 = 0f;
  720. result.M21 = (num8 - (num * num8)) - (num2 * z);
  721. result.M22 = num10 + (num * (1f - num10));
  722. result.M23 = (num6 - (num * num6)) + (num2 * x);
  723. result.M24 = 0f;
  724. result.M31 = (num7 - (num * num7)) + (num2 * y);
  725. result.M32 = (num6 - (num * num6)) - (num2 * x);
  726. result.M33 = num9 + (num * (1f - num9));
  727. result.M34 = 0f;
  728. result.M41 = 0f;
  729. result.M42 = 0f;
  730. result.M43 = 0f;
  731. result.M44 = 1f;
  732. }
  733. #endregion
  734. #region CreateOrthographic (Static)
  735. /// <summary>
  736. /// Create orthographic
  737. /// </summary>
  738. /// <param name="bottom">bottom</param>
  739. /// <param name="farPlane">farPlane</param>
  740. /// <param name="left">left</param>
  741. /// <param name="nearPlane">near plane</param>
  742. /// <param name="right">right</param>
  743. /// <param name="top">top</param>
  744. /// <returns>New Orthographic matrix</returns>
  745. public static Matrix CreateOrthographic(float left, float right,
  746. float bottom, float top, float nearPlane, float farPlane)
  747. {
  748. //Check performance difference again:
  749. Matrix matrix = new Matrix();
  750. matrix.M11 = 2f / (right - left);
  751. matrix.M12 = matrix.M13 = matrix.M14 = 0f;
  752. matrix.M22 = 2f / (top - bottom);
  753. matrix.M21 = matrix.M23 = matrix.M24 = 0f;
  754. matrix.M33 = 1f / (nearPlane - farPlane);
  755. matrix.M31 = matrix.M32 = matrix.M34 = 0f;
  756. matrix.M41 = (left + right) / (left - right);
  757. matrix.M42 = (top + bottom) / (bottom - top);
  758. matrix.M43 = nearPlane / (nearPlane - farPlane);
  759. matrix.M44 = 1f;
  760. return matrix;
  761. }
  762. #endregion
  763. #region CreatePerspective (Static)
  764. /// <summary>
  765. /// Creates a right-handed perspective field of view matrix for a camera.
  766. /// </summary>
  767. /// <param name="fieldOfView">Field of view (Warning: In radians)</param>
  768. /// <param name="aspectRatio">Aspect ratio</param>
  769. /// <param name="nearPlaneDistance">Near plane distance</param>
  770. /// <param name="farPlaneDistance">Far plane distance</param>
  771. /// <returns>Matrix</returns>
  772. public static Matrix CreatePerspective(float fieldOfView,
  773. float aspectRatio, float nearPlaneDistance, float farPlaneDistance)
  774. {
  775. //Check performance difference again:
  776. // Slightly faster in Delta (10%), but overall all this implementations
  777. // are ruffly the same. Same as the following, just faster:
  778. //XnaMatrix.CreatePerspectiveFieldOfView(...)
  779. //TKMatrix.CreatePerspectiveFieldOfView(...)
  780. //SlimDX.Matrix.PerspectiveFovRH(...)
  781. float m22 = 1f / ((float)Math.Tan(fieldOfView * 0.5f));
  782. return new Matrix(
  783. m22 / aspectRatio, 0f, 0f, 0f,
  784. 0f, m22, 0f, 0f,
  785. 0f, 0f, farPlaneDistance / (nearPlaneDistance - farPlaneDistance), -1f,
  786. 0f, 0f, (nearPlaneDistance * farPlaneDistance) /
  787. (nearPlaneDistance - farPlaneDistance), 0f);
  788. }
  789. #endregion
  790. #region Multiply (Static)
  791. /// <summary>
  792. /// Ugly Matrix Multiply method (normally you would just use
  793. /// matrix1*matrix2 to multiply two matrices), but this one is a little
  794. /// faster because we don't need to copy over the matrix values and we can
  795. /// even provide a faster fallback on the xbox platform (using xna).
  796. /// Note: matrix1 or matrix2 can be the same as result, which results in
  797. /// even better performance for performance critical matrix multiply code.
  798. /// </summary>
  799. /// <param name="matrix1">matrix1</param>
  800. /// <param name="matrix2">matrix2</param>
  801. /// <param name="result">result</param>
  802. public static void Multiply(ref Matrix matrix1, ref Matrix matrix2,
  803. ref Matrix result)
  804. {
  805. //Check performance difference again:
  806. // Note: Values are stored in local variables to allow matrix1 or matrix2
  807. // and result be the same matrices (we won't overwrite M11-M44 while
  808. // calculating the result), which happens quite often in optimized code!
  809. float m11 = matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21 +
  810. matrix1.M13 * matrix2.M31 + matrix1.M14 * matrix2.M41;
  811. float m12 = matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22 +
  812. matrix1.M13 * matrix2.M32 + matrix1.M14 * matrix2.M42;
  813. float m13 = matrix1.M11 * matrix2.M13 + matrix1.M12 * matrix2.M23 +
  814. matrix1.M13 * matrix2.M33 + matrix1.M14 * matrix2.M43;
  815. float m14 = matrix1.M11 * matrix2.M14 + matrix1.M12 * matrix2.M24 +
  816. matrix1.M13 * matrix2.M34 + matrix1.M14 * matrix2.M44;
  817. float m21 = matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21 +
  818. matrix1.M23 * matrix2.M31 + matrix1.M24 * matrix2.M41;
  819. float m22 = matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22 +
  820. matrix1.M23 * matrix2.M32 + matrix1.M24 * matrix2.M42;
  821. float m23 = matrix1.M21 * matrix2.M13 + matrix1.M22 * matrix2.M23 +
  822. matrix1.M23 * matrix2.M33 + matrix1.M24 * matrix2.M43;
  823. float m24 = matrix1.M21 * matrix2.M14 + matrix1.M22 * matrix2.M24 +
  824. matrix1.M23 * matrix2.M34 + matrix1.M24 * matrix2.M44;
  825. float m31 = matrix1.M31 * matrix2.M11 + matrix1.M32 * matrix2.M21 +
  826. matrix1.M33 * matrix2.M31 + matrix1.M34 * matrix2.M41;
  827. float m32 = matrix1.M31 * matrix2.M12 + matrix1.M32 * matrix2.M22 +
  828. matrix1.M33 * matrix2.M32 + matrix1.M34 * matrix2.M42;
  829. float m33 = matrix1.M31 * matrix2.M13 + matrix1.M32 * matrix2.M23 +
  830. matrix1.M33 * matrix2.M33 + matrix1.M34 * matrix2.M43;
  831. float m34 = matrix1.M31 * matrix2.M14 + matrix1.M32 * matrix2.M24 +
  832. matrix1.M33 * matrix2.M34 + matrix1.M34 * matrix2.M44;
  833. float m41 = matrix1.M41 * matrix2.M11 + matrix1.M42 * matrix2.M21 +
  834. matrix1.M43 * matrix2.M31 + matrix1.M44 * matrix2.M41;
  835. float m42 = matrix1.M41 * matrix2.M12 + matrix1.M42 * matrix2.M22 +
  836. matrix1.M43 * matrix2.M32 + matrix1.M44 * matrix2.M42;
  837. float m43 = matrix1.M41 * matrix2.M13 + matrix1.M42 * matrix2.M23 +
  838. matrix1.M43 * matrix2.M33 + matrix1.M44 * matrix2.M43;
  839. float m44 = matrix1.M41 * matrix2.M14 + matrix1.M42 * matrix2.M24 +
  840. matrix1.M43 * matrix2.M34 + matrix1.M44 * matrix2.M44;
  841. result.M11 = m11;
  842. result.M12 = m12;
  843. result.M13 = m13;
  844. result.M14 = m14;
  845. result.M21 = m21;
  846. result.M22 = m22;
  847. result.M23 = m23;
  848. result.M24 = m24;
  849. result.M31 = m31;
  850. result.M32 = m32;
  851. result.M33 = m33;
  852. result.M34 = m34;
  853. result.M41 = m41;
  854. result.M42 = m42;
  855. result.M43 = m43;
  856. result.M44 = m44;
  857. }
  858. /// <summary>
  859. /// Ugly Matrix*Vector multiply method (basically transforming a vector
  860. /// with a matrix). Normally you would just use matrix*position to
  861. /// multiply, but this one is a little faster because we don't need to
  862. /// copy over the matrix values and we can even provide a faster fallback
  863. /// on the xbox platform (using xna).
  864. /// Note: position and result can be the same as result, which results in
  865. /// even better performance for performance critical matrix multiply code.
  866. /// </summary>
  867. /// <param name="matrix">matrix</param>
  868. /// <param name="position">position</param>
  869. /// <param name="result">result</param>
  870. public static void Multiply(ref Matrix matrix, ref Vector position,
  871. ref Vector result)
  872. {
  873. //Check performance difference again:
  874. float x = position.X * matrix.M11 + position.Y * matrix.M21 +
  875. position.Z * matrix.M31 + matrix.M41;
  876. float y = position.X * matrix.M12 + position.Y * matrix.M22 +
  877. position.Z * matrix.M32 + matrix.M42;
  878. float z = position.X * matrix.M13 + position.Y * matrix.M23 +
  879. position.Z * matrix.M33 + matrix.M43;
  880. result.X = x;
  881. result.Y = y;
  882. result.Z = z;
  883. }
  884. /// <summary>
  885. /// Multiply matrix with a scale factor. Note: This is the slow version.
  886. /// This method needs to copy 128 bytes around (2 matrices, each 64 bytes).
  887. /// Try to use the ref version instead.
  888. /// </summary>
  889. /// <param name="matrix">Matrix</param>
  890. /// <param name="scaleFactor">Scale factor</param>
  891. /// <returns>
  892. /// New matrix with each value multiplied with scaleFactor
  893. /// </returns>
  894. public static Matrix Multiply(Matrix matrix, float scaleFactor)
  895. {
  896. return new Matrix(
  897. matrix.M11 * scaleFactor, matrix.M12 * scaleFactor,
  898. matrix.M13 * scaleFactor, matrix.M14 * scaleFactor,
  899. matrix.M21 * scaleFactor, matrix.M22 * scaleFactor,
  900. matrix.M23 * scaleFactor, matrix.M24 * scaleFactor,
  901. matrix.M31 * scaleFactor, matrix.M32 * scaleFactor,
  902. matrix.M33 * scaleFactor, matrix.M34 * scaleFactor,
  903. matrix.M41 * scaleFactor, matrix.M42 * scaleFactor,
  904. matrix.M43 * scaleFactor, matrix.M44 * scaleFactor);
  905. }
  906. #endregion
  907. #region Equal (Static)
  908. /// <summary>
  909. /// Equality check. Optimized for speed via ref parameters, which
  910. /// are much faster than copying 128 bytes (2 matrices each 64 bytes).
  911. /// </summary>
  912. public static bool Equal(ref Matrix matrix1, ref Matrix matrix2)
  913. {
  914. // Note: The checks are sorted by the diagonal, which is most important
  915. // and usually the most different for 2 matrices (faster this way!)
  916. // Note: Calling XNA (and OpenTK or SlimDX or SharpDX too) here is
  917. // slower! So it is disabled.
  918. return
  919. matrix1.M11 == matrix2.M11 &&
  920. matrix1.M22 == matrix2.M22 &&
  921. matrix1.M33 == matrix2.M33 &&
  922. matrix1.M44 == matrix2.M44 &&
  923. // Now check the rest if the diagonal matrices were the same.
  924. matrix1.M12 == matrix2.M12 &&
  925. matrix1.M13 == matrix2.M13 &&
  926. matrix1.M14 == matrix2.M14 &&
  927. matrix1.M21 == matrix2.M21 &&
  928. matrix1.M23 == matrix2.M23 &&
  929. matrix1.M24 == matrix2.M24 &&
  930. matrix1.M31 == matrix2.M31 &&
  931. matrix1.M32 == matrix2.M32 &&
  932. matrix1.M34 == matrix2.M34 &&
  933. matrix1.M41 == matrix2.M41 &&
  934. matrix1.M42 == matrix2.M42 &&
  935. matrix1.M43 == matrix2.M43;
  936. }
  937. #endregion
  938. #region Unequal (Static)
  939. /// <summary>
  940. /// Inequality check. Optimized for speed via ref parameters, which
  941. /// are much faster than copying 128 bytes (2 matrices each 64 bytes).
  942. /// </summary>
  943. public static bool Unequal(ref Matrix matrix1, ref Matrix matrix2)
  944. {
  945. // Check if everything is equal, if not return true (this is the unequal
  946. // check), might look a bit strange, but it is fast because of early out.
  947. if (matrix1.M11 == matrix2.M11 &&
  948. matrix1.M12 == matrix2.M12 &&
  949. matrix1.M13 == matrix2.M13 &&
  950. matrix1.M14 == matrix2.M14 &&
  951. matrix1.M21 == matrix2.M21 &&
  952. matrix1.M22 == matrix2.M22 &&
  953. matrix1.M23 == matrix2.M23 &&
  954. matrix1.M24 == matrix2.M24 &&
  955. matrix1.M31 == matrix2.M31 &&
  956. matrix1.M32 == matrix2.M32 &&
  957. matrix1.M33 == matrix2.M33 &&
  958. matrix1.M34 == matrix2.M34 &&
  959. matrix1.M41 == matrix2.M41 &&
  960. matrix1.M42 == matrix2.M42 &&
  961. matrix1.M43 == matrix2.M43)
  962. {
  963. return (matrix1.M44 != matrix2.M44);
  964. }
  965. return true;
  966. }
  967. #endregion
  968. #region TranslationNearlyEqual (Static)
  969. /// <summary>
  970. /// Nearly equals (only checks the Translation of the matrices), two
  971. /// matrices are considered nearly equal if their translation distance
  972. /// is below MathHelper.Epsilon. Use the MatrixNearlyEqual method to
  973. /// compare two complete matrices (much slower than this check).
  974. /// </summary>
  975. /// <param name="matrix1">Matrix 1 to compare</param>
  976. /// <param name="matrix2">Matrix 2 to compare</param>
  977. /// <returns>True if the translations of the matrices are almost the
  978. /// same, false otherwise.</returns>
  979. public static bool TranslationNearlyEqual(ref Matrix matrix1,
  980. ref Matrix matrix2)
  981. {
  982. // Check to see if the absolute difference of each value is smaller than
  983. // the Nearly Equal value.
  984. return (matrix1.Translation - matrix2.Translation).LengthSquared <
  985. MathHelper.Epsilon * MathHelper.Epsilon;
  986. }
  987. #endregion
  988. #region MatrixNearlyEqual (Static)
  989. /// <summary>
  990. /// Matrix nearly equal helper method to compare two matrices that might
  991. /// not be 100% the same because of rounding errors, but they produce
  992. /// pretty much the same results.
  993. /// </summary>
  994. /// <param name="matrix1">Matrix 1 to compare</param>
  995. /// <param name="matrix2">Matrix 2 to compare</param>
  996. /// <returns>True if the matrices are almost the same</returns>
  997. public static bool MatrixNearlyEqual(ref Matrix mat1, ref Matrix mat2)
  998. {
  999. return
  1000. mat1.M11.NearlyEqual(mat2.M11) &&
  1001. mat1.M12.NearlyEqual(mat2.M12) &&
  1002. mat1.M13.NearlyEqual(mat2.M13) &&
  1003. mat1.M14.NearlyEqual(mat2.M14) &&
  1004. mat1.M21.NearlyEqual(mat2.M21) &&
  1005. mat1.M22.NearlyEqual(mat2.M22) &&
  1006. mat1.M23.NearlyEqual(mat2.M23) &&
  1007. mat1.M24.NearlyEqual(mat2.M24) &&
  1008. mat1.M31.NearlyEqual(mat2.M31) &&
  1009. mat1.M32.NearlyEqual(mat2.M32) &&
  1010. mat1.M33.NearlyEqual(mat2.M33) &&
  1011. mat1.M34.NearlyEqual(mat2.M34) &&
  1012. mat1.M41.NearlyEqual(mat2.M41) &&
  1013. mat1.M42.NearlyEqual(mat2.M42) &&
  1014. mat1.M43.NearlyEqual(mat2.M43) &&
  1015. mat1.M44.NearlyEqual(mat2.M44);
  1016. }
  1017. #endregion
  1018. #region FromQuaternion (Static)
  1019. /// <summary>
  1020. /// From quaternion
  1021. /// </summary>
  1022. public static Matrix FromQuaternion(Quaternion quaternion)
  1023. {
  1024. //Check performance difference again:
  1025. // Note: We can only be sure that this is faster on the Xbox, not used
  1026. // often anyway.
  1027. //Note: This could need some more comments, help, etc.
  1028. float qxx = quaternion.X * quaternion.X;
  1029. float qyy = quaternion.Y * quaternion.Y;
  1030. float qzz = quaternion.Z * quaternion.Z;
  1031. float qxy = quaternion.X * quaternion.Y;
  1032. float qzw = quaternion.Z * quaternion.W;
  1033. float qwx = quaternion.Z * quaternion.X;
  1034. float qyw = quaternion.Y * quaternion.W;
  1035. float qyz = quaternion.Y * quaternion.Z;
  1036. float qxw = quaternion.X * quaternion.W;
  1037. return new Matrix(
  1038. 1f - (2f * (qyy + qzz)), 2f * (qxy + qzw), 2f * (qwx - qyw), 0f,
  1039. 2f * (qxy - qzw), 1f - (2f * (qzz + qxx)), 2f * (qyz + qxw), 0f,
  1040. 2f * (qwx + qyw), 2f * (qyz - qxw), 1f - (2f * (qyy + qxx)), 0f,
  1041. 0f, 0f, 0f, 1f);
  1042. }
  1043. #endregion
  1044. #region FromString (Static)
  1045. /// <summary>
  1046. /// Convert a string to a Matrix. The expected format is
  1047. /// (M11, M12, M13, M14)
  1048. /// (M21, M22, M23, M24)
  1049. /// ...
  1050. /// </summary>
  1051. /// <param name="matrixString">The string containing the values in the
  1052. /// correct format.</param>
  1053. public static Matrix FromString(string matrixString)
  1054. {
  1055. // First remove the brackets
  1056. matrixString = matrixString.
  1057. Replace("(", "").
  1058. Replace(")", "");
  1059. // And split the string up into seperate values.
  1060. string[] matrixStrings = matrixString.SplitAndTrim(',');
  1061. //StringSplitOptions.RemoveEmptyEntries);
  1062. // Then check if the length is 16 for 16 values and return the new matrix.
  1063. // If the length is not 16 than return Matrix.Identity.
  1064. if (matrixStrings.Length == 16)
  1065. {
  1066. return new Matrix(
  1067. // First row
  1068. matrixStrings[0].FromInvariantString(1.0f),
  1069. matrixStrings[1].FromInvariantString(0.0f),
  1070. matrixStrings[2].FromInvariantString(0.0f),
  1071. matrixStrings[3].FromInvariantString(0.0f),
  1072. // Second row
  1073. matrixStrings[4].FromInvariantString(0.0f),
  1074. matrixStrings[5].FromInvariantString(1.0f),
  1075. matrixStrings[6].FromInvariantString(0.0f),
  1076. matrixStrings[7].FromInvariantString(0.0f),
  1077. // Third row
  1078. matrixStrings[8].FromInvariantString(0.0f),
  1079. matrixStrings[9].FromInvariantString(0.0f),
  1080. matrixStrings[10].FromInvariantString(1.0f),
  1081. matrixStrings[11].FromInvariantString(0.0f),
  1082. // Forth row
  1083. matrixStrings[12].FromInvariantString(0.0f),
  1084. matrixStrings[13].FromInvariantString(0.0f),
  1085. matrixStrings[14].FromInvariantString(0.0f),
  1086. matrixStrings[15].FromInvariantString(1.0f));
  1087. }
  1088. Log.Warning("Unable to convert matrixString=" + matrixString +
  1089. " because it has not exactly 16 float values!");
  1090. return Identity;
  1091. }
  1092. #endregion
  1093. #region Identity (Static)
  1094. /// <summary>
  1095. /// Returns a new identity matrix (no rotation, translation or scaling)!
  1096. /// Note: Not readonly to allow passing it by ref, but this should never
  1097. /// be set!
  1098. /// </summary>
  1099. public static Matrix Identity = new Matrix(
  1100. 1, 0, 0, 0,
  1101. 0, 1, 0, 0,
  1102. 0, 0, 1, 0,
  1103. 0, 0, 0, 1);
  1104. #endregion
  1105. #region Framework Union Defines (Public)
  1106. #endregion
  1107. #region M11 (Public)
  1108. /// <summary>
  1109. /// 1st row - 1st column value of the Matrix.
  1110. /// </summary>
  1111. [FieldOffset(0)]
  1112. public float M11;
  1113. #endregion
  1114. #region M12 (Public)
  1115. /// <summary>
  1116. /// 1st row - 2nd column value of the Matrix.
  1117. /// </summary>
  1118. [FieldOffset(4)]
  1119. public float M12;
  1120. #endregion
  1121. #region M13 (Public)
  1122. /// <summary>
  1123. /// 1st row - 3rd column value of the Matrix.
  1124. /// </summary>
  1125. [FieldOffset(8)]
  1126. public float M13;
  1127. #endregion
  1128. #region M14 (Public)
  1129. /// <summary>
  1130. /// 1st row - 4th column value of the Matrix.
  1131. /// </summary>
  1132. [FieldOffset(12)]
  1133. public float M14;
  1134. #endregion
  1135. #region M21 (Public)
  1136. /// <summary>
  1137. /// 2nd row - 1st column value of the Matrix.
  1138. /// </summary>
  1139. [FieldOffset(16)]
  1140. public float M21;
  1141. #endregion
  1142. #region M22 (Public)
  1143. /// <summary>
  1144. /// 2nd row - 2nd column value of the Matrix.
  1145. /// </summary>
  1146. [FieldOffset(20)]
  1147. public float M22;
  1148. #endregion
  1149. #region M23 (Public)
  1150. /// <summary>
  1151. /// 2nd row - 3nd column value of the Matrix.
  1152. /// </summary>
  1153. [FieldOffset(24)]
  1154. public float M23;
  1155. #endregion
  1156. #region M24 (Public)
  1157. /// <summary>
  1158. /// 2nd row - 4nd column value of the Matrix.
  1159. /// </summary>
  1160. [FieldOffset(28)]
  1161. public float M24;
  1162. #endregion
  1163. #region M31 (Public)
  1164. /// <summary>
  1165. /// 3rd row - 1st column value of the Matrix.
  1166. /// </summary>
  1167. [FieldOffset(32)]
  1168. public float M31;
  1169. #endregion
  1170. #region M32 (Public)
  1171. /// <summary>
  1172. /// 3rd row - 2nd column value of the Matrix.
  1173. /// </summary>
  1174. [FieldOffset(36)]
  1175. public float M32;
  1176. #endregion
  1177. #region M33 (Public)
  1178. /// <summary>
  1179. /// 3rd row - 3rd column value of the Matrix.
  1180. /// </summary>
  1181. [FieldOffset(40)]
  1182. public float M33;
  1183. #endregion
  1184. #region M34 (Public)
  1185. /// <summary>
  1186. /// 3rd row - 4th column value of the Matrix.
  1187. /// </summary>
  1188. [FieldOffset(44)]
  1189. public float M34;
  1190. #endregion
  1191. #region M41 (Public)
  1192. /// <summary>
  1193. /// 4th row - 1st column value of the Matrix.
  1194. /// </summary>
  1195. [FieldOffset(48)]
  1196. public float M41;
  1197. #endregion
  1198. #region M42 (Public)
  1199. /// <summary>
  1200. /// 4th row - 2nd column value of the Matrix.
  1201. /// </summary>
  1202. [FieldOffset(52)]
  1203. public float M42;
  1204. #endregion
  1205. #region M43 (Public)
  1206. /// <summary>
  1207. /// 4th row - 3rd column value of the Matrix.
  1208. /// </summary>
  1209. [FieldOffset(56)]
  1210. public float M43;
  1211. #endregion
  1212. #region M44 (Public)
  1213. /// <summary>
  1214. /// 4th row - 4th column value of the Matrix.
  1215. /// </summary>
  1216. [FieldOffset(60)]
  1217. public float M44;
  1218. #endregion
  1219. #region Translation (Public)
  1220. /// <summary>
  1221. /// The translation amount representing by the matrix.
  1222. /// This vector shares the same data as M41, M42, M43, see above!
  1223. /// </summary>
  1224. [FieldOffset(48)]
  1225. public Vector Translation;
  1226. #endregion
  1227. #region Scaling (Public)
  1228. /// <summary>
  1229. /// The scaling amount representing by the matrix.
  1230. /// </summary>
  1231. public Vector Scaling
  1232. {
  1233. get
  1234. {
  1235. return new Vector(M11, M22, M33);
  1236. }
  1237. set
  1238. {
  1239. M11 = value.X;
  1240. M22 = value.Y;
  1241. M33 = value.Z;
  1242. }
  1243. }
  1244. #endregion
  1245. #region Right (Public)
  1246. /// <summary>
  1247. /// Right vector of the matrix (M11, M12, M13)
  1248. /// </summary>
  1249. [FieldOffset(0)]
  1250. public Vector Right;
  1251. #endregion
  1252. #region Up (Public)
  1253. /// <summary>
  1254. /// Up vector of the matrix (M21, M22, M23)
  1255. /// </summary>
  1256. [FieldOffset(16)]
  1257. public Vector Up;
  1258. #endregion
  1259. #region Front (Public)
  1260. /// <summary>
  1261. /// Front vector of the matrix (M31, M32, M33)
  1262. /// </summary>
  1263. [FieldOffset(32)]
  1264. public Vector Front;
  1265. #endregion
  1266. #region Determinant (Public)
  1267. /// <summary>
  1268. /// Returns the determinant of the matrix. Uses optimized code paths
  1269. /// because XNAs implementation is twice as fast as ours, OpenTK is
  1270. /// slightly faster (see MatrixPerformance class).
  1271. /// </summary>
  1272. public float Determinant
  1273. {
  1274. get
  1275. {
  1276. //Check performance difference again:
  1277. // Ugly hacky code that is actually lots faster than our clean
  1278. // implementation below (see link on how it works basically).
  1279. float m44x33m43x34 = (M44 * M33) - (M43 * M34);
  1280. float m32x44m42x34 = (M32 * M44) - (M42 * M34);
  1281. float m32x43m42x33 = (M32 * M43) - (M42 * M33);
  1282. float m31x44m41x34 = (M31 * M44) - (M41 * M34);
  1283. float m31x43m41x33 = (M31 * M43) - (M41 * M33);
  1284. float m31x42m41x32 = (M31 * M42) - (M41 * M32);
  1285. return (((((((M22 * m44x33m43x34) - (M23 * m32x44m42x34)) +
  1286. (M24 * m32x43m42x33)) * M11) - ((((M21 * m44x33m43x34) -
  1287. (M23 * m31x44m41x34)) +
  1288. (M24 * m31x43m41x33)) * M12)) +
  1289. ((((M21 * m32x44m42x34) - (M22 * m31x44m41x34)) +
  1290. (M24 * m31x42m41x32)) * M13)) - ((((M21 * m32x43m42x33) -
  1291. (M22 * m31x43m41x33)) +
  1292. (M23 * m31x42m41x32)) * M14));
  1293. }
  1294. }
  1295. #endregion
  1296. #region Inverse (Public)
  1297. /// <summary>
  1298. /// Returns the inverse of the current matrix, which can be slightly
  1299. /// faster than using the static Invert method below depending on the
  1300. /// platform (e.g. with XNA), with Delta matrix code Invert() is a little
  1301. /// faster however, so just use whatever method fits best for you.
  1302. /// Both methods are the slowest matrix methods however, use with care!
  1303. /// </summary>
  1304. public Matrix Inverse
  1305. {
  1306. get
  1307. {
  1308. //Check performance difference again:
  1309. // Ugly hacky code that is actually lots faster than our clean
  1310. // implementation below (see link on how it works basically).
  1311. // This is basically getting the Determinant and then also calculating
  1312. // the inverse matrix in one big ugly swoop.
  1313. float m33x44m34x43 = (M33 * M44) - (M34 * M43);
  1314. float m32x44m34x42 = (M32 * M44) - (M34 * M42);
  1315. float m32x43m33x42 = (M32 * M43) - (M33 * M42);
  1316. float m31x44m34x41 = (M31 * M44) - (M34 * M41);
  1317. float m31x43m33x41 = (M31 * M43) - (M33 * M41);
  1318. float m31x42m32x41 = (M31 * M42) - (M32 * M41);
  1319. float m22m23m24 = ((M22 * m33x44m34x43) - (M23 * m32x44m34x42)) +
  1320. (M24 * m32x43m33x42);
  1321. float m21m23m24 = -(((M21 * m33x44m34x43) - (M23 * m31x44m34x41)) +
  1322. (M24 * m31x43m33x41));
  1323. float m21m22m24 = ((M21 * m32x44m34x42) - (M22 * m31x44m34x41)) +
  1324. (M24 * m31x42m32x41);
  1325. float m21m22m23 = -(((M21 * m32x43m33x42) - (M22 * m31x43m33x41)) +
  1326. (M23 * m31x42m32x41));
  1327. // Inverse(A) = 1/det(A) * B
  1328. float inverseDet = 1f / ((((M11 * m22m23m24) + (M12 * m21m23m24)) +
  1329. (M13 * m21m22m24)) + (M14 * m21m22m23));
  1330. float m23x44m24x43 = (M23 * M44) - (M24 * M43);
  1331. float m22x44m24x42 = (M22 * M44) - (M24 * M42);
  1332. float m22x43m23x42 = (M22 * M43) - (M23 * M42);
  1333. float m21x44m24x41 = (M21 * M44) - (M24 * M41);
  1334. float m21x43m23x41 = (M21 * M43) - (M23 * M41);
  1335. float m21x42m22x41 = (M21 * M42) - (M22 * M41);
  1336. float m23x34m24x33 = (M23 * M34) - (M24 * M33);
  1337. float m22x34m24x32 = (M22 * M34) - (M24 * M32);
  1338. float m22x33m23x32 = (M22 * M33) - (M23 * M32);
  1339. float m21x34m23x31 = (M21 * M34) - (M24 * M31);
  1340. float m21x33m23x31 = (M21 * M33) - (M23 * M31);
  1341. float m21x32m22x31 = (M21 * M32) - (M22 * M31);
  1342. return new Matrix(
  1343. m22m23m24 * inverseDet,
  1344. -(((M12 * m33x44m34x43) - (M13 * m32x44m34x42)) +
  1345. (M14 * m32x43m33x42)) * inverseDet,
  1346. (((M12 * m23x44m24x43) - (M13 * m22x44m24x42)) +
  1347. (M14 * m22x43m23x42)) * inverseDet,
  1348. -(((M12 * m23x34m24x33) - (M13 * m22x34m24x32)) +
  1349. (M14 * m22x33m23x32)) * inverseDet,
  1350. m21m23m24 * inverseDet,
  1351. (((M11 * m33x44m34x43) - (M13 * m31x44m34x41)) +
  1352. (M14 * m31x43m33x41)) * inverseDet,
  1353. -(((M11 * m23x44m24x43) - (M13 * m21x44m24x41)) +
  1354. (M14 * m21x43m23x41)) * inverseDet,
  1355. (((M11 * m23x34m24x33) - (M13 * m21x34m23x31)) +
  1356. (M14 * m21x33m23x31)) * inverseDet,
  1357. m21m22m24 * inverseDet,
  1358. -(((M11 * m32x44m34x42) - (M12 * m31x44m34x41)) +
  1359. (M14 * m31x42m32x41)) * inverseDet,
  1360. (((M11 * m22x44m24x42) - (M12 * m21x44m24x41)) +
  1361. (M14 * m21x42m22x41)) * inverseDet,
  1362. -(((M11 * m22x34m24x32) - (M12 * m21x34m23x31)) +
  1363. (M14 * m21x32m22x31)) * inverseDet,
  1364. m21m22m23 * inverseDet,
  1365. (((M11 * m32x43m33x42) - (M12 * m31x43m33x41)) +
  1366. (M13 * m31x42m32x41)) * inverseDet,
  1367. -(((M11 * m22x43m23x42) - (M12 * m21x43m23x41)) +
  1368. (M13 * m21x42m22x41)) * inverseDet,
  1369. (((M11 * m22x33m23x32) - (M12 * m21x33m23x31)) +
  1370. (M13 * m21x32m22x31)) * inverseDet);
  1371. }
  1372. }
  1373. #endregion
  1374. #region Constructors
  1375. /// <summary>
  1376. /// Create matrix by passing all the necessary values
  1377. /// </summary>
  1378. /// <param name="setM11">Set M11 value (first row)</param>
  1379. /// <param name="setM12">Set M12 value (first row)</param>
  1380. /// <param name="setM13">Set M13 value (first row)</param>
  1381. /// <param name="setM14">Set M14 value (first row)</param>
  1382. /// <param name="setM21">Set M21 value (second row)</param>
  1383. /// <param name="setM22">Set M22 value (second row)</param>
  1384. /// <param name="setM23">Set M23 value (second row)</param>
  1385. /// <param name="setM24">Set M24 value (second row)</param>
  1386. /// <param name="setM31">Set M31 value (third row)</param>
  1387. /// <param name="setM32">Set M32 value (third row)</param>
  1388. /// <param name="setM33">Set M33 value (third row)</param>
  1389. /// <param name="setM34">Set M34 value (third row)</param>
  1390. /// <param name="setM41">Set M41 value (forth row)</param>
  1391. /// <param name="setM42">Set M42 value (forth row)</param>
  1392. /// <param name="setM43">Set M43 value (forth row)</param>
  1393. /// <param name="setM44">Set M44 value (forth row)</param>
  1394. public Matrix(float setM11, float setM12, float setM13, float setM14,
  1395. float setM21, float setM22, float setM23, float setM24,
  1396. float setM31, float setM32, float setM33, float setM34,
  1397. float setM41, float setM42, float setM43, float setM44)
  1398. : this()
  1399. {
  1400. M11 = setM11;
  1401. M12 = setM12;
  1402. M13 = setM13;
  1403. M14 = setM14;
  1404. M21 = setM21;
  1405. M22 = setM22;
  1406. M23 = setM23;
  1407. M24 = setM24;
  1408. M31 = setM31;
  1409. M32 = setM32;
  1410. M33 = setM33;
  1411. M34 = setM34;
  1412. M41 = setM41;
  1413. M42 = setM42;
  1414. M43 = setM43;
  1415. M44 = setM44;
  1416. }
  1417. /// <summary>
  1418. /// Create matrix by passing all 16 matrix values as array.
  1419. /// m11, m12, m13, m14,
  1420. /// m21, m22, m23, m24,
  1421. /// m31, m32, m33, m34,
  1422. /// m41, m42, m43, m44,
  1423. /// </summary>
  1424. /// <param name="setMatrix">Set Matrix</param>
  1425. public Matrix(float[] setMatrix)
  1426. : this()
  1427. {
  1428. int length = setMatrix.Length;
  1429. if (length < 16)
  1430. {
  1431. Log.Warning("The matrix has too little values.\n" +
  1432. "Length: " + length);
  1433. }
  1434. if (length > 16)
  1435. {
  1436. Log.Warning("The matrix has too many values.\n" +
  1437. "Length: " + length);
  1438. }
  1439. // Copy all values.
  1440. M11 = setMatrix[0];
  1441. M12 = setMatrix[1];
  1442. M13 = setMatrix[2];
  1443. M14 = setMatrix[3];
  1444. M21 = setMatrix[4];
  1445. M22 = setMatrix[5];
  1446. M23 = setMatrix[6];
  1447. M24 = setMatrix[7];
  1448. M31 = setMatrix[8];
  1449. M32 = setMatrix[9];
  1450. M33 = setMatrix[10];
  1451. M34 = setMatrix[11];
  1452. M41 = setMatrix[12];
  1453. M42 = setMatrix[13];
  1454. M43 = setMatrix[14];
  1455. M44 = setMatrix[15];
  1456. }
  1457. /// <summary>
  1458. /// Create matrix by passing in 3 vectors for M11-M33, rest stays default.
  1459. /// </summary>
  1460. /// <param name="firstRowVector">Vector for the first row (M11-M13)
  1461. /// </param>
  1462. /// <param name="secondRowVector">Vector for the second row (M21-M23)
  1463. /// </param>
  1464. /// <param name="thirdRowVector">Vector for the third row (M31-M33)
  1465. /// </param>
  1466. public Matrix(Vector firstRowVector, Vector secondRowVector,
  1467. Vector thirdRowVector)
  1468. : this()
  1469. {
  1470. // Copy all values and fill in the blanks
  1471. M11 = firstRowVector.X;
  1472. M12 = firstRowVector.Y;
  1473. M13 = firstRowVector.Z;
  1474. M14 = 0.0f;
  1475. M21 = secondRowVector.X;
  1476. M22 = secondRowVector.Y;
  1477. M23 = secondRowVector.Z;
  1478. M24 = 0.0f;
  1479. M31 = thirdRowVector.X;
  1480. M32 = thirdRowVector.Y;
  1481. M33 = thirdRowVector.Z;
  1482. M34 = 0.0f;
  1483. M41 = 0.0f;
  1484. M42 = 0.0f;
  1485. M43 = 0.0f;
  1486. M44 = 1.0f;
  1487. }
  1488. /// <summary>
  1489. /// Create matrix with help of BinaryReader, we will read 16 floats from
  1490. /// the stream that is linked up with this BinaryReader.
  1491. /// </summary>
  1492. /// <param name="reader">Reader</param>
  1493. public Matrix(BinaryReader reader)
  1494. : this()
  1495. {
  1496. Load(reader);
  1497. }
  1498. #endregion
  1499. #region IEquatable<Matrix> Members
  1500. /// <summary>
  1501. /// Equals, quickly checks if another matrix has the exact same values.
  1502. /// This methods needs to copy 64 bytes around (1 matrix of 64 bytes).
  1503. /// Try to use the ref version instead.
  1504. /// </summary>
  1505. /// <param name="other">Other Matrix to compare to</param>
  1506. /// <returns>True if both matrices are equal, false otherwise.</returns>
  1507. public bool Equals(Matrix other)
  1508. {
  1509. // First check the diagonal values, those mostly differ.
  1510. return
  1511. M11 == other.M11 &&
  1512. M22 == other.M22 &&
  1513. M33 == other.M33 &&
  1514. M44 == other.M44 &&
  1515. // Now check the rest if the diagonal values were the same.
  1516. M12 == other.M12 &&
  1517. M13 == other.M13 &&
  1518. M14 == other.M14 &&
  1519. M21 == other.M21 &&
  1520. M23 == other.M23 &&
  1521. M24 == other.M24 &&
  1522. M31 == other.M31 &&
  1523. M32 == other.M32 &&
  1524. M34 == other.M34 &&
  1525. M41 == other.M41 &&
  1526. M42 == other.M42 &&
  1527. M43 == other.M43;
  1528. }
  1529. #endregion
  1530. #region ISaveLoadBinary Members
  1531. /// <summary>
  1532. /// Load the matrix values from a stream (64 bytes, 16 floats).
  1533. /// </summary>
  1534. /// <param name="reader">The stream that will be used.</param>
  1535. public void Load(BinaryReader reader)
  1536. {
  1537. // Note XNA and OpenTK matrices share the same data, no need to set them
  1538. M11 = reader.ReadSingle();
  1539. M12 = reader.ReadSingle();
  1540. M13 = reader.ReadSingle();
  1541. M14 = reader.ReadSingle();
  1542. M21 = reader.ReadSingle();
  1543. M22 = reader.ReadSingle();
  1544. M23 = reader.ReadSingle();
  1545. M24 = reader.ReadSingle();
  1546. M31 = reader.ReadSingle();
  1547. M32 = reader.ReadSingle();
  1548. M33 = reader.ReadSingle();
  1549. M34 = reader.ReadSingle();
  1550. M41 = reader.ReadSingle();
  1551. M42 = reader.ReadSingle();
  1552. M43 = reader.ReadSingle();
  1553. M44 = reader.ReadSingle();
  1554. }
  1555. /// <summary>
  1556. /// Saves the matrix to a stream (64 bytes, 16 floats).
  1557. /// </summary>
  1558. /// <param name="writer">The stream that will be used.</param>
  1559. public void Save(BinaryWriter writer)
  1560. {
  1561. writer.Write(M11);
  1562. writer.Write(M12);
  1563. writer.Write(M13);
  1564. writer.Write(M14);
  1565. writer.Write(M21);
  1566. writer.Write(M22);
  1567. writer.Write(M23);
  1568. writer.Write(M24);
  1569. writer.Write(M31);
  1570. writer.Write(M32);
  1571. writer.Write(M33);
  1572. writer.Write(M34);
  1573. writer.Write(M41);
  1574. writer.Write(M42);
  1575. writer.Write(M43);
  1576. writer.Write(M44);
  1577. }
  1578. #endregion
  1579. #region op_Multiply (Operator)
  1580. /// <summary>
  1581. /// Multiply operator. Warning: This operator is slower than using ref
  1582. /// version, it needs to copy 192 bytes around (3 matrices, each 64 bytes).
  1583. /// </summary>
  1584. /// <param name="matrix1">Matrix 1 to multiply</param>
  1585. /// <param name="matrix2">Matrix 2 to multiply</param>
  1586. public static Matrix operator *(Matrix matrix1, Matrix matrix2)
  1587. {
  1588. // Other discussions about XNA Multiply performance
  1589. // http://forums.xna.com/forums/t/7061.aspx?PageIndex=2
  1590. // Machine: Core 2 Duo E6600 clocked at 3.0GHz
  1591. // Managed XNA - Matrix.Multiply(ref,ref,out): 557ms
  1592. // Managed w/ P/Invoke to SSE2 function: 341ms
  1593. // Native C++ - Full Compiler Optimizations: 300ms
  1594. // Native C++ - hand-optimized SSE2: 138ms
  1595. // Note: We got like 97ms with this C# code, but a faster PC ^^
  1596. // Try to do some early out code in case matrix2 is an identity matrix
  1597. if (matrix2.M11 == 1.0f &&
  1598. matrix2.M22 == 1.0f &&
  1599. matrix2.M33 == 1.0f &&
  1600. matrix2.M44 == 1.0f &&
  1601. // Ok, just to be sure, we need to check if all the other values
  1602. // are really 0, only then this is an identity matrix
  1603. matrix2.M21 == 0.0f &&
  1604. matrix2.M23 == 0.0f &&
  1605. matrix2.M24 == 0.0f &&
  1606. matrix2.M31 == 0.0f &&
  1607. matrix2.M32 == 0.0f &&
  1608. matrix2.M34 == 0.0f &&
  1609. matrix2.M41 == 0.0f &&
  1610. matrix2.M42 == 0.0f &&
  1611. matrix2.M43 == 0.0f)
  1612. {
  1613. return matrix1;
  1614. }
  1615. return new Matrix(
  1616. // M11
  1617. (matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21) +
  1618. (matrix1.M13 * matrix2.M31) + (matrix1.M14 * matrix2.M41),
  1619. // M12
  1620. (matrix1.M11 * matrix2.M12) + (matrix1.M12 * matrix2.M22) +
  1621. (matrix1.M13 * matrix2.M32) + (matrix1.M14 * matrix2.M42),
  1622. // M13
  1623. (matrix1.M11 * matrix2.M13) + (matrix1.M12 * matrix2.M23) +
  1624. (matrix1.M13 * matrix2.M33) + (matrix1.M14 * matrix2.M43),
  1625. // M14
  1626. (matrix1.M11 * matrix2.M14) + (matrix1.M12 * matrix2.M24) +
  1627. (matrix1.M13 * matrix2.M34) + (matrix1.M14 * matrix2.M44),
  1628. // M21
  1629. (matrix1.M21 * matrix2.M11) + (matrix1.M22 * matrix2.M21) +
  1630. (matrix1.M23 * matrix2.M31) + (matrix1.M24 * matrix2.M41),
  1631. // M22
  1632. (matrix1.M21 * matrix2.M12) + (matrix1.M22 * matrix2.M22) +
  1633. (matrix1.M23 * matrix2.M32) + (matrix1.M24 * matrix2.M42),
  1634. // M23
  1635. (matrix1.M21 * matrix2.M13) + (matrix1.M22 * matrix2.M23) +
  1636. (matrix1.M23 * matrix2.M33) + (matrix1.M24 * matrix2.M43),
  1637. // M24
  1638. (matrix1.M21 * matrix2.M14) + (matrix1.M22 * matrix2.M24) +
  1639. (matrix1.M23 * matrix2.M34) + (matrix1.M24 * matrix2.M44),
  1640. // M31
  1641. (matrix1.M31 * matrix2.M11) + (matrix1.M32 * matrix2.M21) +
  1642. (matrix1.M33 * matrix2.M31) + (matrix1.M34 * matrix2.M41),
  1643. // M32
  1644. (matrix1.M31 * matrix2.M12) + (matrix1.M32 * matrix2.M22) +
  1645. (matrix1.M33 * matrix2.M32) + (matrix1.M34 * matrix2.M42),
  1646. // M33
  1647. (matrix1.M31 * matrix2.M13) + (matrix1.M32 * matrix2.M23) +
  1648. (matrix1.M33 * matrix2.M33) + (matrix1.M34 * matrix2.M43),
  1649. // M34
  1650. (matrix1.M31 * matrix2.M14) + (matrix1.M32 * matrix2.M24) +
  1651. (matrix1.M33 * matrix2.M34) + (matrix1.M34 * matrix2.M44),
  1652. // M41
  1653. (matrix1.M41 * matrix2.M11) + (matrix1.M42 * matrix2.M21) +
  1654. (matrix1.M43 * matrix2.M31) + (matrix1.M44 * matrix2.M41),
  1655. // M42
  1656. (matrix1.M41 * matrix2.M12) + (matrix1.M42 * matrix2.M22) +
  1657. (matrix1.M43 * matrix2.M32) + (matrix1.M44 * matrix2.M42),
  1658. // M43
  1659. (matrix1.M41 * matrix2.M13) + (matrix1.M42 * matrix2.M23) +
  1660. (matrix1.M43 * matrix2.M33) + (matrix1.M44 * matrix2.M43),
  1661. // M44
  1662. (matrix1.M41 * matrix2.M14) + (matrix1.M42 * matrix2.M24) +
  1663. (matrix1.M43 * matrix2.M34) + (matrix1.M44 * matrix2.M44));
  1664. }
  1665. /// <summary>
  1666. /// Operator to multiply matrix with a vector. This is very useful to
  1667. /// transform vectors with the given matrix. Warning: This is slower than
  1668. /// the ref version, this operator needs to copy 88 bytes around (1 matrix,
  1669. /// 64 bytes and 2 vectors, each 12 bytes)
  1670. /// </summary>
  1671. /// <param name="matrix">Matrix to multiply</param>
  1672. /// <param name="vector">Vector to multiply with</param>
  1673. /// <returns>Resulting vector from the multiplication.</returns>
  1674. public static Vector operator *(Matrix matrix, Vector vector)
  1675. {
  1676. // Note: Seems like calling XNA (OpenTK or SlimDX too?) is here slower!
  1677. //Delta: 209ms (10 million calls)
  1678. //XNA: 353ms (10 million calls)
  1679. //OpenTK: 245ms (10 million calls)
  1680. //SlimDX: 265ms (10 million calls)
  1681. // Just use the normal vector * matrix formula (see Vector class)
  1682. return new Vector(
  1683. (vector.X * matrix.M11) + (vector.Y * matrix.M21) +
  1684. (vector.Z * matrix.M31) + matrix.M41,
  1685. (vector.X * matrix.M12) + (vector.Y * matrix.M22) +
  1686. (vector.Z * matrix.M32) + matrix.M42,
  1687. (vector.X * matrix.M13) + (vector.Y * matrix.M23) +
  1688. (vector.Z * matrix.M33) + matrix.M43);
  1689. }
  1690. /// <summary>
  1691. /// Operator to multiply matrix with a scale factor. Try to use the ref
  1692. /// version instead, this needs to copy 128 bytes around (2 matrices, each
  1693. /// 64 bytes).
  1694. /// </summary>
  1695. /// <param name="matrix">Matrix to multiply</param>
  1696. /// <param name="scaleFactor">Scale factor to multiply with</param>
  1697. /// <returns>
  1698. /// New matrix with all values multiplied by scaleFactor.
  1699. /// </returns>
  1700. public static Matrix operator *(Matrix matrix, float scaleFactor)
  1701. {
  1702. //Note: XNA, OpenTK, and SlimDX path