2
0

TimeOfImpact.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /*
  2. * Farseer Physics Engine based on Box2D.XNA port:
  3. * Copyright (c) 2010 Ian Qvist
  4. *
  5. * Box2D.XNA port of Box2D:
  6. * Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
  7. *
  8. * Original source Box2D:
  9. * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
  10. *
  11. * This software is provided 'as-is', without any express or implied
  12. * warranty. In no event will the authors be held liable for any damages
  13. * arising from the use of this software.
  14. * Permission is granted to anyone to use this software for any purpose,
  15. * including commercial applications, and to alter it and redistribute it
  16. * freely, subject to the following restrictions:
  17. * 1. The origin of this software must not be misrepresented; you must not
  18. * claim that you wrote the original software. If you use this software
  19. * in a product, an acknowledgment in the product documentation would be
  20. * appreciated but is not required.
  21. * 2. Altered source versions must be plainly marked as such, and must not be
  22. * misrepresented as being the original software.
  23. * 3. This notice may not be removed or altered from any source distribution.
  24. */
  25. using System;
  26. using System.Diagnostics;
  27. using FarseerPhysics.Common;
  28. using Microsoft.Xna.Framework;
  29. namespace FarseerPhysics.Collision
  30. {
  31. /// <summary>
  32. /// Input parameters for CalculateTimeOfImpact
  33. /// </summary>
  34. public class TOIInput
  35. {
  36. public DistanceProxy ProxyA = new DistanceProxy();
  37. public DistanceProxy ProxyB = new DistanceProxy();
  38. public Sweep SweepA;
  39. public Sweep SweepB;
  40. public float TMax; // defines sweep interval [0, tMax]
  41. }
  42. public enum TOIOutputState
  43. {
  44. Unknown,
  45. Failed,
  46. Overlapped,
  47. Touching,
  48. Seperated,
  49. }
  50. public struct TOIOutput
  51. {
  52. public TOIOutputState State;
  53. public float T;
  54. }
  55. public enum SeparationFunctionType
  56. {
  57. Points,
  58. FaceA,
  59. FaceB
  60. }
  61. public static class SeparationFunction
  62. {
  63. private static Vector2 _axis;
  64. private static Vector2 _localPoint;
  65. private static DistanceProxy _proxyA = new DistanceProxy();
  66. private static DistanceProxy _proxyB = new DistanceProxy();
  67. private static Sweep _sweepA, _sweepB;
  68. private static SeparationFunctionType _type;
  69. public static void Set(ref SimplexCache cache,
  70. DistanceProxy proxyA, ref Sweep sweepA,
  71. DistanceProxy proxyB, ref Sweep sweepB,
  72. float t1)
  73. {
  74. _localPoint = Vector2.Zero;
  75. _proxyA = proxyA;
  76. _proxyB = proxyB;
  77. int count = cache.Count;
  78. Debug.Assert(0 < count && count < 3);
  79. _sweepA = sweepA;
  80. _sweepB = sweepB;
  81. Transform xfA, xfB;
  82. _sweepA.GetTransform(out xfA, t1);
  83. _sweepB.GetTransform(out xfB, t1);
  84. if (count == 1)
  85. {
  86. _type = SeparationFunctionType.Points;
  87. Vector2 localPointA = _proxyA.Vertices[cache.IndexA[0]];
  88. Vector2 localPointB = _proxyB.Vertices[cache.IndexB[0]];
  89. Vector2 pointA = MathUtils.Multiply(ref xfA, localPointA);
  90. Vector2 pointB = MathUtils.Multiply(ref xfB, localPointB);
  91. _axis = pointB - pointA;
  92. _axis.Normalize();
  93. return;
  94. }
  95. else if (cache.IndexA[0] == cache.IndexA[1])
  96. {
  97. // Two points on B and one on A.
  98. _type = SeparationFunctionType.FaceB;
  99. Vector2 localPointB1 = proxyB.Vertices[cache.IndexB[0]];
  100. Vector2 localPointB2 = proxyB.Vertices[cache.IndexB[1]];
  101. Vector2 a = localPointB2 - localPointB1;
  102. _axis = new Vector2(a.Y, -a.X);
  103. _axis.Normalize();
  104. Vector2 normal = MathUtils.Multiply(ref xfB.R, _axis);
  105. _localPoint = 0.5f * (localPointB1 + localPointB2);
  106. Vector2 pointB = MathUtils.Multiply(ref xfB, _localPoint);
  107. Vector2 localPointA = proxyA.Vertices[cache.IndexA[0]];
  108. Vector2 pointA = MathUtils.Multiply(ref xfA, localPointA);
  109. float s = Vector2.Dot(pointA - pointB, normal);
  110. if (s < 0.0f)
  111. {
  112. _axis = -_axis;
  113. s = -s;
  114. }
  115. return;
  116. }
  117. else
  118. {
  119. // Two points on A and one or two points on B.
  120. _type = SeparationFunctionType.FaceA;
  121. Vector2 localPointA1 = _proxyA.Vertices[cache.IndexA[0]];
  122. Vector2 localPointA2 = _proxyA.Vertices[cache.IndexA[1]];
  123. Vector2 a = localPointA2 - localPointA1;
  124. _axis = new Vector2(a.Y, -a.X);
  125. _axis.Normalize();
  126. Vector2 normal = MathUtils.Multiply(ref xfA.R, _axis);
  127. _localPoint = 0.5f * (localPointA1 + localPointA2);
  128. Vector2 pointA = MathUtils.Multiply(ref xfA, _localPoint);
  129. Vector2 localPointB = _proxyB.Vertices[cache.IndexB[0]];
  130. Vector2 pointB = MathUtils.Multiply(ref xfB, localPointB);
  131. float s = Vector2.Dot(pointB - pointA, normal);
  132. if (s < 0.0f)
  133. {
  134. _axis = -_axis;
  135. s = -s;
  136. }
  137. return;
  138. }
  139. }
  140. public static float FindMinSeparation(out int indexA, out int indexB, float t)
  141. {
  142. Transform xfA, xfB;
  143. _sweepA.GetTransform(out xfA, t);
  144. _sweepB.GetTransform(out xfB, t);
  145. switch (_type)
  146. {
  147. case SeparationFunctionType.Points:
  148. {
  149. Vector2 axisA = MathUtils.MultiplyT(ref xfA.R, _axis);
  150. Vector2 axisB = MathUtils.MultiplyT(ref xfB.R, -_axis);
  151. indexA = _proxyA.GetSupport(axisA);
  152. indexB = _proxyB.GetSupport(axisB);
  153. Vector2 localPointA = _proxyA.Vertices[indexA];
  154. Vector2 localPointB = _proxyB.Vertices[indexB];
  155. Vector2 pointA = MathUtils.Multiply(ref xfA, localPointA);
  156. Vector2 pointB = MathUtils.Multiply(ref xfB, localPointB);
  157. float separation = Vector2.Dot(pointB - pointA, _axis);
  158. return separation;
  159. }
  160. case SeparationFunctionType.FaceA:
  161. {
  162. Vector2 normal = MathUtils.Multiply(ref xfA.R, _axis);
  163. Vector2 pointA = MathUtils.Multiply(ref xfA, _localPoint);
  164. Vector2 axisB = MathUtils.MultiplyT(ref xfB.R, -normal);
  165. indexA = -1;
  166. indexB = _proxyB.GetSupport(axisB);
  167. Vector2 localPointB = _proxyB.Vertices[indexB];
  168. Vector2 pointB = MathUtils.Multiply(ref xfB, localPointB);
  169. float separation = Vector2.Dot(pointB - pointA, normal);
  170. return separation;
  171. }
  172. case SeparationFunctionType.FaceB:
  173. {
  174. Vector2 normal = MathUtils.Multiply(ref xfB.R, _axis);
  175. Vector2 pointB = MathUtils.Multiply(ref xfB, _localPoint);
  176. Vector2 axisA = MathUtils.MultiplyT(ref xfA.R, -normal);
  177. indexB = -1;
  178. indexA = _proxyA.GetSupport(axisA);
  179. Vector2 localPointA = _proxyA.Vertices[indexA];
  180. Vector2 pointA = MathUtils.Multiply(ref xfA, localPointA);
  181. float separation = Vector2.Dot(pointA - pointB, normal);
  182. return separation;
  183. }
  184. default:
  185. Debug.Assert(false);
  186. indexA = -1;
  187. indexB = -1;
  188. return 0.0f;
  189. }
  190. }
  191. public static float Evaluate(int indexA, int indexB, float t)
  192. {
  193. Transform xfA, xfB;
  194. _sweepA.GetTransform(out xfA, t);
  195. _sweepB.GetTransform(out xfB, t);
  196. switch (_type)
  197. {
  198. case SeparationFunctionType.Points:
  199. {
  200. Vector2 axisA = MathUtils.MultiplyT(ref xfA.R, _axis);
  201. Vector2 axisB = MathUtils.MultiplyT(ref xfB.R, -_axis);
  202. Vector2 localPointA = _proxyA.Vertices[indexA];
  203. Vector2 localPointB = _proxyB.Vertices[indexB];
  204. Vector2 pointA = MathUtils.Multiply(ref xfA, localPointA);
  205. Vector2 pointB = MathUtils.Multiply(ref xfB, localPointB);
  206. float separation = Vector2.Dot(pointB - pointA, _axis);
  207. return separation;
  208. }
  209. case SeparationFunctionType.FaceA:
  210. {
  211. Vector2 normal = MathUtils.Multiply(ref xfA.R, _axis);
  212. Vector2 pointA = MathUtils.Multiply(ref xfA, _localPoint);
  213. Vector2 axisB = MathUtils.MultiplyT(ref xfB.R, -normal);
  214. Vector2 localPointB = _proxyB.Vertices[indexB];
  215. Vector2 pointB = MathUtils.Multiply(ref xfB, localPointB);
  216. float separation = Vector2.Dot(pointB - pointA, normal);
  217. return separation;
  218. }
  219. case SeparationFunctionType.FaceB:
  220. {
  221. Vector2 normal = MathUtils.Multiply(ref xfB.R, _axis);
  222. Vector2 pointB = MathUtils.Multiply(ref xfB, _localPoint);
  223. Vector2 axisA = MathUtils.MultiplyT(ref xfA.R, -normal);
  224. Vector2 localPointA = _proxyA.Vertices[indexA];
  225. Vector2 pointA = MathUtils.Multiply(ref xfA, localPointA);
  226. float separation = Vector2.Dot(pointA - pointB, normal);
  227. return separation;
  228. }
  229. default:
  230. Debug.Assert(false);
  231. return 0.0f;
  232. }
  233. }
  234. }
  235. public static class TimeOfImpact
  236. {
  237. // CCD via the local separating axis method. This seeks progression
  238. // by computing the largest time at which separation is maintained.
  239. public static int TOICalls, TOIIters, TOIMaxIters;
  240. public static int TOIRootIters, TOIMaxRootIters;
  241. private static DistanceInput _distanceInput = new DistanceInput();
  242. /// <summary>
  243. /// Compute the upper bound on time before two shapes penetrate. Time is represented as
  244. /// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,
  245. /// non-tunneling collision. If you change the time interval, you should call this function
  246. /// again.
  247. /// Note: use Distance() to compute the contact point and normal at the time of impact.
  248. /// </summary>
  249. /// <param name="output">The output.</param>
  250. /// <param name="input">The input.</param>
  251. public static void CalculateTimeOfImpact(out TOIOutput output, TOIInput input)
  252. {
  253. ++TOICalls;
  254. output = new TOIOutput();
  255. output.State = TOIOutputState.Unknown;
  256. output.T = input.TMax;
  257. Sweep sweepA = input.SweepA;
  258. Sweep sweepB = input.SweepB;
  259. // Large rotations can make the root finder fail, so we normalize the
  260. // sweep angles.
  261. sweepA.Normalize();
  262. sweepB.Normalize();
  263. float tMax = input.TMax;
  264. float totalRadius = input.ProxyA.Radius + input.ProxyB.Radius;
  265. float target = Math.Max(Settings.LinearSlop, totalRadius - 3.0f * Settings.LinearSlop);
  266. const float tolerance = 0.25f * Settings.LinearSlop;
  267. Debug.Assert(target > tolerance);
  268. float t1 = 0.0f;
  269. const int k_maxIterations = 20;
  270. int iter = 0;
  271. // Prepare input for distance query.
  272. SimplexCache cache;
  273. _distanceInput.ProxyA = input.ProxyA;
  274. _distanceInput.ProxyB = input.ProxyB;
  275. _distanceInput.UseRadii = false;
  276. // The outer loop progressively attempts to compute new separating axes.
  277. // This loop terminates when an axis is repeated (no progress is made).
  278. for (; ; )
  279. {
  280. Transform xfA, xfB;
  281. sweepA.GetTransform(out xfA, t1);
  282. sweepB.GetTransform(out xfB, t1);
  283. // Get the distance between shapes. We can also use the results
  284. // to get a separating axis.
  285. _distanceInput.TransformA = xfA;
  286. _distanceInput.TransformB = xfB;
  287. DistanceOutput distanceOutput;
  288. Distance.ComputeDistance(out distanceOutput, out cache, _distanceInput);
  289. // If the shapes are overlapped, we give up on continuous collision.
  290. if (distanceOutput.Distance <= 0.0f)
  291. {
  292. // Failure!
  293. output.State = TOIOutputState.Overlapped;
  294. output.T = 0.0f;
  295. break;
  296. }
  297. if (distanceOutput.Distance < target + tolerance)
  298. {
  299. // Victory!
  300. output.State = TOIOutputState.Touching;
  301. output.T = t1;
  302. break;
  303. }
  304. SeparationFunction.Set(ref cache, input.ProxyA, ref sweepA, input.ProxyB, ref sweepB, t1);
  305. // Compute the TOI on the separating axis. We do this by successively
  306. // resolving the deepest point. This loop is bounded by the number of vertices.
  307. bool done = false;
  308. float t2 = tMax;
  309. int pushBackIter = 0;
  310. for (; ; )
  311. {
  312. // Find the deepest point at t2. Store the witness point indices.
  313. int indexA, indexB;
  314. float s2 = SeparationFunction.FindMinSeparation(out indexA, out indexB, t2);
  315. // Is the final configuration separated?
  316. if (s2 > target + tolerance)
  317. {
  318. // Victory!
  319. output.State = TOIOutputState.Seperated;
  320. output.T = tMax;
  321. done = true;
  322. break;
  323. }
  324. // Has the separation reached tolerance?
  325. if (s2 > target - tolerance)
  326. {
  327. // Advance the sweeps
  328. t1 = t2;
  329. break;
  330. }
  331. // Compute the initial separation of the witness points.
  332. float s1 = SeparationFunction.Evaluate(indexA, indexB, t1);
  333. // Check for initial overlap. This might happen if the root finder
  334. // runs out of iterations.
  335. if (s1 < target - tolerance)
  336. {
  337. output.State = TOIOutputState.Failed;
  338. output.T = t1;
  339. done = true;
  340. break;
  341. }
  342. // Check for touching
  343. if (s1 <= target + tolerance)
  344. {
  345. // Victory! t1 should hold the TOI (could be 0.0).
  346. output.State = TOIOutputState.Touching;
  347. output.T = t1;
  348. done = true;
  349. break;
  350. }
  351. // Compute 1D root of: f(x) - target = 0
  352. int rootIterCount = 0;
  353. float a1 = t1, a2 = t2;
  354. for (; ; )
  355. {
  356. // Use a mix of the secant rule and bisection.
  357. float t;
  358. if ((rootIterCount & 1) != 0)
  359. {
  360. // Secant rule to improve convergence.
  361. t = a1 + (target - s1) * (a2 - a1) / (s2 - s1);
  362. }
  363. else
  364. {
  365. // Bisection to guarantee progress.
  366. t = 0.5f * (a1 + a2);
  367. }
  368. float s = SeparationFunction.Evaluate(indexA, indexB, t);
  369. if (Math.Abs(s - target) < tolerance)
  370. {
  371. // t2 holds a tentative value for t1
  372. t2 = t;
  373. break;
  374. }
  375. // Ensure we continue to bracket the root.
  376. if (s > target)
  377. {
  378. a1 = t;
  379. s1 = s;
  380. }
  381. else
  382. {
  383. a2 = t;
  384. s2 = s;
  385. }
  386. ++rootIterCount;
  387. ++TOIRootIters;
  388. if (rootIterCount == 50)
  389. {
  390. break;
  391. }
  392. }
  393. TOIMaxRootIters = Math.Max(TOIMaxRootIters, rootIterCount);
  394. ++pushBackIter;
  395. if (pushBackIter == Settings.MaxPolygonVertices)
  396. {
  397. break;
  398. }
  399. }
  400. ++iter;
  401. ++TOIIters;
  402. if (done)
  403. {
  404. break;
  405. }
  406. if (iter == k_maxIterations)
  407. {
  408. // Root finder got stuck. Semi-victory.
  409. output.State = TOIOutputState.Failed;
  410. output.T = t1;
  411. break;
  412. }
  413. }
  414. TOIMaxIters = Math.Max(TOIMaxIters, iter);
  415. }
  416. }
  417. }