diff --git a/src/modules/physics/box2d/Physics.cpp b/src/modules/physics/box2d/Physics.cpp index abf26e2da..5c3a134f9 100644 --- a/src/modules/physics/box2d/Physics.cpp +++ b/src/modules/physics/box2d/Physics.cpp @@ -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(newBody(world, x, y, type), Acquire::NORETAIN); + StrongRef shape(newCircleShape(0, 0, radius), Acquire::NORETAIN); + StrongRef 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(newBody(world, x, y, type), Acquire::NORETAIN); + StrongRef shape(newRectangleShape(0, 0, w, h, angle), Acquire::NORETAIN); + StrongRef 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 localcoords; + for (int i = 0; i < count; i++) + localcoords.push_back(coords[i] - origin); + + StrongRef body(newBody(world, origin.x, origin.y, type), Acquire::NORETAIN); + StrongRef shape(newPolygonShape(localcoords.data(), count), Acquire::NORETAIN); + StrongRef 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(newBody(world, wx, wy, type), Acquire::NORETAIN); + StrongRef shape(newEdgeShape(x1 - wx, y1 - wy, x2 - wx, y2 - wy, oneSided), Acquire::NORETAIN); + StrongRef 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 localcoords; + for (int i = 0; i < count; i++) + localcoords.push_back(coords[i] - origin); + + StrongRef body(newBody(world, origin.x, origin.y, type), Acquire::NORETAIN); + StrongRef shape(newChainShape(loop, localcoords.data(), count), Acquire::NORETAIN); + StrongRef 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 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) diff --git a/src/modules/physics/box2d/Physics.h b/src/modules/physics/box2d/Physics.h index 6d3305dcd..9c81ffe41 100644 --- a/src/modules/physics/box2d/Physics.h +++ b/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. diff --git a/src/modules/physics/box2d/wrap_Physics.cpp b/src/modules/physics/box2d/wrap_Physics.cpp index 29c3e2e62..7b755f92e 100644 --- a/src/modules/physics/box2d/wrap_Physics.cpp +++ b/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 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 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 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 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 },