Browse Source

Add convenience functions for creating a Body+Shape+Fixture all at once.

#1130.

- Add love.physics.newCircleBody(world, bodytype, x, y, radius)
- Add love.physics.newRectangleBody(world, bodytype, x, y, w, h [, angle])
- Add love.physics.newPolygonBody(world, bodytype, coords)
- Add love.physics.newEdgeBody(world, bodytype, x1, y1, x2, y2 [, onesided])
- Add love.physics.newChainBody(world, bodytype, loop, coords)

All new functions return a Body object. The body's world position is at the center of the given coordinates, and the shape's local origin is at its center.
Sasha Szpakowski 1 year ago
parent
commit
51b439822b

+ 77 - 97
src/modules/physics/box2d/Physics.cpp

@@ -63,27 +63,77 @@ Body *Physics::newBody(World *world, Body::Type type)
 	return new Body(world, b2Vec2(0, 0), type);
 }
 
-CircleShape *Physics::newCircleShape(float radius)
+Body *Physics::newCircleBody(World *world, Body::Type type, float x, float y, float radius)
 {
-	return newCircleShape(0, 0, radius);
+	StrongRef<Body> body(newBody(world, x, y, type), Acquire::NORETAIN);
+	StrongRef<CircleShape> shape(newCircleShape(0, 0, radius), Acquire::NORETAIN);
+	StrongRef<Fixture> fixture(newFixture(body, shape, 1.0f), Acquire::NORETAIN);
+	body->retain();
+	return body.get();
 }
 
-CircleShape *Physics::newCircleShape(float x, float y, float radius)
+Body *Physics::newRectangleBody(World *world, Body::Type type, float x, float y, float w, float h, float angle)
 {
-	b2CircleShape *s = new b2CircleShape();
-	s->m_p = Physics::scaleDown(b2Vec2(x, y));
-	s->m_radius = Physics::scaleDown(radius);
-	return new CircleShape(s);
+	StrongRef<Body> body(newBody(world, x, y, type), Acquire::NORETAIN);
+	StrongRef<PolygonShape> shape(newRectangleShape(0, 0, w, h, angle), Acquire::NORETAIN);
+	StrongRef<Fixture> fixture(newFixture(body, shape, 1.0f), Acquire::NORETAIN);
+	body->retain();
+	return body.get();
+}
+
+Body *Physics::newPolygonBody(World *world, Body::Type type, const Vector2 *coords, int count)
+{
+	Vector2 origin(0, 0);
+
+	for (int i = 0; i < count; i++)
+		origin += coords[i] / count;
+
+	std::vector<Vector2> localcoords;
+	for (int i = 0; i < count; i++)
+		localcoords.push_back(coords[i] - origin);
+
+	StrongRef<Body> body(newBody(world, origin.x, origin.y, type), Acquire::NORETAIN);
+	StrongRef<PolygonShape> shape(newPolygonShape(localcoords.data(), count), Acquire::NORETAIN);
+	StrongRef<Fixture> fixture(newFixture(body, shape, 1.0f), Acquire::NORETAIN);
+	body->retain();
+	return body.get();
 }
 
-PolygonShape *Physics::newRectangleShape(float w, float h)
+Body *Physics::newEdgeBody(World *world, Body::Type type, float x1, float y1, float x2, float y2, bool oneSided)
 {
-	return newRectangleShape(0, 0, w, h, 0);
+	float wx = (x2 - x1) / 2.0f;
+	float wy = (y2 - y1) / 2.0f;
+	StrongRef<Body> body(newBody(world, wx, wy, type), Acquire::NORETAIN);
+	StrongRef<EdgeShape> shape(newEdgeShape(x1 - wx, y1 - wy, x2 - wx, y2 - wy, oneSided), Acquire::NORETAIN);
+	StrongRef<Fixture> fixture(newFixture(body, shape, 1.0f), Acquire::NORETAIN);
+	body->retain();
+	return body.get();
 }
 
-PolygonShape *Physics::newRectangleShape(float x, float y, float w, float h)
+Body *Physics::newChainBody(World *world, Body::Type type, bool loop, const Vector2 *coords, int count)
 {
-	return newRectangleShape(x, y, w, h, 0);
+	Vector2 origin(0, 0);
+
+	for (int i = 0; i < count; i++)
+		origin += coords[i] / count;
+
+	std::vector<Vector2> localcoords;
+	for (int i = 0; i < count; i++)
+		localcoords.push_back(coords[i] - origin);
+
+	StrongRef<Body> body(newBody(world, origin.x, origin.y, type), Acquire::NORETAIN);
+	StrongRef<ChainShape> shape(newChainShape(loop, localcoords.data(), count), Acquire::NORETAIN);
+	StrongRef<Fixture> fixture(newFixture(body, shape, 1.0f), Acquire::NORETAIN);
+	body->retain();
+	return body.get();
+}
+
+CircleShape *Physics::newCircleShape(float x, float y, float radius)
+{
+	b2CircleShape *s = new b2CircleShape();
+	s->m_p = Physics::scaleDown(b2Vec2(x, y));
+	s->m_radius = Physics::scaleDown(radius);
+	return new CircleShape(s);
 }
 
 PolygonShape *Physics::newRectangleShape(float x, float y, float w, float h, float angle)
@@ -109,54 +159,24 @@ EdgeShape *Physics::newEdgeShape(float x1, float y1, float x2, float y2, bool on
 	return new EdgeShape(s);
 }
 
-int Physics::newPolygonShape(lua_State *L)
+PolygonShape *Physics::newPolygonShape(const Vector2 *coords, int count)
 {
-	int argc = lua_gettop(L);
-
-	bool istable = lua_istable(L, 1);
-
-	if (istable)
-		argc = (int) luax_objlen(L, 1);
-
-	if (argc % 2 != 0)
-		return luaL_error(L, "Number of vertex components must be a multiple of two.");
-
 	// 3 to 8 (b2_maxPolygonVertices) vertices
-	int vcount = argc / 2;
-	if (vcount < 3)
-		return luaL_error(L, "Expected a minimum of 3 vertices, got %d.", vcount);
-	else if (vcount > b2_maxPolygonVertices)
-		return luaL_error(L, "Expected a maximum of %d vertices, got %d.", b2_maxPolygonVertices, vcount);
+	if (count < 3)
+		throw love::Exception("Expected a minimum of 3 vertices, got %d.", count);
+	else if (count > b2_maxPolygonVertices)
+		throw love::Exception("Expected a maximum of %d vertices, got %d.", b2_maxPolygonVertices, count);
 
 	b2Vec2 vecs[b2_maxPolygonVertices];
 
-	if (istable)
-	{
-		for (int i = 0; i < vcount; i++)
-		{
-			lua_rawgeti(L, 1, 1 + i * 2);
-			lua_rawgeti(L, 1, 2 + i * 2);
-			float x = (float)luaL_checknumber(L, -2);
-			float y = (float)luaL_checknumber(L, -1);
-			vecs[i] = Physics::scaleDown(b2Vec2(x, y));
-			lua_pop(L, 2);
-		}
-	}
-	else
-	{
-		for (int i = 0; i < vcount; i++)
-		{
-			float x = (float)luaL_checknumber(L, 1 + i * 2);
-			float y = (float)luaL_checknumber(L, 2 + i * 2);
-			vecs[i] = Physics::scaleDown(b2Vec2(x, y));
-		}
-	}
+	for (int i = 0; i < count; i++)
+		vecs[i] = Physics::scaleDown(b2Vec2(coords[i].x, coords[i].y));
 
 	b2PolygonShape *s = new b2PolygonShape();
 
 	try
 	{
-		s->Set(vecs, vcount);
+		s->Set(vecs, count);
 	}
 	catch (love::Exception &)
 	{
@@ -164,72 +184,32 @@ int Physics::newPolygonShape(lua_State *L)
 		throw;
 	}
 
-	PolygonShape *p = new PolygonShape(s);
-	luax_pushtype(L, p);
-	p->release();
-	return 1;
+	return new PolygonShape(s);
 }
 
-int Physics::newChainShape(lua_State *L)
+ChainShape *Physics::newChainShape(bool loop, const Vector2 *coords, int count)
 {
-	int argc = lua_gettop(L)-1; // first argument is looping
+	std::vector<b2Vec2> vecs;
 
-	bool istable = lua_istable(L, 2);
-
-	if (istable)
-		argc = (int) luax_objlen(L, 2);
-
-	if (argc == 0 || argc % 2 != 0)
-		return luaL_error(L, "Number of vertex components must be a multiple of two.");
-
-	int vcount = argc/2;
-	bool loop = luax_checkboolean(L, 1);
-	b2Vec2 *vecs = new b2Vec2[vcount];
-
-	if (istable)
-	{
-		for (int i = 0; i < vcount; i++)
-		{
-			lua_rawgeti(L, 2, 1 + i * 2);
-			lua_rawgeti(L, 2, 2 + i * 2);
-			float x = (float)lua_tonumber(L, -2);
-			float y = (float)lua_tonumber(L, -1);
-			vecs[i] = Physics::scaleDown(b2Vec2(x, y));
-			lua_pop(L, 2);
-		}
-	}
-	else
-	{
-		for (int i = 0; i < vcount; i++)
-		{
-			float x = (float)luaL_checknumber(L, 2 + i * 2);
-			float y = (float)luaL_checknumber(L, 3 + i * 2);
-			vecs[i] = Physics::scaleDown(b2Vec2(x, y));
-		}
-	}
+	for (int i = 0; i < count; i++)
+		vecs.push_back(Physics::scaleDown(b2Vec2(coords[i].x, coords[i].y)));
 
 	b2ChainShape *s = new b2ChainShape();
 
 	try
 	{
 		if (loop)
-			s->CreateLoop(vecs, vcount);
+			s->CreateLoop(vecs.data(), count);
 		else
-			s->CreateChain(vecs, vcount, vecs[0], vecs[vcount-1]);
+			s->CreateChain(vecs.data(), count, vecs[0], vecs[count - 1]);
 	}
 	catch (love::Exception &)
 	{
-		delete[] vecs;
 		delete s;
 		throw;
 	}
 
-	delete[] vecs;
-
-	ChainShape *c = new ChainShape(s);
-	luax_pushtype(L, c);
-	c->release();
-	return 1;
+	return new ChainShape(s);
 }
 
 DistanceJoint *Physics::newDistanceJoint(Body *body1, Body *body2, float x1, float y1, float x2, float y2, bool collideConnected)

+ 13 - 23
src/modules/physics/box2d/Physics.h

@@ -23,6 +23,8 @@
 
 // LOVE
 #include "common/Module.h"
+#include "common/Vector.h"
+
 #include "World.h"
 #include "Contact.h"
 #include "Body.h"
@@ -93,10 +95,15 @@ public:
 	Body *newBody(World *world, Body::Type type);
 
 	/**
-	 * Creates a new CircleShape at (0, 0).
-	 * @param radius The radius of the circle.
+	 * Convenience functions for creating a Body, Shape, and Fixture all in one
+	 * call. The body's world position is the center/average of the given
+	 * coordinates, and the shape is centered at the local origin.
 	 **/
-	CircleShape *newCircleShape(float radius);
+	Body *newCircleBody(World *world, Body::Type type, float x, float y, float radius);
+	Body *newRectangleBody(World *world, Body::Type type, float x, float y, float w, float h, float angle);
+	Body *newPolygonBody(World *world, Body::Type type, const Vector2 *coords, int count);
+	Body *newEdgeBody(World *world, Body::Type type, float x1, float y1, float x2, float y2, bool oneSided);
+	Body *newChainBody(World *world, Body::Type type, bool loop, const Vector2 *coords, int count);
 
 	/**
 	 * Creates a new CircleShape at (x,y) in local coordinates.
@@ -106,24 +113,6 @@ public:
 	 **/
 	CircleShape *newCircleShape(float x, float y, float radius);
 
-	/**
-	 * Shorthand for creating rectangular PolygonShapes. The rectangle
-	 * will be created at the local origin.
-	 * @param w The width of the rectangle.
-	 * @param h The height of the rectangle.
-	 **/
-	PolygonShape *newRectangleShape(float w, float h);
-
-	/**
-	 * Shorthand for creating rectangular PolygonShapes. The rectangle
-	 * will be created at (x,y) in local coordinates.
-	 * @param x The offset along the x-axis.
-	 * @param y The offset along the y-axis.
-	 * @param w The width of the rectangle.
-	 * @param h The height of the rectangle.
-	 **/
-	PolygonShape *newRectangleShape(float x, float y, float w, float h);
-
 	/**
 	 * Shorthand for creating rectangular PolygonShapes. The rectangle
 	 * will be created at (x,y) in local coordinates.
@@ -148,12 +137,13 @@ public:
 	/**
 	 * Creates a new PolygonShape from a variable number of vertices.
 	 **/
-	int newPolygonShape(lua_State *L);
+	//int newPolygonShape(lua_State *L);
+	PolygonShape *newPolygonShape(const Vector2 *coords, int count);
 
 	/**
 	 * Creates a new ChainShape from a variable number of vertices.
 	 **/
-	int newChainShape(lua_State *L);
+	ChainShape *newChainShape(bool loop, const Vector2 *coords, int count);
 
 	/**
 	 * Creates a new DistanceJoint connecting body1 with body2.

+ 259 - 8
src/modules/physics/box2d/wrap_Physics.cpp

@@ -83,6 +83,177 @@ int w_newBody(lua_State *L)
 	return 1;
 }
 
+int w_newCircleBody(lua_State *L)
+{
+	World *world = luax_checkworld(L, 1);
+
+	const char *typestr = luaL_checkstring(L, 2);
+	Body::Type btype = Body::BODY_STATIC;
+	if (!Body::getConstant(typestr, btype))
+		return luax_enumerror(L, "Body type", Body::getConstants(btype), typestr);
+
+	float x = (float)luaL_checknumber(L, 3);
+	float y = (float)luaL_checknumber(L, 4);
+	float radius = (float)luaL_checknumber(L, 5);
+
+	Body *body = nullptr;
+	luax_catchexcept(L, [&]() { body = instance()->newCircleBody(world, btype, x, y, radius); });
+
+	luax_pushtype(L, body);
+	body->release();
+	return 1;
+}
+
+int w_newRectangleBody(lua_State *L)
+{
+	World *world = luax_checkworld(L, 1);
+
+	const char *typestr = luaL_checkstring(L, 2);
+	Body::Type btype = Body::BODY_STATIC;
+	if (!Body::getConstant(typestr, btype))
+		return luax_enumerror(L, "Body type", Body::getConstants(btype), typestr);
+
+	float x = (float)luaL_checknumber(L, 3);
+	float y = (float)luaL_checknumber(L, 4);
+	float w = (float)luaL_checknumber(L, 5);
+	float h = (float)luaL_checknumber(L, 6);
+	float angle = (float)luaL_optnumber(L, 7, 0.0);
+
+	Body *body = nullptr;
+	luax_catchexcept(L, [&]() { body = instance()->newRectangleBody(world, btype, x, y, w, h, angle); });
+
+	luax_pushtype(L, body);
+	body->release();
+	return 1;
+}
+
+int w_newPolygonBody(lua_State *L)
+{
+	World *world = luax_checkworld(L, 1);
+
+	const char *typestr = luaL_checkstring(L, 2);
+	Body::Type btype = Body::BODY_STATIC;
+	if (!Body::getConstant(typestr, btype))
+		return luax_enumerror(L, "Body type", Body::getConstants(btype), typestr);
+
+	int argc = lua_gettop(L);
+
+	bool istable = lua_istable(L, 3);
+	if (istable)
+		argc = (int)luax_objlen(L, 3);
+
+	if (argc % 2 != 0)
+		return luaL_error(L, "Number of vertex components must be a multiple of two.");
+
+	int vcount = argc / 2;
+	std::vector<Vector2> coords;
+
+	if (istable)
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			lua_rawgeti(L, 3, 1 + i * 2);
+			lua_rawgeti(L, 3, 2 + i * 2);
+			float x = (float)luaL_checknumber(L, -2);
+			float y = (float)luaL_checknumber(L, -1);
+			coords.emplace_back(x, y);
+			lua_pop(L, 2);
+		}
+	}
+	else
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			float x = (float)luaL_checknumber(L, 3 + i * 2);
+			float y = (float)luaL_checknumber(L, 4 + i * 2);
+			coords.emplace_back(x, y);
+		}
+	}
+
+	Body *body = nullptr;
+	luax_catchexcept(L, [&]() { body = instance()->newPolygonBody(world, btype, coords.data(), (int)coords.size()); });
+
+	luax_pushtype(L, body);
+	body->release();
+	return 1;
+}
+
+int w_newEdgeBody(lua_State *L)
+{
+	World *world = luax_checkworld(L, 1);
+
+	const char *typestr = luaL_checkstring(L, 2);
+	Body::Type btype = Body::BODY_STATIC;
+	if (!Body::getConstant(typestr, btype))
+		return luax_enumerror(L, "Body type", Body::getConstants(btype), typestr);
+
+	float x1 = (float)luaL_checknumber(L, 3);
+	float y1 = (float)luaL_checknumber(L, 4);
+	float x2 = (float)luaL_checknumber(L, 5);
+	float y2 = (float)luaL_checknumber(L, 6);
+	bool oneSided = luax_optboolean(L, 7, false);
+
+	Body *body = nullptr;
+	luax_catchexcept(L, [&]() { body = instance()->newEdgeBody(world, btype, x1, y1, x2, y2, oneSided); });
+
+	luax_pushtype(L, body);
+	body->release();
+	return 1;
+}
+
+int w_newChainBody(lua_State *L)
+{
+	World *world = luax_checkworld(L, 1);
+
+	const char *typestr = luaL_checkstring(L, 2);
+	Body::Type btype = Body::BODY_STATIC;
+	if (!Body::getConstant(typestr, btype))
+		return luax_enumerror(L, "Body type", Body::getConstants(btype), typestr);
+
+	bool loop = luax_checkboolean(L, 3);
+
+	int argc = lua_gettop(L) - 3;
+
+	bool istable = lua_istable(L, 4);
+	if (istable)
+		argc = (int)luax_objlen(L, 4);
+
+	if (argc == 0 || argc % 2 != 0)
+		return luaL_error(L, "Number of vertex components must be a multiple of two.");
+
+	int vcount = argc / 2;
+	std::vector<Vector2> coords;
+
+	if (istable)
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			lua_rawgeti(L, 4, 1 + i * 2);
+			lua_rawgeti(L, 4, 2 + i * 2);
+			float x = (float)lua_tonumber(L, -2);
+			float y = (float)lua_tonumber(L, -1);
+			coords.emplace_back(x, y);
+			lua_pop(L, 2);
+		}
+	}
+	else
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			float x = (float)luaL_checknumber(L, 4 + i * 2);
+			float y = (float)luaL_checknumber(L, 5 + i * 2);
+			coords.emplace_back(x, y);
+		}
+	}
+
+	Body *body = nullptr;
+	luax_catchexcept(L, [&]() { body = instance()->newChainBody(world, btype, loop, coords.data(), (int)coords.size()); });
+
+	luax_pushtype(L, body);
+	body->release();
+	return 1;
+}
+
 int w_newFixture(lua_State *L)
 {
 	Body *body = luax_checkbody(L, 1);
@@ -103,7 +274,7 @@ int w_newCircleShape(lua_State *L)
 	{
 		float radius = (float)luaL_checknumber(L, 1);
 		CircleShape *shape;
-		luax_catchexcept(L, [&](){ shape = instance()->newCircleShape(radius); });
+		luax_catchexcept(L, [&](){ shape = instance()->newCircleShape(0, 0, radius); });
 		luax_pushtype(L, shape);
 		shape->release();
 		return 1;
@@ -132,7 +303,7 @@ int w_newRectangleShape(lua_State *L)
 		float w = (float)luaL_checknumber(L, 1);
 		float h = (float)luaL_checknumber(L, 2);
 		PolygonShape *shape;
-		luax_catchexcept(L, [&](){ shape = instance()->newRectangleShape(w, h); });
+		luax_catchexcept(L, [&](){ shape = instance()->newRectangleShape(0, 0, w, h, 0); });
 		luax_pushtype(L, shape);
 		shape->release();
 		return 1;
@@ -170,16 +341,91 @@ int w_newEdgeShape(lua_State *L)
 
 int w_newPolygonShape(lua_State *L)
 {
-	int ret = 0;
-	luax_catchexcept(L, [&](){ ret = instance()->newPolygonShape(L); });
-	return ret;
+	int argc = lua_gettop(L);
+
+	bool istable = lua_istable(L, 1);
+
+	if (istable)
+		argc = (int)luax_objlen(L, 1);
+
+	if (argc % 2 != 0)
+		return luaL_error(L, "Number of vertex components must be a multiple of two.");
+
+	int vcount = argc / 2;
+	std::vector<Vector2> coords;
+
+	if (istable)
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			lua_rawgeti(L, 1, 1 + i * 2);
+			lua_rawgeti(L, 1, 2 + i * 2);
+			float x = (float)luaL_checknumber(L, -2);
+			float y = (float)luaL_checknumber(L, -1);
+			coords.emplace_back(x, y);
+			lua_pop(L, 2);
+		}
+	}
+	else
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			float x = (float)luaL_checknumber(L, 1 + i * 2);
+			float y = (float)luaL_checknumber(L, 2 + i * 2);
+			coords.emplace_back(x, y);
+		}
+	}
+
+	PolygonShape *shape = nullptr;
+	luax_catchexcept(L, [&](){ shape = instance()->newPolygonShape(coords.data(), (int)coords.size()); });
+	luax_pushtype(L, shape);
+	shape->release();
+	return 1;
 }
 
 int w_newChainShape(lua_State *L)
 {
-	int ret = 0;
-	luax_catchexcept(L, [&](){ ret = instance()->newChainShape(L); });
-	return ret;
+	int argc = lua_gettop(L) - 1; // first argument is looping
+
+	bool istable = lua_istable(L, 2);
+
+	if (istable)
+		argc = (int)luax_objlen(L, 2);
+
+	if (argc == 0 || argc % 2 != 0)
+		return luaL_error(L, "Number of vertex components must be a multiple of two.");
+
+	int vcount = argc / 2;
+	bool loop = luax_checkboolean(L, 1);
+	std::vector<Vector2> coords;
+
+	if (istable)
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			lua_rawgeti(L, 2, 1 + i * 2);
+			lua_rawgeti(L, 2, 2 + i * 2);
+			float x = (float)lua_tonumber(L, -2);
+			float y = (float)lua_tonumber(L, -1);
+			coords.emplace_back(x, y);
+			lua_pop(L, 2);
+		}
+	}
+	else
+	{
+		for (int i = 0; i < vcount; i++)
+		{
+			float x = (float)luaL_checknumber(L, 2 + i * 2);
+			float y = (float)luaL_checknumber(L, 3 + i * 2);
+			coords.emplace_back(x, y);
+		}
+	}
+
+	ChainShape *shape = nullptr;
+	luax_catchexcept(L, [&]() { shape = instance()->newChainShape(loop, coords.data(), coords.size()); });
+	luax_pushtype(L, shape);
+	shape->release();
+	return 1;
 }
 
 int w_newDistanceJoint(lua_State *L)
@@ -573,6 +819,11 @@ static const luaL_Reg functions[] =
 {
 	{ "newWorld", w_newWorld },
 	{ "newBody", w_newBody },
+	{ "newCircleBody", w_newCircleBody },
+	{ "newRectangleBody", w_newRectangleBody },
+	{ "newPolygonBody", w_newPolygonBody },
+	{ "newEdgeBody", w_newEdgeBody },
+	{ "newChainBody", w_newChainBody },
 	{ "newFixture", w_newFixture },
 	{ "newCircleShape", w_newCircleShape },
 	{ "newRectangleShape", w_newRectangleShape },