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.Shatter; 23 24 import core.stdc.stdint : uintptr_t, uint32_t; 25 import core.stdc.stdlib; 26 import core.stdc.string; 27 28 import std.math; 29 30 alias M_PI_2 = PI_2; 31 32 import demo.dchip; 33 34 import demo.ChipmunkDebugDraw; 35 import demo.ChipmunkDemo; 36 import demo.types; 37 38 enum DENSITY = (1.0 / 10000.0); 39 enum MAX_VERTEXES_PER_VORONOI = 16; 40 41 struct WorleyContex 42 { 43 uint32_t seed; 44 cpFloat cellSize = 0; 45 int width, height; 46 cpBB bb; 47 cpVect focus; 48 } 49 50 static cpVect HashVect(uint32_t x, uint32_t y, uint32_t seed) 51 { 52 // cpFloat border = 0.21f; 53 cpFloat border = 0.05f; 54 uint32_t h = cast(uint32_t)(x * 1640531513 ^ y * 2654435789) + seed; 55 56 return cpv( 57 cpflerp(border, 1.0f - border, cast(cpFloat)( h & 0xFFFF) / cast(cpFloat)0xFFFF), 58 cpflerp(border, 1.0f - border, cast(cpFloat)((h >> 16) & 0xFFFF) / cast(cpFloat)0xFFFF) 59 ); 60 } 61 62 static cpVect WorleyPoint(int i, int j, WorleyContex* context) 63 { 64 cpFloat size = context.cellSize; 65 int width = context.width; 66 int height = context.height; 67 cpBB bb = context.bb; 68 69 // cpVect fv = cpv(0.5, 0.5); 70 cpVect fv = HashVect(i, j, context.seed); 71 72 return cpv( 73 cpflerp(bb.l, bb.r, 0.5f) + size * (i + fv.x - width * 0.5f), 74 cpflerp(bb.b, bb.t, 0.5f) + size * (j + fv.y - height * 0.5f) 75 ); 76 } 77 78 static int ClipCell(cpShape* shape, cpVect center, int in_i, int in_j, WorleyContex* context, cpVect* verts, cpVect* clipped, int count) 79 { 80 cpVect other = WorleyPoint(in_i, in_j, context); 81 82 // printf(" other %dx%d: (% 5.2f, % 5.2f) ", in_i, in_j, other.x, other.y); 83 if (cpShapeNearestPointQuery(shape, other, null) > 0.0f) 84 { 85 // printf("excluded\n"); 86 memcpy(clipped, verts, count * cpVect.sizeof); 87 return count; 88 } 89 else 90 { 91 // printf("clipped\n"); 92 } 93 94 cpVect n = cpvsub(other, center); 95 cpFloat dist = cpvdot(n, cpvlerp(center, other, 0.5f)); 96 97 int clipped_count = 0; 98 99 for (int j = 0, i = count - 1; j < count; i = j, j++) 100 { 101 cpVect a = verts[i]; 102 cpFloat a_dist = cpvdot(a, n) - dist; 103 104 if (a_dist <= 0.0) 105 { 106 clipped[clipped_count] = a; 107 clipped_count++; 108 } 109 110 cpVect b = verts[j]; 111 cpFloat b_dist = cpvdot(b, n) - dist; 112 113 if (a_dist * b_dist < 0.0f) 114 { 115 cpFloat t = cpfabs(a_dist) / (cpfabs(a_dist) + cpfabs(b_dist)); 116 117 clipped[clipped_count] = cpvlerp(a, b, t); 118 clipped_count++; 119 } 120 } 121 122 return clipped_count; 123 } 124 125 static void ShatterCell(cpSpace* space, cpShape* shape, cpVect cell, int cell_i, int cell_j, WorleyContex* context) 126 { 127 // printf("cell %dx%d: (% 5.2f, % 5.2f)\n", cell_i, cell_j, cell.x, cell.y); 128 129 cpBody* body_ = cpShapeGetBody(shape); 130 131 cpVect* ping = cast(cpVect*)alloca(MAX_VERTEXES_PER_VORONOI * cpVect.sizeof); 132 cpVect* pong = cast(cpVect*)alloca(MAX_VERTEXES_PER_VORONOI * cpVect.sizeof); 133 134 int count = cpPolyShapeGetNumVerts(shape); 135 count = (count > MAX_VERTEXES_PER_VORONOI ? MAX_VERTEXES_PER_VORONOI : count); 136 137 for (int i = 0; i < count; i++) 138 { 139 ping[i] = cpBodyLocal2World(body_, cpPolyShapeGetVert(shape, i)); 140 } 141 142 for (int i = 0; i < context.width; i++) 143 { 144 for (int j = 0; j < context.height; j++) 145 { 146 if ( 147 !(i == cell_i && j == cell_j) && 148 cpShapeNearestPointQuery(shape, cell, null) < 0.0f 149 ) 150 { 151 count = ClipCell(shape, cell, i, j, context, ping, pong, count); 152 memcpy(ping, pong, count * cpVect.sizeof); 153 } 154 } 155 } 156 157 cpVect centroid = cpCentroidForPoly(count, ping); 158 cpFloat mass = cpAreaForPoly(count, ping) * DENSITY; 159 cpFloat moment = cpMomentForPoly(mass, count, ping, cpvneg(centroid)); 160 161 cpBody* new_body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); 162 cpBodySetPos(new_body, centroid); 163 cpBodySetVel(new_body, cpBodyGetVelAtWorldPoint(body_, centroid)); 164 cpBodySetAngVel(new_body, cpBodyGetAngVel(body_)); 165 166 cpShape* new_shape = cpSpaceAddShape(space, cpPolyShapeNew(new_body, count, ping, cpvneg(centroid))); 167 168 // Copy whatever properties you have set on the original shape that are important 169 cpShapeSetFriction(new_shape, cpShapeGetFriction(shape)); 170 } 171 172 static void ShatterShape(cpSpace* space, cpShape* shape, cpFloat cellSize, cpVect focus) 173 { 174 cpSpaceRemoveShape(space, shape); 175 cpSpaceRemoveBody(space, shape.body_); 176 177 cpBB bb = cpShapeGetBB(shape); 178 int width = cast(int)((bb.r - bb.l) / cellSize) + 1; 179 int height = cast(int)((bb.t - bb.b) / cellSize) + 1; 180 181 // printf("Splitting as %dx%d\n", width, height); 182 WorleyContex context = { rand(), cellSize, width, height, bb, focus }; 183 184 for (int i = 0; i < context.width; i++) 185 { 186 for (int j = 0; j < context.height; j++) 187 { 188 cpVect cell = WorleyPoint(i, j, &context); 189 190 if (cpShapeNearestPointQuery(shape, cell, null) < 0.0f) 191 { 192 ShatterCell(space, shape, cell, i, j, &context); 193 } 194 } 195 } 196 197 cpBodyFree(shape.body_); 198 cpShapeFree(shape); 199 } 200 201 static void update(cpSpace* space, double dt) 202 { 203 cpSpaceStep(space, dt); 204 205 if (ChipmunkDemoRightDown) 206 { 207 cpNearestPointQueryInfo info; 208 209 if (cpSpaceNearestPointQueryNearest(space, ChipmunkDemoMouse, 0, GRABABLE_MASK_BIT, CP_NO_GROUP, &info)) 210 { 211 cpBB bb = cpShapeGetBB(info.shape); 212 cpFloat cell_size = cpfmax(bb.r - bb.l, bb.t - bb.b) / 5.0f; 213 214 if (cell_size > 5.0f) 215 { 216 ShatterShape(space, info.shape, cell_size, ChipmunkDemoMouse); 217 } 218 else 219 { 220 // printf("Too small to splinter %f\n", cell_size); 221 } 222 } 223 } 224 } 225 226 static cpSpace* init() 227 { 228 ChipmunkDemoMessageString = "Right click something to shatter it.".dup; 229 230 cpSpace* space = cpSpaceNew(); 231 cpSpaceSetIterations(space, 30); 232 cpSpaceSetGravity(space, cpv(0, -500)); 233 cpSpaceSetSleepTimeThreshold(space, 0.5f); 234 cpSpaceSetCollisionSlop(space, 0.5f); 235 236 cpBody * body_; 237 cpBody * staticBody = cpSpaceGetStaticBody(space); 238 cpShape* shape; 239 240 // Create segments around the edge of the screen. 241 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-1000, -240), cpv(1000, -240), 0.0f)); 242 cpShapeSetElasticity(shape, 1.0f); 243 cpShapeSetFriction(shape, 1.0f); 244 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 245 246 cpFloat width = 200.0f; 247 cpFloat height = 200.0f; 248 cpFloat mass = width * height * DENSITY; 249 cpFloat moment = cpMomentForBox(mass, width, height); 250 251 body_ = cpSpaceAddBody(space, cpBodyNew(mass, moment)); 252 253 shape = cpSpaceAddShape(space, cpBoxShapeNew(body_, width, height)); 254 cpShapeSetFriction(shape, 0.6f); 255 256 return space; 257 } 258 259 static void destroy(cpSpace* space) 260 { 261 ChipmunkDemoFreeSpaceChildren(space); 262 cpSpaceFree(space); 263 } 264 265 ChipmunkDemo Shatter = { 266 "Shatter.", 267 1.0f / 60.0f, 268 &init, 269 &update, 270 &ChipmunkDemoDefaultDrawImpl, 271 &destroy, 272 };