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
32namespace love
33{
34namespace physics
35{
36namespace box2d
37{
38
39love::Type World::type("World", &Object::type);
40
41World::ContactCallback::ContactCallback(World *world)
42 : ref(nullptr)
43 , L(nullptr)
44 , world(world)
45{
46}
47
48World::ContactCallback::~ContactCallback()
49{
50 if (ref != nullptr)
51 delete ref;
52}
53
54void 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
103World::ContactFilter::ContactFilter()
104 : ref(nullptr)
105 , L(nullptr)
106{
107}
108
109World::ContactFilter::~ContactFilter()
110{
111 if (ref != nullptr)
112 delete ref;
113}
114
115bool 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
144World::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
152World::QueryCallback::~QueryCallback()
153{
154}
155
156bool 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
174World::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
182World::RayCastCallback::~RayCastCallback()
183{
184}
185
186float32 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
212void 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
219void 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
226World::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
244World::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
262World::~World()
263{
264 destroy();
265}
266
267void World::update(float dt)
268{
269 update(dt, 8, 3); // Box2D 2.3's recommended defaults.
270}
271
272void 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
303void World::BeginContact(b2Contact *contact)
304{
305 begin.process(contact);
306}
307
308void 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
318void 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
324void World::PostSolve(b2Contact *contact, const b2ContactImpulse *impulse)
325{
326 postsolve.process(contact, impulse);
327}
328
329bool 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
339bool World::isValid() const
340{
341 return world != nullptr;
342}
343
344int 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
397int 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
406void World::setCallbacksL(lua_State *L)
407{
408 begin.L = end.L = presolve.L = postsolve.L = filter.L = L;
409}
410
411int 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
423int World::getContactFilter(lua_State *L)
424{
425 filter.ref ? filter.ref->push(L) : lua_pushnil(L);
426 return 1;
427}
428
429void World::setGravity(float x, float y)
430{
431 world->SetGravity(Physics::scaleDown(b2Vec2(x, y)));
432}
433
434int 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
442void World::translateOrigin(float x, float y)
443{
444 world->ShiftOrigin(Physics::scaleDown(b2Vec2(x, y)));
445}
446
447void World::setSleepingAllowed(bool allow)
448{
449 world->SetAllowSleeping(allow);
450}
451
452bool World::isSleepingAllowed() const
453{
454 return world->GetAllowSleeping();
455}
456
457bool World::isLocked() const
458{
459 return world->IsLocked();
460}
461
462int World::getBodyCount() const
463{
464 return world->GetBodyCount()-1; // ignore the ground body
465}
466
467int World::getJointCount() const
468{
469 return world->GetJointCount();
470}
471
472int World::getContactCount() const
473{
474 return world->GetContactCount();
475}
476
477int 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
499int 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
517int 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
539b2Body *World::getGroundBody() const
540{
541 return groundBody;
542}
543
544int 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
559int 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
573void 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
615void World::registerObject(void *b2object, love::Object *object)
616{
617 box2dObjectMap[b2object] = object;
618}
619
620void World::unregisterObject(void *b2object)
621{
622 box2dObjectMap.erase(b2object);
623}
624
625love::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