GjkEpa.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. // Inspired by http://vec3.ca/gjk/implementation/
  6. #include <AnKi/Collision/GjkEpa.h>
  7. namespace anki {
  8. /// GJK support
  9. class GjkSupport
  10. {
  11. public:
  12. Vec4 m_v;
  13. Vec4 m_v0;
  14. Vec4 m_v1;
  15. Bool operator==(const GjkSupport& b) const
  16. {
  17. return m_v == b.m_v && m_v0 == b.m_v0 && m_v1 == b.m_v1;
  18. }
  19. };
  20. class GjkContext
  21. {
  22. public:
  23. const void* m_shape0;
  24. const void* m_shape1;
  25. GjkSupportCallback m_shape0Callback;
  26. GjkSupportCallback m_shape1Callback;
  27. Array<GjkSupport, 4> m_simplex;
  28. U32 m_count; ///< Simplex count
  29. Vec4 m_dir;
  30. };
  31. /// Helper of (axb)xa
  32. static Vec4 crossAba(const Vec4& a, const Vec4& b)
  33. {
  34. // We need to calculate the (axb)xa but we can use the triple product property ax(bxc) = b(a.c) - c(a.b) to make it
  35. // faster Vec4 out = b * (a.dot(a)) - a * (a.dot(b));
  36. return a.cross(b.cross(a));
  37. }
  38. static void support(const GjkContext& ctx, GjkSupport& support)
  39. {
  40. support.m_v0 = ctx.m_shape0Callback(ctx.m_shape0, ctx.m_dir);
  41. support.m_v1 = ctx.m_shape1Callback(ctx.m_shape1, -ctx.m_dir);
  42. support.m_v = support.m_v0 - support.m_v1;
  43. }
  44. static Bool update(GjkContext& ctx, const GjkSupport& a)
  45. {
  46. if(ctx.m_count == 2)
  47. {
  48. Vec4 ao = -a.m_v;
  49. // Compute the vectors parallel to the edges we'll test
  50. const Vec4 ab = ctx.m_simplex[1].m_v - a.m_v;
  51. const Vec4 ac = ctx.m_simplex[2].m_v - a.m_v;
  52. // Compute the triangle's normal
  53. const Vec4 abc = ab.cross(ac);
  54. // Compute a vector within the plane of the triangle, Pointing away from the edge ab
  55. const Vec4 abp = ab.cross(abc);
  56. if(abp.dot(ao) > 0.0)
  57. {
  58. // The origin lies outside the triangle, near the edge ab
  59. ctx.m_simplex[2] = ctx.m_simplex[1];
  60. ctx.m_simplex[1] = a;
  61. ctx.m_dir = crossAba(ab, ao);
  62. return false;
  63. }
  64. // Perform a similar test for the edge ac
  65. const Vec4 acp = abc.cross(ac);
  66. if(acp.dot(ao) > 0.0)
  67. {
  68. ctx.m_simplex[1] = a;
  69. ctx.m_dir = crossAba(ac, ao);
  70. return false;
  71. }
  72. // If we get here, then the origin must be within the triangle, but we care whether it is above or below it, so
  73. // test
  74. if(abc.dot(ao) > 0.0)
  75. {
  76. ctx.m_simplex[3] = ctx.m_simplex[2];
  77. ctx.m_simplex[2] = ctx.m_simplex[1];
  78. ctx.m_simplex[1] = a;
  79. ctx.m_dir = abc;
  80. }
  81. else
  82. {
  83. ctx.m_simplex[3] = ctx.m_simplex[1];
  84. ctx.m_simplex[1] = a;
  85. ctx.m_dir = -abc;
  86. }
  87. ctx.m_count = 3;
  88. // Again, need a tetrahedron to enclose the origin
  89. return false;
  90. }
  91. else if(ctx.m_count == 3)
  92. {
  93. const Vec4 ao = -a.m_v;
  94. Vec4 ab = ctx.m_simplex[1].m_v - a.m_v;
  95. Vec4 ac = ctx.m_simplex[2].m_v - a.m_v;
  96. Vec4 abc = ab.cross(ac);
  97. Vec4 ad, acd, adb;
  98. if(abc.dot(ao) > 0.0)
  99. {
  100. // In front of triangle ABC
  101. goto check_face;
  102. }
  103. ad = ctx.m_simplex[3].m_v - a.m_v;
  104. acd = ac.cross(ad);
  105. if(acd.dot(ao) > 0.0)
  106. {
  107. // In front of triangle ACD
  108. ctx.m_simplex[1] = ctx.m_simplex[2];
  109. ctx.m_simplex[2] = ctx.m_simplex[3];
  110. ab = ac;
  111. ac = ad;
  112. abc = acd;
  113. goto check_face;
  114. }
  115. adb = ad.cross(ab);
  116. if(adb.dot(ao) > 0.0)
  117. {
  118. // In front of triangle ADB
  119. ctx.m_simplex[2] = ctx.m_simplex[1];
  120. ctx.m_simplex[1] = ctx.m_simplex[3];
  121. ac = ab;
  122. ab = ad;
  123. abc = adb;
  124. goto check_face;
  125. }
  126. // Behind all three faces, the origin is in the tetrahedron, we're done
  127. ctx.m_simplex[0] = a;
  128. ctx.m_count = 4;
  129. return true;
  130. check_face:
  131. // We have a CCW wound triangle ABC the point is in front of this triangle
  132. // it is NOT "below" edge BC
  133. // it is NOT "above" the plane through A that's parallel to BC
  134. const Vec4 abp = ab.cross(abc);
  135. if(abp.dot(ao) > 0.0)
  136. {
  137. ctx.m_simplex[2] = ctx.m_simplex[1];
  138. ctx.m_simplex[1] = a;
  139. ctx.m_dir = crossAba(ab, ao);
  140. ctx.m_count = 2;
  141. return false;
  142. }
  143. const Vec4 acp = abc.cross(ac);
  144. if(acp.dot(ao) > 0.0)
  145. {
  146. ctx.m_simplex[1] = a;
  147. ctx.m_dir = crossAba(ac, ao);
  148. ctx.m_count = 2;
  149. return false;
  150. }
  151. ctx.m_simplex[3] = ctx.m_simplex[2];
  152. ctx.m_simplex[2] = ctx.m_simplex[1];
  153. ctx.m_simplex[1] = a;
  154. ctx.m_dir = abc;
  155. ctx.m_count = 3;
  156. return false;
  157. }
  158. ANKI_ASSERT(0);
  159. return true;
  160. }
  161. Bool gjkIntersection(const void* shape0, GjkSupportCallback shape0Callback, const void* shape1, GjkSupportCallback shape1Callback)
  162. {
  163. ANKI_ASSERT(shape0 && shape0Callback && shape1 && shape1Callback);
  164. GjkContext ctx;
  165. ctx.m_shape0 = shape0;
  166. ctx.m_shape1 = shape1;
  167. ctx.m_shape0Callback = shape0Callback;
  168. ctx.m_shape1Callback = shape1Callback;
  169. // Chose random direction
  170. ctx.m_dir = Vec4(1.0, 0.0, 0.0, 0.0);
  171. // Do cases 1, 2
  172. support(ctx, ctx.m_simplex[2]);
  173. if(ctx.m_simplex[2].m_v.dot(ctx.m_dir) < 0.0)
  174. {
  175. return false;
  176. }
  177. ctx.m_dir = -ctx.m_simplex[2].m_v;
  178. support(ctx, ctx.m_simplex[1]);
  179. if(ctx.m_simplex[1].m_v.dot(ctx.m_dir) < 0.0)
  180. {
  181. return false;
  182. }
  183. ctx.m_dir = crossAba(ctx.m_simplex[2].m_v - ctx.m_simplex[1].m_v, -ctx.m_simplex[1].m_v);
  184. ctx.m_count = 2;
  185. U iterations = 20;
  186. while(iterations--)
  187. {
  188. GjkSupport a;
  189. support(ctx, a);
  190. if(a.m_v.dot(ctx.m_dir) < 0.0)
  191. {
  192. return false;
  193. }
  194. if(update(ctx, a))
  195. {
  196. return true;
  197. }
  198. }
  199. return true;
  200. }
  201. } // end namespace anki