diff --git a/src/modules/physics/box2d/World.cpp b/src/modules/physics/box2d/World.cpp index 0aff2ff5b..27b41dd8e 100644 --- a/src/modules/physics/box2d/World.cpp +++ b/src/modules/physics/box2d/World.cpp @@ -237,6 +237,30 @@ float World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &po return 0; } +World::RayCastOneCallback::RayCastOneCallback(uint16 categoryMask, bool any) + : hit(false) + , hitPoint() + , hitNormal() + , hitFraction(1.0f) + , categoryMask(categoryMask) + , any(any) +{ +} + +float World::RayCastOneCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction) +{ + if (categoryMask != 0xFFFF && (categoryMask & fixture->GetFilterData().categoryBits) == 0) + return -1; + + hit = true; + hitPoint = point; + hitNormal = normal; + hitFraction = fraction; + + // Returning the fraction makes sure it doesn't process anything farther away in subsequent iterations. + return any ? 0 : fraction; +} + void World::SayGoodbye(b2Fixture *fixture) { Fixture *f = (Fixture *)(fixture->GetUserData().pointer); @@ -612,6 +636,54 @@ int World::rayCast(lua_State *L) return 0; } +int World::rayCastAny(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); + uint16 categoryMaskBits = (uint16)luaL_optinteger(L, 5, 0xFFFF); + b2Vec2 v1 = Physics::scaleDown(b2Vec2(x1, y1)); + b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2)); + RayCastOneCallback raycast(categoryMaskBits, true); + world->RayCast(&raycast, v1, v2); + if (raycast.hit) + { + b2Vec2 hitPoint = Physics::scaleUp(raycast.hitPoint); + lua_pushnumber(L, hitPoint.x); + lua_pushnumber(L, hitPoint.y); + lua_pushnumber(L, raycast.hitNormal.x); + lua_pushnumber(L, raycast.hitNormal.y); + lua_pushnumber(L, raycast.hitFraction); + return 5; + } + return 0; +} + +int World::rayCastClosest(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); + uint16 categoryMaskBits = (uint16)luaL_optinteger(L, 5, 0xFFFF); + b2Vec2 v1 = Physics::scaleDown(b2Vec2(x1, y1)); + b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2)); + RayCastOneCallback raycast(categoryMaskBits, false); + world->RayCast(&raycast, v1, v2); + if (raycast.hit) + { + b2Vec2 hitPoint = Physics::scaleUp(raycast.hitPoint); + lua_pushnumber(L, hitPoint.x); + lua_pushnumber(L, hitPoint.y); + lua_pushnumber(L, raycast.hitNormal.x); + lua_pushnumber(L, raycast.hitNormal.y); + lua_pushnumber(L, raycast.hitFraction); + return 5; + } + return 0; +} + void World::destroy() { if (world == nullptr) diff --git a/src/modules/physics/box2d/World.h b/src/modules/physics/box2d/World.h index 208b202ea..75c62bb29 100644 --- a/src/modules/physics/box2d/World.h +++ b/src/modules/physics/box2d/World.h @@ -94,8 +94,8 @@ class World : public Object, public b2ContactListener, public b2ContactFilter, p { public: QueryCallback(World *world, lua_State *L, int idx); - ~QueryCallback(); - virtual bool ReportFixture(b2Fixture *fixture); + virtual ~QueryCallback(); + bool ReportFixture(b2Fixture *fixture) override; private: World *world; lua_State *L; @@ -107,8 +107,8 @@ class World : public Object, public b2ContactListener, public b2ContactFilter, p { public: CollectCallback(World *world, lua_State *L); - ~CollectCallback(); - virtual bool ReportFixture(b2Fixture *fixture); + virtual ~CollectCallback(); + bool ReportFixture(b2Fixture *fixture) override; private: World *world; lua_State *L; @@ -119,8 +119,8 @@ class World : public Object, public b2ContactListener, public b2ContactFilter, p { public: RayCastCallback(World *world, lua_State *L, int idx); - ~RayCastCallback(); - virtual float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction); + virtual ~RayCastCallback(); + float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction) override; private: World *world; lua_State *L; @@ -128,6 +128,23 @@ class World : public Object, public b2ContactListener, public b2ContactFilter, p int userargs; }; + class RayCastOneCallback : public b2RayCastCallback + { + public: + RayCastOneCallback(uint16 categoryMask, bool any); + virtual ~RayCastOneCallback() {}; + float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction) override; + + bool hit; + b2Vec2 hitPoint; + b2Vec2 hitNormal; + float hitFraction; + + private: + uint16 categoryMask; + bool any; + }; + /** * Creates a new world. **/ @@ -298,6 +315,9 @@ class World : public Object, public b2ContactListener, public b2ContactFilter, p **/ int rayCast(lua_State *L); + int rayCastAny(lua_State *L); + int rayCastClosest(lua_State *L); + /** * Destroy this world. **/ diff --git a/src/modules/physics/box2d/wrap_World.cpp b/src/modules/physics/box2d/wrap_World.cpp index 87080dc20..b80b9ae65 100644 --- a/src/modules/physics/box2d/wrap_World.cpp +++ b/src/modules/physics/box2d/wrap_World.cpp @@ -185,7 +185,7 @@ int w_World_queryFixturesInArea(lua_State *L) return t->queryFixturesInArea(L); } -int w_World_queryBoundingBox(lua_State* L) +int w_World_queryBoundingBox(lua_State *L) { luax_markdeprecated(L, 1, "World:queryBoundingBox", API_METHOD, DEPRECATED_RENAMED, "World:queryFixturesInArea"); return w_World_queryFixturesInArea(L); @@ -209,6 +209,24 @@ int w_World_rayCast(lua_State *L) return ret; } +int w_World_rayCastAny(lua_State *L) +{ + World *t = luax_checkworld(L, 1); + lua_remove(L, 1); + int ret = 0; + luax_catchexcept(L, [&]() { ret = t->rayCastAny(L); }); + return ret; +} + +int w_World_rayCastClosest(lua_State *L) +{ + World *t = luax_checkworld(L, 1); + lua_remove(L, 1); + int ret = 0; + luax_catchexcept(L, [&]() { ret = t->rayCastClosest(L); }); + return ret; +} + int w_World_destroy(lua_State *L) { World *t = luax_checkworld(L, 1); @@ -245,6 +263,8 @@ static const luaL_Reg w_World_functions[] = { "queryFixturesInArea", w_World_queryFixturesInArea }, { "getFixturesInArea", w_World_getFixturesInArea }, { "rayCast", w_World_rayCast }, + { "rayCastAny", w_World_rayCastAny }, + { "rayCastClosest", w_World_rayCastClosest }, { "destroy", w_World_destroy }, { "isDestroyed", w_World_isDestroyed },