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 }