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.Joints; 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 static cpBody* addBall(cpSpace* space, cpVect pos, cpVect boxOffset) 37 { 38 cpFloat radius = 15.0f; 39 cpFloat mass = 1.0f; 40 cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); 41 cpBodySetPos(body_, cpvadd(pos, boxOffset)); 42 43 cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body_, radius, cpvzero)); 44 cpShapeSetElasticity(shape, 0.0f); 45 cpShapeSetFriction(shape, 0.7f); 46 47 return body_; 48 } 49 50 static cpBody* addLever(cpSpace* space, cpVect pos, cpVect boxOffset) 51 { 52 cpFloat mass = 1.0f; 53 cpVect a = cpv(0, 15); 54 cpVect b = cpv(0, -15); 55 56 cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b))); 57 cpBodySetPos(body_, cpvadd(pos, cpvadd(boxOffset, cpv(0, -15)))); 58 59 cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(body_, a, b, 5.0f)); 60 cpShapeSetElasticity(shape, 0.0f); 61 cpShapeSetFriction(shape, 0.7f); 62 63 return body_; 64 } 65 66 static cpBody* addBar(cpSpace* space, cpVect pos, cpVect boxOffset) 67 { 68 cpFloat mass = 2.0f; 69 cpVect a = cpv(0, 30); 70 cpVect b = cpv(0, -30); 71 72 cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b))); 73 cpBodySetPos(body_, cpvadd(pos, boxOffset)); 74 75 cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(body_, a, b, 5.0f)); 76 cpShapeSetElasticity(shape, 0.0f); 77 cpShapeSetFriction(shape, 0.7f); 78 cpShapeSetGroup(shape, 1); 79 80 return body_; 81 } 82 83 static cpBody* addWheel(cpSpace* space, cpVect pos, cpVect boxOffset) 84 { 85 cpFloat radius = 15.0f; 86 cpFloat mass = 1.0f; 87 cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); 88 cpBodySetPos(body_, cpvadd(pos, boxOffset)); 89 90 cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body_, radius, cpvzero)); 91 cpShapeSetElasticity(shape, 0.0f); 92 cpShapeSetFriction(shape, 0.7f); 93 cpShapeSetGroup(shape, 1); // use a group to keep the car parts from colliding 94 95 return body_; 96 } 97 98 static cpBody* addChassis(cpSpace* space, cpVect pos, cpVect boxOffset) 99 { 100 cpFloat mass = 5.0f; 101 cpFloat width = 80; 102 cpFloat height = 30; 103 104 cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height))); 105 cpBodySetPos(body_, cpvadd(pos, boxOffset)); 106 107 cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body_, width, height)); 108 cpShapeSetElasticity(shape, 0.0f); 109 cpShapeSetFriction(shape, 0.7f); 110 cpShapeSetGroup(shape, 1); // use a group to keep the car parts from colliding 111 112 return body_; 113 } 114 115 static cpSpace* init() 116 { 117 cpSpace* space = cpSpaceNew(); 118 cpSpaceSetIterations(space, 10); 119 cpSpaceSetGravity(space, cpv(0, -100)); 120 cpSpaceSetSleepTimeThreshold(space, 0.5f); 121 122 cpBody * staticBody = cpSpaceGetStaticBody(space); 123 cpShape* shape; 124 125 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); 126 cpShapeSetElasticity(shape, 1.0f); 127 cpShapeSetFriction(shape, 1.0f); 128 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 129 130 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 120), cpv(320, 120), 0.0f)); 131 cpShapeSetElasticity(shape, 1.0f); 132 cpShapeSetFriction(shape, 1.0f); 133 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 134 135 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 0), cpv(320, 0), 0.0f)); 136 cpShapeSetElasticity(shape, 1.0f); 137 cpShapeSetFriction(shape, 1.0f); 138 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 139 140 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -120), cpv(320, -120), 0.0f)); 141 cpShapeSetElasticity(shape, 1.0f); 142 cpShapeSetFriction(shape, 1.0f); 143 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 144 145 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); 146 cpShapeSetElasticity(shape, 1.0f); 147 cpShapeSetFriction(shape, 1.0f); 148 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 149 150 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); 151 cpShapeSetElasticity(shape, 1.0f); 152 cpShapeSetFriction(shape, 1.0f); 153 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 154 155 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160, -240), cpv(-160, 240), 0.0f)); 156 cpShapeSetElasticity(shape, 1.0f); 157 cpShapeSetFriction(shape, 1.0f); 158 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 159 160 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0, -240), cpv(0, 240), 0.0f)); 161 cpShapeSetElasticity(shape, 1.0f); 162 cpShapeSetFriction(shape, 1.0f); 163 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 164 165 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(160, -240), cpv(160, 240), 0.0f)); 166 cpShapeSetElasticity(shape, 1.0f); 167 cpShapeSetFriction(shape, 1.0f); 168 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 169 170 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); 171 cpShapeSetElasticity(shape, 1.0f); 172 cpShapeSetFriction(shape, 1.0f); 173 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 174 175 cpVect boxOffset; 176 cpBody* body1; 177 cpBody* body2; 178 179 cpVect posA = cpv(50, 60); 180 cpVect posB = cpv(110, 60); 181 182 auto POS_A() { return cpvadd(boxOffset, posA); } 183 auto POS_B() { return cpvadd(boxOffset, posB); } 184 185 // Pin Joints - Link shapes with a solid bar or pin. 186 // Keeps the anchor points the same distance apart from when the joint was created. 187 boxOffset = cpv(-320, -240); 188 body1 = addBall(space, posA, boxOffset); 189 body2 = addBall(space, posB, boxOffset); 190 cpSpaceAddConstraint(space, cpPinJointNew(body1, body2, cpv(15, 0), cpv(-15, 0))); 191 192 // Slide Joints - Like pin joints but with a min/max distance. 193 // Can be used for a cheap approximation of a rope. 194 boxOffset = cpv(-160, -240); 195 body1 = addBall(space, posA, boxOffset); 196 body2 = addBall(space, posB, boxOffset); 197 cpSpaceAddConstraint(space, cpSlideJointNew(body1, body2, cpv(15, 0), cpv(-15, 0), 20.0f, 40.0f)); 198 199 // Pivot Joints - Holds the two anchor points together. Like a swivel. 200 boxOffset = cpv(0, -240); 201 body1 = addBall(space, posA, boxOffset); 202 body2 = addBall(space, posB, boxOffset); 203 cpSpaceAddConstraint(space, cpPivotJointNew(body1, body2, cpvadd(boxOffset, cpv(80, 60)))); 204 205 // cpPivotJointNew() takes it's anchor parameter in world coordinates. The anchors are calculated from that 206 // cpPivotJointNew2() lets you specify the two anchor points explicitly 207 208 // Groove Joints - Like a pivot joint, but one of the anchors is a line segment that the pivot can slide in 209 boxOffset = cpv(160, -240); 210 body1 = addBall(space, posA, boxOffset); 211 body2 = addBall(space, posB, boxOffset); 212 cpSpaceAddConstraint(space, cpGrooveJointNew(body1, body2, cpv(30, 30), cpv(30, -30), cpv(-30, 0))); 213 214 // Damped Springs 215 boxOffset = cpv(-320, -120); 216 body1 = addBall(space, posA, boxOffset); 217 body2 = addBall(space, posB, boxOffset); 218 cpSpaceAddConstraint(space, cpDampedSpringNew(body1, body2, cpv(15, 0), cpv(-15, 0), 20.0f, 5.0f, 0.3f)); 219 220 // Damped Rotary Springs 221 boxOffset = cpv(-160, -120); 222 body1 = addBar(space, posA, boxOffset); 223 body2 = addBar(space, posB, boxOffset); 224 225 // Add some pin joints to hold the circles in place. 226 cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); 227 cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); 228 cpSpaceAddConstraint(space, cpDampedRotarySpringNew(body1, body2, 0.0f, 3000.0f, 60.0f)); 229 230 // Rotary Limit Joint 231 boxOffset = cpv(0, -120); 232 body1 = addLever(space, posA, boxOffset); 233 body2 = addLever(space, posB, boxOffset); 234 235 // Add some pin joints to hold the circles in place. 236 cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); 237 cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); 238 239 // Hold their rotation within 90 degrees of each other. 240 cpSpaceAddConstraint(space, cpRotaryLimitJointNew(body1, body2, -M_PI_2, M_PI_2)); 241 242 // Ratchet Joint - A rotary ratchet, like a socket wrench 243 boxOffset = cpv(160, -120); 244 body1 = addLever(space, posA, boxOffset); 245 body2 = addLever(space, posB, boxOffset); 246 247 // Add some pin joints to hold the circles in place. 248 cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); 249 cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); 250 251 // Ratchet every 90 degrees 252 cpSpaceAddConstraint(space, cpRatchetJointNew(body1, body2, 0.0f, M_PI_2)); 253 254 // Gear Joint - Maintain a specific angular velocity ratio 255 boxOffset = cpv(-320, 0); 256 body1 = addBar(space, posA, boxOffset); 257 body2 = addBar(space, posB, boxOffset); 258 259 // Add some pin joints to hold the circles in place. 260 cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); 261 cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); 262 263 // Force one to sping 2x as fast as the other 264 cpSpaceAddConstraint(space, cpGearJointNew(body1, body2, 0.0f, 2.0f)); 265 266 // Simple Motor - Maintain a specific angular relative velocity 267 boxOffset = cpv(-160, 0); 268 body1 = addBar(space, posA, boxOffset); 269 body2 = addBar(space, posB, boxOffset); 270 271 // Add some pin joints to hold the circles in place. 272 cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); 273 cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); 274 275 // Make them spin at 1/2 revolution per second in relation to each other. 276 cpSpaceAddConstraint(space, cpSimpleMotorNew(body1, body2, M_PI)); 277 278 // Make a car with some nice soft suspension 279 boxOffset = cpv(0, 0); 280 cpBody* wheel1 = addWheel(space, posA, boxOffset); 281 cpBody* wheel2 = addWheel(space, posB, boxOffset); 282 cpBody* chassis = addChassis(space, cpv(80, 100), boxOffset); 283 284 cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel1, cpv(-30, -10), cpv(-30, -40), cpvzero)); 285 cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel2, cpv(30, -10), cpv(30, -40), cpvzero)); 286 287 cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel1, cpv(-30, 0), cpvzero, 50.0f, 20.0f, 10.0f)); 288 cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel2, cpv(30, 0), cpvzero, 50.0f, 20.0f, 10.0f)); 289 290 return space; 291 } 292 293 static void update(cpSpace* space, double dt) 294 { 295 cpSpaceStep(space, dt); 296 } 297 298 static void destroy(cpSpace* space) 299 { 300 ChipmunkDemoFreeSpaceChildren(space); 301 cpSpaceFree(space); 302 } 303 304 ChipmunkDemo Joints = { 305 "Joints and Constraints", 306 1.0 / 60.0, 307 &init, 308 &update, 309 &ChipmunkDemoDefaultDrawImpl, 310 &destroy, 311 };