/** * 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