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