CollideEdge.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*
  2. * Box2D.XNA port of Box2D:
  3. * Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
  4. *
  5. * Original source Box2D:
  6. * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
  7. *
  8. * This software is provided 'as-is', without any express or implied
  9. * warranty. In no event will the authors be held liable for any damages
  10. * arising from the use of this software.
  11. * Permission is granted to anyone to use this software for any purpose,
  12. * including commercial applications, and to alter it and redistribute it
  13. * freely, subject to the following restrictions:
  14. * 1. The origin of this software must not be misrepresented; you must not
  15. * claim that you wrote the original software. If you use this software
  16. * in a product, an acknowledgment in the product documentation would be
  17. * appreciated but is not required.
  18. * 2. Altered source versions must be plainly marked as such, and must not be
  19. * misrepresented as being the original software.
  20. * 3. This notice may not be removed or altered from any source distribution.
  21. */
  22. #include <Box2D/Collision/b2Collision.h>
  23. #include <Box2D/Collision/Shapes/b2CircleShape.h>
  24. #include <Box2D/Collision/Shapes/b2EdgeShape.h>
  25. #include <Box2D/Collision/Shapes/b2PolygonShape.h>
  26. enum b2EdgeType
  27. {
  28. b2_isolated,
  29. b2_concave,
  30. b2_flat,
  31. b2_convex
  32. };
  33. // Compute contact points for edge versus circle.
  34. // This accounts for edge connectivity.
  35. void b2CollideEdgeAndCircle(b2Manifold* manifold,
  36. const b2EdgeShape* edgeA, const b2Transform& xfA,
  37. const b2CircleShape* circleB, const b2Transform& xfB)
  38. {
  39. manifold->pointCount = 0;
  40. // Compute circle in frame of edge
  41. b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p));
  42. b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2;
  43. b2Vec2 e = B - A;
  44. // Barycentric coordinates
  45. float32 u = b2Dot(e, B - Q);
  46. float32 v = b2Dot(e, Q - A);
  47. float32 radius = edgeA->m_radius + circleB->m_radius;
  48. b2ContactFeature cf;
  49. cf.indexB = 0;
  50. cf.typeB = b2ContactFeature::e_vertex;
  51. // Region A
  52. if (v <= 0.0f)
  53. {
  54. b2Vec2 P = A;
  55. b2Vec2 d = Q - P;
  56. float32 dd = b2Dot(d, d);
  57. if (dd > radius * radius)
  58. {
  59. return;
  60. }
  61. // Is there an edge connected to A?
  62. if (edgeA->m_hasVertex0)
  63. {
  64. b2Vec2 A1 = edgeA->m_vertex0;
  65. b2Vec2 B1 = A;
  66. b2Vec2 e1 = B1 - A1;
  67. float32 u1 = b2Dot(e1, B1 - Q);
  68. // Is the circle in Region AB of the previous edge?
  69. if (u1 > 0.0f)
  70. {
  71. return;
  72. }
  73. }
  74. cf.indexA = 0;
  75. cf.typeA = b2ContactFeature::e_vertex;
  76. manifold->pointCount = 1;
  77. manifold->type = b2Manifold::e_circles;
  78. manifold->localNormal.SetZero();
  79. manifold->localPoint = P;
  80. manifold->points[0].id.key = 0;
  81. manifold->points[0].id.cf = cf;
  82. manifold->points[0].localPoint = circleB->m_p;
  83. return;
  84. }
  85. // Region B
  86. if (u <= 0.0f)
  87. {
  88. b2Vec2 P = B;
  89. b2Vec2 d = Q - P;
  90. float32 dd = b2Dot(d, d);
  91. if (dd > radius * radius)
  92. {
  93. return;
  94. }
  95. // Is there an edge connected to B?
  96. if (edgeA->m_hasVertex3)
  97. {
  98. b2Vec2 B2 = edgeA->m_vertex3;
  99. b2Vec2 A2 = B;
  100. b2Vec2 e2 = B2 - A2;
  101. float32 v2 = b2Dot(e2, Q - A2);
  102. // Is the circle in Region AB of the next edge?
  103. if (v2 > 0.0f)
  104. {
  105. return;
  106. }
  107. }
  108. cf.indexA = 1;
  109. cf.typeA = b2ContactFeature::e_vertex;
  110. manifold->pointCount = 1;
  111. manifold->type = b2Manifold::e_circles;
  112. manifold->localNormal.SetZero();
  113. manifold->localPoint = P;
  114. manifold->points[0].id.key = 0;
  115. manifold->points[0].id.cf = cf;
  116. manifold->points[0].localPoint = circleB->m_p;
  117. return;
  118. }
  119. // Region AB
  120. float32 den = b2Dot(e, e);
  121. b2Assert(den > 0.0f);
  122. b2Vec2 P = (1.0f / den) * (u * A + v * B);
  123. b2Vec2 d = Q - P;
  124. float32 dd = b2Dot(d, d);
  125. if (dd > radius * radius)
  126. {
  127. return;
  128. }
  129. b2Vec2 n(-e.y, e.x);
  130. if (b2Dot(n, Q - A) < 0.0f)
  131. {
  132. n.Set(-n.x, -n.y);
  133. }
  134. n.Normalize();
  135. cf.indexA = 0;
  136. cf.typeA = b2ContactFeature::e_face;
  137. manifold->pointCount = 1;
  138. manifold->type = b2Manifold::e_faceA;
  139. manifold->localNormal = n;
  140. manifold->localPoint = A;
  141. manifold->points[0].id.key = 0;
  142. manifold->points[0].id.cf = cf;
  143. manifold->points[0].localPoint = circleB->m_p;
  144. }
  145. struct b2EPAxis
  146. {
  147. enum Type
  148. {
  149. e_unknown,
  150. e_edgeA,
  151. e_edgeB,
  152. };
  153. Type type;
  154. int32 index;
  155. float32 separation;
  156. };
  157. static b2EPAxis b2EPEdgeSeparation(const b2Vec2& v1, const b2Vec2& v2, const b2Vec2& n, const b2PolygonShape* polygonB, float32 radius)
  158. {
  159. // EdgeA separation
  160. b2EPAxis axis;
  161. axis.type = b2EPAxis::e_edgeA;
  162. axis.index = 0;
  163. axis.separation = b2Dot(n, polygonB->m_vertices[0] - v1);
  164. for (int32 i = 1; i < polygonB->m_vertexCount; ++i)
  165. {
  166. float32 s = b2Dot(n, polygonB->m_vertices[i] - v1);
  167. if (s < axis.separation)
  168. {
  169. axis.separation = s;
  170. }
  171. }
  172. return axis;
  173. }
  174. static b2EPAxis b2EPPolygonSeparation(const b2Vec2& v1, const b2Vec2& v2, const b2Vec2& n, const b2PolygonShape* polygonB, float32 radius)
  175. {
  176. // PolygonB separation
  177. b2EPAxis axis;
  178. axis.type = b2EPAxis::e_edgeB;
  179. axis.index = 0;
  180. axis.separation = -FLT_MAX;
  181. for (int32 i = 0; i < polygonB->m_vertexCount; ++i)
  182. {
  183. float32 s1 = b2Dot(polygonB->m_normals[i], v1 - polygonB->m_vertices[i]);
  184. float32 s2 = b2Dot(polygonB->m_normals[i], v2 - polygonB->m_vertices[i]);
  185. float32 s = b2Min(s1, s2);
  186. if (s > axis.separation)
  187. {
  188. axis.index = i;
  189. axis.separation = s;
  190. if (s > radius)
  191. {
  192. return axis;
  193. }
  194. }
  195. }
  196. return axis;
  197. }
  198. static void b2FindIncidentEdge(b2ClipVertex c[2], const b2PolygonShape* poly1, int32 edge1, const b2PolygonShape* poly2)
  199. {
  200. int32 count1 = poly1->m_vertexCount;
  201. const b2Vec2* normals1 = poly1->m_normals;
  202. int32 count2 = poly2->m_vertexCount;
  203. const b2Vec2* vertices2 = poly2->m_vertices;
  204. const b2Vec2* normals2 = poly2->m_normals;
  205. b2Assert(0 <= edge1 && edge1 < count1);
  206. // Get the normal of the reference edge in poly2's frame.
  207. b2Vec2 normal1 = normals1[edge1];
  208. // Find the incident edge on poly2.
  209. int32 index = 0;
  210. float32 minDot = b2_maxFloat;
  211. for (int32 i = 0; i < count2; ++i)
  212. {
  213. float32 dot = b2Dot(normal1, normals2[i]);
  214. if (dot < minDot)
  215. {
  216. minDot = dot;
  217. index = i;
  218. }
  219. }
  220. // Build the clip vertices for the incident edge.
  221. int32 i1 = index;
  222. int32 i2 = i1 + 1 < count2 ? i1 + 1 : 0;
  223. c[0].v = vertices2[i1];
  224. c[0].id.cf.indexA = (uint8)edge1;
  225. c[0].id.cf.indexB = (uint8)i1;
  226. c[0].id.cf.typeA = b2ContactFeature::e_face;
  227. c[0].id.cf.typeB = b2ContactFeature::e_vertex;
  228. c[1].v = vertices2[i2];
  229. c[1].id.cf.indexA = (uint8)edge1;
  230. c[1].id.cf.indexB = (uint8)i2;
  231. c[1].id.cf.typeA = b2ContactFeature::e_face;
  232. c[1].id.cf.typeB = b2ContactFeature::e_vertex;
  233. }
  234. // Collide and edge and polygon. This uses the SAT and clipping to produce up to 2 contact points.
  235. // Edge adjacency is handle to produce locally valid contact points and normals. This is intended
  236. // to allow the polygon to slide smoothly over an edge chain.
  237. //
  238. // Algorithm
  239. // 1. Classify front-side or back-side collision with edge.
  240. // 2. Compute separation
  241. // 3. Process adjacent edges
  242. // 4. Classify adjacent edge as convex, flat, null, or concave
  243. // 5. Skip null or concave edges. Concave edges get a separate manifold.
  244. // 6. If the edge is flat, compute contact points as normal. Discard boundary points.
  245. // 7. If the edge is convex, compute it's separation.
  246. // 8. Use the minimum separation of up to three edges. If the minimum separation
  247. // is not the primary edge, return.
  248. // 9. If the minimum separation is the primary edge, compute the contact points and return.
  249. void b2CollideEdgeAndPolygon( b2Manifold* manifold,
  250. const b2EdgeShape* edgeA, const b2Transform& xfA,
  251. const b2PolygonShape* polygonB_in, const b2Transform& xfB)
  252. {
  253. manifold->pointCount = 0;
  254. b2Transform xf = b2MulT(xfA, xfB);
  255. // Create a polygon for edge shape A
  256. b2PolygonShape polygonA;
  257. polygonA.SetAsEdge(edgeA->m_vertex1, edgeA->m_vertex2);
  258. // Build polygonB in frame A
  259. b2PolygonShape polygonB;
  260. polygonB.m_radius = polygonB_in->m_radius;
  261. polygonB.m_vertexCount = polygonB_in->m_vertexCount;
  262. polygonB.m_centroid = b2Mul(xf, polygonB_in->m_centroid);
  263. for (int32 i = 0; i < polygonB.m_vertexCount; ++i)
  264. {
  265. polygonB.m_vertices[i] = b2Mul(xf, polygonB_in->m_vertices[i]);
  266. polygonB.m_normals[i] = b2Mul(xf.R, polygonB_in->m_normals[i]);
  267. }
  268. float32 totalRadius = polygonA.m_radius + polygonB.m_radius;
  269. // Edge geometry
  270. b2Vec2 v1 = edgeA->m_vertex1;
  271. b2Vec2 v2 = edgeA->m_vertex2;
  272. b2Vec2 e = v2 - v1;
  273. b2Vec2 edgeNormal(e.y, -e.x);
  274. edgeNormal.Normalize();
  275. // Determine side
  276. bool isFrontSide = b2Dot(edgeNormal, polygonB.m_centroid - v1) >= 0.0f;
  277. if (isFrontSide == false)
  278. {
  279. edgeNormal = -edgeNormal;
  280. }
  281. // Compute primary separating axis
  282. b2EPAxis edgeAxis = b2EPEdgeSeparation(v1, v2, edgeNormal, &polygonB, totalRadius);
  283. if (edgeAxis.separation > totalRadius)
  284. {
  285. // Shapes are separated
  286. return;
  287. }
  288. // Classify adjacent edges
  289. b2EdgeType types[2] = {b2_isolated, b2_isolated};
  290. if (edgeA->m_hasVertex0)
  291. {
  292. b2Vec2 v0 = edgeA->m_vertex0;
  293. float32 s = b2Dot(edgeNormal, v0 - v1);
  294. if (s > 0.1f * b2_linearSlop)
  295. {
  296. types[0] = b2_concave;
  297. }
  298. else if (s >= -0.1f * b2_linearSlop)
  299. {
  300. types[0] = b2_flat;
  301. }
  302. else
  303. {
  304. types[0] = b2_convex;
  305. }
  306. }
  307. if (edgeA->m_hasVertex3)
  308. {
  309. b2Vec2 v3 = edgeA->m_vertex3;
  310. float32 s = b2Dot(edgeNormal, v3 - v2);
  311. if (s > 0.1f * b2_linearSlop)
  312. {
  313. types[1] = b2_concave;
  314. }
  315. else if (s >= -0.1f * b2_linearSlop)
  316. {
  317. types[1] = b2_flat;
  318. }
  319. else
  320. {
  321. types[1] = b2_convex;
  322. }
  323. }
  324. if (types[0] == b2_convex)
  325. {
  326. // Check separation on previous edge.
  327. b2Vec2 v0 = edgeA->m_vertex0;
  328. b2Vec2 e0 = v1 - v0;
  329. b2Vec2 n0(e0.y, -e0.x);
  330. n0.Normalize();
  331. if (isFrontSide == false)
  332. {
  333. n0 = -n0;
  334. }
  335. b2EPAxis axis1 = b2EPEdgeSeparation(v0, v1, n0, &polygonB, totalRadius);
  336. if (axis1.separation > edgeAxis.separation)
  337. {
  338. // The polygon should collide with previous edge
  339. return;
  340. }
  341. }
  342. if (types[1] == b2_convex)
  343. {
  344. // Check separation on next edge.
  345. b2Vec2 v3 = edgeA->m_vertex3;
  346. b2Vec2 e2 = v3 - v2;
  347. b2Vec2 n2(e2.y, -e2.x);
  348. n2.Normalize();
  349. if (isFrontSide == false)
  350. {
  351. n2 = -n2;
  352. }
  353. b2EPAxis axis2 = b2EPEdgeSeparation(v2, v3, n2, &polygonB, totalRadius);
  354. if (axis2.separation > edgeAxis.separation)
  355. {
  356. // The polygon should collide with the next edge
  357. return;
  358. }
  359. }
  360. b2EPAxis polygonAxis = b2EPPolygonSeparation(v1, v2, edgeNormal, &polygonB, totalRadius);
  361. if (polygonAxis.separation > totalRadius)
  362. {
  363. return;
  364. }
  365. // Use hysteresis for jitter reduction.
  366. const float32 k_relativeTol = 0.98f;
  367. const float32 k_absoluteTol = 0.001f;
  368. b2EPAxis primaryAxis;
  369. if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol)
  370. {
  371. primaryAxis = polygonAxis;
  372. }
  373. else
  374. {
  375. primaryAxis = edgeAxis;
  376. }
  377. b2PolygonShape* poly1;
  378. b2PolygonShape* poly2;
  379. b2ClipVertex incidentEdge[2];
  380. if (primaryAxis.type == b2EPAxis::e_edgeA)
  381. {
  382. poly1 = &polygonA;
  383. poly2 = &polygonB;
  384. if (isFrontSide == false)
  385. {
  386. primaryAxis.index = 1;
  387. }
  388. manifold->type = b2Manifold::e_faceA;
  389. }
  390. else
  391. {
  392. poly1 = &polygonB;
  393. poly2 = &polygonA;
  394. manifold->type = b2Manifold::e_faceB;
  395. }
  396. int32 edge1 = primaryAxis.index;
  397. b2FindIncidentEdge(incidentEdge, poly1, primaryAxis.index, poly2);
  398. int32 count1 = poly1->m_vertexCount;
  399. const b2Vec2* vertices1 = poly1->m_vertices;
  400. int32 iv1 = edge1;
  401. int32 iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
  402. b2Vec2 v11 = vertices1[iv1];
  403. b2Vec2 v12 = vertices1[iv2];
  404. b2Vec2 tangent = v12 - v11;
  405. tangent.Normalize();
  406. b2Vec2 normal = b2Cross(tangent, 1.0f);
  407. b2Vec2 planePoint = 0.5f * (v11 + v12);
  408. // Face offset.
  409. float32 frontOffset = b2Dot(normal, v11);
  410. // Side offsets, extended by polytope skin thickness.
  411. float32 sideOffset1 = -b2Dot(tangent, v11) + totalRadius;
  412. float32 sideOffset2 = b2Dot(tangent, v12) + totalRadius;
  413. // Clip incident edge against extruded edge1 side edges.
  414. b2ClipVertex clipPoints1[2];
  415. b2ClipVertex clipPoints2[2];
  416. int np;
  417. // Clip to box side 1
  418. np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1);
  419. if (np < b2_maxManifoldPoints)
  420. {
  421. return;
  422. }
  423. // Clip to negative box side 1
  424. np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2);
  425. if (np < b2_maxManifoldPoints)
  426. {
  427. return;
  428. }
  429. // Now clipPoints2 contains the clipped points.
  430. if (primaryAxis.type == b2EPAxis::e_edgeA)
  431. {
  432. manifold->localNormal = normal;
  433. manifold->localPoint = planePoint;
  434. }
  435. else
  436. {
  437. manifold->localNormal = b2MulT(xf.R, normal);
  438. manifold->localPoint = b2MulT(xf, planePoint);
  439. }
  440. int32 pointCount = 0;
  441. for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
  442. {
  443. float32 separation;
  444. separation = b2Dot(normal, clipPoints2[i].v) - frontOffset;
  445. if (separation <= totalRadius)
  446. {
  447. b2ManifoldPoint* cp = manifold->points + pointCount;
  448. if (primaryAxis.type == b2EPAxis::e_edgeA)
  449. {
  450. cp->localPoint = b2MulT(xf, clipPoints2[i].v);
  451. cp->id = clipPoints2[i].id;
  452. }
  453. else
  454. {
  455. cp->localPoint = clipPoints2[i].v;
  456. cp->id.cf.typeA = clipPoints2[i].id.cf.typeB;
  457. cp->id.cf.typeB = clipPoints2[i].id.cf.typeA;
  458. cp->id.cf.indexA = clipPoints2[i].id.cf.indexB;
  459. cp->id.cf.indexB = clipPoints2[i].id.cf.indexA;
  460. }
  461. if (cp->id.cf.typeA == b2ContactFeature::e_vertex && types[cp->id.cf.indexA] == b2_flat)
  462. {
  463. continue;
  464. }
  465. ++pointCount;
  466. }
  467. }
  468. manifold->pointCount = pointCount;
  469. }