123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- /**
- * Copyright (c) 2006-2016 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
- #include "World.h"
- #include "Fixture.h"
- #include "Shape.h"
- #include "Contact.h"
- #include "Physics.h"
- #include "common/Memoizer.h"
- #include "common/Reference.h"
- namespace love
- {
- namespace physics
- {
- namespace box2d
- {
- World::ContactCallback::ContactCallback()
- : ref(nullptr)
- , L(nullptr)
- {
- }
- World::ContactCallback::~ContactCallback()
- {
- if (ref != nullptr)
- delete ref;
- }
- void World::ContactCallback::process(b2Contact *contact, const b2ContactImpulse *impulse)
- {
- // Process contacts.
- if (ref != nullptr && L != nullptr)
- {
- ref->push(L);
- // Push first fixture.
- {
- Fixture *a = (Fixture *)Memoizer::find(contact->GetFixtureA());
- if (a != nullptr)
- luax_pushtype(L, PHYSICS_FIXTURE_ID, a);
- else
- throw love::Exception("A fixture has escaped Memoizer!");
- }
- // Push second fixture.
- {
- Fixture *b = (Fixture *)Memoizer::find(contact->GetFixtureB());
- if (b != nullptr)
- luax_pushtype(L, PHYSICS_FIXTURE_ID, b);
- else
- throw love::Exception("A fixture has escaped Memoizer!");
- }
- Contact *cobj = (Contact *)Memoizer::find(contact);
- if (!cobj)
- cobj = new Contact(contact);
- else
- cobj->retain();
- luax_pushtype(L, PHYSICS_CONTACT_ID, cobj);
- cobj->release();
- int args = 3;
- if (impulse)
- {
- for (int c = 0; c < impulse->count; c++)
- {
- lua_pushnumber(L, Physics::scaleUp(impulse->normalImpulses[c]));
- lua_pushnumber(L, Physics::scaleUp(impulse->tangentImpulses[c]));
- args += 2;
- }
- }
- lua_call(L, args, 0);
- }
- }
- World::ContactFilter::ContactFilter()
- : ref(nullptr)
- , L(nullptr)
- {
- }
- World::ContactFilter::~ContactFilter()
- {
- if (ref != nullptr)
- delete ref;
- }
- bool World::ContactFilter::process(Fixture *a, Fixture *b)
- {
- // Handle masks, reimplemented from the manual
- int filterA[3], filterB[3];
- // [0] categoryBits
- // [1] maskBits
- // [2] groupIndex
- a->getFilterData(filterA);
- b->getFilterData(filterB);
- if (filterA[2] != 0 && // 0 is the default group, so this does not count
- filterA[2] == filterB[2]) // if they are in the same group
- return filterA[2] > 0; // Negative indexes mean you don't collide
- if ((filterA[1] & filterB[0]) == 0 ||
- (filterB[1] & filterA[0]) == 0)
- return false; // A and B aren't set to collide
- if (ref != nullptr && L != nullptr)
- {
- ref->push(L);
- luax_pushtype(L, PHYSICS_FIXTURE_ID, a);
- luax_pushtype(L, PHYSICS_FIXTURE_ID, b);
- lua_call(L, 2, 1);
- return luax_toboolean(L, -1);
- }
- return true;
- }
- World::QueryCallback::QueryCallback(lua_State *L, int idx)
- : L(L)
- , funcidx(idx)
- {
- luaL_checktype(L, funcidx, LUA_TFUNCTION);
- }
- World::QueryCallback::~QueryCallback()
- {
- }
- bool World::QueryCallback::ReportFixture(b2Fixture *fixture)
- {
- if (L != nullptr)
- {
- lua_pushvalue(L, funcidx);
- Fixture *f = (Fixture *)Memoizer::find(fixture);
- if (!f)
- throw love::Exception("A fixture has escaped Memoizer!");
- luax_pushtype(L, PHYSICS_FIXTURE_ID, f);
- lua_call(L, 1, 1);
- bool cont = luax_toboolean(L, -1);
- lua_pop(L, 1);
- return cont;
- }
- return true;
- }
- World::RayCastCallback::RayCastCallback(lua_State *L, int idx)
- : L(L)
- , funcidx(idx)
- {
- luaL_checktype(L, funcidx, LUA_TFUNCTION);
- }
- World::RayCastCallback::~RayCastCallback()
- {
- }
- float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction)
- {
- if (L != nullptr)
- {
- lua_pushvalue(L, funcidx);
- Fixture *f = (Fixture *)Memoizer::find(fixture);
- if (!f)
- throw love::Exception("A fixture has escaped Memoizer!");
- luax_pushtype(L, PHYSICS_FIXTURE_ID, f);
- b2Vec2 scaledPoint = Physics::scaleUp(point);
- lua_pushnumber(L, scaledPoint.x);
- lua_pushnumber(L, scaledPoint.y);
- lua_pushnumber(L, normal.x);
- lua_pushnumber(L, normal.y);
- lua_pushnumber(L, fraction);
- lua_call(L, 6, 1);
- if (!lua_isnumber(L, -1))
- luaL_error(L, "Raycast callback didn't return a number!");
- float32 fraction = (float32) lua_tonumber(L, -1);
- lua_pop(L, 1);
- return fraction;
- }
- return 0;
- }
- void World::SayGoodbye(b2Fixture *fixture)
- {
- Fixture *f = (Fixture *)Memoizer::find(fixture);
- // Hint implicit destruction with true.
- if (f) f->destroy(true);
- }
- void World::SayGoodbye(b2Joint *joint)
- {
- Joint *j = (Joint *)Memoizer::find(joint);
- // Hint implicit destruction with true.
- if (j) j->destroyJoint(true);
- }
- World::World()
- : world(nullptr)
- , destructWorld(false)
- {
- world = new b2World(b2Vec2(0,0));
- world->SetAllowSleeping(true);
- world->SetContactListener(this);
- world->SetContactFilter(this);
- world->SetDestructionListener(this);
- b2BodyDef def;
- groundBody = world->CreateBody(&def);
- Memoizer::add(world, this);
- }
- World::World(b2Vec2 gravity, bool sleep)
- : world(nullptr)
- , destructWorld(false)
- {
- world = new b2World(Physics::scaleDown(gravity));
- world->SetAllowSleeping(sleep);
- world->SetContactListener(this);
- world->SetContactFilter(this);
- world->SetDestructionListener(this);
- b2BodyDef def;
- groundBody = world->CreateBody(&def);
- Memoizer::add(world, this);
- }
- World::~World()
- {
- destroy();
- }
- void World::update(float dt)
- {
- update(dt, 8, 3); // Box2D 2.3's recommended defaults.
- }
- void World::update(float dt, int velocityIterations, int positionIterations)
- {
- world->Step(dt, velocityIterations, positionIterations);
- // Destroy all objects marked during the time step.
- for (Body *b : destructBodies)
- {
- if (b->body != nullptr) b->destroy();
- // Release for reference in vector.
- b->release();
- }
- for (Fixture *f : destructFixtures)
- {
- if (f->isValid()) f->destroy();
- // Release for reference in vector.
- f->release();
- }
- for (Joint *j : destructJoints)
- {
- if (j->isValid()) j->destroyJoint();
- // Release for reference in vector.
- j->release();
- }
- destructBodies.clear();
- destructFixtures.clear();
- destructJoints.clear();
- if (destructWorld)
- destroy();
- }
- void World::BeginContact(b2Contact *contact)
- {
- begin.process(contact);
- }
- void World::EndContact(b2Contact *contact)
- {
- end.process(contact);
- // Letting the Contact know that the b2Contact will be destroyed any second.
- Contact *c = (Contact *)Memoizer::find(contact);
- if (c != NULL)
- c->invalidate();
- }
- void World::PreSolve(b2Contact *contact, const b2Manifold *oldManifold)
- {
- B2_NOT_USED(oldManifold); // not sure what to do with this
- presolve.process(contact);
- }
- void World::PostSolve(b2Contact *contact, const b2ContactImpulse *impulse)
- {
- postsolve.process(contact, impulse);
- }
- bool World::ShouldCollide(b2Fixture *fixtureA, b2Fixture *fixtureB)
- {
- // Fixtures should be memoized, if we created them
- Fixture *a = (Fixture *)Memoizer::find(fixtureA);
- Fixture *b = (Fixture *)Memoizer::find(fixtureB);
- if (!a || !b)
- throw love::Exception("A fixture has escaped Memoizer!");
- return filter.process(a, b);
- }
- bool World::isValid() const
- {
- return world != nullptr;
- }
- int World::setCallbacks(lua_State *L)
- {
- int nargs = lua_gettop(L);
- for (int i = 1; i <= 4; i++)
- {
- if (!lua_isnoneornil(L, i))
- luaL_checktype(L, i, LUA_TFUNCTION);
- }
- delete begin.ref;
- begin.ref = nullptr;
- delete end.ref;
- end.ref = nullptr;
- delete presolve.ref;
- presolve.ref = nullptr;
- delete postsolve.ref;
- postsolve.ref = nullptr;
- if (nargs >= 1)
- {
- lua_pushvalue(L, 1);
- begin.ref = luax_refif(L, LUA_TFUNCTION);
- begin.L = L;
- }
- if (nargs >= 2)
- {
- lua_pushvalue(L, 2);
- end.ref = luax_refif(L, LUA_TFUNCTION);
- end.L = L;
- }
- if (nargs >= 3)
- {
- lua_pushvalue(L, 3);
- presolve.ref = luax_refif(L, LUA_TFUNCTION);
- presolve.L = L;
- }
- if (nargs >= 4)
- {
- lua_pushvalue(L, 4);
- postsolve.ref = luax_refif(L, LUA_TFUNCTION);
- postsolve.L = L;
- }
- return 0;
- }
- int World::getCallbacks(lua_State *L)
- {
- begin.ref ? begin.ref->push(L) : lua_pushnil(L);
- end.ref ? end.ref->push(L) : lua_pushnil(L);
- presolve.ref ? presolve.ref->push(L) : lua_pushnil(L);
- postsolve.ref ? postsolve.ref->push(L) : lua_pushnil(L);
- return 4;
- }
- void World::setCallbacksL(lua_State *L)
- {
- begin.L = end.L = presolve.L = postsolve.L = filter.L = L;
- }
- int World::setContactFilter(lua_State *L)
- {
- if (!lua_isnoneornil(L, 1))
- luaL_checktype(L, 1, LUA_TFUNCTION);
- if (filter.ref)
- delete filter.ref;
- filter.ref = luax_refif(L, LUA_TFUNCTION);
- filter.L = L;
- return 0;
- }
- int World::getContactFilter(lua_State *L)
- {
- filter.ref ? filter.ref->push(L) : lua_pushnil(L);
- return 1;
- }
- void World::setGravity(float x, float y)
- {
- world->SetGravity(Physics::scaleDown(b2Vec2(x, y)));
- }
- int World::getGravity(lua_State *L)
- {
- b2Vec2 v = Physics::scaleUp(world->GetGravity());
- lua_pushnumber(L, v.x);
- lua_pushnumber(L, v.y);
- return 2;
- }
- void World::translateOrigin(float x, float y)
- {
- world->ShiftOrigin(Physics::scaleDown(b2Vec2(x, y)));
- }
- void World::setSleepingAllowed(bool allow)
- {
- world->SetAllowSleeping(allow);
- }
- bool World::isSleepingAllowed() const
- {
- return world->GetAllowSleeping();
- }
- bool World::isLocked() const
- {
- return world->IsLocked();
- }
- int World::getBodyCount() const
- {
- return world->GetBodyCount()-1; // ignore the ground body
- }
- int World::getJointCount() const
- {
- return world->GetJointCount();
- }
- int World::getContactCount() const
- {
- return world->GetContactCount();
- }
- int World::getBodyList(lua_State *L) const
- {
- lua_newtable(L);
- b2Body *b = world->GetBodyList();
- int i = 1;
- do
- {
- if (!b)
- break;
- if (b == groundBody)
- continue;
- Body *body = (Body *)Memoizer::find(b);
- if (!body)
- throw love::Exception("A body has escaped Memoizer!");
- luax_pushtype(L, PHYSICS_BODY_ID, body);
- lua_rawseti(L, -2, i);
- i++;
- }
- while ((b = b->GetNext()));
- return 1;
- }
- int World::getJointList(lua_State *L) const
- {
- lua_newtable(L);
- b2Joint *j = world->GetJointList();
- int i = 1;
- do
- {
- if (!j) break;
- Joint *joint = (Joint *)Memoizer::find(j);
- if (!joint) throw love::Exception("A joint has escaped Memoizer!");
- luax_pushtype(L, PHYSICS_JOINT_ID, joint);
- lua_rawseti(L, -2, i);
- i++;
- }
- while ((j = j->GetNext()));
- return 1;
- }
- int World::getContactList(lua_State *L) const
- {
- lua_newtable(L);
- b2Contact *c = world->GetContactList();
- int i = 1;
- do
- {
- if (!c) break;
- Contact *contact = (Contact *)Memoizer::find(c);
- if (!contact)
- contact = new Contact(c);
- else
- contact->retain();
- luax_pushtype(L, PHYSICS_CONTACT_ID, contact);
- contact->release();
- lua_rawseti(L, -2, i);
- i++;
- }
- while ((c = c->GetNext()));
- return 1;
- }
- b2Body *World::getGroundBody() const
- {
- return groundBody;
- }
- int World::queryBoundingBox(lua_State *L)
- {
- b2AABB box;
- float lx = (float)luaL_checknumber(L, 1);
- float ly = (float)luaL_checknumber(L, 2);
- float ux = (float)luaL_checknumber(L, 3);
- float uy = (float)luaL_checknumber(L, 4);
- box.lowerBound = Physics::scaleDown(b2Vec2(lx, ly));
- box.upperBound = Physics::scaleDown(b2Vec2(ux, uy));
- luaL_checktype(L, 5, LUA_TFUNCTION);
- QueryCallback query(L, 5);
- world->QueryAABB(&query, box);
- return 0;
- }
- int World::rayCast(lua_State *L)
- {
- float x1 = (float)luaL_checknumber(L, 1);
- float y1 = (float)luaL_checknumber(L, 2);
- float x2 = (float)luaL_checknumber(L, 3);
- float y2 = (float)luaL_checknumber(L, 4);
- b2Vec2 v1 = Physics::scaleDown(b2Vec2(x1, y1));
- b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2));
- luaL_checktype(L, 5, LUA_TFUNCTION);
- RayCastCallback raycast(L, 5);
- world->RayCast(&raycast, v1, v2);
- return 0;
- }
- void World::destroy()
- {
- if (world == nullptr)
- return;
- if (world->IsLocked())
- {
- destructWorld = true;
- return;
- }
- // Cleaning up the world.
- b2Body *b = world->GetBodyList();
- while (b)
- {
- b2Body *t = b;
- b = b->GetNext();
- if (t == groundBody)
- continue;
- Body *body = (Body *)Memoizer::find(t);
- if (!body)
- throw love::Exception("A body has escaped Memoizer!");
- body->destroy();
- }
- world->DestroyBody(groundBody);
- Memoizer::remove(world);
- delete world;
- world = nullptr;
- }
- } // box2d
- } // physics
- } // love
|