diff --git a/3rd/box2d/include/box2d_bindings.hpp b/3rd/box2d/include/box2d_bindings.hpp index 2eb1c585..798e14bd 100644 --- a/3rd/box2d/include/box2d_bindings.hpp +++ b/3rd/box2d/include/box2d_bindings.hpp @@ -26,6 +26,11 @@ using namespace pkpy; namespace imbox2d{ +inline PyObject* get_body_object(b2Body* p){ + auto userdata = p->GetUserData().pointer; + return reinterpret_cast(userdata); +} + // maybe we will use this class later struct PyDebugDraw: b2Draw{ PK_ALWAYS_PASS_BY_POINTER(PyDebugDraw) @@ -63,16 +68,16 @@ struct PyContactListener: b2ContactListener{ PyContactListener(VM* vm): vm(vm){} void _contact_f(b2Contact* contact, StrName name){ - auto a = contact->GetFixtureA()->GetBody()->GetUserData().pointer; - auto b = contact->GetFixtureB()->GetBody()->GetUserData().pointer; - Body* bodyA = reinterpret_cast(a); - Body* bodyB = reinterpret_cast(b); + b2Body* bodyA = contact->GetFixtureA()->GetBody(); + b2Body* bodyB = contact->GetFixtureB()->GetBody(); + PyObject* a = get_body_object(bodyA); + PyObject* b = get_body_object(bodyB); PyObject* self; PyObject* f; - f = vm->get_unbound_method(bodyA->obj, name, &self, false); - if(f != nullptr) vm->call_method(self, f, VAR_T(PyBody, bodyB)); - f = vm->get_unbound_method(bodyB->obj, name, &self, false); - if(f != nullptr) vm->call_method(self, f, VAR_T(PyBody, bodyA)); + f = vm->get_unbound_method(a, name, &self, false); + if(f != nullptr) vm->call_method(self, f, b); + f = vm->get_unbound_method(b, name, &self, false); + if(f != nullptr) vm->call_method(self, f, a); } void BeginContact(b2Contact* contact) override { @@ -100,7 +105,31 @@ struct PyBody{ PK_OBJ_MARK(node_like); } + PyBody& _() { return *this; } + b2Body& _b2Body() { return *body; } + b2Fixture& _b2Fixture() { return *fixture; } + static void _register(VM* vm, PyObject* mod, PyObject* type); + + // methods + + b2Vec2 get_position() const { return body->GetPosition(); } + void set_position(b2Vec2 v){ body->SetTransform(v, body->GetAngle()); } + float get_rotation() const { return body->GetAngle(); } + void set_rotation(float v){ body->SetTransform(body->GetPosition(), v); } + + void apply_force(b2Vec2 force, b2Vec2 point){ body->ApplyForce(force, point, true); } + void apply_force_to_center(b2Vec2 force){ body->ApplyForceToCenter(force, true); } + void apply_torque(float torque){ body->ApplyTorque(torque, true); } + void apply_impulse(b2Vec2 impulse, b2Vec2 point){ + body->ApplyLinearImpulse(impulse, point, true); + } + void apply_impulse_to_center(b2Vec2 impulse){ + body->ApplyLinearImpulseToCenter(impulse, true); + } + void apply_angular_impulse(float impulse){ + body->ApplyAngularImpulse(impulse, true); + } }; struct PyWorld { @@ -223,12 +252,6 @@ struct Body final{ } }; - -inline PyObject* get_body_object(b2Body* p){ - auto userdata = p->GetUserData().pointer; - return reinterpret_cast(userdata); -} - } // namespace imbox2d namespace pkpy{ diff --git a/3rd/box2d/src/box2d_Body.cpp b/3rd/box2d/src/box2d_Body.cpp index 62c6490d..9f80f8fa 100644 --- a/3rd/box2d/src/box2d_Body.cpp +++ b/3rd/box2d/src/box2d_Body.cpp @@ -1,12 +1,12 @@ #include "box2d/b2_world.h" #include "box2d/b2_world_callbacks.h" #include "box2d_bindings.hpp" +#include "pocketpy/bindings.h" using namespace pkpy; namespace imbox2d{ - void PyBody::_register(VM* vm, PyObject* mod, PyObject* type){ vm->bind(type, "__new__(cls, world: World, node: _NodeLike = None)", [](VM* vm, ArgsView args){ @@ -23,6 +23,66 @@ void PyBody::_register(VM* vm, PyObject* mod, PyObject* type){ body.node_like = node; return obj; }); + + PK_REGISTER_PROPERTY(PyBody, "type: int", _b2Body, GetType, SetType) + PK_REGISTER_PROPERTY(PyBody, "gravity_scale: float", _b2Body, GetGravityScale, SetGravityScale) + PK_REGISTER_PROPERTY(PyBody, "fixed_rotation: bool", _b2Body, IsFixedRotation, SetFixedRotation) + PK_REGISTER_PROPERTY(PyBody, "enabled: bool", _b2Body, IsEnabled, SetEnabled) + PK_REGISTER_PROPERTY(PyBody, "bullet: bool", _b2Body, IsBullet, SetBullet) + + PK_REGISTER_READONLY_PROPERTY(PyBody, "mass: float", _b2Body, GetMass) + PK_REGISTER_READONLY_PROPERTY(PyBody, "inertia: float", _b2Body, GetInertia) + + PK_REGISTER_PROPERTY(PyBody, "position: vec2", _, get_position, set_position) + PK_REGISTER_PROPERTY(PyBody, "rotation: float", _, get_rotation, set_rotation) + PK_REGISTER_PROPERTY(PyBody, "velocity: vec2", _b2Body, GetLinearVelocity, SetLinearVelocity) + PK_REGISTER_PROPERTY(PyBody, "angular_velocity: float", _b2Body, GetAngularVelocity, SetAngularVelocity) + PK_REGISTER_PROPERTY(PyBody, "damping: float", _b2Body, GetLinearDamping, SetLinearDamping) + PK_REGISTER_PROPERTY(PyBody, "angular_damping: float", _b2Body, GetAngularDamping, SetAngularDamping) + + PK_REGISTER_PROPERTY(PyBody, "density: float", _b2Fixture, GetDensity, SetDensity) + PK_REGISTER_PROPERTY(PyBody, "friction: float", _b2Fixture, GetFriction, SetFriction) + PK_REGISTER_PROPERTY(PyBody, "restitution: float", _b2Fixture, GetRestitution, SetRestitution) + PK_REGISTER_PROPERTY(PyBody, "restitution_threshold: float", _b2Fixture, GetRestitutionThreshold, SetRestitutionThreshold) + PK_REGISTER_PROPERTY(PyBody, "is_trigger: bool", _b2Fixture, IsSensor, SetSensor) + + // methods + _bind(vm, type, "apply_force(self, force: vec2, point: vec2)", &PyBody::apply_force); + _bind(vm, type, "apply_force_to_center(self, force: vec2)", &PyBody::apply_force_to_center); + _bind(vm, type, "apply_torque(self, torque: float)", &PyBody::apply_torque); + _bind(vm, type, "apply_impulse(self, impulse: vec2, point: vec2)", &PyBody::apply_impulse); + _bind(vm, type, "apply_impulse_to_center(self, impulse: vec2)", &PyBody::apply_impulse_to_center); + _bind(vm, type, "apply_angular_impulse(self, impulse: float)", &PyBody::apply_angular_impulse); + + // get_node + vm->bind(type, "get_node(self)", [](VM* vm, ArgsView args){ + PyBody& body = CAST(PyBody&, args[1]); + return body.node_like; + }); + + // get_contacts + vm->bind(type, "get_contacts(self) -> list[Body]", [](VM* vm, ArgsView args){ + PyBody& self = _CAST(PyBody&, args[0]); + b2ContactEdge* edge = self.body->GetContactList(); + List list; + while(edge){ + b2Fixture* fixtureB = edge->contact->GetFixtureB(); + b2Body* bodyB = fixtureB->GetBody(); + list.push_back(get_body_object(bodyB)); + edge = edge->next; + } + return VAR(std::move(list)); + }); + + // destroy + vm->bind(type, "destroy(self)", [](VM* vm, ArgsView args){ + PyBody& body = CAST(PyBody&, args[1]); + body.body->GetWorld()->DestroyBody(body.body); + body.body = nullptr; + body.fixture = nullptr; + body.node_like = nullptr; + return vm->None; + }); } } // namespace imbox2d \ No newline at end of file diff --git a/3rd/box2d/src/box2d_World.cpp b/3rd/box2d/src/box2d_World.cpp index 5bcd6daf..4acb0003 100644 --- a/3rd/box2d/src/box2d_World.cpp +++ b/3rd/box2d/src/box2d_World.cpp @@ -48,7 +48,7 @@ void PyWorld::_register(VM* vm, PyObject* mod, PyObject* type){ }); // gravity - vm->bind_property(type, "gravity", "vec2", [](VM* vm, ArgsView args){ + vm->bind_property(type, "gravity: vec2", [](VM* vm, ArgsView args){ PyWorld& self = _CAST(PyWorld&, args[0]); return VAR(self.world.GetGravity()); }, [](VM* vm, ArgsView args){ diff --git a/3rd/box2d/src/box2d_bindings.cpp b/3rd/box2d/src/box2d_bindings.cpp deleted file mode 100644 index d70fa187..00000000 --- a/3rd/box2d/src/box2d_bindings.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "box2d_bindings.hpp" - -namespace pkpy{ - -namespace imbox2d{ - - - - void PyBody::_register(VM* vm, PyObject* mod, PyObject* type){ - vm->bind_notimplemented_constructor(type); - PK_REGISTER_READONLY_PROPERTY(PyBody, debug_color, "vec4"); - - PK_REGISTER_PROPERTY(PyBody, position, "vec2"); - PK_REGISTER_PROPERTY(PyBody, rotation, "float"); - PK_REGISTER_PROPERTY(PyBody, velocity, "vec2"); - PK_REGISTER_PROPERTY(PyBody, angular_velocity, "float"); - PK_REGISTER_PROPERTY(PyBody, damping, "float"); - PK_REGISTER_PROPERTY(PyBody, angular_damping, "float"); - PK_REGISTER_PROPERTY(PyBody, gravity_scale, "float"); - PK_REGISTER_PROPERTY(PyBody, type, "int"); - PK_REGISTER_READONLY_PROPERTY(PyBody, mass, "float"); - PK_REGISTER_READONLY_PROPERTY(PyBody, inertia, "float"); - - // fixture settings - PK_REGISTER_PROPERTY(PyBody, density, "float"); - PK_REGISTER_PROPERTY(PyBody, friction, "float"); - PK_REGISTER_PROPERTY(PyBody, restitution, "float"); - PK_REGISTER_PROPERTY(PyBody, restitution_threshold, "float"); - PK_REGISTER_PROPERTY(PyBody, is_trigger, "bool"); - - // methods - _bind_opaque(vm, type, "apply_force(self, force: vec2, point: vec2)", &Body::apply_force); - _bind_opaque(vm, type, "apply_force_to_center(self, force: vec2)", &Body::apply_force_to_center); - _bind_opaque(vm, type, "apply_torque(self, torque: float)", &Body::apply_torque); - _bind_opaque(vm, type, "apply_linear_impulse(self, impulse: vec2, point: vec2)", &Body::apply_linear_impulse); - _bind_opaque(vm, type, "apply_linear_impulse_to_center(self, impulse: vec2)", &Body::apply_linear_impulse_to_center); - _bind_opaque(vm, type, "apply_angular_impulse(self, impulse: float)", &Body::apply_angular_impulse); - - vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){ - PyBody& self = _CAST(PyBody&, lhs); - if(is_non_tagged_type(rhs, PyBody::_type(vm))) return vm->NotImplemented; - PyBody& other = _CAST(PyBody&, rhs); - return VAR(self->body == other->body); - }); - - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ - PyBody& self = _CAST(PyBody&, obj); - return VAR(fmt("body, ">")); - }); - - // destroy - _bind_opaque(vm, type, "destroy(self)", &Body::destroy); - - // contacts - vm->bind(type, "get_contacts(self) -> list", [](VM* vm, ArgsView args){ - PyBody& self = _CAST(PyBody&, args[0]); - b2ContactEdge* edge = self->body->GetContactList(); - List list; - while(edge){ - b2Fixture* fixtureB = edge->contact->GetFixtureB(); - b2Body* bodyB = fixtureB->GetBody(); - PyObject* objB = reinterpret_cast(bodyB->GetUserData().pointer)->obj; - list.push_back(objB); - edge = edge->next; - } - return VAR(std::move(list)); - }); - - // userdata - vm->bind(type, "get_node(self)", [](VM* vm, ArgsView args){ - PyBody& self = _CAST(PyBody&, args[0]); - return self->obj; - }); - - // shape - vm->bind(type, "set_box_shape(self, hx: float, hy: float)", [](VM* vm, ArgsView args){ - PyBody& self = _CAST(PyBody&, args[0]); - float hx = CAST(float, args[1]); - float hy = CAST(float, args[2]); - b2PolygonShape shape; - shape.SetAsBox(hx, hy); - self->_update_fixture(&shape); - return vm->None; - }); - - vm->bind(type, "set_circle_shape(self, radius: float)", [](VM* vm, ArgsView args){ - PyBody& self = _CAST(PyBody&, args[0]); - float radius = CAST(float, args[1]); - b2CircleShape shape; - shape.m_radius = radius; - self->_update_fixture(&shape); - return vm->None; - }); - - vm->bind(type, "set_polygon_shape(self, points: list[vec2])", [](VM* vm, ArgsView args){ - PyBody& self = _CAST(PyBody&, args[0]); - List& points = CAST(List&, args[1]); - if(points.size() > b2_maxPolygonVertices || points.size() < 3){ - vm->ValueError(fmt("invalid polygon vertices count: ", points.size())); - return vm->None; - } - std::vector vertices(points.size()); - for(int i = 0; i < points.size(); ++i){ - vertices[i] = CAST(b2Vec2, points[i]); - } - b2PolygonShape shape; - shape.Set(vertices.data(), vertices.size()); - self->_update_fixture(&shape); - return vm->None; - }); - - vm->bind(type, "set_chain_shape(self, points: list[vec2])", [](VM* vm, ArgsView args){ - PyBody& self = _CAST(PyBody&, args[0]); - List& points = CAST(List&, args[1]); - std::vector vertices(points.size()); - for(int i = 0; i < points.size(); ++i){ - vertices[i] = CAST(b2Vec2, points[i]); - } - b2ChainShape shape; - shape.CreateLoop(vertices.data(), vertices.size()); - self->_update_fixture(&shape); - return vm->None; - }); - - vm->bind(type, "get_shape_info(self) -> tuple", [](VM* vm, ArgsView args){ - PyBody& self = _CAST(PyBody&, args[0]); - b2Shape* shape = self->fixture->GetShape(); - switch(shape->GetType()){ - case b2Shape::e_polygon:{ - b2PolygonShape* poly = static_cast(shape); - Tuple points(poly->m_count + 1); - for(int i = 0; i < poly->m_count; ++i){ - points[i] = VAR(poly->m_vertices[i]); - } - points[poly->m_count] = points[0]; - return VAR(Tuple({ - VAR("polygon"), VAR(std::move(points)) - })); - } - case b2Shape::e_circle:{ - b2CircleShape* circle = static_cast(shape); - return VAR(Tuple({ - VAR("circle"), VAR(circle->m_radius) - })); - } - case b2Shape::e_chain:{ - b2ChainShape* chain = static_cast(shape); - Tuple points(chain->m_count); - for(int i = 0; i < chain->m_count; ++i){ - points[i] = VAR(chain->m_vertices[i]); - } - return VAR(Tuple({ - VAR("chain"), VAR(std::move(points)) - })); - } - default: - vm->ValueError("unsupported shape type"); - return vm->None; - } - }); - } - - - - - - -} - -} // namespace pkpy \ No newline at end of file diff --git a/docs/modules/box2d.md b/docs/modules/box2d.md index 92319377..b4cfa410 100644 --- a/docs/modules/box2d.md +++ b/docs/modules/box2d.md @@ -90,13 +90,16 @@ class World: class Body: type: int # 0-static, 1-kinematic, 2-dynamic, by default 2 - mass: float - inertia: float gravity_scale: float fixed_rotation: bool enabled: bool bullet: bool # whether to use continuous collision detection + @property + def mass(self) -> float: ... + @property + def inertia(self) -> float: ... + position: vec2 rotation: float # in radians (counter-clockwise) velocity: vec2 # linear velocity