1 /* 2 * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 */ 22 module dchip.cpCollision; 23 24 import core.stdc.stdlib : alloca; 25 26 import std..string; 27 28 import dchip.cpArbiter; 29 import dchip.cpBB; 30 import dchip.cpBody; 31 import dchip.chipmunk; 32 import dchip.chipmunk_private; 33 import dchip.chipmunk_types; 34 import dchip.cpPolyShape; 35 import dchip.cpShape; 36 import dchip.cpSpace; 37 import dchip.cpVect; 38 import dchip.util; 39 40 /+ #if DEBUG && 0 41 #include "ChipmunkDemo.h" 42 #define DRAW_ALL 0 43 #define DRAW_GJK (0 || DRAW_ALL) 44 #define DRAW_EPA (0 || DRAW_ALL) 45 #define DRAW_CLOSEST (0 || DRAW_ALL) 46 #define DRAW_CLIP (0 || DRAW_ALL) 47 48 #define PRINT_LOG 0 49 #endif +/ 50 51 enum ENABLE_CACHING = 1; 52 53 enum MAX_GJK_ITERATIONS = 30; 54 enum MAX_EPA_ITERATIONS = 30; 55 enum WARN_GJK_ITERATIONS = 20; 56 enum WARN_EPA_ITERATIONS = 20; 57 58 // Add contact points for circle to circle collisions. 59 // Used by several collision tests. 60 // TODO should accept hash parameter 61 int CircleToCircleQuery(const cpVect p1, const cpVect p2, const cpFloat r1, const cpFloat r2, cpHashValue hash, cpContact* con) 62 { 63 cpFloat mindist = r1 + r2; 64 cpVect delta = cpvsub(p2, p1); 65 cpFloat distsq = cpvlengthsq(delta); 66 67 if (distsq < mindist * mindist) 68 { 69 cpFloat dist = cpfsqrt(distsq); 70 cpVect n = (dist ? cpvmult(delta, 1.0f / dist) : cpv(1.0f, 0.0f)); 71 cpContactInit(con, cpvlerp(p1, p2, r1 / (r1 + r2)), n, dist - mindist, hash); 72 73 return 1; 74 } 75 else 76 { 77 return 0; 78 } 79 } 80 81 //MARK: Support Points and Edges: 82 83 int PolySupportPointIndex(const int count, const cpVect* verts, const cpVect n) 84 { 85 cpFloat max = -INFINITY; 86 int index = 0; 87 88 for (int i = 0; i < count; i++) 89 { 90 cpVect v = verts[i]; 91 cpFloat d = cpvdot(v, n); 92 93 if (d > max) 94 { 95 max = d; 96 index = i; 97 } 98 } 99 100 return index; 101 } 102 103 struct SupportPoint 104 { 105 cpVect p; 106 cpCollisionID id; 107 } 108 109 SupportPoint SupportPointNew(cpVect p, cpCollisionID id) 110 { 111 SupportPoint point = { p, id }; 112 return point; 113 } 114 115 alias SupportPointFunc = SupportPoint function(const cpShape* shape, const cpVect n); 116 117 SupportPoint CircleSupportPoint(const cpCircleShape* circle, const cpVect n) 118 { 119 return SupportPointNew(circle.tc, 0); 120 } 121 122 SupportPoint SegmentSupportPoint(const cpSegmentShape* seg, const cpVect n) 123 { 124 if (cpvdot(seg.ta, n) > cpvdot(seg.tb, n)) 125 { 126 return SupportPointNew(seg.ta, 0); 127 } 128 else 129 { 130 return SupportPointNew(seg.tb, 1); 131 } 132 } 133 134 SupportPoint PolySupportPoint(const cpPolyShape* poly, const cpVect n) 135 { 136 const cpVect* verts = poly.tVerts; 137 int i = PolySupportPointIndex(poly.numVerts, verts, n); 138 return SupportPointNew(verts[i], i); 139 } 140 141 struct MinkowskiPoint 142 { 143 cpVect a, b; 144 cpVect ab; 145 cpCollisionID id; 146 } 147 148 MinkowskiPoint MinkowskiPointNew(const SupportPoint a, const SupportPoint b) 149 { 150 MinkowskiPoint point = { a.p, b.p, cpvsub(b.p, a.p), (a.id & 0xFF) << 8 | (b.id & 0xFF) }; 151 return point; 152 } 153 154 struct SupportContext 155 { 156 const cpShape* shape1; 157 const cpShape* shape2; 158 SupportPointFunc func1, func2; 159 } 160 161 MinkowskiPoint Support(const SupportContext* ctx, const cpVect n) 162 { 163 SupportPoint a = ctx.func1(ctx.shape1, cpvneg(n)); 164 SupportPoint b = ctx.func2(ctx.shape2, n); 165 return MinkowskiPointNew(a, b); 166 } 167 168 struct EdgePoint 169 { 170 cpVect p; 171 cpHashValue hash; 172 } 173 174 struct Edge 175 { 176 EdgePoint a, b; 177 cpFloat r = 0; 178 cpVect n; 179 } 180 181 Edge EdgeNew(cpVect va, cpVect vb, cpHashValue ha, cpHashValue hb, cpFloat r) 182 { 183 Edge edge = { { va, ha }, { vb, hb }, r, cpvnormalize(cpvperp(cpvsub(vb, va))) }; 184 return edge; 185 } 186 187 Edge SupportEdgeForPoly(const cpPolyShape* poly, const cpVect n) 188 { 189 int numVerts = poly.numVerts; 190 int i1 = PolySupportPointIndex(poly.numVerts, poly.tVerts, n); 191 192 // TODO get rid of mod eventually, very expensive on ARM 193 int i0 = (i1 - 1 + numVerts) % numVerts; 194 int i2 = (i1 + 1) % numVerts; 195 196 cpVect* verts = cast(cpVect*)poly.tVerts; 197 198 if (cpvdot(n, poly.tPlanes[i1].n) > cpvdot(n, poly.tPlanes[i2].n)) 199 { 200 Edge edge = { { verts[i0], CP_HASH_PAIR(poly, i0) }, { verts[i1], CP_HASH_PAIR(poly, i1) }, poly.r, poly.tPlanes[i1].n }; 201 return edge; 202 } 203 else 204 { 205 Edge edge = { { verts[i1], CP_HASH_PAIR(poly, i1) }, { verts[i2], CP_HASH_PAIR(poly, i2) }, poly.r, poly.tPlanes[i2].n }; 206 return edge; 207 } 208 } 209 210 Edge SupportEdgeForSegment(const cpSegmentShape* seg, const cpVect n) 211 { 212 if (cpvdot(seg.tn, n) > 0.0) 213 { 214 Edge edge = { { seg.ta, CP_HASH_PAIR(seg, 0) }, { seg.tb, CP_HASH_PAIR(seg, 1) }, seg.r, seg.tn }; 215 return edge; 216 } 217 else 218 { 219 Edge edge = { { seg.tb, CP_HASH_PAIR(seg, 1) }, { seg.ta, CP_HASH_PAIR(seg, 0) }, seg.r, cpvneg(seg.tn) }; 220 return edge; 221 } 222 } 223 224 cpFloat ClosestT(const cpVect a, const cpVect b) 225 { 226 cpVect delta = cpvsub(b, a); 227 return -cpfclamp(cpvdot(delta, cpvadd(a, b)) / cpvlengthsq(delta), -1.0f, 1.0f); 228 } 229 230 cpVect LerpT(const cpVect a, const cpVect b, const cpFloat t) 231 { 232 cpFloat ht = 0.5f * t; 233 return cpvadd(cpvmult(a, 0.5f - ht), cpvmult(b, 0.5f + ht)); 234 } 235 236 struct ClosestPoints 237 { 238 cpVect a, b; 239 cpVect n; 240 cpFloat d = 0; 241 cpCollisionID id; 242 } 243 244 ClosestPoints ClosestPointsNew(const MinkowskiPoint v0, const MinkowskiPoint v1) 245 { 246 cpFloat t = ClosestT(v0.ab, v1.ab); 247 cpVect p = LerpT(v0.ab, v1.ab, t); 248 249 cpVect pa = LerpT(v0.a, v1.a, t); 250 cpVect pb = LerpT(v0.b, v1.b, t); 251 cpCollisionID id = (v0.id & 0xFFFF) << 16 | (v1.id & 0xFFFF); 252 253 cpVect delta = cpvsub(v1.ab, v0.ab); 254 cpVect n = cpvnormalize(cpvperp(delta)); 255 cpFloat d = -cpvdot(n, p); 256 257 if (d <= 0.0f || (0.0f < t && t < 1.0f)) 258 { 259 ClosestPoints points = { pa, pb, cpvneg(n), d, id }; 260 return points; 261 } 262 else 263 { 264 cpFloat d2 = cpvlength(p); 265 cpVect n_ = cpvmult(p, 1.0f / (d2 + CPFLOAT_MIN)); 266 267 ClosestPoints points = { pa, pb, n_, d2, id }; 268 return points; 269 } 270 } 271 272 //MARK: EPA Functions 273 274 cpFloat ClosestDist(const cpVect v0, const cpVect v1) 275 { 276 return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1))); 277 } 278 279 ClosestPoints EPARecurse(const SupportContext* ctx, const int count, const MinkowskiPoint* hull, const int iteration) 280 { 281 int mini = 0; 282 cpFloat minDist = INFINITY; 283 284 // TODO: precalculate this when building the hull and save a step. 285 for (int j = 0, i = count - 1; j < count; i = j, j++) 286 { 287 cpFloat d = ClosestDist(hull[i].ab, hull[j].ab); 288 289 if (d < minDist) 290 { 291 minDist = d; 292 mini = i; 293 } 294 } 295 296 MinkowskiPoint v0 = hull[mini]; 297 MinkowskiPoint v1 = hull[(mini + 1) % count]; 298 cpAssertSoft(!cpveql(v0.ab, v1.ab), "!cpveql(v0.ab, v1.ab)", "Internal Error: EPA vertexes are the same (%d and %d)", mini, (mini + 1) % count); 299 300 MinkowskiPoint p = Support(ctx, cpvperp(cpvsub(v1.ab, v0.ab))); 301 302 /+ #if DRAW_EPA 303 cpVect verts[count]; 304 305 for (int i = 0; i < count; i++) 306 verts[i] = hull[i].ab; 307 308 ChipmunkDebugDrawPolygon(count, verts, RGBAColor(1, 1, 0, 1), RGBAColor(1, 1, 0, 0.25)); 309 ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 0, 0, 1)); 310 311 ChipmunkDebugDrawPoints(5, 1, (cpVect[]){ p.ab }, RGBAColor(1, 1, 1, 1)); 312 #endif +/ 313 314 cpFloat area2x = cpvcross(cpvsub(v1.ab, v0.ab), cpvadd(cpvsub(p.ab, v0.ab), cpvsub(p.ab, v1.ab))); 315 316 if (area2x > 0.0f && iteration < MAX_EPA_ITERATIONS) 317 { 318 int count2 = 1; 319 MinkowskiPoint* hull2 = cast(MinkowskiPoint*)alloca((count + 1) * MinkowskiPoint.sizeof); 320 hull2[0] = p; 321 322 for (int i = 0; i < count; i++) 323 { 324 int index = (mini + 1 + i) % count; 325 326 cpVect h0 = hull2[count2 - 1].ab; 327 cpVect h1 = hull[index].ab; 328 cpVect h2 = (i + 1 < count ? hull[(index + 1) % count] : p).ab; 329 330 // TODO: Should this be changed to an area2x check? 331 if (cpvcross(cpvsub(h2, h0), cpvsub(h1, h0)) > 0.0f) 332 { 333 hull2[count2] = hull[index]; 334 count2++; 335 } 336 } 337 338 return EPARecurse(ctx, count2, hull2, iteration + 1); 339 } 340 else 341 { 342 cpAssertWarn(iteration < WARN_EPA_ITERATIONS, "High EPA iterations: %d", iteration); 343 return ClosestPointsNew(v0, v1); 344 } 345 } 346 347 ClosestPoints EPA(const SupportContext* ctx, const MinkowskiPoint v0, const MinkowskiPoint v1, const MinkowskiPoint v2) 348 { 349 // TODO: allocate a NxM array here and do an in place convex hull reduction in EPARecurse 350 MinkowskiPoint[3] hull; 351 hull[0] = v0; 352 hull[1] = v1; 353 hull[2] = v2; 354 return EPARecurse(ctx, 3, hull.ptr, 1); 355 } 356 357 //MARK: GJK Functions. 358 359 ClosestPoints GJKRecurse(const SupportContext* ctx, const MinkowskiPoint v0, const MinkowskiPoint v1, const int iteration) 360 { 361 if (iteration > MAX_GJK_ITERATIONS) 362 { 363 cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); 364 return ClosestPointsNew(v0, v1); 365 } 366 367 cpVect delta = cpvsub(v1.ab, v0.ab); 368 369 if (cpvcross(delta, cpvadd(v0.ab, v1.ab)) > 0.0f) 370 { 371 // Origin is behind axis. Flip and try again. 372 return GJKRecurse(ctx, v1, v0, iteration + 1); 373 } 374 else 375 { 376 cpFloat t = ClosestT(v0.ab, v1.ab); 377 cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(delta) : cpvneg(LerpT(v0.ab, v1.ab, t))); 378 MinkowskiPoint p = Support(ctx, n); 379 380 /+ #if DRAW_GJK 381 ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1)); 382 cpVect c = cpvlerp(v0.ab, v1.ab, 0.5); 383 ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1)); 384 385 ChipmunkDebugDrawPoints(5.0, 1, &p.ab, RGBAColor(1, 1, 1, 1)); 386 #endif +/ 387 388 if ( 389 cpvcross(cpvsub(v1.ab, p.ab), cpvadd(v1.ab, p.ab)) > 0.0f && 390 cpvcross(cpvsub(v0.ab, p.ab), cpvadd(v0.ab, p.ab)) < 0.0f 391 ) 392 { 393 cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK.EPA iterations: %d", iteration); 394 395 // The triangle v0, p, v1 contains the origin. Use EPA to find the MSA. 396 return EPA(ctx, v0, p, v1); 397 } 398 else 399 { 400 // The new point must be farther along the normal than the existing points. 401 if (cpvdot(p.ab, n) <= cpfmax(cpvdot(v0.ab, n), cpvdot(v1.ab, n))) 402 { 403 cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); 404 return ClosestPointsNew(v0, v1); 405 } 406 else 407 { 408 if (ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)) 409 { 410 return GJKRecurse(ctx, v0, p, iteration + 1); 411 } 412 else 413 { 414 return GJKRecurse(ctx, p, v1, iteration + 1); 415 } 416 } 417 } 418 } 419 } 420 421 SupportPoint ShapePoint(const cpShape* shape, const int i) 422 { 423 switch (shape.klass.type) 424 { 425 case CP_CIRCLE_SHAPE: 426 { 427 return SupportPointNew((cast(cpCircleShape*)shape).tc, 0); 428 } 429 430 case CP_SEGMENT_SHAPE: 431 { 432 cpSegmentShape* seg = cast(cpSegmentShape*)shape; 433 return SupportPointNew(i == 0 ? seg.ta : seg.tb, i); 434 } 435 436 case CP_POLY_SHAPE: 437 { 438 cpPolyShape* poly = cast(cpPolyShape*)shape; 439 440 // Poly shapes may change vertex count. 441 int index = (i < poly.numVerts ? i : 0); 442 return SupportPointNew(poly.tVerts[index], index); 443 } 444 445 default: 446 { 447 return SupportPointNew(cpvzero, 0); 448 } 449 } 450 } 451 452 ClosestPoints GJK(const SupportContext* ctx, cpCollisionID* id) 453 { 454 /+ #if DRAW_GJK || DRAW_EPA 455 456 // draw the minkowski difference origin 457 cpVect origin = cpvzero; 458 ChipmunkDebugDrawPoints(5.0, 1, &origin, RGBAColor(1, 0, 0, 1)); 459 460 int mdiffCount = ctx.count1 * ctx.count2; 461 cpVect* mdiffVerts = alloca(mdiffCount * sizeof(cpVect)); 462 463 for (int i = 0; i < ctx.count1; i++) 464 { 465 for (int j = 0; j < ctx.count2; j++) 466 { 467 cpVect v1 = ShapePoint(ctx.count1, ctx.verts1, i).p; 468 cpVect v2 = ShapePoint(ctx.count2, ctx.verts2, j).p; 469 mdiffVerts[i * ctx.count2 + j] = cpvsub(v2, v1); 470 } 471 } 472 473 cpVect* hullVerts = alloca(mdiffCount * sizeof(cpVect)); 474 int hullCount = cpConvexHull(mdiffCount, mdiffVerts, hullVerts, null, 0.0); 475 476 ChipmunkDebugDrawPolygon(hullCount, hullVerts, RGBAColor(1, 0, 0, 1), RGBAColor(1, 0, 0, 0.25)); 477 ChipmunkDebugDrawPoints(2.0, mdiffCount, mdiffVerts, RGBAColor(1, 0, 0, 1)); 478 #endif +/ 479 480 MinkowskiPoint v0, v1; 481 482 if (*id && ENABLE_CACHING) 483 { 484 v0 = MinkowskiPointNew(ShapePoint(ctx.shape1, (*id >> 24) & 0xFF), ShapePoint(ctx.shape2, (*id >> 16) & 0xFF)); 485 v1 = MinkowskiPointNew(ShapePoint(ctx.shape1, (*id >> 8) & 0xFF), ShapePoint(ctx.shape2, (*id ) & 0xFF)); 486 } 487 else 488 { 489 cpVect axis = cpvperp(cpvsub(cpBBCenter(ctx.shape1.bb), cpBBCenter(ctx.shape2.bb))); 490 v0 = Support(ctx, axis); 491 v1 = Support(ctx, cpvneg(axis)); 492 } 493 494 ClosestPoints points = GJKRecurse(ctx, v0, v1, 1); 495 *id = points.id; 496 return points; 497 } 498 499 //MARK: Contact Clipping 500 501 void Contact1(cpFloat dist, cpVect a, cpVect b, cpFloat refr, cpFloat incr, cpVect n, cpHashValue hash, cpContact* arr) 502 { 503 cpFloat rsum = refr + incr; 504 cpFloat alpha = (rsum > 0.0f ? refr / rsum : 0.5f); 505 cpVect point = cpvlerp(a, b, alpha); 506 507 cpContactInit(arr, point, n, dist - rsum, hash); 508 } 509 510 int Contact2(cpVect refp, cpVect inca, cpVect incb, cpFloat refr, cpFloat incr, cpVect refn, cpVect n, cpHashValue hash, cpContact* arr) 511 { 512 cpFloat cian = cpvcross(inca, refn); 513 cpFloat cibn = cpvcross(incb, refn); 514 cpFloat crpn = cpvcross(refp, refn); 515 cpFloat t = 1.0f - cpfclamp01((cibn - crpn) / (cibn - cian)); 516 517 cpVect point = cpvlerp(inca, incb, t); 518 cpFloat pd = cpvdot(cpvsub(point, refp), refn); 519 520 if (t > 0.0f && pd <= 0.0f) 521 { 522 cpFloat rsum = refr + incr; 523 cpFloat alpha = (rsum > 0.0f ? incr * (1.0f - (rsum + pd) / rsum) : -0.5f * pd); 524 525 cpContactInit(arr, cpvadd(point, cpvmult(refn, alpha)), n, pd, hash); 526 return 1; 527 } 528 else 529 { 530 return 0; 531 } 532 } 533 534 int ClipContacts(const Edge ref_, const Edge inc, const ClosestPoints points, const cpFloat nflip, cpContact* arr) 535 { 536 cpVect inc_offs = cpvmult(inc.n, inc.r); 537 cpVect ref_offs = cpvmult(ref_.n, ref_.r); 538 539 cpVect inca = cpvadd(inc.a.p, inc_offs); 540 cpVect incb = cpvadd(inc.b.p, inc_offs); 541 542 cpVect closest_inca = cpClosetPointOnSegment(inc.a.p, ref_.a.p, ref_.b.p); 543 cpVect closest_incb = cpClosetPointOnSegment(inc.b.p, ref_.a.p, ref_.b.p); 544 545 cpVect msa = cpvmult(points.n, nflip * points.d); 546 cpFloat cost_a = cpvdistsq(cpvsub(inc.a.p, closest_inca), msa); 547 cpFloat cost_b = cpvdistsq(cpvsub(inc.b.p, closest_incb), msa); 548 549 /+ #if DRAW_CLIP 550 ChipmunkDebugDrawSegment(ref_.a.p, ref_.b.p, RGBAColor(1, 0, 0, 1)); 551 ChipmunkDebugDrawSegment(inc.a.p, inc.b.p, RGBAColor(0, 1, 0, 1)); 552 ChipmunkDebugDrawSegment(inca, incb, RGBAColor(0, 1, 0, 1)); 553 554 cpVect cref = cpvlerp(ref_.a.p, ref_.b.p, 0.5); 555 ChipmunkDebugDrawSegment(cref, cpvadd(cref, cpvmult(ref_.n, 5.0)), RGBAColor(1, 0, 0, 1)); 556 557 cpVect cinc = cpvlerp(inc.a.p, inc.b.p, 0.5); 558 ChipmunkDebugDrawSegment(cinc, cpvadd(cinc, cpvmult(inc.n, 5.0)), RGBAColor(1, 0, 0, 1)); 559 560 ChipmunkDebugDrawPoints(5.0, 2, (cpVect[]){ ref_.a.p, inc.a.p }, RGBAColor(1, 1, 0, 1)); 561 ChipmunkDebugDrawPoints(5.0, 2, (cpVect[]){ ref_.b.p, inc.b.p }, RGBAColor(0, 1, 1, 1)); 562 563 if (cost_a < cost_b) 564 { 565 ChipmunkDebugDrawSegment(closest_inca, inc.a.p, RGBAColor(1, 0, 1, 1)); 566 } 567 else 568 { 569 ChipmunkDebugDrawSegment(closest_incb, inc.b.p, RGBAColor(1, 0, 1, 1)); 570 } 571 #endif +/ 572 573 cpHashValue hash_iarb = CP_HASH_PAIR(inc.a.hash, ref_.b.hash); 574 cpHashValue hash_ibra = CP_HASH_PAIR(inc.b.hash, ref_.a.hash); 575 576 if (cost_a < cost_b) 577 { 578 cpVect refp = cpvadd(ref_.a.p, ref_offs); 579 Contact1(points.d, closest_inca, inc.a.p, ref_.r, inc.r, points.n, hash_iarb, arr); 580 return Contact2(refp, inca, incb, ref_.r, inc.r, ref_.n, points.n, hash_ibra, arr + 1) + 1; 581 } 582 else 583 { 584 cpVect refp = cpvadd(ref_.b.p, ref_offs); 585 Contact1(points.d, closest_incb, inc.b.p, ref_.r, inc.r, points.n, hash_ibra, arr); 586 return Contact2(refp, incb, inca, ref_.r, inc.r, ref_.n, points.n, hash_iarb, arr + 1) + 1; 587 } 588 } 589 590 int ContactPoints(const Edge e1, const Edge e2, const ClosestPoints points, cpContact* arr) 591 { 592 cpFloat mindist = e1.r + e2.r; 593 594 if (points.d <= mindist) 595 { 596 cpFloat pick = cpvdot(e1.n, points.n) + cpvdot(e2.n, points.n); 597 598 if ( 599 (pick != 0.0f && pick > 0.0f) || 600 601 // If the edges are both perfectly aligned weird things happen. 602 // This is *very* common at the start of a simulation. 603 // Pick the longest edge as the reference to break the tie. 604 (pick == 0.0f && (cpvdistsq(e1.a.p, e1.b.p) > cpvdistsq(e2.a.p, e2.b.p))) 605 ) 606 { 607 return ClipContacts(e1, e2, points, 1.0f, arr); 608 } 609 else 610 { 611 return ClipContacts(e2, e1, points, -1.0f, arr); 612 } 613 } 614 else 615 { 616 return 0; 617 } 618 } 619 620 //MARK: Collision Functions 621 622 alias CollisionFunc = int function(const cpShape* a, const cpShape* b, cpCollisionID* id, cpContact* arr); 623 624 // Collide circle shapes. 625 int CircleToCircle(const cpCircleShape* c1, const cpCircleShape* c2, cpCollisionID* id, cpContact* arr) 626 { 627 return CircleToCircleQuery(c1.tc, c2.tc, c1.r, c2.r, 0, arr); 628 } 629 630 int CircleToSegment(const cpCircleShape* circleShape, const cpSegmentShape* segmentShape, cpCollisionID* id, cpContact* con) 631 { 632 cpVect seg_a = segmentShape.ta; 633 cpVect seg_b = segmentShape.tb; 634 cpVect center = circleShape.tc; 635 636 cpVect seg_delta = cpvsub(seg_b, seg_a); 637 cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a)) / cpvlengthsq(seg_delta)); 638 cpVect closest = cpvadd(seg_a, cpvmult(seg_delta, closest_t)); 639 640 if (CircleToCircleQuery(center, closest, circleShape.r, segmentShape.r, 0, con)) 641 { 642 cpVect n = con[0].n; 643 644 // Reject endcap collisions if tangents are provided. 645 if ( 646 (closest_t != 0.0f || cpvdot(n, cpvrotate(segmentShape.a_tangent, segmentShape.shape.body_.rot)) >= 0.0) && 647 (closest_t != 1.0f || cpvdot(n, cpvrotate(segmentShape.b_tangent, segmentShape.shape.body_.rot)) >= 0.0) 648 ) 649 { 650 return 1; 651 } 652 } 653 654 return 0; 655 } 656 657 int SegmentToSegment(const cpSegmentShape* seg1, const cpSegmentShape* seg2, cpCollisionID* id, cpContact* arr) 658 { 659 SupportContext context = { cast(cpShape*)seg1, cast(cpShape*)seg2, safeCast!SupportPointFunc(&SegmentSupportPoint), safeCast!SupportPointFunc(&SegmentSupportPoint) }; 660 ClosestPoints points = GJK(&context, id); 661 662 /+ #if DRAW_CLOSEST 663 #if PRINT_LOG 664 665 // ChipmunkDemoPrintString("Distance: %.2f\n", points.d); 666 #endif 667 668 ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1)); 669 ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1)); 670 ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); 671 ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); 672 #endif +/ 673 674 cpVect n = points.n; 675 cpVect rot1 = seg1.shape.body_.rot; 676 cpVect rot2 = seg2.shape.body_.rot; 677 678 if ( 679 points.d <= (seg1.r + seg2.r) && 680 ( 681 (!cpveql(points.a, seg1.ta) || cpvdot(n, cpvrotate(seg1.a_tangent, rot1)) <= 0.0) && 682 (!cpveql(points.a, seg1.tb) || cpvdot(n, cpvrotate(seg1.b_tangent, rot1)) <= 0.0) && 683 (!cpveql(points.b, seg2.ta) || cpvdot(n, cpvrotate(seg2.a_tangent, rot2)) >= 0.0) && 684 (!cpveql(points.b, seg2.tb) || cpvdot(n, cpvrotate(seg2.b_tangent, rot2)) >= 0.0) 685 ) 686 ) 687 { 688 return ContactPoints(SupportEdgeForSegment(seg1, n), SupportEdgeForSegment(seg2, cpvneg(n)), points, arr); 689 } 690 else 691 { 692 return 0; 693 } 694 } 695 696 int PolyToPoly(const cpPolyShape* poly1, const cpPolyShape* poly2, cpCollisionID* id, cpContact* arr) 697 { 698 SupportContext context = { cast(cpShape*)poly1, cast(cpShape*)poly2, safeCast!SupportPointFunc(&PolySupportPoint), safeCast!SupportPointFunc(&PolySupportPoint) }; 699 ClosestPoints points = GJK(&context, id); 700 701 /+ #if DRAW_CLOSEST 702 #if PRINT_LOG 703 704 // ChipmunkDemoPrintString("Distance: %.2f\n", points.d); 705 #endif 706 707 ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); 708 ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); 709 ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); 710 ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); 711 #endif +/ 712 713 if (points.d - poly1.r - poly2.r <= 0.0) 714 { 715 return ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, arr); 716 } 717 else 718 { 719 return 0; 720 } 721 } 722 723 int SegmentToPoly(const cpSegmentShape* seg, const cpPolyShape* poly, cpCollisionID* id, cpContact* arr) 724 { 725 SupportContext context = { cast(cpShape*)seg, cast(cpShape*)poly, safeCast!SupportPointFunc(&SegmentSupportPoint), safeCast!SupportPointFunc(&PolySupportPoint) }; 726 ClosestPoints points = GJK(&context, id); 727 728 /+ #if DRAW_CLOSEST 729 #if PRINT_LOG 730 731 // ChipmunkDemoPrintString("Distance: %.2f\n", points.d); 732 #endif 733 734 ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); 735 ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); 736 ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); 737 ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); 738 #endif +/ 739 740 // Reject endcap collisions if tangents are provided. 741 cpVect n = points.n; 742 cpVect rot = seg.shape.body_.rot; 743 744 if ( 745 points.d - seg.r - poly.r <= 0.0 && 746 ( 747 (!cpveql(points.a, seg.ta) || cpvdot(n, cpvrotate(seg.a_tangent, rot)) <= 0.0) && 748 (!cpveql(points.a, seg.tb) || cpvdot(n, cpvrotate(seg.b_tangent, rot)) <= 0.0) 749 ) 750 ) 751 { 752 return ContactPoints(SupportEdgeForSegment(seg, n), SupportEdgeForPoly(poly, cpvneg(n)), points, arr); 753 } 754 else 755 { 756 return 0; 757 } 758 } 759 760 // This one is less gross, but still gross. 761 // TODO: Comment me! 762 int CircleToPoly(const cpCircleShape* circle, const cpPolyShape* poly, cpCollisionID* id, cpContact* con) 763 { 764 SupportContext context = { cast(cpShape*)circle, cast(cpShape*)poly, safeCast!SupportPointFunc(&CircleSupportPoint), safeCast!SupportPointFunc(&PolySupportPoint) }; 765 ClosestPoints points = GJK(&context, id); 766 767 /+ #if DRAW_CLOSEST 768 ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); 769 ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); 770 ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); 771 ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); 772 #endif +/ 773 774 cpFloat mindist = circle.r + poly.r; 775 776 if (points.d - mindist <= 0.0) 777 { 778 cpVect p = cpvlerp(points.a, points.b, circle.r / (mindist)); 779 cpContactInit(con, p, points.n, points.d - mindist, 0); 780 return 1; 781 } 782 else 783 { 784 return 0; 785 } 786 } 787 788 /* const */ __gshared CollisionFunc[9] builtinCollisionFuncs; 789 /* const */ __gshared CollisionFunc* colfuncs; 790 /* const */ __gshared CollisionFunc[9] segmentCollisions; 791 792 void _initModuleCtor_cpCollision() 793 { 794 builtinCollisionFuncs = [ 795 safeCast!CollisionFunc(&CircleToCircle), 796 null, 797 null, 798 safeCast!CollisionFunc(&CircleToSegment), 799 null, 800 null, 801 safeCast!CollisionFunc(&CircleToPoly), 802 safeCast!CollisionFunc(&SegmentToPoly), 803 safeCast!CollisionFunc(&PolyToPoly), 804 ]; 805 806 colfuncs = builtinCollisionFuncs.ptr; 807 808 segmentCollisions = [ 809 safeCast!CollisionFunc(&CircleToCircle), 810 null, 811 null, 812 safeCast!CollisionFunc(&CircleToSegment), 813 safeCast!CollisionFunc(&SegmentToSegment), 814 null, 815 safeCast!CollisionFunc(&CircleToPoly), 816 safeCast!CollisionFunc(&SegmentToPoly), 817 safeCast!CollisionFunc(&PolyToPoly), 818 ]; 819 } 820 821 void cpEnableSegmentToSegmentCollisions() 822 { 823 colfuncs = segmentCollisions.ptr; 824 } 825 826 int cpCollideShapes(const cpShape* a, const cpShape* b, cpCollisionID* id, cpContact* arr) 827 { 828 // Their shape types must be in order. 829 cpAssertSoft(a.klass.type <= b.klass.type, "Internal Error: Collision shapes passed to cpCollideShapes() are not sorted."); 830 831 CollisionFunc cfunc = colfuncs[a.klass.type + b.klass.type * CP_NUM_SHAPES]; 832 833 int numContacts = (cfunc ? cfunc(a, b, id, arr) : 0); 834 cpAssertSoft(numContacts <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Too many contact points returned."); 835 836 return numContacts; 837 }