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 };