ray_cast.cpp
Go to the documentation of this file.
1 // MIT License
2 
3 // Copyright (c) 2019 Erin Catto
4 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 
23 #include "settings.h"
24 #include "test.h"
25 #include "imgui/imgui.h"
26 
27 enum
28 {
30 };
31 
32 // This test demonstrates how to use the world ray-cast feature.
33 // NOTE: we are intentionally filtering one of the polygons, therefore
34 // the ray will always miss one type of polygon.
35 
36 // This callback finds the closest hit. Polygon 0 is filtered.
38 {
39 public:
41  {
42  m_hit = false;
43  }
44 
45  float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction) override
46  {
47  uintptr_t index = fixture->GetUserData().pointer;
48  if (index == 1)
49  {
50  // By returning -1, we instruct the calling code to ignore this fixture and
51  // continue the ray-cast to the next fixture.
52  return -1.0f;
53  }
54 
55  m_hit = true;
56  m_point = point;
57  m_normal = normal;
58 
59  // By returning the current fraction, we instruct the calling code to clip the ray and
60  // continue the ray-cast to the next fixture. WARNING: do not assume that fixtures
61  // are reported in order. However, by clipping, we can always get the closest fixture.
62  return fraction;
63  }
64 
65  bool m_hit;
68 };
69 
70 // This callback finds any hit. Polygon 0 is filtered. For this type of query we are usually
71 // just checking for obstruction, so the actual fixture and hit point are irrelevant.
73 {
74 public:
76  {
77  m_hit = false;
78  }
79 
80  float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override
81  {
82  uintptr_t index = fixture->GetUserData().pointer;
83  if (index == 1)
84  {
85  // By returning -1, we instruct the calling code to ignore this fixture and
86  // continue the ray-cast to the next fixture.
87  return -1.0f;
88  }
89 
90  m_hit = true;
91  m_point = point;
92  m_normal = normal;
93 
94  // At this point we have a hit, so we know the ray is obstructed.
95  // By returning 0, we instruct the calling code to terminate the ray-cast.
96  return 0.0f;
97  }
98 
99  bool m_hit;
102 };
103 
104 // This ray cast collects multiple hits along the ray. Polygon 0 is filtered.
105 // The fixtures are not necessary reported in order, so we might not capture
106 // the closest fixture.
108 {
109 public:
110  enum
111  {
112  e_maxCount = 3
113  };
114 
116  {
117  m_count = 0;
118  }
119 
120  float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override
121  {
122  uintptr_t index = fixture->GetUserData().pointer;
123  if (index == 1)
124  {
125  // By returning -1, we instruct the calling code to ignore this fixture and
126  // continue the ray-cast to the next fixture.
127  return -1.0f;
128  }
129 
130  b2Assert(m_count < e_maxCount);
131 
132  m_points[m_count] = point;
133  m_normals[m_count] = normal;
134  ++m_count;
135 
136  if (m_count == e_maxCount)
137  {
138  // At this point the buffer is full.
139  // By returning 0, we instruct the calling code to terminate the ray-cast.
140  return 0.0f;
141  }
142 
143  // By returning 1, we instruct the caller to continue without clipping the ray.
144  return 1.0f;
145  }
146 
147  b2Vec2 m_points[e_maxCount];
148  b2Vec2 m_normals[e_maxCount];
150 };
151 
152 
153 class RayCast : public Test
154 {
155 public:
156 
157  enum Mode
158  {
159  e_any = 0,
160  e_closest = 1,
161  e_multiple = 2
162  };
163 
165  {
166  // Ground body
167  {
168  b2BodyDef bd;
169  b2Body* ground = m_world->CreateBody(&bd);
170 
171  b2EdgeShape shape;
172  shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
173  ground->CreateFixture(&shape, 0.0f);
174  }
175 
176  {
177  b2Vec2 vertices[3];
178  vertices[0].Set(-0.5f, 0.0f);
179  vertices[1].Set(0.5f, 0.0f);
180  vertices[2].Set(0.0f, 1.5f);
181  m_polygons[0].Set(vertices, 3);
182  }
183 
184  {
185  b2Vec2 vertices[3];
186  vertices[0].Set(-0.1f, 0.0f);
187  vertices[1].Set(0.1f, 0.0f);
188  vertices[2].Set(0.0f, 1.5f);
189  m_polygons[1].Set(vertices, 3);
190  }
191 
192  {
193  float w = 1.0f;
194  float b = w / (2.0f + b2Sqrt(2.0f));
195  float s = b2Sqrt(2.0f) * b;
196 
197  b2Vec2 vertices[8];
198  vertices[0].Set(0.5f * s, 0.0f);
199  vertices[1].Set(0.5f * w, b);
200  vertices[2].Set(0.5f * w, b + s);
201  vertices[3].Set(0.5f * s, w);
202  vertices[4].Set(-0.5f * s, w);
203  vertices[5].Set(-0.5f * w, b + s);
204  vertices[6].Set(-0.5f * w, b);
205  vertices[7].Set(-0.5f * s, 0.0f);
206 
207  m_polygons[2].Set(vertices, 8);
208  }
209 
210  {
211  m_polygons[3].SetAsBox(0.5f, 0.5f);
212  }
213 
214  {
215  m_circle.m_radius = 0.5f;
216  }
217 
218  {
219  m_edge.SetTwoSided(b2Vec2(-1.0f, 0.0f), b2Vec2(1.0f, 0.0f));
220  }
221 
222  m_bodyIndex = 0;
223  memset(m_bodies, 0, sizeof(m_bodies));
224 
225  m_degrees = 0.0f;
226 
227  m_mode = e_closest;
228  }
229 
230  void Create(int32 index)
231  {
232  if (m_bodies[m_bodyIndex] != NULL)
233  {
234  m_world->DestroyBody(m_bodies[m_bodyIndex]);
235  m_bodies[m_bodyIndex] = NULL;
236  }
237 
238  b2BodyDef bd;
239 
240  float x = RandomFloat(-10.0f, 10.0f);
241  float y = RandomFloat(0.0f, 20.0f);
242  bd.position.Set(x, y);
243  bd.angle = RandomFloat(-b2_pi, b2_pi);
244 
245  if (index == 4)
246  {
247  bd.angularDamping = 0.02f;
248  }
249 
250  m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
251 
252  if (index < 4)
253  {
254  b2FixtureDef fd;
255  fd.shape = m_polygons + index;
256  fd.friction = 0.3f;
257  fd.userData.pointer = index + 1;
258  m_bodies[m_bodyIndex]->CreateFixture(&fd);
259  }
260  else if (index < 5)
261  {
262  b2FixtureDef fd;
263  fd.shape = &m_circle;
264  fd.friction = 0.3f;
265  fd.userData.pointer = index + 1;
266  m_bodies[m_bodyIndex]->CreateFixture(&fd);
267  }
268  else
269  {
270  b2FixtureDef fd;
271  fd.shape = &m_edge;
272  fd.friction = 0.3f;
273  fd.userData.pointer = index + 1;
274 
275  m_bodies[m_bodyIndex]->CreateFixture(&fd);
276  }
277 
278  m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
279  }
280 
281  void DestroyBody()
282  {
283  for (int32 i = 0; i < e_maxBodies; ++i)
284  {
285  if (m_bodies[i] != NULL)
286  {
287  m_world->DestroyBody(m_bodies[i]);
288  m_bodies[i] = NULL;
289  return;
290  }
291  }
292  }
293 
294  void UpdateUI() override
295  {
296  ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
297  ImGui::SetNextWindowSize(ImVec2(210.0f, 285.0f));
298  ImGui::Begin("Ray-cast Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
299 
300  if (ImGui::Button("Shape 1"))
301  {
302  Create(0);
303  }
304 
305  if (ImGui::Button("Shape 2"))
306  {
307  Create(1);
308  }
309 
310  if (ImGui::Button("Shape 3"))
311  {
312  Create(2);
313  }
314 
315  if (ImGui::Button("Shape 4"))
316  {
317  Create(3);
318  }
319 
320  if (ImGui::Button("Shape 5"))
321  {
322  Create(4);
323  }
324 
325  if (ImGui::Button("Shape 6"))
326  {
327  Create(5);
328  }
329 
330  if (ImGui::Button("Destroy Shape"))
331  {
332  DestroyBody();
333  }
334 
335  ImGui::RadioButton("Any", &m_mode, e_any);
336  ImGui::RadioButton("Closest", &m_mode, e_closest);
337  ImGui::RadioButton("Multiple", &m_mode, e_multiple);
338 
339  ImGui::SliderFloat("Angle", &m_degrees, 0.0f, 360.0f, "%.0f");
340 
341  ImGui::End();
342  }
343 
344  void Step(Settings& settings) override
345  {
346  Test::Step(settings);
347 
348  g_debugDraw.DrawString(5, m_textLine, "Shape 1 is intentionally ignored by the ray");
349  m_textLine += m_textIncrement;
350  switch (m_mode)
351  {
352  case e_closest:
353  g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: closest - find closest fixture along the ray");
354  break;
355 
356  case e_any:
357  g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: any - check for obstruction");
358  break;
359 
360  case e_multiple:
361  g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: multiple - gather multiple fixtures");
362  break;
363  }
364 
365  m_textLine += m_textIncrement;
366 
367  float angle = b2_pi * m_degrees / 180.0f;
368  float L = 11.0f;
369  b2Vec2 point1(0.0f, 10.0f);
370  b2Vec2 d(L * cosf(angle), L * sinf(angle));
371  b2Vec2 point2 = point1 + d;
372 
373  if (m_mode == e_closest)
374  {
375  RayCastClosestCallback callback;
376  m_world->RayCast(&callback, point1, point2);
377 
378  if (callback.m_hit)
379  {
380  g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
381  g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
382  b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
383  g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
384  }
385  else
386  {
387  g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
388  }
389  }
390  else if (m_mode == e_any)
391  {
392  RayCastAnyCallback callback;
393  m_world->RayCast(&callback, point1, point2);
394 
395  if (callback.m_hit)
396  {
397  g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
398  g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
399  b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
400  g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
401  }
402  else
403  {
404  g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
405  }
406  }
407  else if (m_mode == e_multiple)
408  {
409  RayCastMultipleCallback callback;
410  m_world->RayCast(&callback, point1, point2);
411  g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
412 
413  for (int32 i = 0; i < callback.m_count; ++i)
414  {
415  b2Vec2 p = callback.m_points[i];
416  b2Vec2 n = callback.m_normals[i];
417  g_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
418  g_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
419  b2Vec2 head = p + 0.5f * n;
420  g_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
421  }
422  }
423 
424 #if 0
425  // This case was failing.
426  {
427  b2Vec2 vertices[4];
428  //vertices[0].Set(-22.875f, -3.0f);
429  //vertices[1].Set(22.875f, -3.0f);
430  //vertices[2].Set(22.875f, 3.0f);
431  //vertices[3].Set(-22.875f, 3.0f);
432 
433  b2PolygonShape shape;
434  //shape.Set(vertices, 4);
435  shape.SetAsBox(22.875f, 3.0f);
436 
437  b2RayCastInput input;
438  input.p1.Set(10.2725f,1.71372f);
439  input.p2.Set(10.2353f,2.21807f);
440  //input.maxFraction = 0.567623f;
441  input.maxFraction = 0.56762173f;
442 
443  b2Transform xf;
444  xf.SetIdentity();
445  xf.position.Set(23.0f, 5.0f);
446 
447  b2RayCastOutput output;
448  bool hit;
449  hit = shape.RayCast(&output, input, xf);
450  hit = false;
451 
452  b2Color color(1.0f, 1.0f, 1.0f);
453  b2Vec2 vs[4];
454  for (int32 i = 0; i < 4; ++i)
455  {
456  vs[i] = b2Mul(xf, shape.m_vertices[i]);
457  }
458 
459  g_debugDraw.DrawPolygon(vs, 4, color);
460  g_debugDraw.DrawSegment(input.p1, input.p2, color);
461  }
462 #endif
463  }
464 
465  static Test* Create()
466  {
467  return new RayCast;
468  }
469 
471  b2Body* m_bodies[e_maxBodies];
472  b2PolygonShape m_polygons[4];
475  float m_degrees;
477 };
478 
479 static int testIndex = RegisterTest("Collision", "Ray Cast", RayCast::Create);
const b2Shape * shape
Definition: b2_fixture.h:76
d
IMGUI_API void SetNextWindowSize(const ImVec2 &size, ImGuiCond cond=0)
Definition: imgui.cpp:6054
#define b2Sqrt(x)
Definition: b2_math.h:37
IMGUI_API bool RadioButton(const char *label, bool active)
b2Fixture * CreateFixture(const b2FixtureDef *def)
Definition: b2_body.cpp:165
b2Vec2 b2Mul(const b2Mat22 &A, const b2Vec2 &v)
Definition: b2_math.h:422
int32 m_mode
Definition: ray_cast.cpp:476
TF2SIMD_FORCE_INLINE tf2Scalar angle(const Quaternion &q1, const Quaternion &q2)
f
void DrawPoint(const b2Vec2 &p, float size, const b2Color &color) override
Draw a point.
Definition: draw.cpp:766
Definition: imgui.h:164
void SetIdentity()
Set this to the identity transform.
Definition: b2_math.h:347
XmlRpcServer s
Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
Definition: b2_collision.h:153
static Test * Create()
Definition: ray_cast.cpp:465
void Set(const b2Vec2 &position, float angle)
Set this based on the position and angle.
Definition: b2_math.h:354
Definition: test.h:80
float angularDamping
Definition: b2_body.h:99
b2FixtureUserData & GetUserData()
Definition: b2_fixture.h:278
float RandomFloat()
Random number in range [-1,1].
Definition: test.h:37
b2EdgeShape m_edge
Definition: ray_cast.cpp:474
A solid circle shape.
A 2D column vector.
Definition: b2_math.h:41
void DrawPolygon(const b2Vec2 *vertices, int32 vertexCount, const b2Color &color) override
Draw a closed polygon provided in CCW order.
Definition: draw.cpp:639
int32 m_bodyIndex
Definition: ray_cast.cpp:470
bool RayCast(b2RayCastOutput *output, const b2RayCastInput &input, const b2Transform &transform, int32 childIndex) const override
signed int int32
Definition: b2_types.h:28
void SetTwoSided(const b2Vec2 &v1, const b2Vec2 &v2)
Set this as an isolated edge. Collision is two-sided.
Color for debug drawing. Each value has the range [0,1].
Definition: b2_draw.h:30
void SetAsBox(float hx, float hy)
float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float) override
Definition: ray_cast.cpp:120
IMGUI_API bool Begin(const char *name, bool *p_open=NULL, ImGuiWindowFlags flags=0)
Definition: imgui.cpp:4736
A rigid body. These are created via b2World::CreateBody.
Definition: b2_body.h:128
b2Vec2 m_normals[e_maxCount]
Definition: ray_cast.cpp:148
static int testIndex
Definition: ray_cast.cpp:479
void Step(Settings &settings) override
Definition: ray_cast.cpp:344
void Set(float x_, float y_)
Set this vector to some specified coordinates.
Definition: b2_math.h:53
b2FixtureUserData userData
Use this to store application specific fixture data.
Definition: b2_fixture.h:79
b2CircleShape m_circle
Definition: ray_cast.cpp:473
void UpdateUI() override
Definition: ray_cast.cpp:294
float m_degrees
Definition: ray_cast.cpp:475
b2Vec2 m_vertices[b2_maxPolygonVertices]
IMGUI_API bool Button(const char *label, const ImVec2 &size=ImVec2(0, 0))
IMGUI_API void End()
Definition: imgui.cpp:5371
uintptr_t pointer
For legacy compatibility.
Definition: b2_settings.h:78
float angle
The world angle of the body in radians.
Definition: b2_body.h:81
void DestroyBody()
Definition: ray_cast.cpp:281
IMGUI_API void SetNextWindowPos(const ImVec2 &pos, ImGuiCond cond=0, const ImVec2 &pivot=ImVec2(0, 0))
Definition: imgui.cpp:6045
#define b2_pi
Definition: b2_common.h:41
void Create(int32 index)
Definition: ray_cast.cpp:230
b2Vec2 position
Definition: b2_body.h:78
IMGUI_API bool SliderFloat(const char *label, float *v, float v_min, float v_max, const char *format="%.3f", float power=1.0f)
int RegisterTest(const char *category, const char *name, TestCreateFcn *fcn)
Definition: test.cpp:458
void DrawString(int x, int y, const char *string,...)
Definition: draw.cpp:772
float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction) override
Definition: ray_cast.cpp:45
virtual void Step(Settings &settings)
Definition: test.cpp:278
b2Vec2 m_points[e_maxCount]
Definition: ray_cast.cpp:147
DebugDraw g_debugDraw
Definition: draw.cpp:32
#define b2Assert(A)
Definition: b2_common.h:37
void DrawSegment(const b2Vec2 &p1, const b2Vec2 &p2, const b2Color &color) override
Draw a line segment.
Definition: draw.cpp:742
float friction
The friction coefficient, usually in the range [0,1].
Definition: b2_fixture.h:82
float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float) override
Definition: ray_cast.cpp:80


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:21