AnimationSamplingTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Numerics;
  5. using System.Text;
  6. using NUnit.Framework;
  7. namespace SharpGLTF
  8. {
  9. [TestFixture]
  10. [AttachmentPathFormat("*/TestResults/Animations/?", true)]
  11. [Category("Core.Animations")]
  12. public class AnimationSamplingTests
  13. {
  14. [Test]
  15. public void TestAnimationSplit()
  16. {
  17. var anim0 = new[]
  18. {
  19. (0.1f, 1),
  20. };
  21. var anim1 = new[]
  22. {
  23. (0.1f, 1),
  24. (0.2f, 2)
  25. };
  26. var anim2 = new[]
  27. {
  28. (0.1f, 1),
  29. (0.2f, 2),
  30. (3.2f, 2),
  31. (3.3f, 2)
  32. };
  33. var anim3 = new[]
  34. {
  35. (2.1f, 1),
  36. (2.2f, 2),
  37. (3.2f, 3),
  38. (3.3f, 4),
  39. (4.0f, 5),
  40. (4.1f, 6),
  41. (5.0f, 7),
  42. };
  43. void checkSegment(int time, (float,int)[] segment)
  44. {
  45. // should check all times are incremental
  46. Assert.That(segment.Length, Is.GreaterThan(1));
  47. Assert.That(segment.First().Item1, Is.LessThanOrEqualTo(time));
  48. Assert.That(segment.Last().Item1, Is.GreaterThan(time));
  49. }
  50. var r0 = Animations.CurveSampler.SplitByTime(anim0).ToArray();
  51. Assert.That(r0, Has.Length.EqualTo(1));
  52. Assert.That(r0[0], Has.Length.EqualTo(1));
  53. var r1 = Animations.CurveSampler.SplitByTime(anim1).ToArray();
  54. Assert.That(r1, Has.Length.EqualTo(1));
  55. Assert.That(r1[0], Has.Length.EqualTo(2));
  56. var r2 = Animations.CurveSampler.SplitByTime(anim2).ToArray();
  57. Assert.That(r2, Has.Length.EqualTo(4));
  58. Assert.That(r2[0], Has.Length.EqualTo(3));
  59. Assert.That(r2[1], Has.Length.EqualTo(2)); checkSegment(1, r2[1]);
  60. Assert.That(r2[2], Has.Length.EqualTo(2)); checkSegment(2, r2[2]);
  61. Assert.That(r2[3], Has.Length.EqualTo(3)); checkSegment(3, r2[3]);
  62. var r3 = Animations.CurveSampler.SplitByTime(anim3).ToArray();
  63. Assert.That(r3, Has.Length.EqualTo(6));
  64. Assert.That(r3[0], Has.Length.EqualTo(1));
  65. Assert.That(r3[1], Has.Length.EqualTo(1));
  66. Assert.That(r3[2], Has.Length.EqualTo(3));
  67. Assert.That(r3[3], Has.Length.EqualTo(4)); checkSegment(3, r3[3]);
  68. Assert.That(r3[3], Has.Length.EqualTo(4)); checkSegment(4, r3[4]);
  69. Assert.That(r3[5], Has.Length.EqualTo(1));
  70. }
  71. [Test]
  72. public void TestFastSampler()
  73. {
  74. var curve = Enumerable
  75. .Range(0, 1000)
  76. .Select(idx => (0.1f * (float)idx, new Vector3(idx, idx, idx)))
  77. .ToArray();
  78. var slowSampler = Animations.CurveSampler.CreateSampler(curve, true, false);
  79. var fastSampler = Animations.CurveSampler.CreateSampler(curve, true, true);
  80. foreach (var k in curve)
  81. {
  82. Assert.That(slowSampler.GetPoint(k.Item1), Is.EqualTo(k.Item2));
  83. Assert.That(fastSampler.GetPoint(k.Item1), Is.EqualTo(k.Item2));
  84. }
  85. for(float t=0; t < 100; t+=0.232f)
  86. {
  87. var dv = slowSampler.GetPoint(t);
  88. var fv = fastSampler.GetPoint(t);
  89. Assert.That(fv, Is.EqualTo(dv));
  90. }
  91. }
  92. [TestCase(0, 0, 0, 1, 1, 1, 1, 0)]
  93. [TestCase(0, 0, 0.1f, 5, 0.7f, 3, 1, 0)]
  94. public void TestHermiteInterpolation1(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y, float p4x, float p4y)
  95. {
  96. var p1 = new Vector2(p1x, p1y);
  97. var p2 = new Vector2(p2x, p2y);
  98. var p3 = new Vector2(p3x, p3y);
  99. var p4 = new Vector2(p4x, p4y);
  100. var ppp = new List<Vector2>();
  101. for (float amount = 0; amount <= 1; amount += 0.01f)
  102. {
  103. var (startPosition, endPosition, startTangent, endTangent) = Animations.CurveSampler.CreateHermitePointWeights(amount);
  104. var p = Vector2.Zero;
  105. p += p1 * startPosition;
  106. p += p4 * endPosition;
  107. p += (p2 - p1) * 4 * startTangent;
  108. p += (p4 - p3) * 4 * endTangent;
  109. ppp.Add(p);
  110. }
  111. // now lets calculate an arbitrary point and tangent
  112. float k = 0.3f;
  113. var hb = Animations.CurveSampler.CreateHermitePointWeights(k);
  114. var ht = Animations.CurveSampler.CreateHermiteTangentWeights(k);
  115. var pp = p1 * hb.StartPosition + p4 * hb.EndPosition + (p2 - p1) * 4 * hb.StartTangent + (p4 - p3) * 4 * hb.EndTangent;
  116. var pt = p1 * ht.StartPosition + p4 * ht.EndPosition + (p2 - p1) * 4 * ht.StartTangent + (p4 - p3) * 4 * ht.EndTangent;
  117. // plotting
  118. var series1 = ppp.ToPointSeries("sampling");
  119. var series2 = new[] { p1, p2, p3, p4 }.ToLineSeries("source");
  120. var series3 = new[] { pp, pp + pt }.ToLineSeries("tangent");
  121. new[] { series1, series2, series3 }.AttachToCurrentTest("plot.html");
  122. }
  123. [Test]
  124. public void TestHermiteAsLinearInterpolation()
  125. {
  126. var p1 = new Vector2(1, 0);
  127. var p2 = new Vector2(3, 1);
  128. var t = p2 - p1;
  129. var ppp = new List<Vector2>();
  130. for (float amount = 0; amount <= 1; amount += 0.1f)
  131. {
  132. var (startPosition, endPosition, startTangent, endTangent) = Animations.CurveSampler.CreateHermitePointWeights(amount);
  133. var p = Vector2.Zero;
  134. p += p1 * startPosition;
  135. p += p2 * endPosition;
  136. p += t * startTangent;
  137. p += t * endTangent;
  138. ppp.Add(p);
  139. }
  140. var series1 = ppp.ToPointSeries().WithLineType(Plotting.LineType.Star);
  141. new[] { series1 }.AttachToCurrentTest("plot.html");
  142. }
  143. [Test]
  144. public void TestHermiteAsSphericalInterpolation()
  145. {
  146. // given two quaternions, we must find a tangent quaternion so that the quaternion
  147. // hermite interpolation gives roughly the same results as a plain spherical interpolation.
  148. // reference implementation with matrices
  149. var m1 = Matrix4x4.CreateFromAxisAngle(Vector3.UnitX, 1);
  150. var m2 = Matrix4x4.CreateFromAxisAngle(Vector3.UnitY, 2);
  151. var mt = Matrix4x4.Multiply(m2, Matrix4x4.Transpose(m1));
  152. var m2bis = Matrix4x4.Multiply(mt, m1); // roundtrip; M2 == M2BIS
  153. // implementation with quaternions
  154. var q1 = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 1);
  155. var q2 = Quaternion.CreateFromAxisAngle(Vector3.UnitY, 2);
  156. var qt = Quaternion.Concatenate(q2, Quaternion.Conjugate(q1));
  157. var q2bis = Quaternion.Concatenate(qt, q1); // roundtrip; Q2 == Q2BIS
  158. NumericsAssert.AreEqual(qt, Animations.CurveSampler.CreateTangent(q1, q2), 0.000001f);
  159. var angles = new List<Vector2>();
  160. for (float amount = 0; amount <= 1; amount += 0.025f)
  161. {
  162. // slerp interpolation
  163. var sq = Quaternion.Normalize(Quaternion.Slerp(q1, q2, amount));
  164. // hermite interpolation with a unit tangent
  165. var hermite = Animations.CurveSampler.CreateHermitePointWeights(amount);
  166. var hq = default(Quaternion);
  167. hq += q1 * hermite.StartPosition;
  168. hq += q2 * hermite.EndPosition;
  169. hq += qt * hermite.StartTangent;
  170. hq += qt * hermite.EndTangent;
  171. hq = Quaternion.Normalize(hq);
  172. // check
  173. NumericsAssert.AreEqual(sq, hq, 0.1f);
  174. NumericsAssert.AngleLessOrEqual(sq, hq, 0.22f);
  175. // diff
  176. var a = (sq, hq).GetAngle() * 180.0f / 3.141592f;
  177. angles.Add(new Vector2(amount, a));
  178. }
  179. angles.ToPointSeries()
  180. .WithLineType(Plotting.LineType.Continuous)
  181. .AttachToCurrentTest("plot.html");
  182. }
  183. private static (float, (Vector3, Vector3, Vector3))[] _TransAnim = new []
  184. {
  185. (0.0f, ( Vector3.Zero, new Vector3(0, 0, 0),new Vector3(0, 0, 0))),
  186. (1.0f, (new Vector3(0, 0, 0), new Vector3(1, 0, 0),new Vector3(0, 1, 0))),
  187. (2.0f, (new Vector3(0, -1, 0), new Vector3(2, 0, 0),new Vector3(0, 0, 0))),
  188. (3.0f, (new Vector3(0, 0, 0), new Vector3(3, 0, 0), Vector3.Zero ))
  189. };
  190. private static (float, (Quaternion, Quaternion, Quaternion))[] _RotAnim = new[]
  191. {
  192. (0.0f, (new Quaternion(0,0,0,0), Quaternion.CreateFromYawPitchRoll(+1.6f, 0, 0), new Quaternion(0,0,0,0))),
  193. (1.0f, (new Quaternion(0,0,0,0), Quaternion.Identity, new Quaternion(0,0,0,0))),
  194. (2.0f, (new Quaternion(0,0,0,0), Quaternion.CreateFromYawPitchRoll(-1.6f, 0, 0), new Quaternion(0,0,0,0))),
  195. (3.0f, (new Quaternion(0,0,0,0), Quaternion.Identity, new Quaternion(0,0,0,0))),
  196. (4.0f, (new Quaternion(0,0,0,0), Quaternion.CreateFromYawPitchRoll(+1.6f, 0, 0), new Quaternion(0,0,0,0))),
  197. };
  198. [Test]
  199. public void TestVector3CubicSplineSampling()
  200. {
  201. var sampler = Animations.CurveSampler.CreateSampler(_TransAnim);
  202. var points = new List<Vector3>();
  203. for(int i=0; i < 300; ++i)
  204. {
  205. var sample = sampler.GetPoint(((float)i) / 100.0f);
  206. points.Add( sample );
  207. }
  208. points
  209. .Select(p => new Vector2(p.X, p.Y))
  210. .ToPointSeries()
  211. .AttachToCurrentTest("plot.html");
  212. }
  213. [Test]
  214. public void TestQuaternionCubicSplineSampling()
  215. {
  216. var sampler = Animations.CurveSampler.CreateSampler(_RotAnim);
  217. var a = sampler.GetPoint(0);
  218. var b = sampler.GetPoint(1);
  219. var bc = sampler.GetPoint(1.5f);
  220. var c = sampler.GetPoint(2);
  221. }
  222. }
  223. }