/** * Copyright (c) 2006-2012 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(0) { } World::ContactCallback::~ContactCallback() { if (ref != 0) delete ref; } void World::ContactCallback::process(b2Contact *contact, const b2ContactImpulse *impulse) { // Process contacts. if (ref != 0) { lua_State *L = ref->getL(); ref->push(); // Push first fixture. { Fixture *a = (Fixture *)Memoizer::find(contact->GetFixtureA()); if (a != 0) { a->retain(); luax_newtype(L, "Fixture", PHYSICS_FIXTURE_T, (void *)a); } else throw love::Exception("A fixture has escaped Memoizer!"); } // Push second fixture. { Fixture *b = (Fixture *)Memoizer::find(contact->GetFixtureB()); if (b != 0) { b->retain(); luax_newtype(L, "Fixture", PHYSICS_FIXTURE_T, (void *)b); } else throw love::Exception("A fixture has escaped Memoizer!"); } Contact *c = new Contact(contact); luax_newtype(L, "Contact", (PHYSICS_CONTACT_T), (void *)c); 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(0) { } World::ContactFilter::~ContactFilter() { if (ref != 0) 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 != 0) { lua_State *L = ref->getL(); ref->push(); luax_newtype(L, "Fixture", PHYSICS_FIXTURE_T, (void *)a); luax_newtype(L, "Fixture", PHYSICS_FIXTURE_T, (void *)b); lua_call(L, 2, 1); return luax_toboolean(L, -1); } return true; } World::QueryCallback::QueryCallback() : ref(0) { } World::QueryCallback::~QueryCallback() { if (ref != 0) delete ref; } bool World::QueryCallback::ReportFixture(b2Fixture *fixture) { if (ref != 0) { lua_State *L = ref->getL(); ref->push(); Fixture *f = (Fixture *)Memoizer::find(fixture); if (!f) throw love::Exception("A fixture has escaped Memoizer!"); f->retain(); luax_newtype(L, "Fixture", PHYSICS_FIXTURE_T, (void *)f); lua_call(L, 1, 1); return luax_toboolean(L, -1); } return true; } World::RayCastCallback::RayCastCallback() : ref(0) { } World::RayCastCallback::~RayCastCallback() { if (ref != 0) delete ref; } float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction) { if (ref != 0) { lua_State *L = ref->getL(); ref->push(); Fixture *f = (Fixture *)Memoizer::find(fixture); if (!f) throw love::Exception("A fixture has escaped Memoizer!"); f->retain(); luax_newtype(L, "Fixture", PHYSICS_FIXTURE_T, (void *)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!"); return (float32)lua_tonumber(L, -1); } 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(NULL), destructWorld(false) { world = new b2World(b2Vec2(0,0)); this->retain(); // The Box2D world holds a reference to this World. 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(NULL), destructWorld(false) { world = new b2World(Physics::scaleDown(gravity)); // The Box2D world holds a reference to this World. this->retain(); world->SetAllowSleeping(sleep); world->SetContactListener(this); world->SetContactFilter(this); world->SetDestructionListener(this); b2BodyDef def; groundBody = world->CreateBody(&def); Memoizer::add(world, this); } World::~World() { } void World::update(float dt) { world->Step(dt, 8, 6); // Destroy all objects marked during the time step. for (std::vector::iterator i = destructBodies.begin(); i < destructBodies.end(); i++) { Body *b = *i; if (b->body != 0) b->destroy(); // Release for reference in vector. b->release(); } for (std::vector::iterator i = destructFixtures.begin(); i < destructFixtures.end(); i++) { Fixture *f = *i; if (f->isValid()) f->destroy(); // Release for reference in vector. f->release(); } for (std::vector::iterator i = destructJoints.begin(); i < destructJoints.end(); i++) { Joint *j = *i; 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); } 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); if (!a) throw love::Exception("A fixture has escaped Memoizer!"); a->retain(); Fixture *b = (Fixture *)Memoizer::find(fixtureB); if (!b) throw love::Exception("A fixture has escaped Memoizer!"); b->retain(); return filter.process(a, b); } bool World::isValid() const { return world != 0; } int World::setCallbacks(lua_State *L) { int n = lua_gettop(L); luax_assert_argc(L, 1, 4); switch (n) { case 4: if (postsolve.ref) delete postsolve.ref; postsolve.ref = luax_refif (L, LUA_TFUNCTION); case 3: if (presolve.ref) delete presolve.ref; presolve.ref = luax_refif (L, LUA_TFUNCTION); case 2: if (end.ref) delete end.ref; end.ref = luax_refif (L, LUA_TFUNCTION); case 1: if (begin.ref) delete begin.ref; begin.ref = luax_refif (L, LUA_TFUNCTION); } return 0; } int World::getCallbacks(lua_State *L) { begin.ref ? begin.ref->push() : lua_pushnil(L); end.ref ? end.ref->push() : lua_pushnil(L); presolve.ref ? presolve.ref->push() : lua_pushnil(L); postsolve.ref ? postsolve.ref->push() : lua_pushnil(L); return 4; } int World::setContactFilter(lua_State *L) { luax_assert_argc(L, 1); if (filter.ref) delete filter.ref; filter.ref = luax_refif (L, LUA_TFUNCTION); return 0; } int World::getContactFilter(lua_State *L) { filter.ref ? filter.ref->push() : 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::setAllowSleeping(bool allow) { world->SetAllowSleeping(allow); } bool World::getAllowSleeping() 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!"); body->retain(); luax_newtype(L, "Body", PHYSICS_BODY_T, (void *)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!"); joint->retain(); luax_newtype(L, "Joint", PHYSICS_JOINT_T, (void *)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) throw love::Exception("A contact has escaped Memoizer!"); contact->retain(); luax_newtype(L, "Contact", PHYSICS_CONTACT_T, (void *)contact); lua_rawseti(L, -2, i); i++; } while ((c = c->GetNext())); return 1; } b2Body *World::getGroundBody() const { return groundBody; } int World::queryBoundingBox(lua_State *L) { luax_assert_argc(L, 5); 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)); if (query.ref) delete query.ref; query.ref = luax_refif (L, LUA_TFUNCTION); world->QueryAABB(&query, box); return 0; } int World::rayCast(lua_State *L) { luax_assert_argc(L, 5); 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)); if (raycast.ref) delete raycast.ref; raycast.ref = luax_refif (L, LUA_TFUNCTION); world->RayCast(&raycast, v1, v2); return 0; } void World::destroy() { 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 = 0; // Box2D world destroyed. Release its reference. this->release(); } } // box2d } // physics } // love