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.
This commit is contained in:
Sasha Szpakowski
2023-10-04 19:56:22 -03:00
parent 03b6b9ff13
commit 51b439822b
3 changed files with 350 additions and 129 deletions
+78 -98
View File
@@ -63,9 +63,69 @@ 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();
}
Body *Physics::newRectangleBody(World *world, Body::Type type, float x, float y, float w, float h, float angle)
{
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();
}
Body *Physics::newEdgeBody(World *world, Body::Type type, float x1, float y1, float x2, float y2, bool oneSided)
{
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();
}
Body *Physics::newChainBody(World *world, Body::Type type, bool loop, 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<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)
@@ -76,16 +136,6 @@ CircleShape *Physics::newCircleShape(float x, float y, float radius)
return new CircleShape(s);
}
PolygonShape *Physics::newRectangleShape(float w, float h)
{
return newRectangleShape(0, 0, w, h, 0);
}
PolygonShape *Physics::newRectangleShape(float x, float y, float w, float h)
{
return newRectangleShape(x, y, w, h, 0);
}
PolygonShape *Physics::newRectangleShape(float x, float y, float w, float h, float angle)
{
b2PolygonShape *s = new b2PolygonShape();
@@ -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
View File
@@ -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
View File
@@ -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 },