AffineTransformMatrixTests.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Numerics;
  4. using System.Text;
  5. using System.Xml;
  6. using Newtonsoft.Json;
  7. using NUnit.Framework;
  8. using Plotly;
  9. namespace SharpGLTF.Transforms
  10. {
  11. [Category("Core.Transforms")]
  12. public class AffineTransformMatrixTests
  13. {
  14. [TestCase(0, 0, 0, 0, 0, 0)]
  15. [TestCase(1, 2, 4, 3, 2, 1)]
  16. [TestCase(-1, 1, 3, 2, 0, 1)]
  17. [TestCase(0, 0, 1, 0, 1, 0)]
  18. [TestCase(0, -1, 1, -2, 1, 0)]
  19. public void CalculateInverseBindMatrix(float mx, float my, float mz, float jx, float jy, float jz)
  20. {
  21. var model = Matrix4x4.CreateFromYawPitchRoll(mx, my, mz);
  22. var joint = Matrix4x4.CreateFromYawPitchRoll(jx, jy, jz);
  23. joint.Translation = new Vector3(jx, jy, jz);
  24. var invBindMatrix = SkinnedTransform.CalculateInverseBinding(model, joint);
  25. Matrix4x4.Invert(model, out Matrix4x4 xform);
  26. Matrix4x4.Invert(joint * xform, out Matrix4x4 result);
  27. NumericsAssert.AreEqual(result, invBindMatrix, 0.000001f);
  28. Matrix4x4.Invert(joint, out Matrix4x4 invJoint);
  29. result = model * invJoint;
  30. NumericsAssert.AreEqual(result, invBindMatrix, 0.000001f);
  31. }
  32. [Test]
  33. public void TestMatrixNormalization()
  34. {
  35. void testMatrix(Matrix4x4 m, float tolerance = 0)
  36. {
  37. var o = m;
  38. Matrix4x4Factory.NormalizeMatrix(ref m);
  39. NumericsAssert.AreEqual(o, m, tolerance);
  40. Assert.IsTrue(Matrix4x4.Decompose(m, out _, out _, out _));
  41. Assert.IsTrue(Matrix4x4.Invert(m, out _));
  42. }
  43. void testSkewed(Func<Matrix4x4, Matrix4x4> mf, float tolerance = 0)
  44. {
  45. var m = Matrix4x4.Identity;
  46. var o = m = mf(m);
  47. Assert.IsFalse(Matrix4x4.Decompose(m, out _, out _, out _));
  48. Matrix4x4Factory.NormalizeMatrix(ref m);
  49. NumericsAssert.AreEqual(o, m, tolerance);
  50. Assert.IsTrue(Matrix4x4.Decompose(m, out _, out _, out _));
  51. Assert.IsTrue(Matrix4x4.Invert(m, out _));
  52. }
  53. testSkewed(m => { m.M12 += 0.34f; return m; }, 0.34f);
  54. testSkewed(m => { m.M13 += 0.34f; return m; }, 0.34f);
  55. testSkewed(m => { m.M21 += 0.34f; return m; }, 0.34f);
  56. testSkewed(m => { m.M23 += 0.34f; return m; }, 0.34f);
  57. testSkewed(m => { m.M31 += 0.34f; return m; }, 0.34f);
  58. testSkewed(m => { m.M32 += 0.34f; return m; }, 0.34f);
  59. testSkewed(m => { m.M12 += 0.1f; m.M23 -= 0.1f; m.M31 += 0.05f; return m; }, 0.20f);
  60. // test normalization with uneven scaling
  61. testMatrix(Matrix4x4.CreateScale(0.0001f) * Matrix4x4.CreateFromYawPitchRoll(1, 2, 3), 0.0001f);
  62. testMatrix(Matrix4x4.CreateScale(1000) * Matrix4x4.CreateFromYawPitchRoll(1, 2, 3), 0.0002f);
  63. var SxR = Matrix4x4.CreateScale(5, 1, 1) * Matrix4x4.CreateFromYawPitchRoll(1, 2, 3); // Decomposable
  64. var RxS = Matrix4x4.CreateFromYawPitchRoll(1, 2, 3) * Matrix4x4.CreateScale(5, 1, 1); // Not Decomposable
  65. Assert.IsTrue(Matrix4x4.Decompose(SxR, out _, out _, out _));
  66. testMatrix(SxR, 0.0001f);
  67. Assert.IsFalse(Matrix4x4.Decompose(RxS, out _, out _, out _));
  68. testMatrix(RxS, 100);
  69. }
  70. [Test]
  71. public void TestAffineTransformIdentity()
  72. {
  73. var asMatrix = new AffineTransform(Matrix4x4.Identity);
  74. var asDecomposed = new AffineTransform(null, null, null);
  75. NumericsAssert.AreEqual(Matrix4x4.Identity, asMatrix.Matrix);
  76. NumericsAssert.AreEqual(Matrix4x4.Identity, asDecomposed.Matrix);
  77. Assert.IsTrue(asMatrix.IsIdentity);
  78. Assert.IsTrue(asDecomposed.IsIdentity);
  79. }
  80. [TestCase(false, false, false, false)]
  81. [TestCase(false, false, false, true)]
  82. [TestCase(false, false, true, false)]
  83. [TestCase(false, false, true, true)]
  84. [TestCase(false, true, false, false)]
  85. [TestCase(false, true, false, true)]
  86. [TestCase(false, true, true, false)]
  87. [TestCase(false, true, true, true)]
  88. [TestCase(true, false, false, false)]
  89. [TestCase(true, false, false, true)]
  90. [TestCase(true, false, true, false)]
  91. [TestCase(true, false, true, true)]
  92. [TestCase(true, true, false, false)]
  93. [TestCase(true, true, false, true)]
  94. [TestCase(true, true, true, false)]
  95. [TestCase(true, true, true, true)]
  96. public void TestAffineTransformMult(bool sa, bool sb, bool ra, bool rb)
  97. {
  98. var s_a = sa ? new Vector3(1, 2, 4) : Vector3.One;
  99. var r_a = ra ? Quaternion.CreateFromYawPitchRoll(1, 2, 3) : Quaternion.Identity;
  100. var t_a = new Vector3(1, 5, -9);
  101. var s_b = sb ? new Vector3(1, 2, 4) : Vector3.One;
  102. var r_b = rb ? Quaternion.CreateFromYawPitchRoll(1, 0, 2) : Quaternion.Identity;
  103. var t_b = new Vector3(3, -4, 2);
  104. var mat_a = Matrix4x4.CreateScale(s_a) * Matrix4x4.CreateFromQuaternion(r_a) * Matrix4x4.CreateTranslation(t_a);
  105. var mat_b = Matrix4x4.CreateScale(s_b) * Matrix4x4.CreateFromQuaternion(r_b) * Matrix4x4.CreateTranslation(t_b);
  106. var mat_ab = Matrix4x4.Multiply(mat_a, mat_b);
  107. var mat_ba = Matrix4x4.Multiply(mat_b, mat_a);
  108. var srt_a = new AffineTransform(s_a, r_a, t_a);
  109. var srt_b = new AffineTransform(s_b, r_b, t_b);
  110. var srt_ab = AffineTransform.Multiply(srt_a, srt_b);
  111. var srt_ba = AffineTransform.Multiply(srt_b, srt_a);
  112. TestContext.WriteLine($"A({sa},{ra}) x B({sb},{rb}) = {srt_ab.IsSRT}");
  113. TestContext.WriteLine($"B({sb},{rb}) x A({sa},{ra}) = {srt_ba.IsSRT}");
  114. NumericsAssert.AreEqual(mat_ab, srt_ab.Matrix, 0.00001f);
  115. NumericsAssert.AreEqual(mat_ba, srt_ba.Matrix, 0.00001f);
  116. }
  117. [TestCase(true, 1, 10, 100, 0, 0, 0, 5,5,5)]
  118. [TestCase(true, 1, 1, 1, 0, 0, 0, 0, 0, 0)]
  119. [TestCase(true, 1, 1, 1, 2, 0, -1, 100, 50, 0)]
  120. [TestCase(true, 5, 5, 5, 1, 2, 3, 100, 50, 0)]
  121. [TestCase(false, 1, 2, 3, 1, 2, 3, 1, 2, 3)]
  122. [TestCase(false, 1, 2, 30, 1, 2, 3, 1, 2, 3)]
  123. [TestCase(false, -1, -2, 3, 0, 1, 0, 1, 0, 0)]
  124. public void TestAffineTransformInverse(bool isInvertibleToSRT, float sx, float sy, float sz, float y, float p, float r, float tx, float ty, float tz)
  125. {
  126. var xf = new AffineTransform(new Vector3(sx, sy, sz), Quaternion.CreateFromYawPitchRoll(y, p, r), new Vector3(tx, ty, tz));
  127. Assert.IsTrue(AffineTransform.TryInvert(xf, out var xi));
  128. Assert.IsTrue(Matrix4x4.Invert(xf.Matrix, out var mi));
  129. mi.M44 = 1f;
  130. if (isInvertibleToSRT) Assert.IsTrue(xi.IsSRT);
  131. var xmi = xi.Matrix;
  132. var tolerance = NumericsAssert.AreGeometryicallyEquivalent(mi, xmi, 0.00001f);
  133. TestContext.WriteLine(tolerance);
  134. Assert.IsTrue(AffineTransform.AreGeometricallyEquivalent(mi, xi, 0.00001f));
  135. }
  136. }
  137. }