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.cpBody; 23 24 import std..string; 25 26 import dchip.chipmunk; 27 import dchip.chipmunk_private; 28 import dchip.chipmunk_types; 29 import dchip.constraints_util; 30 import dchip.cpArray; 31 import dchip.cpArbiter; 32 import dchip.cpConstraint; 33 import dchip.cpShape; 34 import dchip.cpSpace; 35 import dchip.cpSpaceComponent; 36 import dchip.cpVect; 37 38 /// Chipmunk's rigid body_ type. Rigid bodies hold the physical properties of an object like 39 /// it's mass, and position and velocity of it's center of gravity. They don't have an shape on their own. 40 /// They are given a shape by creating collision shapes (cpShape) that point to the body_. 41 42 /// Rigid body_ velocity update function type. 43 alias cpBodyVelocityFunc = void function(cpBody* bdy, cpVect gravity, cpFloat damping, cpFloat dt); 44 45 /// Rigid body_ position update function type. 46 alias cpBodyPositionFunc = void function(cpBody* bdy, cpFloat dt); 47 48 /// Used internally to track information on the collision graph. 49 /// @private 50 struct cpComponentNode 51 { 52 cpBody* root; 53 cpBody* next; 54 cpFloat idleTime = 0; 55 } 56 57 /// Chipmunk's rigid body_ struct. 58 struct cpBody 59 { 60 /// Function that is called to integrate the body_'s velocity. (Defaults to cpBodyUpdateVelocity) 61 cpBodyVelocityFunc velocity_func; 62 63 /// Function that is called to integrate the body_'s position. (Defaults to cpBodyUpdatePosition) 64 cpBodyPositionFunc position_func; 65 66 /// Mass of the body_. 67 /// Must agree with cpBody.m_inv! Use cpBodySetMass() when changing the mass for this reason. 68 cpFloat m = 0; 69 70 /// Mass inverse. 71 cpFloat m_inv = 0; 72 73 /// Moment of inertia of the body_. 74 /// Must agree with cpBody.i_inv! Use cpBodySetMoment() when changing the moment for this reason. 75 cpFloat i = 0; 76 77 /// Moment of inertia inverse. 78 cpFloat i_inv = 0; 79 80 /// Position of the rigid body_'s center of gravity. 81 cpVect p; 82 83 /// Velocity of the rigid body_'s center of gravity. 84 cpVect v; 85 86 /// Force acting on the rigid body_'s center of gravity. 87 cpVect f; 88 89 /// Rotation of the body_ around it's center of gravity in radians. 90 /// Must agree with cpBody.rot! Use cpBodySetAngle() when changing the angle for this reason. 91 cpFloat a = 0; 92 93 /// Angular velocity of the body_ around it's center of gravity in radians/second. 94 cpFloat w = 0; 95 96 /// Torque applied to the body_ around it's center of gravity. 97 cpFloat t = 0; 98 99 /// Cached unit length vector representing the angle of the body_. 100 /// Used for fast rotations using cpvrotate(). 101 cpVect rot; 102 103 /// User definable data pointer. 104 /// Generally this points to your the game object class so you can access it 105 /// when given a cpBody reference in a callback. 106 cpDataPointer data; 107 108 /// Maximum velocity allowed when updating the velocity. 109 cpFloat v_limit = 0; 110 111 /// Maximum rotational rate (in radians/second) allowed when updating the angular velocity. 112 cpFloat w_limit = 0; 113 114 version (CHIP_ALLOW_PRIVATE_ACCESS) 115 cpVect v_bias; 116 else 117 package cpVect v_bias; 118 119 version (CHIP_ALLOW_PRIVATE_ACCESS) 120 cpFloat w_bias = 0; 121 else 122 package cpFloat w_bias = 0; 123 124 version (CHIP_ALLOW_PRIVATE_ACCESS) 125 cpSpace * space; 126 else 127 package cpSpace * space; 128 129 version (CHIP_ALLOW_PRIVATE_ACCESS) 130 cpShape * shapeList; 131 else 132 package cpShape * shapeList; 133 134 version (CHIP_ALLOW_PRIVATE_ACCESS) 135 cpArbiter * arbiterList; 136 else 137 package cpArbiter *arbiterList; 138 139 version (CHIP_ALLOW_PRIVATE_ACCESS) 140 cpConstraint * constraintList; 141 else 142 package cpConstraint * constraintList; 143 144 version (CHIP_ALLOW_PRIVATE_ACCESS) 145 cpComponentNode node; 146 else 147 package cpComponentNode node; 148 } 149 150 /// Check that the properties of a body_ is sane. 151 version (CHIP_ENABLE_WARNINGS) 152 { 153 void cpBodyAssertSane(T)(T bdy) 154 { 155 cpBodySanityCheck(bdy); 156 } 157 } 158 else 159 { 160 void cpBodyAssertSane(T)(T bdy) { } 161 } 162 163 // Defined in cpSpace.c 164 /// Wake up a sleeping or idle body_. 165 void cpBodyActivate(cpBody* body_) 166 { 167 if (!cpBodyIsRogue(body_)) 168 { 169 body_.node.idleTime = 0.0f; 170 ComponentActivate(ComponentRoot(body_)); 171 } 172 173 mixin(CP_BODY_FOREACH_ARBITER!("body_", "arb", q{ 174 // Reset the idle timer of things the body_ is touching as well. 175 // That way things don't get left hanging in the air. 176 cpBody* other = (arb.body_a == body_ ? arb.body_b : arb.body_a); 177 178 if (!cpBodyIsStatic(other)) 179 other.node.idleTime = 0.0f; 180 })); 181 } 182 183 /// Wake up any sleeping or idle bodies touching a static body_. 184 void cpBodyActivateStatic(cpBody* body_, cpShape* filter) 185 { 186 cpAssertHard(cpBodyIsStatic(body_), "cpBodyActivateStatic() called on a non-static body_."); 187 188 mixin(CP_BODY_FOREACH_ARBITER!("body_", "arb", q{ 189 if (!filter || filter == arb.a || filter == arb.b) 190 { 191 cpBodyActivate(arb.body_a == body_ ? arb.body_b : arb.body_a); 192 } 193 })); 194 195 // TODO should also activate joints? 196 } 197 198 /// Force a body_ to fall asleep immediately. 199 void cpBodySleep(cpBody* body_) 200 { 201 cpBodySleepWithGroup(body_, null); 202 } 203 204 /// Force a body_ to fall asleep immediately along with other bodies in a group. 205 void cpBodySleepWithGroup(cpBody* body_, cpBody* group) 206 { 207 cpAssertHard(!cpBodyIsRogue(body_), "Rogue (and static) bodies cannot be put to sleep."); 208 209 cpSpace* space = body_.space; 210 cpAssertHard(!space.locked, "Bodies cannot be put to sleep during a query or a call to cpSpaceStep(). Put these calls into a post-step callback."); 211 cpAssertHard(group == null || cpBodyIsSleeping(group), "Cannot use a non-sleeping body_ as a group identifier."); 212 213 if (cpBodyIsSleeping(body_)) 214 { 215 cpAssertHard(ComponentRoot(body_) == ComponentRoot(group), "The body_ is already sleeping and it's group cannot be reassigned."); 216 return; 217 } 218 219 mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", "cpShapeUpdate(shape, body_.p, body_.rot);")); 220 cpSpaceDeactivateBody(space, body_); 221 222 if (group) 223 { 224 cpBody* root = ComponentRoot(group); 225 226 cpComponentNode node = { root, root.node.next, 0.0f }; 227 body_.node = node; 228 229 root.node.next = body_; 230 } 231 else 232 { 233 cpComponentNode node = { body_, null, 0.0f }; 234 body_.node = node; 235 236 cpArrayPush(space.sleepingComponents, body_); 237 } 238 239 cpArrayDeleteObj(space.bodies, body_); 240 } 241 242 /// Returns true if the body_ is sleeping. 243 cpBool cpBodyIsSleeping(const cpBody* bdy) 244 { 245 return (bdy.node.root != (cast(cpBody*)null)); 246 } 247 248 /// Returns true if the body_ is static. 249 cpBool cpBodyIsStatic(const cpBody* bdy) 250 { 251 return bdy.node.idleTime == INFINITY; 252 } 253 254 /// Returns true if the body_ has not been added to a space. 255 /// Note: Static bodies are a subtype of rogue bodies. 256 cpBool cpBodyIsRogue(const cpBody* bdy) 257 { 258 return (bdy.space == (cast(cpSpace*)null)); 259 } 260 261 mixin template CP_DefineBodyStructGetter(type, string member, string name) 262 { 263 mixin(q{ 264 type cpBodyGet%s(const cpBody * bdy) { return cast(typeof(return))bdy.%s; } 265 }.format(name, member)); 266 } 267 268 mixin template CP_DefineBodyStructSetter(type, string member, string name) 269 { 270 mixin(q{ 271 void cpBodySet%s(cpBody * bdy, const type value) 272 { 273 cpBodyActivate(bdy); 274 bdy.%s = cast(typeof(bdy.%s))value; 275 cpBodyAssertSane(bdy); 276 } 277 }.format(name, member, member)); 278 } 279 280 mixin template CP_DefineBodyStructProperty(type, string member, string name) 281 { 282 mixin CP_DefineBodyStructGetter!(type, member, name); 283 mixin CP_DefineBodyStructSetter!(type, member, name); 284 } 285 286 // TODO add to docs 287 mixin CP_DefineBodyStructGetter!(cpSpace*, "space", "Space"); 288 289 mixin CP_DefineBodyStructGetter!(cpFloat, "m", "Mass"); 290 291 mixin CP_DefineBodyStructGetter!(cpFloat, "i", "Moment"); 292 293 mixin CP_DefineBodyStructGetter!(cpVect, "p", "Pos"); 294 295 mixin CP_DefineBodyStructProperty!(cpVect, "v", "Vel"); 296 mixin CP_DefineBodyStructProperty!(cpVect, "f", "Force"); 297 mixin CP_DefineBodyStructGetter!(cpFloat, "a", "Angle"); 298 299 mixin CP_DefineBodyStructProperty!(cpFloat, "w", "AngVel"); 300 mixin CP_DefineBodyStructProperty!(cpFloat, "t", "Torque"); 301 mixin CP_DefineBodyStructGetter!(cpVect, "rot", "Rot"); 302 mixin CP_DefineBodyStructProperty!(cpFloat, "v_limit", "VelLimit"); 303 mixin CP_DefineBodyStructProperty!(cpFloat, "w_limit", "AngVelLimit"); 304 mixin CP_DefineBodyStructProperty!(cpDataPointer, "data", "UserData"); 305 306 /// Convert body_ relative/local coordinates to absolute/world coordinates. 307 cpVect cpBodyLocal2World(const cpBody* bdy, const cpVect v) 308 { 309 return cpvadd(bdy.p, cpvrotate(v, bdy.rot)); 310 } 311 312 /// Convert body_ absolute/world coordinates to relative/local coordinates. 313 cpVect cpBodyWorld2Local(const cpBody* bdy, const cpVect v) 314 { 315 return cpvunrotate(cpvsub(v, bdy.p), bdy.rot); 316 } 317 318 /// Get the kinetic energy of a body_. 319 cpFloat cpBodyKineticEnergy(const cpBody* bdy) 320 { 321 // Need to do some fudging to avoid NaNs 322 cpFloat vsq = cpvdot(bdy.v, bdy.v); 323 cpFloat wsq = bdy.w * bdy.w; 324 return (vsq ? vsq * bdy.m : 0.0f) + (wsq ? wsq * bdy.i : 0.0f); 325 } 326 327 /// Body/shape iterator callback function type. 328 alias cpBodyShapeIteratorFunc= void function(cpBody* bdy, cpShape* shape, void* data); 329 330 /// Body/constraint iterator callback function type. 331 alias cpBodyConstraintIteratorFunc= void function(cpBody* bdy, cpConstraint* constraint, void* data); 332 333 /// Body/arbiter iterator callback function type. 334 alias cpBodyArbiterIteratorFunc = void function(cpBody* bdy, cpArbiter* arbiter, void* data); 335 336 // initialized in cpInitChipmunk() 337 __gshared cpBody cpStaticBodySingleton; 338 339 cpBody* cpBodyAlloc() 340 { 341 return cast(cpBody*)cpcalloc(1, cpBody.sizeof); 342 } 343 344 cpBody* cpBodyInit(cpBody* body_, cpFloat m, cpFloat i) 345 { 346 body_.space = null; 347 body_.shapeList = null; 348 body_.arbiterList = null; 349 body_.constraintList = null; 350 351 body_.velocity_func = &cpBodyUpdateVelocity; 352 body_.position_func = &cpBodyUpdatePosition; 353 354 cpComponentNode node = { null, null, 0.0f }; 355 body_.node = node; 356 357 body_.p = cpvzero; 358 body_.v = cpvzero; 359 body_.f = cpvzero; 360 361 body_.w = 0.0f; 362 body_.t = 0.0f; 363 364 body_.v_bias = cpvzero; 365 body_.w_bias = 0.0f; 366 367 body_.v_limit = cast(cpFloat)INFINITY; 368 body_.w_limit = cast(cpFloat)INFINITY; 369 370 body_.data = null; 371 372 // Setters must be called after full initialization so the sanity checks don't assert on garbage data. 373 cpBodySetMass(body_, m); 374 cpBodySetMoment(body_, i); 375 cpBodySetAngle(body_, 0.0f); 376 377 return body_; 378 } 379 380 cpBody* cpBodyNew(cpFloat m, cpFloat i) 381 { 382 return cpBodyInit(cpBodyAlloc(), m, i); 383 } 384 385 cpBody* cpBodyInitStatic(cpBody* body_) 386 { 387 cpBodyInit(body_, cast(cpFloat)INFINITY, cast(cpFloat)INFINITY); 388 body_.node.idleTime = cast(cpFloat)INFINITY; 389 390 return body_; 391 } 392 393 cpBody* cpBodyNewStatic() 394 { 395 return cpBodyInitStatic(cpBodyAlloc()); 396 } 397 398 void cpBodyDestroy(cpBody* body_) 399 { 400 } 401 402 extern(C) void cpBodyFreeVoid(void* body_) 403 { 404 cpBodyFree(cast(cpBody*)body_); 405 } 406 407 void cpBodyFree(cpBody* body_) 408 { 409 if (body_) 410 { 411 cpBodyDestroy(body_); 412 cpfree(body_); 413 } 414 } 415 416 void cpv_assert_nan(cpVect v, string message) 417 { 418 cpAssertSoft(v.x == v.x && v.y == v.y, message); 419 } 420 421 void cpv_assert_infinite(cpVect v, string message) 422 { 423 cpAssertSoft(cpfabs(v.x) != INFINITY && cpfabs(v.y) != INFINITY, message); 424 } 425 426 void cpv_assert_sane(cpVect v, string message) 427 { 428 cpv_assert_nan(v, message); 429 cpv_assert_infinite(v, message); 430 } 431 432 void cpBodySanityCheck(cpBody* body_) 433 { 434 cpAssertSoft(body_.m == body_.m && body_.m_inv == body_.m_inv, "Body's mass is invalid."); 435 cpAssertSoft(body_.i == body_.i && body_.i_inv == body_.i_inv, "Body's moment is invalid."); 436 437 cpv_assert_sane(body_.p, "Body's position is invalid."); 438 cpv_assert_sane(body_.v, "Body's velocity is invalid."); 439 cpv_assert_sane(body_.f, "Body's force is invalid."); 440 441 cpAssertSoft(body_.a == body_.a && cpfabs(body_.a) != INFINITY, "Body's angle is invalid."); 442 cpAssertSoft(body_.w == body_.w && cpfabs(body_.w) != INFINITY, "Body's angular velocity is invalid."); 443 cpAssertSoft(body_.t == body_.t && cpfabs(body_.t) != INFINITY, "Body's torque is invalid."); 444 445 cpv_assert_sane(body_.rot, "Body's rotation vector is invalid."); 446 447 cpAssertSoft(body_.v_limit == body_.v_limit, "Body's velocity limit is invalid."); 448 cpAssertSoft(body_.w_limit == body_.w_limit, "Body's angular velocity limit is invalid."); 449 } 450 451 void cpBodySetMass(cpBody* body_, cpFloat mass) 452 { 453 cpAssertHard(mass > 0.0f, "Mass must be positive and non-zero."); 454 455 cpBodyActivate(body_); 456 body_.m = mass; 457 body_.m_inv = 1.0f / mass; 458 cpBodyAssertSane(body_); 459 } 460 461 void cpBodySetMoment(cpBody* body_, cpFloat moment) 462 { 463 cpAssertHard(moment > 0.0f, "Moment of Inertia must be positive and non-zero."); 464 465 cpBodyActivate(body_); 466 body_.i = moment; 467 body_.i_inv = 1.0f / moment; 468 cpBodyAssertSane(body_); 469 } 470 471 void cpBodyAddShape(cpBody* body_, cpShape* shape) 472 { 473 cpShape* next = body_.shapeList; 474 475 if (next) 476 next.prev = shape; 477 478 shape.next = next; 479 body_.shapeList = shape; 480 } 481 482 void cpBodyRemoveShape(cpBody* body_, cpShape* shape) 483 { 484 cpShape* prev = shape.prev; 485 cpShape* next = shape.next; 486 487 if (prev) 488 { 489 prev.next = next; 490 } 491 else 492 { 493 body_.shapeList = next; 494 } 495 496 if (next) 497 { 498 next.prev = prev; 499 } 500 501 shape.prev = null; 502 shape.next = null; 503 } 504 505 cpConstraint* filterConstraints(cpConstraint* node, cpBody* body_, cpConstraint* filter) 506 { 507 if (node == filter) 508 { 509 return cpConstraintNext(node, body_); 510 } 511 else if (node.a == body_) 512 { 513 node.next_a = filterConstraints(node.next_a, body_, filter); 514 } 515 else 516 { 517 node.next_b = filterConstraints(node.next_b, body_, filter); 518 } 519 520 return node; 521 } 522 523 void cpBodyRemoveConstraint(cpBody* body_, cpConstraint* constraint) 524 { 525 body_.constraintList = filterConstraints(body_.constraintList, body_, constraint); 526 } 527 528 void cpBodySetPos(cpBody* body_, cpVect pos) 529 { 530 cpBodyActivate(body_); 531 body_.p = pos; 532 cpBodyAssertSane(body_); 533 } 534 535 void setAngle(cpBody* body_, cpFloat angle) 536 { 537 body_.a = angle; //fmod(a, (cpFloat)M_PI*2.0f); 538 body_.rot = cpvforangle(angle); 539 cpBodyAssertSane(body_); 540 } 541 542 void cpBodySetAngle(cpBody* body_, cpFloat angle) 543 { 544 cpBodyActivate(body_); 545 setAngle(body_, angle); 546 } 547 548 void cpBodyUpdateVelocity(cpBody* body_, cpVect gravity, cpFloat damping, cpFloat dt) 549 { 550 body_.v = cpvclamp(cpvadd(cpvmult(body_.v, damping), cpvmult(cpvadd(gravity, cpvmult(body_.f, body_.m_inv)), dt)), body_.v_limit); 551 552 cpFloat w_limit = body_.w_limit; 553 body_.w = cpfclamp(body_.w * damping + body_.t * body_.i_inv * dt, -w_limit, w_limit); 554 555 cpBodySanityCheck(body_); 556 } 557 558 void cpBodyUpdatePosition(cpBody* body_, cpFloat dt) 559 { 560 body_.p = cpvadd(body_.p, cpvmult(cpvadd(body_.v, body_.v_bias), dt)); 561 setAngle(body_, body_.a + (body_.w + body_.w_bias) * dt); 562 563 body_.v_bias = cpvzero; 564 body_.w_bias = 0.0f; 565 566 cpBodySanityCheck(body_); 567 } 568 569 void cpBodyResetForces(cpBody* body_) 570 { 571 cpBodyActivate(body_); 572 body_.f = cpvzero; 573 body_.t = 0.0f; 574 } 575 576 void cpBodyApplyForce(cpBody* body_, cpVect force, cpVect r) 577 { 578 cpBodyActivate(body_); 579 body_.f = cpvadd(body_.f, force); 580 body_.t += cpvcross(r, force); 581 } 582 583 void cpBodyApplyImpulse(cpBody* body_, const cpVect j, const cpVect r) 584 { 585 cpBodyActivate(body_); 586 apply_impulse(body_, j, r); 587 } 588 589 cpVect cpBodyGetVelAtPoint(cpBody* body_, cpVect r) 590 { 591 return cpvadd(body_.v, cpvmult(cpvperp(r), body_.w)); 592 } 593 594 cpVect cpBodyGetVelAtWorldPoint(cpBody* body_, cpVect point) 595 { 596 return cpBodyGetVelAtPoint(body_, cpvsub(point, body_.p)); 597 } 598 599 cpVect cpBodyGetVelAtLocalPoint(cpBody* body_, cpVect point) 600 { 601 return cpBodyGetVelAtPoint(body_, cpvrotate(point, body_.rot)); 602 } 603 604 void cpBodyEachShape(cpBody* body_, cpBodyShapeIteratorFunc func, void* data) 605 { 606 cpShape* shape = body_.shapeList; 607 608 while (shape) 609 { 610 cpShape* next = shape.next; 611 func(body_, shape, data); 612 shape = next; 613 } 614 } 615 616 void cpBodyEachConstraint(cpBody* body_, cpBodyConstraintIteratorFunc func, void* data) 617 { 618 cpConstraint* constraint = body_.constraintList; 619 620 while (constraint) 621 { 622 cpConstraint* next = cpConstraintNext(constraint, body_); 623 func(body_, constraint, data); 624 constraint = next; 625 } 626 } 627 628 void cpBodyEachArbiter(cpBody* body_, cpBodyArbiterIteratorFunc func, void* data) 629 { 630 cpArbiter* arb = body_.arbiterList; 631 632 while (arb) 633 { 634 cpArbiter* next = cpArbiterNext(arb, body_); 635 636 arb.swappedColl = (body_ == arb.body_b); 637 func(body_, arb, data); 638 639 arb = next; 640 } 641 } 642 643 void cpBodyPushArbiter(cpBody* body_, cpArbiter* arb) 644 { 645 cpAssertSoft(cpArbiterThreadForBody(arb, body_).next == null, "Internal Error: Dangling contact graph pointers detected. (A)"); 646 cpAssertSoft(cpArbiterThreadForBody(arb, body_).prev == null, "Internal Error: Dangling contact graph pointers detected. (B)"); 647 648 cpArbiter* next = body_.arbiterList; 649 cpAssertSoft(next == null || cpArbiterThreadForBody(next, body_).prev == null, "Internal Error: Dangling contact graph pointers detected. (C)"); 650 cpArbiterThreadForBody(arb, body_).next = next; 651 652 if (next) 653 cpArbiterThreadForBody(next, body_).prev = arb; 654 body_.arbiterList = arb; 655 } 656 657 /** Workaround for https://github.com/slembcke/Chipmunk2D/issues/56. */ 658 void cpBodyActivateWrap(cpBody* body_, void* data) 659 { 660 cpBodyActivate(body_); 661 }