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 demo.Chains;
23 
24 import core.stdc.stdlib;
25 
26 import std.math;
27 
28 alias M_PI_2 = PI_2;
29 
30 import demo.dchip;
31 
32 import demo.ChipmunkDebugDraw;
33 import demo.ChipmunkDemo;
34 import demo.types;
35 
36 enum CHAIN_COUNT = 8;
37 enum LINK_COUNT = 10;
38 
39 static void BreakablejointPostStepRemove(cpSpace* space, cpConstraint* joint, void* unused)
40 {
41     cpSpaceRemoveConstraint(space, joint);
42     cpConstraintFree(joint);
43 }
44 
45 static void BreakableJointPostSolve(cpConstraint* joint, cpSpace* space)
46 {
47     cpFloat dt = cpSpaceGetCurrentTimeStep(space);
48 
49     // Convert the impulse to a force by dividing it by the timestep.
50     cpFloat force    = cpConstraintGetImpulse(joint) / dt;
51     cpFloat maxForce = cpConstraintGetMaxForce(joint);
52 
53     // If the force is almost as big as the joint's max force, break it.
54     if (force > 0.9 * maxForce)
55     {
56         cpSpaceAddPostStepCallback(space, safeCast!cpPostStepFunc(&BreakablejointPostStepRemove), joint, null);
57     }
58 }
59 
60 static void update(cpSpace* space, double dt)
61 {
62     cpSpaceStep(space, dt);
63 }
64 
65 static cpSpace* init()
66 {
67     cpSpace* space = cpSpaceNew();
68     cpSpaceSetIterations(space, 30);
69     cpSpaceSetGravity(space, cpv(0, -100));
70     cpSpaceSetSleepTimeThreshold(space, 0.5f);
71 
72     cpBody * body_;
73     cpBody * staticBody = cpSpaceGetStaticBody(space);
74     cpShape* shape;
75 
76     // Create segments around the edge of the screen.
77     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f));
78     cpShapeSetElasticity(shape, 1.0f);
79     cpShapeSetFriction(shape, 1.0f);
80     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
81 
82     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f));
83     cpShapeSetElasticity(shape, 1.0f);
84     cpShapeSetFriction(shape, 1.0f);
85     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
86 
87     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f));
88     cpShapeSetElasticity(shape, 1.0f);
89     cpShapeSetFriction(shape, 1.0f);
90     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
91 
92     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f));
93     cpShapeSetElasticity(shape, 1.0f);
94     cpShapeSetFriction(shape, 1.0f);
95     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
96 
97     cpFloat mass   = 1;
98     cpFloat width  = 20;
99     cpFloat height = 30;
100 
101     cpFloat spacing = width * 0.3;
102 
103     // Add lots of boxes.
104     for (int i = 0; i < CHAIN_COUNT; i++)
105     {
106         cpBody* prev = null;
107 
108         for (int j = 0; j < LINK_COUNT; j++)
109         {
110             cpVect pos = cpv(40 * (i - (CHAIN_COUNT - 1) / 2.0), 240 - (j + 0.5) * height - (j + 1) * spacing);
111 
112             body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height)));
113             cpBodySetPos(body_, pos);
114 
115             shape = cpSpaceAddShape(space, cpSegmentShapeNew(body_, cpv(0, (height - width) / 2.0), cpv(0, (width - height) / 2.0), width / 2.0));
116             cpShapeSetFriction(shape, 0.8f);
117 
118             cpFloat breakingForce = 80000;
119 
120             cpConstraint* constraint = null;
121 
122             if (prev == null)
123             {
124                 constraint = cpSpaceAddConstraint(space, cpSlideJointNew(body_, staticBody, cpv(0, height / 2), cpv(pos.x, 240), 0, spacing));
125             }
126             else
127             {
128                 constraint = cpSpaceAddConstraint(space, cpSlideJointNew(body_, prev, cpv(0, height / 2), cpv(0, -height / 2), 0, spacing));
129             }
130 
131             cpConstraintSetMaxForce(constraint, breakingForce);
132             cpConstraintSetPostSolveFunc(constraint, &BreakableJointPostSolve);
133 
134             prev = body_;
135         }
136     }
137 
138     cpFloat radius = 15.0f;
139     body_ = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero)));
140     cpBodySetPos(body_, cpv(0, -240 + radius + 5));
141     cpBodySetVel(body_, cpv(0, 300));
142 
143     shape = cpSpaceAddShape(space, cpCircleShapeNew(body_, radius, cpvzero));
144     cpShapeSetElasticity(shape, 0.0f);
145     cpShapeSetFriction(shape, 0.9f);
146 
147     return space;
148 }
149 
150 static void destroy(cpSpace* space)
151 {
152     ChipmunkDemoFreeSpaceChildren(space);
153     cpSpaceFree(space);
154 }
155 
156 ChipmunkDemo Chains = {
157     "Breakable Chains",
158     1.0 / 180.0,
159     &init,
160     &update,
161     &ChipmunkDemoDefaultDrawImpl,
162     &destroy,
163 };