Browse Source

Add World:rayCastAny and World:rayCastClosest.

They return hit position, normal, and ray fraction (if there is a hit), and also take an optional fixture category bitmask parameter.
Sasha Szpakowski 1 year ago
parent
commit
28a898df1f

+ 72 - 0
src/modules/physics/box2d/World.cpp

@@ -237,6 +237,30 @@ float World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &po
 	return 0;
 	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)
 void World::SayGoodbye(b2Fixture *fixture)
 {
 {
 	Fixture *f = (Fixture *)(fixture->GetUserData().pointer);
 	Fixture *f = (Fixture *)(fixture->GetUserData().pointer);
@@ -612,6 +636,54 @@ int World::rayCast(lua_State *L)
 	return 0;
 	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()
 void World::destroy()
 {
 {
 	if (world == nullptr)
 	if (world == nullptr)

+ 26 - 6
src/modules/physics/box2d/World.h

@@ -94,8 +94,8 @@ public:
 	{
 	{
 	public:
 	public:
 		QueryCallback(World *world, lua_State *L, int idx);
 		QueryCallback(World *world, lua_State *L, int idx);
-		~QueryCallback();
-		virtual bool ReportFixture(b2Fixture *fixture);
+		virtual ~QueryCallback();
+		bool ReportFixture(b2Fixture *fixture) override;
 	private:
 	private:
 		World *world;
 		World *world;
 		lua_State *L;
 		lua_State *L;
@@ -107,8 +107,8 @@ public:
 	{
 	{
 	public:
 	public:
 		CollectCallback(World *world, lua_State *L);
 		CollectCallback(World *world, lua_State *L);
-		~CollectCallback();
-		virtual bool ReportFixture(b2Fixture *fixture);
+		virtual ~CollectCallback();
+		bool ReportFixture(b2Fixture *fixture) override;
 	private:
 	private:
 		World *world;
 		World *world;
 		lua_State *L;
 		lua_State *L;
@@ -119,8 +119,8 @@ public:
 	{
 	{
 	public:
 	public:
 		RayCastCallback(World *world, lua_State *L, int idx);
 		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:
 	private:
 		World *world;
 		World *world;
 		lua_State *L;
 		lua_State *L;
@@ -128,6 +128,23 @@ public:
 		int userargs;
 		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.
 	 * Creates a new world.
 	 **/
 	 **/
@@ -298,6 +315,9 @@ public:
 	 **/
 	 **/
 	int rayCast(lua_State *L);
 	int rayCast(lua_State *L);
 
 
+	int rayCastAny(lua_State *L);
+	int rayCastClosest(lua_State *L);
+
 	/**
 	/**
 	 * Destroy this world.
 	 * Destroy this world.
 	 **/
 	 **/

+ 21 - 1
src/modules/physics/box2d/wrap_World.cpp

@@ -185,7 +185,7 @@ int w_World_queryFixturesInArea(lua_State *L)
 	return t->queryFixturesInArea(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");
 	luax_markdeprecated(L, 1, "World:queryBoundingBox", API_METHOD, DEPRECATED_RENAMED, "World:queryFixturesInArea");
 	return w_World_queryFixturesInArea(L);
 	return w_World_queryFixturesInArea(L);
@@ -209,6 +209,24 @@ int w_World_rayCast(lua_State *L)
 	return ret;
 	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)
 int w_World_destroy(lua_State *L)
 {
 {
 	World *t = luax_checkworld(L, 1);
 	World *t = luax_checkworld(L, 1);
@@ -245,6 +263,8 @@ static const luaL_Reg w_World_functions[] =
 	{ "queryFixturesInArea", w_World_queryFixturesInArea },
 	{ "queryFixturesInArea", w_World_queryFixturesInArea },
 	{ "getFixturesInArea", w_World_getFixturesInArea },
 	{ "getFixturesInArea", w_World_getFixturesInArea },
 	{ "rayCast", w_World_rayCast },
 	{ "rayCast", w_World_rayCast },
+	{ "rayCastAny", w_World_rayCastAny },
+	{ "rayCastClosest", w_World_rayCastClosest },
 	{ "destroy", w_World_destroy },
 	{ "destroy", w_World_destroy },
 	{ "isDestroyed", w_World_isDestroyed },
 	{ "isDestroyed", w_World_isDestroyed },