1 | /** |
2 | * Copyright (c) 2006-2023 LOVE Development Team |
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 | * |
8 | * Permission is granted to anyone to use this software for any purpose, |
9 | * including commercial applications, and to alter it and redistribute it |
10 | * freely, subject to the following restrictions: |
11 | * |
12 | * 1. The origin of this software must not be misrepresented; you must not |
13 | * claim that you wrote the original software. If you use this software |
14 | * in a product, an acknowledgment in the product documentation would be |
15 | * appreciated but is not required. |
16 | * 2. Altered source versions must be plainly marked as such, and must not be |
17 | * misrepresented as being the original software. |
18 | * 3. This notice may not be removed or altered from any source distribution. |
19 | **/ |
20 | |
21 | #include "World.h" |
22 | |
23 | #include "Fixture.h" |
24 | #include "Shape.h" |
25 | #include "Contact.h" |
26 | #include "Physics.h" |
27 | #include "common/Reference.h" |
28 | |
29 | // Needed for World::getJoints. It should be moved to wrapper code... |
30 | #include "wrap_Joint.h" |
31 | |
32 | namespace love |
33 | { |
34 | namespace physics |
35 | { |
36 | namespace box2d |
37 | { |
38 | |
39 | love::Type World::type("World" , &Object::type); |
40 | |
41 | World::ContactCallback::ContactCallback(World *world) |
42 | : ref(nullptr) |
43 | , L(nullptr) |
44 | , world(world) |
45 | { |
46 | } |
47 | |
48 | World::ContactCallback::~ContactCallback() |
49 | { |
50 | if (ref != nullptr) |
51 | delete ref; |
52 | } |
53 | |
54 | void World::ContactCallback::process(b2Contact *contact, const b2ContactImpulse *impulse) |
55 | { |
56 | // Process contacts. |
57 | if (ref != nullptr && L != nullptr) |
58 | { |
59 | ref->push(L); |
60 | |
61 | // Push first fixture. |
62 | { |
63 | Fixture *a = (Fixture *)world->findObject(contact->GetFixtureA()); |
64 | if (a != nullptr) |
65 | luax_pushtype(L, a); |
66 | else |
67 | throw love::Exception("A fixture has escaped Memoizer!" ); |
68 | } |
69 | |
70 | // Push second fixture. |
71 | { |
72 | Fixture *b = (Fixture *)world->findObject(contact->GetFixtureB()); |
73 | if (b != nullptr) |
74 | luax_pushtype(L, b); |
75 | else |
76 | throw love::Exception("A fixture has escaped Memoizer!" ); |
77 | } |
78 | |
79 | Contact *cobj = (Contact *)world->findObject(contact); |
80 | if (!cobj) |
81 | cobj = new Contact(world, contact); |
82 | else |
83 | cobj->retain(); |
84 | |
85 | luax_pushtype(L, cobj); |
86 | cobj->release(); |
87 | |
88 | int args = 3; |
89 | if (impulse) |
90 | { |
91 | for (int c = 0; c < impulse->count; c++) |
92 | { |
93 | lua_pushnumber(L, Physics::scaleUp(impulse->normalImpulses[c])); |
94 | lua_pushnumber(L, Physics::scaleUp(impulse->tangentImpulses[c])); |
95 | args += 2; |
96 | } |
97 | } |
98 | lua_call(L, args, 0); |
99 | } |
100 | |
101 | } |
102 | |
103 | World::ContactFilter::ContactFilter() |
104 | : ref(nullptr) |
105 | , L(nullptr) |
106 | { |
107 | } |
108 | |
109 | World::ContactFilter::~ContactFilter() |
110 | { |
111 | if (ref != nullptr) |
112 | delete ref; |
113 | } |
114 | |
115 | bool World::ContactFilter::process(Fixture *a, Fixture *b) |
116 | { |
117 | // Handle masks, reimplemented from the manual |
118 | int filterA[3], filterB[3]; |
119 | // [0] categoryBits |
120 | // [1] maskBits |
121 | // [2] groupIndex |
122 | a->getFilterData(filterA); |
123 | b->getFilterData(filterB); |
124 | |
125 | if (filterA[2] != 0 && // 0 is the default group, so this does not count |
126 | filterA[2] == filterB[2]) // if they are in the same group |
127 | return filterA[2] > 0; // Negative indexes mean you don't collide |
128 | |
129 | if ((filterA[1] & filterB[0]) == 0 || |
130 | (filterB[1] & filterA[0]) == 0) |
131 | return false; // A and B aren't set to collide |
132 | |
133 | if (ref != nullptr && L != nullptr) |
134 | { |
135 | ref->push(L); |
136 | luax_pushtype(L, a); |
137 | luax_pushtype(L, b); |
138 | lua_call(L, 2, 1); |
139 | return luax_toboolean(L, -1); |
140 | } |
141 | return true; |
142 | } |
143 | |
144 | World::QueryCallback::QueryCallback(World *world, lua_State *L, int idx) |
145 | : world(world) |
146 | , L(L) |
147 | , funcidx(idx) |
148 | { |
149 | luaL_checktype(L, funcidx, LUA_TFUNCTION); |
150 | } |
151 | |
152 | World::QueryCallback::~QueryCallback() |
153 | { |
154 | } |
155 | |
156 | bool World::QueryCallback::ReportFixture(b2Fixture *fixture) |
157 | { |
158 | if (L != nullptr) |
159 | { |
160 | lua_pushvalue(L, funcidx); |
161 | Fixture *f = (Fixture *)world->findObject(fixture); |
162 | if (!f) |
163 | throw love::Exception("A fixture has escaped Memoizer!" ); |
164 | luax_pushtype(L, f); |
165 | lua_call(L, 1, 1); |
166 | bool cont = luax_toboolean(L, -1); |
167 | lua_pop(L, 1); |
168 | return cont; |
169 | } |
170 | |
171 | return true; |
172 | } |
173 | |
174 | World::RayCastCallback::RayCastCallback(World *world, lua_State *L, int idx) |
175 | : world(world) |
176 | , L(L) |
177 | , funcidx(idx) |
178 | { |
179 | luaL_checktype(L, funcidx, LUA_TFUNCTION); |
180 | } |
181 | |
182 | World::RayCastCallback::~RayCastCallback() |
183 | { |
184 | } |
185 | |
186 | float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction) |
187 | { |
188 | if (L != nullptr) |
189 | { |
190 | lua_pushvalue(L, funcidx); |
191 | Fixture *f = (Fixture *)world->findObject(fixture); |
192 | if (!f) |
193 | throw love::Exception("A fixture has escaped Memoizer!" ); |
194 | luax_pushtype(L, f); |
195 | b2Vec2 scaledPoint = Physics::scaleUp(point); |
196 | lua_pushnumber(L, scaledPoint.x); |
197 | lua_pushnumber(L, scaledPoint.y); |
198 | lua_pushnumber(L, normal.x); |
199 | lua_pushnumber(L, normal.y); |
200 | lua_pushnumber(L, fraction); |
201 | lua_call(L, 6, 1); |
202 | if (!lua_isnumber(L, -1)) |
203 | luaL_error(L, "Raycast callback didn't return a number!" ); |
204 | float32 fraction = (float32) lua_tonumber(L, -1); |
205 | lua_pop(L, 1); |
206 | return fraction; |
207 | } |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | void World::SayGoodbye(b2Fixture *fixture) |
213 | { |
214 | Fixture *f = (Fixture *)findObject(fixture); |
215 | // Hint implicit destruction with true. |
216 | if (f) f->destroy(true); |
217 | } |
218 | |
219 | void World::SayGoodbye(b2Joint *joint) |
220 | { |
221 | Joint *j = (Joint *)findObject(joint); |
222 | // Hint implicit destruction with true. |
223 | if (j) j->destroyJoint(true); |
224 | } |
225 | |
226 | World::World() |
227 | : world(nullptr) |
228 | , destructWorld(false) |
229 | , begin(this) |
230 | , end(this) |
231 | , presolve(this) |
232 | , postsolve(this) |
233 | { |
234 | world = new b2World(b2Vec2(0,0)); |
235 | world->SetAllowSleeping(true); |
236 | world->SetContactListener(this); |
237 | world->SetContactFilter(this); |
238 | world->SetDestructionListener(this); |
239 | b2BodyDef def; |
240 | groundBody = world->CreateBody(&def); |
241 | registerObject(world, this); |
242 | } |
243 | |
244 | World::World(b2Vec2 gravity, bool sleep) |
245 | : world(nullptr) |
246 | , destructWorld(false) |
247 | , begin(this) |
248 | , end(this) |
249 | , presolve(this) |
250 | , postsolve(this) |
251 | { |
252 | world = new b2World(Physics::scaleDown(gravity)); |
253 | world->SetAllowSleeping(sleep); |
254 | world->SetContactListener(this); |
255 | world->SetContactFilter(this); |
256 | world->SetDestructionListener(this); |
257 | b2BodyDef def; |
258 | groundBody = world->CreateBody(&def); |
259 | registerObject(world, this); |
260 | } |
261 | |
262 | World::~World() |
263 | { |
264 | destroy(); |
265 | } |
266 | |
267 | void World::update(float dt) |
268 | { |
269 | update(dt, 8, 3); // Box2D 2.3's recommended defaults. |
270 | } |
271 | |
272 | void World::update(float dt, int velocityIterations, int positionIterations) |
273 | { |
274 | world->Step(dt, velocityIterations, positionIterations); |
275 | |
276 | // Destroy all objects marked during the time step. |
277 | for (Body *b : destructBodies) |
278 | { |
279 | if (b->body != nullptr) b->destroy(); |
280 | // Release for reference in vector. |
281 | b->release(); |
282 | } |
283 | for (Fixture *f : destructFixtures) |
284 | { |
285 | if (f->isValid()) f->destroy(); |
286 | // Release for reference in vector. |
287 | f->release(); |
288 | } |
289 | for (Joint *j : destructJoints) |
290 | { |
291 | if (j->isValid()) j->destroyJoint(); |
292 | // Release for reference in vector. |
293 | j->release(); |
294 | } |
295 | destructBodies.clear(); |
296 | destructFixtures.clear(); |
297 | destructJoints.clear(); |
298 | |
299 | if (destructWorld) |
300 | destroy(); |
301 | } |
302 | |
303 | void World::BeginContact(b2Contact *contact) |
304 | { |
305 | begin.process(contact); |
306 | } |
307 | |
308 | void World::EndContact(b2Contact *contact) |
309 | { |
310 | end.process(contact); |
311 | |
312 | // Letting the Contact know that the b2Contact will be destroyed any second. |
313 | Contact *c = (Contact *)findObject(contact); |
314 | if (c != nullptr) |
315 | c->invalidate(); |
316 | } |
317 | |
318 | void World::PreSolve(b2Contact *contact, const b2Manifold *oldManifold) |
319 | { |
320 | B2_NOT_USED(oldManifold); // not sure what to do with this |
321 | presolve.process(contact); |
322 | } |
323 | |
324 | void World::PostSolve(b2Contact *contact, const b2ContactImpulse *impulse) |
325 | { |
326 | postsolve.process(contact, impulse); |
327 | } |
328 | |
329 | bool World::ShouldCollide(b2Fixture *fixtureA, b2Fixture *fixtureB) |
330 | { |
331 | // Fixtures should be memoized, if we created them |
332 | Fixture *a = (Fixture *)findObject(fixtureA); |
333 | Fixture *b = (Fixture *)findObject(fixtureB); |
334 | if (!a || !b) |
335 | throw love::Exception("A fixture has escaped Memoizer!" ); |
336 | return filter.process(a, b); |
337 | } |
338 | |
339 | bool World::isValid() const |
340 | { |
341 | return world != nullptr; |
342 | } |
343 | |
344 | int World::setCallbacks(lua_State *L) |
345 | { |
346 | int nargs = lua_gettop(L); |
347 | |
348 | for (int i = 1; i <= 4; i++) |
349 | { |
350 | if (!lua_isnoneornil(L, i)) |
351 | luaL_checktype(L, i, LUA_TFUNCTION); |
352 | } |
353 | |
354 | delete begin.ref; |
355 | begin.ref = nullptr; |
356 | |
357 | delete end.ref; |
358 | end.ref = nullptr; |
359 | |
360 | delete presolve.ref; |
361 | presolve.ref = nullptr; |
362 | |
363 | delete postsolve.ref; |
364 | postsolve.ref = nullptr; |
365 | |
366 | if (nargs >= 1) |
367 | { |
368 | lua_pushvalue(L, 1); |
369 | begin.ref = luax_refif(L, LUA_TFUNCTION); |
370 | begin.L = L; |
371 | } |
372 | |
373 | if (nargs >= 2) |
374 | { |
375 | lua_pushvalue(L, 2); |
376 | end.ref = luax_refif(L, LUA_TFUNCTION); |
377 | end.L = L; |
378 | } |
379 | |
380 | if (nargs >= 3) |
381 | { |
382 | lua_pushvalue(L, 3); |
383 | presolve.ref = luax_refif(L, LUA_TFUNCTION); |
384 | presolve.L = L; |
385 | } |
386 | |
387 | if (nargs >= 4) |
388 | { |
389 | lua_pushvalue(L, 4); |
390 | postsolve.ref = luax_refif(L, LUA_TFUNCTION); |
391 | postsolve.L = L; |
392 | } |
393 | |
394 | return 0; |
395 | } |
396 | |
397 | int World::getCallbacks(lua_State *L) |
398 | { |
399 | begin.ref ? begin.ref->push(L) : lua_pushnil(L); |
400 | end.ref ? end.ref->push(L) : lua_pushnil(L); |
401 | presolve.ref ? presolve.ref->push(L) : lua_pushnil(L); |
402 | postsolve.ref ? postsolve.ref->push(L) : lua_pushnil(L); |
403 | return 4; |
404 | } |
405 | |
406 | void World::setCallbacksL(lua_State *L) |
407 | { |
408 | begin.L = end.L = presolve.L = postsolve.L = filter.L = L; |
409 | } |
410 | |
411 | int World::setContactFilter(lua_State *L) |
412 | { |
413 | if (!lua_isnoneornil(L, 1)) |
414 | luaL_checktype(L, 1, LUA_TFUNCTION); |
415 | |
416 | if (filter.ref) |
417 | delete filter.ref; |
418 | filter.ref = luax_refif(L, LUA_TFUNCTION); |
419 | filter.L = L; |
420 | return 0; |
421 | } |
422 | |
423 | int World::getContactFilter(lua_State *L) |
424 | { |
425 | filter.ref ? filter.ref->push(L) : lua_pushnil(L); |
426 | return 1; |
427 | } |
428 | |
429 | void World::setGravity(float x, float y) |
430 | { |
431 | world->SetGravity(Physics::scaleDown(b2Vec2(x, y))); |
432 | } |
433 | |
434 | int World::getGravity(lua_State *L) |
435 | { |
436 | b2Vec2 v = Physics::scaleUp(world->GetGravity()); |
437 | lua_pushnumber(L, v.x); |
438 | lua_pushnumber(L, v.y); |
439 | return 2; |
440 | } |
441 | |
442 | void World::translateOrigin(float x, float y) |
443 | { |
444 | world->ShiftOrigin(Physics::scaleDown(b2Vec2(x, y))); |
445 | } |
446 | |
447 | void World::setSleepingAllowed(bool allow) |
448 | { |
449 | world->SetAllowSleeping(allow); |
450 | } |
451 | |
452 | bool World::isSleepingAllowed() const |
453 | { |
454 | return world->GetAllowSleeping(); |
455 | } |
456 | |
457 | bool World::isLocked() const |
458 | { |
459 | return world->IsLocked(); |
460 | } |
461 | |
462 | int World::getBodyCount() const |
463 | { |
464 | return world->GetBodyCount()-1; // ignore the ground body |
465 | } |
466 | |
467 | int World::getJointCount() const |
468 | { |
469 | return world->GetJointCount(); |
470 | } |
471 | |
472 | int World::getContactCount() const |
473 | { |
474 | return world->GetContactCount(); |
475 | } |
476 | |
477 | int World::getBodies(lua_State *L) const |
478 | { |
479 | lua_newtable(L); |
480 | b2Body *b = world->GetBodyList(); |
481 | int i = 1; |
482 | do |
483 | { |
484 | if (!b) |
485 | break; |
486 | if (b == groundBody) |
487 | continue; |
488 | Body *body = (Body *)findObject(b); |
489 | if (!body) |
490 | throw love::Exception("A body has escaped Memoizer!" ); |
491 | luax_pushtype(L, body); |
492 | lua_rawseti(L, -2, i); |
493 | i++; |
494 | } |
495 | while ((b = b->GetNext())); |
496 | return 1; |
497 | } |
498 | |
499 | int World::getJoints(lua_State *L) const |
500 | { |
501 | lua_newtable(L); |
502 | b2Joint *j = world->GetJointList(); |
503 | int i = 1; |
504 | do |
505 | { |
506 | if (!j) break; |
507 | Joint *joint = (Joint *)findObject(j); |
508 | if (!joint) throw love::Exception("A joint has escaped Memoizer!" ); |
509 | luax_pushjoint(L, joint); |
510 | lua_rawseti(L, -2, i); |
511 | i++; |
512 | } |
513 | while ((j = j->GetNext())); |
514 | return 1; |
515 | } |
516 | |
517 | int World::getContacts(lua_State *L) |
518 | { |
519 | lua_newtable(L); |
520 | b2Contact *c = world->GetContactList(); |
521 | int i = 1; |
522 | do |
523 | { |
524 | if (!c) break; |
525 | Contact *contact = (Contact *)findObject(c); |
526 | if (!contact) |
527 | contact = new Contact(this, c); |
528 | else |
529 | contact->retain(); |
530 | luax_pushtype(L, contact); |
531 | contact->release(); |
532 | lua_rawseti(L, -2, i); |
533 | i++; |
534 | } |
535 | while ((c = c->GetNext())); |
536 | return 1; |
537 | } |
538 | |
539 | b2Body *World::getGroundBody() const |
540 | { |
541 | return groundBody; |
542 | } |
543 | |
544 | int World::queryBoundingBox(lua_State *L) |
545 | { |
546 | b2AABB box; |
547 | float lx = (float)luaL_checknumber(L, 1); |
548 | float ly = (float)luaL_checknumber(L, 2); |
549 | float ux = (float)luaL_checknumber(L, 3); |
550 | float uy = (float)luaL_checknumber(L, 4); |
551 | box.lowerBound = Physics::scaleDown(b2Vec2(lx, ly)); |
552 | box.upperBound = Physics::scaleDown(b2Vec2(ux, uy)); |
553 | luaL_checktype(L, 5, LUA_TFUNCTION); |
554 | QueryCallback query(this, L, 5); |
555 | world->QueryAABB(&query, box); |
556 | return 0; |
557 | } |
558 | |
559 | int World::rayCast(lua_State *L) |
560 | { |
561 | float x1 = (float)luaL_checknumber(L, 1); |
562 | float y1 = (float)luaL_checknumber(L, 2); |
563 | float x2 = (float)luaL_checknumber(L, 3); |
564 | float y2 = (float)luaL_checknumber(L, 4); |
565 | b2Vec2 v1 = Physics::scaleDown(b2Vec2(x1, y1)); |
566 | b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2)); |
567 | luaL_checktype(L, 5, LUA_TFUNCTION); |
568 | RayCastCallback raycast(this, L, 5); |
569 | world->RayCast(&raycast, v1, v2); |
570 | return 0; |
571 | } |
572 | |
573 | void World::destroy() |
574 | { |
575 | if (world == nullptr) |
576 | return; |
577 | |
578 | if (world->IsLocked()) |
579 | { |
580 | destructWorld = true; |
581 | return; |
582 | } |
583 | |
584 | // Remove userdata reference to avoid it sticking around after GC |
585 | if (begin.ref) begin.ref->unref(); |
586 | if (end.ref) end.ref->unref(); |
587 | if (presolve.ref) presolve.ref->unref(); |
588 | if (postsolve.ref) postsolve.ref->unref(); |
589 | if (filter.ref) filter.ref->unref(); |
590 | |
591 | //disable callbacks |
592 | begin.ref = end.ref = presolve.ref = postsolve.ref = filter.ref = nullptr; |
593 | |
594 | // Cleaning up the world. |
595 | b2Body *b = world->GetBodyList(); |
596 | while (b) |
597 | { |
598 | b2Body *t = b; |
599 | b = b->GetNext(); |
600 | if (t == groundBody) |
601 | continue; |
602 | Body *body = (Body *)findObject(t); |
603 | if (!body) |
604 | throw love::Exception("A body has escaped Memoizer!" ); |
605 | body->destroy(); |
606 | } |
607 | |
608 | world->DestroyBody(groundBody); |
609 | unregisterObject(world); |
610 | |
611 | delete world; |
612 | world = nullptr; |
613 | } |
614 | |
615 | void World::registerObject(void *b2object, love::Object *object) |
616 | { |
617 | box2dObjectMap[b2object] = object; |
618 | } |
619 | |
620 | void World::unregisterObject(void *b2object) |
621 | { |
622 | box2dObjectMap.erase(b2object); |
623 | } |
624 | |
625 | love::Object *World::findObject(void *b2object) const |
626 | { |
627 | auto it = box2dObjectMap.find(b2object); |
628 | if (it != box2dObjectMap.end()) |
629 | return it->second; |
630 | else |
631 | return nullptr; |
632 | } |
633 | |
634 | } // box2d |
635 | } // physics |
636 | } // love |
637 | |