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.cpSpace; 23 24 import std..string; 25 26 import dchip.cpArray; 27 import dchip.cpBB; 28 import dchip.cpBBTree; 29 import dchip.cpBody; 30 import dchip.chipmunk; 31 import dchip.chipmunk_private; 32 import dchip.chipmunk_types; 33 import dchip.cpArbiter; 34 import dchip.cpConstraint; 35 import dchip.cpHashSet; 36 import dchip.cpShape; 37 import dchip.cpSpaceHash; 38 import dchip.cpSpaceStep; 39 import dchip.cpSpatialIndex; 40 import dchip.cpVect; 41 import dchip.util; 42 43 alias cpSpaceArbiterApplyImpulseFunc = void function(cpArbiter* arb); 44 45 /// Basic Unit of Simulation in Chipmunk 46 struct cpSpace 47 { 48 /// Number of iterations to use in the impulse solver to solve contacts. 49 int iterations; 50 51 /// Gravity to pass to rigid bodies when integrating velocity. 52 cpVect gravity; 53 54 /// Damping rate expressed as the fraction of velocity bodies retain each second. 55 /// A value of 0.9 would mean that each body_'s velocity will drop 10% per second. 56 /// The default value is 1.0, meaning no damping is applied. 57 /// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring. 58 cpFloat damping = 0; 59 60 /// Speed threshold for a body_ to be considered idle. 61 /// The default value of 0 means to let the space guess a good threshold based on gravity. 62 cpFloat idleSpeedThreshold = 0; 63 64 /// Time a group of bodies must remain idle in order to fall asleep. 65 /// Enabling sleeping also implicitly enables the the contact graph. 66 /// The default value of INFINITY disables the sleeping algorithm. 67 cpFloat sleepTimeThreshold = 0; 68 69 /// Amount of encouraged penetration between colliding shapes. 70 /// Used to reduce oscillating contacts and keep the collision cache warm. 71 /// Defaults to 0.1. If you have poor simulation quality, 72 /// increase this number as much as possible without allowing visible amounts of overlap. 73 cpFloat collisionSlop = 0; 74 75 /// Determines how fast overlapping shapes are pushed apart. 76 /// Expressed as a fraction of the error remaining after each second. 77 /// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz. 78 cpFloat collisionBias = 0; 79 80 /// Number of frames that contact information should persist. 81 /// Defaults to 3. There is probably never a reason to change this value. 82 cpTimestamp collisionPersistence; 83 84 /// Rebuild the contact graph during each step. Must be enabled to use the cpBodyEachArbiter() function. 85 /// Disabled by default for a small performance boost. Enabled implicitly when the sleeping feature is enabled. 86 cpBool enableContactGraph; 87 88 /// User definable data pointer. 89 /// Generally this points to your game's controller or game state 90 /// class so you can access it when given a cpSpace reference in a callback. 91 cpDataPointer data; 92 93 /// The designated static body_ for this space. 94 /// You can modify this body_, or replace it with your own static body_. 95 /// By default it points to a statically allocated cpBody in the cpSpace struct. 96 cpBody* staticBody; 97 98 version (CHIP_ALLOW_PRIVATE_ACCESS) 99 cpTimestamp stamp; 100 else 101 package cpTimestamp stamp; 102 103 version (CHIP_ALLOW_PRIVATE_ACCESS) 104 cpFloat curr_dt = 0; 105 else 106 package cpFloat curr_dt = 0; 107 108 version (CHIP_ALLOW_PRIVATE_ACCESS) 109 cpArray * bodies; 110 else 111 package cpArray * bodies; 112 113 version (CHIP_ALLOW_PRIVATE_ACCESS) 114 cpArray * rousedBodies; 115 else 116 package cpArray * rousedBodies; 117 118 version (CHIP_ALLOW_PRIVATE_ACCESS) 119 cpArray * sleepingComponents; 120 else 121 package cpArray * sleepingComponents; 122 123 version (CHIP_ALLOW_PRIVATE_ACCESS) 124 cpSpatialIndex * staticShapes; 125 else 126 package cpSpatialIndex * staticShapes; 127 128 version (CHIP_ALLOW_PRIVATE_ACCESS) 129 cpSpatialIndex * activeShapes; 130 else 131 package cpSpatialIndex * activeShapes; 132 133 version (CHIP_ALLOW_PRIVATE_ACCESS) 134 cpArray * arbiters; 135 else 136 package cpArray * arbiters; 137 138 version (CHIP_ALLOW_PRIVATE_ACCESS) 139 cpContactBufferHeader * contactBuffersHead; 140 else 141 package cpContactBufferHeader * contactBuffersHead; 142 143 version (CHIP_ALLOW_PRIVATE_ACCESS) 144 cpHashSet * cachedArbiters; 145 else 146 package cpHashSet * cachedArbiters; 147 148 version (CHIP_ALLOW_PRIVATE_ACCESS) 149 cpArray * pooledArbiters; 150 else 151 package cpArray * pooledArbiters; 152 153 version (CHIP_ALLOW_PRIVATE_ACCESS) 154 cpArray * constraints; 155 else 156 package cpArray * constraints; 157 158 version (CHIP_ALLOW_PRIVATE_ACCESS) 159 cpArray * allocatedBuffers; 160 else 161 package cpArray * allocatedBuffers; 162 163 version (CHIP_ALLOW_PRIVATE_ACCESS) 164 int locked; 165 else 166 package int locked; 167 168 version (CHIP_ALLOW_PRIVATE_ACCESS) 169 cpHashSet * collisionHandlers; 170 else 171 package cpHashSet * collisionHandlers; 172 173 version (CHIP_ALLOW_PRIVATE_ACCESS) 174 cpCollisionHandler defaultHandler; 175 else 176 package cpCollisionHandler defaultHandler; 177 178 version (CHIP_ALLOW_PRIVATE_ACCESS) 179 cpBool skipPostStep; 180 else 181 package cpBool skipPostStep; 182 183 version (CHIP_ALLOW_PRIVATE_ACCESS) 184 cpArray * postStepCallbacks; 185 else 186 package cpArray * postStepCallbacks; 187 188 version (CHIP_ALLOW_PRIVATE_ACCESS) 189 cpBody _staticBody; 190 else 191 package cpBody _staticBody; 192 } 193 194 mixin template CP_DefineSpaceStructGetter(type, string member, string name) 195 { 196 mixin(q{ 197 type cpSpaceGet%s(const cpSpace * space) { return cast(typeof(return))space.%s; } 198 }.format(name, member)); 199 } 200 201 mixin template CP_DefineSpaceStructSetter(type, string member, string name) 202 { 203 mixin(q{ 204 void cpSpaceSet%s(cpSpace * space, type value) { space.%s = value; } 205 }.format(name, member)); 206 } 207 208 mixin template CP_DefineSpaceStructProperty(type, string member, string name) 209 { 210 mixin CP_DefineSpaceStructGetter!(type, member, name); 211 mixin CP_DefineSpaceStructSetter!(type, member, name); 212 } 213 214 mixin CP_DefineSpaceStructProperty!(int, "iterations", "Iterations"); 215 mixin CP_DefineSpaceStructProperty!(cpVect, "gravity", "Gravity"); 216 mixin CP_DefineSpaceStructProperty!(cpFloat, "damping", "Damping"); 217 mixin CP_DefineSpaceStructProperty!(cpFloat, "idleSpeedThreshold", "IdleSpeedThreshold"); 218 mixin CP_DefineSpaceStructProperty!(cpFloat, "sleepTimeThreshold", "SleepTimeThreshold"); 219 mixin CP_DefineSpaceStructProperty!(cpFloat, "collisionSlop", "CollisionSlop"); 220 mixin CP_DefineSpaceStructProperty!(cpFloat, "collisionBias", "CollisionBias"); 221 mixin CP_DefineSpaceStructProperty!(cpTimestamp, "collisionPersistence", "CollisionPersistence"); 222 mixin CP_DefineSpaceStructProperty!(cpBool, "enableContactGraph", "EnableContactGraph"); 223 mixin CP_DefineSpaceStructProperty!(cpDataPointer, "data", "UserData"); 224 mixin CP_DefineSpaceStructGetter!(cpBody*, "staticBody", "StaticBody"); 225 mixin CP_DefineSpaceStructGetter!(cpFloat, "curr_dt", "CurrentTimeStep"); 226 227 /// returns true from inside a callback and objects cannot be added/removed. 228 cpBool cpSpaceIsLocked(cpSpace* space) 229 { 230 return cast(bool)space.locked; 231 } 232 233 /// Post Step callback function type. 234 alias cpPostStepFunc = void function(cpSpace* space, void* key, void* data); 235 236 /// Point query callback function type. 237 alias cpSpacePointQueryFunc = void function(cpShape* shape, void* data); 238 239 /// Nearest point query callback function type. 240 alias cpSpaceNearestPointQueryFunc = void function(cpShape* shape, cpFloat distance, cpVect point, void* data); 241 242 /// Segment query callback function type. 243 alias cpSpaceSegmentQueryFunc = void function(cpShape* shape, cpFloat t, cpVect n, void* data); 244 245 /// Rectangle Query callback function type. 246 alias cpSpaceBBQueryFunc = void function(cpShape* shape, void* data); 247 248 /// Shape query callback function type. 249 alias cpSpaceShapeQueryFunc = void function(cpShape* shape, cpContactPointSet* points, void* data); 250 251 /// Space/body_ iterator callback function type. 252 alias cpSpaceBodyIteratorFunc = void function(cpBody* bdy, void* data); 253 254 /// Space/body_ iterator callback function type. 255 alias cpSpaceShapeIteratorFunc = void function(cpShape* shape, void* data); 256 257 /// Space/constraint iterator callback function type. 258 alias cpSpaceConstraintIteratorFunc = void function(cpConstraint* constraint, void* data); 259 260 // Equal function for arbiterSet. 261 cpBool arbiterSetEql(cpShape** shapes, cpArbiter* arb) 262 { 263 cpShape* a = shapes[0]; 264 cpShape* b = shapes[1]; 265 266 return ((a == arb.a && b == arb.b) || (b == arb.a && a == arb.b)); 267 } 268 269 //MARK: Collision Handler Set HelperFunctions 270 271 // Equals function for collisionHandlers. 272 cpBool handlerSetEql(cpCollisionHandler* check, cpCollisionHandler* pair) 273 { 274 return ((check.a == pair.a && check.b == pair.b) || (check.b == pair.a && check.a == pair.b)); 275 } 276 277 // Transformation function for collisionHandlers. 278 void* handlerSetTrans(cpCollisionHandler* handler, void* unused) 279 { 280 cpCollisionHandler* copy = cast(cpCollisionHandler*)cpcalloc(1, cpCollisionHandler.sizeof); 281 (*copy) = (*handler); 282 283 return copy; 284 } 285 286 //MARK: Misc Helper Funcs 287 288 // Default collision functions. 289 cpBool alwaysCollide(cpArbiter* arb, cpSpace* space, void* data) 290 { 291 return 1; 292 } 293 294 void nothing(cpArbiter* arb, cpSpace* space, void* data) 295 { 296 } 297 298 // function to get the estimated velocity of a shape for the cpBBTree. 299 cpVect shapeVelocityFunc(cpShape* shape) 300 { 301 return shape.body_.v; 302 } 303 304 //MARK: Memory Management Functions 305 306 cpSpace* cpSpaceAlloc() 307 { 308 return cast(cpSpace*)cpcalloc(1, cpSpace.sizeof); 309 } 310 311 __gshared cpCollisionHandler cpDefaultCollisionHandler = { 0, 0, &alwaysCollide, &alwaysCollide, ¬hing, ¬hing, null }; 312 313 cpSpace* cpSpaceInit(cpSpace* space) 314 { 315 version (CHIP_ENABLE_WARNINGS) 316 { 317 static cpBool done = cpFalse; 318 319 if (!done) 320 { 321 import std.stdio; 322 stderr.writefln("Initializing cpSpace - Chipmunk v%s (Debug Enabled)", cpVersionString); 323 stderr.writefln("Compile without the CHIP_ENABLE_WARNINGS to disable debug mode and runtime assertion checks"); 324 done = cpTrue; 325 } 326 } 327 328 space.iterations = 10; 329 330 space.gravity = cpvzero; 331 space.damping = 1.0f; 332 333 space.collisionSlop = 0.1f; 334 space.collisionBias = cpfpow(1.0f - 0.1f, 60.0f); 335 space.collisionPersistence = 3; 336 337 space.locked = 0; 338 space.stamp = 0; 339 340 space.staticShapes = cpBBTreeNew(cast(cpSpatialIndexBBFunc)&cpShapeGetBB, null); 341 space.activeShapes = cpBBTreeNew(cast(cpSpatialIndexBBFunc)&cpShapeGetBB, space.staticShapes); 342 cpBBTreeSetVelocityFunc(space.activeShapes, cast(cpBBTreeVelocityFunc)&shapeVelocityFunc); 343 344 space.allocatedBuffers = cpArrayNew(0); 345 346 space.bodies = cpArrayNew(0); 347 space.sleepingComponents = cpArrayNew(0); 348 space.rousedBodies = cpArrayNew(0); 349 350 space.sleepTimeThreshold = INFINITY; 351 space.idleSpeedThreshold = 0.0f; 352 space.enableContactGraph = cpFalse; 353 354 space.arbiters = cpArrayNew(0); 355 space.pooledArbiters = cpArrayNew(0); 356 357 space.contactBuffersHead = null; 358 space.cachedArbiters = cpHashSetNew(0, cast(cpHashSetEqlFunc)&arbiterSetEql); 359 360 space.constraints = cpArrayNew(0); 361 362 space.defaultHandler = cpDefaultCollisionHandler; 363 space.collisionHandlers = cpHashSetNew(0, cast(cpHashSetEqlFunc)&handlerSetEql); 364 cpHashSetSetDefaultValue(space.collisionHandlers, &cpDefaultCollisionHandler); 365 366 space.postStepCallbacks = cpArrayNew(0); 367 space.skipPostStep = cpFalse; 368 369 cpBodyInitStatic(&space._staticBody); 370 space.staticBody = &space._staticBody; 371 372 return space; 373 } 374 375 cpSpace* cpSpaceNew() 376 { 377 return cpSpaceInit(cpSpaceAlloc()); 378 } 379 380 extern(C) void cpConstraintFreeWrap(void* constraint) 381 { 382 cpConstraintFree(cast(cpConstraint*)constraint); 383 } 384 385 /** Workarounds for https://github.com/slembcke/Chipmunk2D/issues/56. */ 386 void freeWrap(void *ptr, void *unused) { cpfree(ptr); } 387 void shapeFreeWrap(void *ptr, void *unused) { cpShapeFree(cast(cpShape*)ptr); } 388 void bodyFreeWrap(void* ptr, void *unused) { cpBodyFree(cast(cpBody*)ptr); } 389 void constraintFreeWrap(void* ptr, void *unused) { cpConstraintFree(cast(cpConstraint*)ptr); } 390 391 void cpSpaceDestroy(cpSpace* space) 392 { 393 cpSpaceEachBody(space, &cpBodyActivateWrap, null); 394 395 cpSpatialIndexFree(space.staticShapes); 396 cpSpatialIndexFree(space.activeShapes); 397 398 cpArrayFree(space.bodies); 399 cpArrayFree(space.sleepingComponents); 400 cpArrayFree(space.rousedBodies); 401 402 cpArrayFree(space.constraints); 403 404 cpHashSetFree(space.cachedArbiters); 405 406 cpArrayFree(space.arbiters); 407 cpArrayFree(space.pooledArbiters); 408 409 if (space.allocatedBuffers) 410 { 411 cpArrayFreeEach(space.allocatedBuffers, &cpfree); 412 cpArrayFree(space.allocatedBuffers); 413 } 414 415 if (space.postStepCallbacks) 416 { 417 cpArrayFreeEach(space.postStepCallbacks, &cpfree); 418 cpArrayFree(space.postStepCallbacks); 419 } 420 421 if (space.collisionHandlers) 422 cpHashSetEach(space.collisionHandlers, &freeWrap, null); 423 cpHashSetFree(space.collisionHandlers); 424 } 425 426 void cpSpaceFree(cpSpace* space) 427 { 428 if (space) 429 { 430 cpSpaceDestroy(space); 431 cpfree(space); 432 } 433 } 434 435 void cpAssertSpaceUnlocked(S)(S space) 436 { 437 cpAssertHard(!space.locked, 438 "This operation cannot be done safely during a call to cpSpaceStep() or during a query. "~ 439 "Put these calls into a post-step callback." 440 ); 441 } 442 443 //MARK: Collision Handler Function Management 444 445 void cpSpaceAddCollisionHandler( 446 cpSpace* space, 447 cpCollisionType a, cpCollisionType b, 448 cpCollisionBeginFunc begin, 449 cpCollisionPreSolveFunc preSolve, 450 cpCollisionPostSolveFunc postSolve, 451 cpCollisionSeparateFunc separate, 452 void* data 453 ) 454 { 455 cpAssertSpaceUnlocked(space); 456 457 // Remove any old function so the new one will get added. 458 cpSpaceRemoveCollisionHandler(space, a, b); 459 460 cpCollisionHandler handler = { 461 a, b, 462 begin ? begin : &alwaysCollide, 463 preSolve ? preSolve : &alwaysCollide, 464 postSolve ? postSolve : ¬hing, 465 separate ? separate : ¬hing, 466 data 467 }; 468 469 cpHashSetInsert(space.collisionHandlers, CP_HASH_PAIR(a, b), &handler, null, cast(cpHashSetTransFunc)&handlerSetTrans); 470 } 471 472 void cpSpaceRemoveCollisionHandler(cpSpace* space, cpCollisionType a, cpCollisionType b) 473 { 474 cpAssertSpaceUnlocked(space); 475 476 struct IDS 477 { 478 cpCollisionType a, b; 479 } 480 IDS ids = { a, b }; 481 cpCollisionHandler* old_handler = cast(cpCollisionHandler*)cpHashSetRemove(space.collisionHandlers, CP_HASH_PAIR(a, b), &ids); 482 cpfree(old_handler); 483 } 484 485 void cpSpaceSetDefaultCollisionHandler( 486 cpSpace* space, 487 cpCollisionBeginFunc begin, 488 cpCollisionPreSolveFunc preSolve, 489 cpCollisionPostSolveFunc postSolve, 490 cpCollisionSeparateFunc separate, 491 void* data 492 ) 493 { 494 cpAssertSpaceUnlocked(space); 495 496 cpCollisionHandler handler = { 497 0, 0, 498 begin ? begin : &alwaysCollide, 499 preSolve ? preSolve : &alwaysCollide, 500 postSolve ? postSolve : ¬hing, 501 separate ? separate : ¬hing, 502 data 503 }; 504 505 space.defaultHandler = handler; 506 cpHashSetSetDefaultValue(space.collisionHandlers, &space.defaultHandler); 507 } 508 509 //MARK: Body, Shape, and Joint Management 510 cpShape* cpSpaceAddShape(cpSpace* space, cpShape* shape) 511 { 512 cpBody* body_ = shape.body_; 513 514 if (cpBodyIsStatic(body_)) 515 return cpSpaceAddStaticShape(space, shape); 516 517 cpAssertHard(shape.space != space, "You have already added this shape to this space. You must not add it a second time."); 518 cpAssertHard(!shape.space, "You have already added this shape to another space. You cannot add it to a second."); 519 cpAssertSpaceUnlocked(space); 520 521 cpBodyActivate(body_); 522 cpBodyAddShape(body_, shape); 523 524 cpShapeUpdate(shape, body_.p, body_.rot); 525 cpSpatialIndexInsert(space.activeShapes, shape, shape.hashid); 526 shape.space = space; 527 528 return shape; 529 } 530 531 cpShape* cpSpaceAddStaticShape(cpSpace* space, cpShape* shape) 532 { 533 cpAssertHard(shape.space != space, "You have already added this shape to this space. You must not add it a second time."); 534 cpAssertHard(!shape.space, "You have already added this shape to another space. You cannot add it to a second."); 535 cpAssertHard(cpBodyIsRogue(shape.body_), "You are adding a static shape to a dynamic body_. Did you mean to attach it to a static or rogue body_? See the documentation for more information."); 536 cpAssertSpaceUnlocked(space); 537 538 cpBody* body_ = shape.body_; 539 cpBodyAddShape(body_, shape); 540 cpShapeUpdate(shape, body_.p, body_.rot); 541 cpSpatialIndexInsert(space.staticShapes, shape, shape.hashid); 542 shape.space = space; 543 544 return shape; 545 } 546 547 cpBody* cpSpaceAddBody(cpSpace* space, cpBody* body_) 548 { 549 cpAssertHard(!cpBodyIsStatic(body_), "Do not add static bodies to a space. Static bodies do not move and should not be simulated."); 550 cpAssertHard(body_.space != space, "You have already added this body_ to this space. You must not add it a second time."); 551 cpAssertHard(!body_.space, "You have already added this body_ to another space. You cannot add it to a second."); 552 cpAssertSpaceUnlocked(space); 553 554 cpArrayPush(space.bodies, body_); 555 body_.space = space; 556 557 return body_; 558 } 559 560 cpConstraint* cpSpaceAddConstraint(cpSpace* space, cpConstraint* constraint) 561 { 562 cpAssertHard(constraint.space != space, "You have already added this constraint to this space. You must not add it a second time."); 563 cpAssertHard(!constraint.space, "You have already added this constraint to another space. You cannot add it to a second."); 564 cpAssertHard(constraint.a && constraint.b, "Constraint is attached to a null body_."); 565 cpAssertSpaceUnlocked(space); 566 567 cpBodyActivate(constraint.a); 568 cpBodyActivate(constraint.b); 569 cpArrayPush(space.constraints, constraint); 570 571 // Push onto the heads of the bodies' constraint lists 572 cpBody* a = constraint.a; 573 cpBody* b = constraint.b; 574 constraint.next_a = a.constraintList; 575 a.constraintList = constraint; 576 constraint.next_b = b.constraintList; 577 b.constraintList = constraint; 578 constraint.space = space; 579 580 return constraint; 581 } 582 583 struct arbiterFilterContext 584 { 585 cpSpace* space; 586 cpBody* body_; 587 cpShape* shape; 588 }; 589 590 cpBool cachedArbitersFilter(cpArbiter* arb, arbiterFilterContext* context) 591 { 592 cpShape* shape = context.shape; 593 cpBody * body_ = context.body_; 594 595 // Match on the filter shape, or if it's null the filter body_ 596 if ( 597 (body_ == arb.body_a && (shape == arb.a || shape == null)) || 598 (body_ == arb.body_b && (shape == arb.b || shape == null)) 599 ) 600 { 601 // Call separate when removing shapes. 602 if (shape && arb.state != cpArbiterStateCached) 603 cpArbiterCallSeparate(arb, context.space); 604 605 cpArbiterUnthread(arb); 606 cpArrayDeleteObj(context.space.arbiters, arb); 607 cpArrayPush(context.space.pooledArbiters, arb); 608 609 return cpFalse; 610 } 611 612 return cpTrue; 613 } 614 615 void cpSpaceFilterArbiters(cpSpace* space, cpBody* body_, cpShape* filter) 616 { 617 cpSpaceLock(space); 618 { 619 arbiterFilterContext context = { space, body_, filter }; 620 cpHashSetFilter(space.cachedArbiters, safeCast!cpHashSetFilterFunc(&cachedArbitersFilter), &context); 621 } 622 cpSpaceUnlock(space, cpTrue); 623 } 624 625 void cpSpaceRemoveShape(cpSpace* space, cpShape* shape) 626 { 627 cpBody* body_ = shape.body_; 628 629 if (cpBodyIsStatic(body_)) 630 { 631 cpSpaceRemoveStaticShape(space, shape); 632 } 633 else 634 { 635 cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a shape that was not added to the space. (Removed twice maybe?)"); 636 cpAssertSpaceUnlocked(space); 637 638 cpBodyActivate(body_); 639 cpBodyRemoveShape(body_, shape); 640 cpSpaceFilterArbiters(space, body_, shape); 641 cpSpatialIndexRemove(space.activeShapes, shape, shape.hashid); 642 shape.space = null; 643 } 644 } 645 646 void cpSpaceRemoveStaticShape(cpSpace* space, cpShape* shape) 647 { 648 cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a static or sleeping shape that was not added to the space. (Removed twice maybe?)"); 649 cpAssertSpaceUnlocked(space); 650 651 cpBody* body_ = shape.body_; 652 653 if (cpBodyIsStatic(body_)) 654 cpBodyActivateStatic(body_, shape); 655 cpBodyRemoveShape(body_, shape); 656 cpSpaceFilterArbiters(space, body_, shape); 657 cpSpatialIndexRemove(space.staticShapes, shape, shape.hashid); 658 shape.space = null; 659 } 660 661 void cpSpaceRemoveBody(cpSpace* space, cpBody* body_) 662 { 663 cpAssertHard(cpSpaceContainsBody(space, body_), "Cannot remove a body_ that was not added to the space. (Removed twice maybe?)"); 664 cpAssertSpaceUnlocked(space); 665 666 cpBodyActivate(body_); 667 668 // cpSpaceFilterArbiters(space, body_, null); 669 cpArrayDeleteObj(space.bodies, body_); 670 body_.space = null; 671 } 672 673 void cpSpaceRemoveConstraint(cpSpace* space, cpConstraint* constraint) 674 { 675 cpAssertHard(cpSpaceContainsConstraint(space, constraint), "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)"); 676 cpAssertSpaceUnlocked(space); 677 678 cpBodyActivate(constraint.a); 679 cpBodyActivate(constraint.b); 680 cpArrayDeleteObj(space.constraints, constraint); 681 682 cpBodyRemoveConstraint(constraint.a, constraint); 683 cpBodyRemoveConstraint(constraint.b, constraint); 684 constraint.space = null; 685 } 686 687 cpBool cpSpaceContainsShape(cpSpace* space, cpShape* shape) 688 { 689 return (shape.space == space); 690 } 691 692 cpBool cpSpaceContainsBody(cpSpace* space, cpBody* body_) 693 { 694 return (body_.space == space); 695 } 696 697 cpBool cpSpaceContainsConstraint(cpSpace* space, cpConstraint* constraint) 698 { 699 return (constraint.space == space); 700 } 701 702 //MARK: Static/rogue body_ conversion. 703 704 void cpSpaceConvertBodyToStatic(cpSpace* space, cpBody* body_) 705 { 706 cpAssertHard(!cpBodyIsStatic(body_), "Body is already static."); 707 cpAssertHard(cpBodyIsRogue(body_), "Remove the body_ from the space before calling this function."); 708 cpAssertSpaceUnlocked(space); 709 710 cpBodySetMass(body_, INFINITY); 711 cpBodySetMoment(body_, INFINITY); 712 713 cpBodySetVel(body_, cpvzero); 714 cpBodySetAngVel(body_, 0.0f); 715 716 body_.node.idleTime = INFINITY; 717 718 mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", q{ 719 cpSpatialIndexRemove(space.activeShapes, shape, shape.hashid); 720 cpSpatialIndexInsert(space.staticShapes, shape, shape.hashid); 721 })); 722 } 723 724 void cpSpaceConvertBodyToDynamic(cpSpace* space, cpBody* body_, cpFloat m, cpFloat i) 725 { 726 cpAssertHard(cpBodyIsStatic(body_), "Body is already dynamic."); 727 cpAssertSpaceUnlocked(space); 728 729 cpBodyActivateStatic(body_, null); 730 731 cpBodySetMass(body_, m); 732 cpBodySetMoment(body_, i); 733 734 body_.node.idleTime = 0.0f; 735 mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", q{ 736 cpSpatialIndexRemove(space.staticShapes, shape, shape.hashid); 737 cpSpatialIndexInsert(space.activeShapes, shape, shape.hashid); 738 })); 739 } 740 741 //MARK: Iteration 742 743 void cpSpaceEachBody(cpSpace* space, cpSpaceBodyIteratorFunc func, void* data) 744 { 745 cpSpaceLock(space); 746 { 747 cpArray* bodies = space.bodies; 748 749 for (int i = 0; i < bodies.num; i++) 750 { 751 func(cast(cpBody*)bodies.arr[i], data); 752 } 753 754 cpArray* components = space.sleepingComponents; 755 756 for (int i = 0; i < components.num; i++) 757 { 758 cpBody* root = cast(cpBody*)components.arr[i]; 759 760 cpBody* body_ = root; 761 762 while (body_) 763 { 764 cpBody* next = body_.node.next; 765 func(body_, data); 766 body_ = next; 767 } 768 } 769 } 770 cpSpaceUnlock(space, cpTrue); 771 } 772 773 struct spaceShapeContext 774 { 775 cpSpaceShapeIteratorFunc func; 776 void* data; 777 } 778 779 void spaceEachShapeIterator(cpShape* shape, spaceShapeContext* context) 780 { 781 context.func(shape, context.data); 782 } 783 784 void cpSpaceEachShape(cpSpace* space, cpSpaceShapeIteratorFunc func, void* data) 785 { 786 cpSpaceLock(space); 787 { 788 spaceShapeContext context = { func, data }; 789 cpSpatialIndexEach(space.activeShapes, safeCast!cpSpatialIndexIteratorFunc(&spaceEachShapeIterator), &context); 790 cpSpatialIndexEach(space.staticShapes, safeCast!cpSpatialIndexIteratorFunc(&spaceEachShapeIterator), &context); 791 } 792 cpSpaceUnlock(space, cpTrue); 793 } 794 795 void cpSpaceEachConstraint(cpSpace* space, cpSpaceConstraintIteratorFunc func, void* data) 796 { 797 cpSpaceLock(space); 798 { 799 cpArray* constraints = space.constraints; 800 801 for (int i = 0; i < constraints.num; i++) 802 { 803 func(cast(cpConstraint*)constraints.arr[i], data); 804 } 805 } 806 cpSpaceUnlock(space, cpTrue); 807 } 808 809 //MARK: Spatial Index Management 810 811 void updateBBCache(cpShape* shape, void* unused) 812 { 813 cpBody* body_ = shape.body_; 814 cpShapeUpdate(shape, body_.p, body_.rot); 815 } 816 817 void cpSpaceReindexStatic(cpSpace* space) 818 { 819 cpAssertHard(!space.locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete."); 820 821 cpSpatialIndexEach(space.staticShapes, safeCast!cpSpatialIndexIteratorFunc(&updateBBCache), null); 822 cpSpatialIndexReindex(space.staticShapes); 823 } 824 825 void cpSpaceReindexShape(cpSpace* space, cpShape* shape) 826 { 827 cpAssertHard(!space.locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete."); 828 829 cpBody* body_ = shape.body_; 830 cpShapeUpdate(shape, body_.p, body_.rot); 831 832 // attempt to rehash the shape in both hashes 833 cpSpatialIndexReindexObject(space.activeShapes, shape, shape.hashid); 834 cpSpatialIndexReindexObject(space.staticShapes, shape, shape.hashid); 835 } 836 837 void cpSpaceReindexShapesForBody(cpSpace* space, cpBody* body_) 838 { 839 mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", "cpSpaceReindexShape(space, shape);")); 840 } 841 842 void copyShapes(cpShape* shape, cpSpatialIndex* index) 843 { 844 cpSpatialIndexInsert(index, shape, shape.hashid); 845 } 846 847 void cpSpaceUseSpatialHash(cpSpace* space, cpFloat dim, int count) 848 { 849 cpSpatialIndex* staticShapes = cpSpaceHashNew(dim, count, safeCast!cpSpatialIndexBBFunc(&cpShapeGetBB), null); 850 cpSpatialIndex* activeShapes = cpSpaceHashNew(dim, count, safeCast!cpSpatialIndexBBFunc(&cpShapeGetBB), staticShapes); 851 852 cpSpatialIndexEach(space.staticShapes, safeCast!cpSpatialIndexIteratorFunc(©Shapes), staticShapes); 853 cpSpatialIndexEach(space.activeShapes, safeCast!cpSpatialIndexIteratorFunc(©Shapes), activeShapes); 854 855 cpSpatialIndexFree(space.staticShapes); 856 cpSpatialIndexFree(space.activeShapes); 857 858 space.staticShapes = staticShapes; 859 space.activeShapes = activeShapes; 860 }