1 /*
2 * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty.  In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 * Permission is granted to anyone to use this software for any purpose,
8 * including commercial applications, and to alter it and redistribute it
9 * freely, subject to the following restrictions:
10 * 1. The origin of this software must not be misrepresented; you must not
11 * claim that you wrote the original software. If you use this software
12 * in a product, an acknowledgment in the product documentation would be
13 * appreciated but is not required.
14 * 2. Altered source versions must be plainly marked as such, and must not be
15 * misrepresented as being the original software.
16 * 3. This notice may not be removed or altered from any source distribution.
17 */
18 
19 #ifndef RAY_CAST_H
20 #define RAY_CAST_H
21 
22 // This test demonstrates how to use the world ray-cast feature.
23 // NOTE: we are intentionally filtering one of the polygons, therefore
24 // the ray will always miss one type of polygon.
25 
26 // This callback finds the closest hit. Polygon 0 is filtered.
27 class RayCastClosestCallback : public b2RayCastCallback
28 {
29 public:
RayCastClosestCallback()30     RayCastClosestCallback()
31     {
32         m_hit = false;
33     }
34 
ReportFixture(b2Fixture * fixture,const b2Vec2 & point,const b2Vec2 & normal,float32 fraction)35     float32 ReportFixture(  b2Fixture* fixture, const b2Vec2& point,
36         const b2Vec2& normal, float32 fraction)
37     {
38         b2Body* body = fixture->GetBody();
39         void* userData = body->GetUserData();
40         if (userData)
41         {
42             int32 index = *(int32*)userData;
43             if (index == 0)
44             {
45                 // filter
46                 return -1.0f;
47             }
48         }
49 
50         m_hit = true;
51         m_point = point;
52         m_normal = normal;
53         return fraction;
54     }
55 
56     bool m_hit;
57     b2Vec2 m_point;
58     b2Vec2 m_normal;
59 };
60 
61 // This callback finds any hit. Polygon 0 is filtered.
62 class RayCastAnyCallback : public b2RayCastCallback
63 {
64 public:
RayCastAnyCallback()65     RayCastAnyCallback()
66     {
67         m_hit = false;
68     }
69 
ReportFixture(b2Fixture * fixture,const b2Vec2 & point,const b2Vec2 & normal,float32 fraction)70     float32 ReportFixture(  b2Fixture* fixture, const b2Vec2& point,
71         const b2Vec2& normal, float32 fraction)
72     {
73         b2Body* body = fixture->GetBody();
74         void* userData = body->GetUserData();
75         if (userData)
76         {
77             int32 index = *(int32*)userData;
78             if (index == 0)
79             {
80                 // filter
81                 return -1.0f;
82             }
83         }
84 
85         m_hit = true;
86         m_point = point;
87         m_normal = normal;
88         return 0.0f;
89     }
90 
91     bool m_hit;
92     b2Vec2 m_point;
93     b2Vec2 m_normal;
94 };
95 
96 // This ray cast collects multiple hits along the ray. Polygon 0 is filtered.
97 class RayCastMultipleCallback : public b2RayCastCallback
98 {
99 public:
100     enum
101     {
102         e_maxCount = 3
103     };
104 
RayCastMultipleCallback()105     RayCastMultipleCallback()
106     {
107         m_count = 0;
108     }
109 
ReportFixture(b2Fixture * fixture,const b2Vec2 & point,const b2Vec2 & normal,float32 fraction)110     float32 ReportFixture(  b2Fixture* fixture, const b2Vec2& point,
111         const b2Vec2& normal, float32 fraction)
112     {
113         b2Body* body = fixture->GetBody();
114         void* userData = body->GetUserData();
115         if (userData)
116         {
117             int32 index = *(int32*)userData;
118             if (index == 0)
119             {
120                 // filter
121                 return -1.0f;
122             }
123         }
124 
125         b2Assert(m_count < e_maxCount);
126 
127         m_points[m_count] = point;
128         m_normals[m_count] = normal;
129         ++m_count;
130 
131         if (m_count == e_maxCount)
132         {
133             return 0.0f;
134         }
135 
136         return 1.0f;
137     }
138 
139     b2Vec2 m_points[e_maxCount];
140     b2Vec2 m_normals[e_maxCount];
141     int32 m_count;
142 };
143 
144 
145 class RayCast : public Test
146 {
147 public:
148 
149     enum
150     {
151         e_maxBodies = 256
152     };
153 
154     enum Mode
155     {
156         e_closest,
157         e_any,
158         e_multiple
159     };
160 
RayCast()161     RayCast()
162     {
163         // Ground body
164         {
165             b2BodyDef bd;
166             b2Body* ground = m_world->CreateBody(&bd);
167 
168             b2EdgeShape shape;
169             shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
170             ground->CreateFixture(&shape, 0.0f);
171         }
172 
173         {
174             b2Vec2 vertices[3];
175             vertices[0].Set(-0.5f, 0.0f);
176             vertices[1].Set(0.5f, 0.0f);
177             vertices[2].Set(0.0f, 1.5f);
178             m_polygons[0].Set(vertices, 3);
179         }
180 
181         {
182             b2Vec2 vertices[3];
183             vertices[0].Set(-0.1f, 0.0f);
184             vertices[1].Set(0.1f, 0.0f);
185             vertices[2].Set(0.0f, 1.5f);
186             m_polygons[1].Set(vertices, 3);
187         }
188 
189         {
190             float32 w = 1.0f;
191             float32 b = w / (2.0f + b2Sqrt(2.0f));
192             float32 s = b2Sqrt(2.0f) * b;
193 
194             b2Vec2 vertices[8];
195             vertices[0].Set(0.5f * s, 0.0f);
196             vertices[1].Set(0.5f * w, b);
197             vertices[2].Set(0.5f * w, b + s);
198             vertices[3].Set(0.5f * s, w);
199             vertices[4].Set(-0.5f * s, w);
200             vertices[5].Set(-0.5f * w, b + s);
201             vertices[6].Set(-0.5f * w, b);
202             vertices[7].Set(-0.5f * s, 0.0f);
203 
204             m_polygons[2].Set(vertices, 8);
205         }
206 
207         {
208             m_polygons[3].SetAsBox(0.5f, 0.5f);
209         }
210 
211         {
212             m_circle.m_radius = 0.5f;
213         }
214 
215         m_bodyIndex = 0;
216         memset(m_bodies, 0, sizeof(m_bodies));
217 
218         m_angle = 0.0f;
219 
220         m_mode = e_closest;
221     }
222 
Create(int32 index)223     void Create(int32 index)
224     {
225         if (m_bodies[m_bodyIndex] != NULL)
226         {
227             m_world->DestroyBody(m_bodies[m_bodyIndex]);
228             m_bodies[m_bodyIndex] = NULL;
229         }
230 
231         b2BodyDef bd;
232 
233         float32 x = RandomFloat(-10.0f, 10.0f);
234         float32 y = RandomFloat(0.0f, 20.0f);
235         bd.position.Set(x, y);
236         bd.angle = RandomFloat(-b2_pi, b2_pi);
237 
238         m_userData[m_bodyIndex] = index;
239         bd.userData = m_userData + m_bodyIndex;
240 
241         if (index == 4)
242         {
243             bd.angularDamping = 0.02f;
244         }
245 
246         m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
247 
248         if (index < 4)
249         {
250             b2FixtureDef fd;
251             fd.shape = m_polygons + index;
252             fd.friction = 0.3f;
253             m_bodies[m_bodyIndex]->CreateFixture(&fd);
254         }
255         else
256         {
257             b2FixtureDef fd;
258             fd.shape = &m_circle;
259             fd.friction = 0.3f;
260 
261             m_bodies[m_bodyIndex]->CreateFixture(&fd);
262         }
263 
264         m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
265     }
266 
DestroyBody()267     void DestroyBody()
268     {
269         for (int32 i = 0; i < e_maxBodies; ++i)
270         {
271             if (m_bodies[i] != NULL)
272             {
273                 m_world->DestroyBody(m_bodies[i]);
274                 m_bodies[i] = NULL;
275                 return;
276             }
277         }
278     }
279 
Keyboard(unsigned char key)280     void Keyboard(unsigned char key)
281     {
282         switch (key)
283         {
284         case '1':
285         case '2':
286         case '3':
287         case '4':
288         case '5':
289             Create(key - '1');
290             break;
291 
292         case 'd':
293             DestroyBody();
294             break;
295 
296         case 'm':
297             if (m_mode == e_closest)
298             {
299                 m_mode = e_any;
300             }
301             else if (m_mode == e_any)
302             {
303                 m_mode = e_multiple;
304             }
305             else if (m_mode == e_multiple)
306             {
307                 m_mode = e_closest;
308             }
309         }
310     }
311 
Step(Settings * settings)312     void Step(Settings* settings)
313     {
314         bool advanceRay = settings->pause == 0 || settings->singleStep;
315 
316         Test::Step(settings);
317         m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff, m to change the mode");
318         m_textLine += 15;
319         m_debugDraw.DrawString(5, m_textLine, "Mode = %d", m_mode);
320         m_textLine += 15;
321 
322         float32 L = 11.0f;
323         b2Vec2 point1(0.0f, 10.0f);
324         b2Vec2 d(L * cosf(m_angle), L * sinf(m_angle));
325         b2Vec2 point2 = point1 + d;
326 
327         if (m_mode == e_closest)
328         {
329             RayCastClosestCallback callback;
330             m_world->RayCast(&callback, point1, point2);
331 
332             if (callback.m_hit)
333             {
334                 m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
335                 m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
336                 b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
337                 m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
338             }
339             else
340             {
341                 m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
342             }
343         }
344         else if (m_mode == e_any)
345         {
346             RayCastAnyCallback callback;
347             m_world->RayCast(&callback, point1, point2);
348 
349             if (callback.m_hit)
350             {
351                 m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
352                 m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
353                 b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
354                 m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
355             }
356             else
357             {
358                 m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
359             }
360         }
361         else if (m_mode == e_multiple)
362         {
363             RayCastMultipleCallback callback;
364             m_world->RayCast(&callback, point1, point2);
365             m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
366 
367             for (int32 i = 0; i < callback.m_count; ++i)
368             {
369                 b2Vec2 p = callback.m_points[i];
370                 b2Vec2 n = callback.m_normals[i];
371                 m_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
372                 m_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
373                 b2Vec2 head = p + 0.5f * n;
374                 m_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
375             }
376         }
377 
378         if (advanceRay)
379         {
380             m_angle += 0.25f * b2_pi / 180.0f;
381         }
382 
383 #if 0
384         // This case was failing.
385         {
386             b2Vec2 vertices[4];
387             //vertices[0].Set(-22.875f, -3.0f);
388             //vertices[1].Set(22.875f, -3.0f);
389             //vertices[2].Set(22.875f, 3.0f);
390             //vertices[3].Set(-22.875f, 3.0f);
391 
392             b2PolygonShape shape;
393             //shape.Set(vertices, 4);
394             shape.SetAsBox(22.875f, 3.0f);
395 
396             b2RayCastInput input;
397             input.p1.Set(10.2725f,1.71372f);
398             input.p2.Set(10.2353f,2.21807f);
399             //input.maxFraction = 0.567623f;
400             input.maxFraction = 0.56762173f;
401 
402             b2Transform xf;
403             xf.SetIdentity();
404             xf.position.Set(23.0f, 5.0f);
405 
406             b2RayCastOutput output;
407             bool hit;
408             hit = shape.RayCast(&output, input, xf);
409             hit = false;
410 
411             b2Color color(1.0f, 1.0f, 1.0f);
412             b2Vec2 vs[4];
413             for (int32 i = 0; i < 4; ++i)
414             {
415                 vs[i] = b2Mul(xf, shape.m_vertices[i]);
416             }
417 
418             m_debugDraw.DrawPolygon(vs, 4, color);
419             m_debugDraw.DrawSegment(input.p1, input.p2, color);
420         }
421 #endif
422     }
423 
Create()424     static Test* Create()
425     {
426         return new RayCast;
427     }
428 
429     int32 m_bodyIndex;
430     b2Body* m_bodies[e_maxBodies];
431     int32 m_userData[e_maxBodies];
432     b2PolygonShape m_polygons[4];
433     b2CircleShape m_circle;
434 
435     float32 m_angle;
436 
437     Mode m_mode;
438 };
439 
440 #endif
441