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.cpSpaceComponent; 23 24 import core.stdc..string; 25 26 import dchip.chipmunk; 27 import dchip.chipmunk_types; 28 import dchip.chipmunk_private; 29 import dchip.cpArray; 30 import dchip.cpArbiter; 31 import dchip.cpBody; 32 import dchip.cpConstraint; 33 import dchip.cpHashSet; 34 import dchip.cpShape; 35 import dchip.cpSpace; 36 import dchip.cpSpaceStep; 37 import dchip.cpSpaceQuery; 38 import dchip.cpSpatialIndex; 39 import dchip.cpVect; 40 import dchip.util; 41 42 void cpSpaceActivateBody(cpSpace* space, cpBody* body_) 43 { 44 cpAssertHard(!cpBodyIsRogue(body_), "Internal error: Attempting to activate a rogue body_."); 45 46 if (space.locked) 47 { 48 // cpSpaceActivateBody() is called again once the space is unlocked 49 if (!cpArrayContains(space.rousedBodies, body_)) 50 cpArrayPush(space.rousedBodies, body_); 51 } 52 else 53 { 54 cpAssertSoft(body_.node.root == null && body_.node.next == null, "Internal error: Activating body_ non-null node pointers."); 55 cpArrayPush(space.bodies, body_); 56 57 mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", q{ 58 cpSpatialIndexRemove(space.staticShapes, shape, shape.hashid); 59 cpSpatialIndexInsert(space.activeShapes, shape, shape.hashid); 60 })); 61 62 mixin(CP_BODY_FOREACH_ARBITER!("body_", "arb", q{ 63 cpBody* bodyA = arb.body_a; 64 65 // Arbiters are shared between two bodies that are always woken up together. 66 // You only want to restore the arbiter once, so bodyA is arbitrarily chosen to own the arbiter. 67 // The edge case is when static bodies are involved as the static bodies never actually sleep. 68 // If the static body_ is bodyB then all is good. If the static body_ is bodyA, that can easily be checked. 69 if (body_ == bodyA || cpBodyIsStatic(bodyA)) 70 { 71 int numContacts = arb.numContacts; 72 cpContact* contacts = arb.contacts; 73 74 // Restore contact values back to the space's contact buffer memory 75 arb.contacts = cpContactBufferGetArray(space); 76 memcpy(arb.contacts, contacts, numContacts * cpContact.sizeof); 77 cpSpacePushContacts(space, numContacts); 78 79 // Reinsert the arbiter into the arbiter cache 80 cpShape* a = arb.a; 81 cpShape* b = arb.b; 82 cpShape*[2] shape_pair; 83 shape_pair[0] = a; 84 shape_pair[1] = b; 85 cpHashValue arbHashID = CP_HASH_PAIR(cast(cpHashValue)a, cast(cpHashValue)b); 86 cpHashSetInsert(space.cachedArbiters, arbHashID, shape_pair.ptr, arb, null); 87 88 // Update the arbiter's state 89 arb.stamp = space.stamp; 90 arb.handler = cpSpaceLookupHandler(space, a.collision_type, b.collision_type); 91 cpArrayPush(space.arbiters, arb); 92 93 cpfree(contacts); 94 } 95 })); 96 97 mixin(CP_BODY_FOREACH_CONSTRAINT!("body_", "constraint", q{ 98 cpBody* bodyA = constraint.a; 99 100 if (body_ == bodyA || cpBodyIsStatic(bodyA)) 101 cpArrayPush(space.constraints, constraint); 102 })); 103 } 104 } 105 106 void cpSpaceDeactivateBody(cpSpace* space, cpBody* body_) 107 { 108 cpAssertHard(!cpBodyIsRogue(body_), "Internal error: Attempting to deactivate a rouge body_."); 109 110 cpArrayDeleteObj(space.bodies, body_); 111 112 mixin(CP_BODY_FOREACH_SHAPE!("body_", "shape", q{ 113 cpSpatialIndexRemove(space.activeShapes, shape, shape.hashid); 114 cpSpatialIndexInsert(space.staticShapes, shape, shape.hashid); 115 })); 116 117 mixin(CP_BODY_FOREACH_ARBITER!("body_", "arb", q{ 118 cpBody* bodyA = arb.body_a; 119 120 if (body_ == bodyA || cpBodyIsStatic(bodyA)) 121 { 122 cpSpaceUncacheArbiter(space, arb); 123 124 // Save contact values to a new block of memory so they won't time out 125 size_t bytes = arb.numContacts * cpContact.sizeof; 126 cpContact* contacts = cast(cpContact*)cpcalloc(1, bytes); 127 memcpy(contacts, arb.contacts, bytes); 128 arb.contacts = contacts; 129 } 130 })); 131 132 mixin(CP_BODY_FOREACH_CONSTRAINT!("body_", "constraint", q{ 133 cpBody* bodyA = constraint.a; 134 135 if (body_ == bodyA || cpBodyIsStatic(bodyA)) 136 cpArrayDeleteObj(space.constraints, constraint); 137 })); 138 } 139 140 cpBody* ComponentRoot(cpBody* body_) 141 { 142 return (body_ ? body_.node.root : null); 143 } 144 145 void ComponentActivate(cpBody* root) 146 { 147 if (!root || !cpBodyIsSleeping(root)) 148 return; 149 cpAssertHard(!cpBodyIsRogue(root), "Internal Error: ComponentActivate() called on a rogue body_."); 150 151 cpSpace* space = root.space; 152 cpBody * body_ = root; 153 154 while (body_) 155 { 156 cpBody* next = body_.node.next; 157 158 body_.node.idleTime = 0.0f; 159 body_.node.root = null; 160 body_.node.next = null; 161 cpSpaceActivateBody(space, body_); 162 163 body_ = next; 164 } 165 166 cpArrayDeleteObj(space.sleepingComponents, root); 167 } 168 169 void ComponentAdd(cpBody* root, cpBody* body_) 170 { 171 body_.node.root = root; 172 173 if (body_ != root) 174 { 175 body_.node.next = root.node.next; 176 root.node.next = body_; 177 } 178 } 179 180 void FloodFillComponent(cpBody* root, cpBody* body_) 181 { 182 // Rogue bodies cannot be put to sleep and prevent bodies they are touching from sleepining anyway. 183 // Static bodies (which are a type of rogue body_) are effectively sleeping all the time. 184 if (!cpBodyIsRogue(body_)) 185 { 186 cpBody* other_root = ComponentRoot(body_); 187 188 if (other_root == null) 189 { 190 ComponentAdd(root, body_); 191 mixin(CP_BODY_FOREACH_ARBITER!("body_", "arb", "FloodFillComponent(root, (body_ == arb.body_a ? arb.body_b : arb.body_a));")); 192 mixin(CP_BODY_FOREACH_CONSTRAINT!("body_", "constraint", "FloodFillComponent(root, (body_ == constraint.a ? constraint.b : constraint.a));")); 193 } 194 else 195 { 196 cpAssertSoft(other_root == root, "Internal Error: Inconsistency dectected in the contact graph."); 197 } 198 } 199 } 200 201 cpBool ComponentActive(cpBody* root, cpFloat threshold) 202 { 203 mixin(CP_BODY_FOREACH_COMPONENT!("root", "body_", q{ 204 if (body_.node.idleTime < threshold) 205 return cpTrue; 206 })); 207 208 return cpFalse; 209 } 210 211 void cpSpaceProcessComponents(cpSpace* space, cpFloat dt) 212 { 213 cpBool sleep = (space.sleepTimeThreshold != INFINITY); 214 cpArray* bodies = space.bodies; 215 216 version (CHIP_ENABLE_WARNINGS) 217 { 218 for (int i = 0; i < bodies.num; i++) 219 { 220 cpBody* body_ = cast(cpBody*)bodies.arr[i]; 221 222 cpAssertSoft(body_.node.next == null, "Internal Error: Dangling next pointer detected in contact graph."); 223 cpAssertSoft(body_.node.root == null, "Internal Error: Dangling root pointer detected in contact graph."); 224 } 225 } 226 227 // Calculate the kinetic energy of all the bodies. 228 if (sleep) 229 { 230 cpFloat dv = space.idleSpeedThreshold; 231 cpFloat dvsq = (dv ? dv * dv : cpvlengthsq(space.gravity) * dt * dt); 232 233 // update idling and reset component nodes 234 for (int i = 0; i < bodies.num; i++) 235 { 236 cpBody* body_ = cast(cpBody*)bodies.arr[i]; 237 238 // Need to deal with infinite mass objects 239 cpFloat keThreshold = (dvsq ? body_.m * dvsq : 0.0f); 240 body_.node.idleTime = (cpBodyKineticEnergy(body_) > keThreshold ? 0.0f : body_.node.idleTime + dt); 241 } 242 } 243 244 // Awaken any sleeping bodies found and then push arbiters to the bodies' lists. 245 cpArray* arbiters = space.arbiters; 246 247 for (int i = 0, count = arbiters.num; i < count; i++) 248 { 249 cpArbiter* arb = cast(cpArbiter*)arbiters.arr[i]; 250 cpBody* a = arb.body_a; 251 cpBody* b = arb.body_b; 252 253 if (sleep) 254 { 255 if ((cpBodyIsRogue(b) && !cpBodyIsStatic(b)) || cpBodyIsSleeping(a)) 256 cpBodyActivate(a); 257 258 if ((cpBodyIsRogue(a) && !cpBodyIsStatic(a)) || cpBodyIsSleeping(b)) 259 cpBodyActivate(b); 260 } 261 262 cpBodyPushArbiter(a, arb); 263 cpBodyPushArbiter(b, arb); 264 } 265 266 if (sleep) 267 { 268 // Bodies should be held active if connected by a joint to a non-static rouge body_. 269 cpArray* constraints = space.constraints; 270 271 for (int i = 0; i < constraints.num; i++) 272 { 273 cpConstraint* constraint = cast(cpConstraint*)constraints.arr[i]; 274 cpBody* a = constraint.a; 275 cpBody* b = constraint.b; 276 277 if (cpBodyIsRogue(b) && !cpBodyIsStatic(b)) 278 cpBodyActivate(a); 279 280 if (cpBodyIsRogue(a) && !cpBodyIsStatic(a)) 281 cpBodyActivate(b); 282 } 283 284 // Generate components and deactivate sleeping ones 285 for (int i = 0; i < bodies.num; ) 286 { 287 cpBody* body_ = cast(cpBody*)bodies.arr[i]; 288 289 if (ComponentRoot(body_) == null) 290 { 291 // Body not in a component yet. Perform a DFS to flood fill mark 292 // the component in the contact graph using this body_ as the root. 293 FloodFillComponent(body_, body_); 294 295 // Check if the component should be put to sleep. 296 if (!ComponentActive(body_, space.sleepTimeThreshold)) 297 { 298 cpArrayPush(space.sleepingComponents, body_); 299 mixin(CP_BODY_FOREACH_COMPONENT!("body_", "other", "cpSpaceDeactivateBody(space, other);")); 300 301 // cpSpaceDeactivateBody() removed the current body_ from the list. 302 // Skip incrementing the index counter. 303 continue; 304 } 305 } 306 307 i++; 308 309 // Only sleeping bodies retain their component node pointers. 310 body_.node.root = null; 311 body_.node.next = null; 312 } 313 } 314 } 315 316 void activateTouchingHelper(cpShape* shape, cpContactPointSet* points, cpShape* other) 317 { 318 cpBodyActivate(shape.body_); 319 } 320 321 void cpSpaceActivateShapesTouchingShape(cpSpace* space, cpShape* shape) 322 { 323 if (space.sleepTimeThreshold != INFINITY) 324 { 325 cpSpaceShapeQuery(space, shape, safeCast!cpSpaceShapeQueryFunc(&activateTouchingHelper), shape); 326 } 327 }