box2d_bindings.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. #include "box2d_bindings.hpp"
  2. namespace pkpy{
  3. void add_module_box2d(VM *vm){
  4. PyObject* mod = vm->new_module("box2d");
  5. imbox2d::PyBody::register_class(vm, mod);
  6. imbox2d::PyWorld::register_class(vm, mod);
  7. }
  8. namespace imbox2d{
  9. // maybe we will use this class later
  10. class PyDebugDraw: public b2Draw{
  11. VM* vm;
  12. public:
  13. PyDebugDraw(VM* vm): vm(vm){}
  14. void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
  15. }
  16. void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
  17. }
  18. void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) override{
  19. }
  20. void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) override{
  21. }
  22. void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override{
  23. }
  24. void DrawTransform(const b2Transform& xf) override{
  25. }
  26. void DrawPoint(const b2Vec2& p, float size, const b2Color& color) override{
  27. }
  28. };
  29. class PyContactListener : public b2ContactListener{
  30. VM* vm;
  31. public:
  32. PyContactListener(VM* vm): vm(vm){}
  33. void _contact_f(b2Contact* contact, StrName name){
  34. auto a = contact->GetFixtureA()->GetBody()->GetUserData().pointer;
  35. auto b = contact->GetFixtureB()->GetBody()->GetUserData().pointer;
  36. Body* bodyA = reinterpret_cast<Body*>(a);
  37. Body* bodyB = reinterpret_cast<Body*>(b);
  38. PyObject* self;
  39. PyObject* f;
  40. f = vm->get_unbound_method(bodyA->obj, name, &self, false);
  41. if(f != nullptr) vm->call_method(self, f, VAR_T(PyBody, bodyB));
  42. f = vm->get_unbound_method(bodyB->obj, name, &self, false);
  43. if(f != nullptr) vm->call_method(self, f, VAR_T(PyBody, bodyA));
  44. }
  45. void BeginContact(b2Contact* contact) override {
  46. DEF_SNAME(on_contact_begin);
  47. _contact_f(contact, on_contact_begin);
  48. }
  49. void EndContact(b2Contact* contact) override {
  50. DEF_SNAME(on_contact_end);
  51. _contact_f(contact, on_contact_end);
  52. }
  53. };
  54. void PyBody::_register(VM* vm, PyObject* mod, PyObject* type){
  55. vm->bind_notimplemented_constructor<PyBody>(type);
  56. PK_REGISTER_READONLY_PROPERTY(PyBody, debug_color, "vec4");
  57. PK_REGISTER_PROPERTY(PyBody, position, "vec2");
  58. PK_REGISTER_PROPERTY(PyBody, rotation, "float");
  59. PK_REGISTER_PROPERTY(PyBody, velocity, "vec2");
  60. PK_REGISTER_PROPERTY(PyBody, angular_velocity, "float");
  61. PK_REGISTER_PROPERTY(PyBody, damping, "float");
  62. PK_REGISTER_PROPERTY(PyBody, angular_damping, "float");
  63. PK_REGISTER_PROPERTY(PyBody, gravity_scale, "float");
  64. PK_REGISTER_PROPERTY(PyBody, type, "int");
  65. PK_REGISTER_READONLY_PROPERTY(PyBody, mass, "float");
  66. PK_REGISTER_READONLY_PROPERTY(PyBody, inertia, "float");
  67. // fixture settings
  68. PK_REGISTER_PROPERTY(PyBody, density, "float");
  69. PK_REGISTER_PROPERTY(PyBody, friction, "float");
  70. PK_REGISTER_PROPERTY(PyBody, restitution, "float");
  71. PK_REGISTER_PROPERTY(PyBody, restitution_threshold, "float");
  72. PK_REGISTER_PROPERTY(PyBody, is_trigger, "bool");
  73. // methods
  74. _bind_opaque<PyBody>(vm, type, "apply_force(self, force: vec2, point: vec2)", &Body::apply_force);
  75. _bind_opaque<PyBody>(vm, type, "apply_force_to_center(self, force: vec2)", &Body::apply_force_to_center);
  76. _bind_opaque<PyBody>(vm, type, "apply_torque(self, torque: float)", &Body::apply_torque);
  77. _bind_opaque<PyBody>(vm, type, "apply_linear_impulse(self, impulse: vec2, point: vec2)", &Body::apply_linear_impulse);
  78. _bind_opaque<PyBody>(vm, type, "apply_linear_impulse_to_center(self, impulse: vec2)", &Body::apply_linear_impulse_to_center);
  79. _bind_opaque<PyBody>(vm, type, "apply_angular_impulse(self, impulse: float)", &Body::apply_angular_impulse);
  80. vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
  81. PyBody& self = _CAST(PyBody&, lhs);
  82. if(is_non_tagged_type(rhs, PyBody::_type(vm))) return vm->NotImplemented;
  83. PyBody& other = _CAST(PyBody&, rhs);
  84. return VAR(self->body == other->body);
  85. });
  86. vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
  87. PyBody& self = _CAST(PyBody&, obj);
  88. return VAR(fmt("<Body* at ", self->body, ">"));
  89. });
  90. // destroy
  91. _bind_opaque<PyBody>(vm, type, "destroy(self)", &Body::destroy);
  92. // contacts
  93. vm->bind(type, "get_contacts(self) -> list", [](VM* vm, ArgsView args){
  94. PyBody& self = _CAST(PyBody&, args[0]);
  95. b2ContactEdge* edge = self->body->GetContactList();
  96. List list;
  97. while(edge){
  98. b2Fixture* fixtureB = edge->contact->GetFixtureB();
  99. b2Body* bodyB = fixtureB->GetBody();
  100. PyObject* objB = reinterpret_cast<Body*>(bodyB->GetUserData().pointer)->obj;
  101. list.push_back(objB);
  102. edge = edge->next;
  103. }
  104. return VAR(std::move(list));
  105. });
  106. // userdata
  107. vm->bind(type, "get_node(self)", [](VM* vm, ArgsView args){
  108. PyBody& self = _CAST(PyBody&, args[0]);
  109. return self->obj;
  110. });
  111. // shape
  112. vm->bind(type, "set_box_shape(self, hx: float, hy: float)", [](VM* vm, ArgsView args){
  113. PyBody& self = _CAST(PyBody&, args[0]);
  114. float hx = CAST(float, args[1]);
  115. float hy = CAST(float, args[2]);
  116. b2PolygonShape shape;
  117. shape.SetAsBox(hx, hy);
  118. self->_update_fixture(&shape);
  119. return vm->None;
  120. });
  121. vm->bind(type, "set_circle_shape(self, radius: float)", [](VM* vm, ArgsView args){
  122. PyBody& self = _CAST(PyBody&, args[0]);
  123. float radius = CAST(float, args[1]);
  124. b2CircleShape shape;
  125. shape.m_radius = radius;
  126. self->_update_fixture(&shape);
  127. return vm->None;
  128. });
  129. vm->bind(type, "set_polygon_shape(self, points: list[vec2])", [](VM* vm, ArgsView args){
  130. PyBody& self = _CAST(PyBody&, args[0]);
  131. List& points = CAST(List&, args[1]);
  132. if(points.size() > b2_maxPolygonVertices || points.size() < 3){
  133. vm->ValueError(fmt("invalid polygon vertices count: ", points.size()));
  134. return vm->None;
  135. }
  136. std::vector<b2Vec2> vertices(points.size());
  137. for(int i = 0; i < points.size(); ++i){
  138. vertices[i] = CAST(b2Vec2, points[i]);
  139. }
  140. b2PolygonShape shape;
  141. shape.Set(vertices.data(), vertices.size());
  142. self->_update_fixture(&shape);
  143. return vm->None;
  144. });
  145. vm->bind(type, "set_chain_shape(self, points: list[vec2])", [](VM* vm, ArgsView args){
  146. PyBody& self = _CAST(PyBody&, args[0]);
  147. List& points = CAST(List&, args[1]);
  148. std::vector<b2Vec2> vertices(points.size());
  149. for(int i = 0; i < points.size(); ++i){
  150. vertices[i] = CAST(b2Vec2, points[i]);
  151. }
  152. b2ChainShape shape;
  153. shape.CreateLoop(vertices.data(), vertices.size());
  154. self->_update_fixture(&shape);
  155. return vm->None;
  156. });
  157. vm->bind(type, "get_shape_info(self) -> tuple", [](VM* vm, ArgsView args){
  158. PyBody& self = _CAST(PyBody&, args[0]);
  159. b2Shape* shape = self->fixture->GetShape();
  160. switch(shape->GetType()){
  161. case b2Shape::e_polygon:{
  162. b2PolygonShape* poly = static_cast<b2PolygonShape*>(shape);
  163. Tuple points(poly->m_count + 1);
  164. for(int i = 0; i < poly->m_count; ++i){
  165. points[i] = VAR(poly->m_vertices[i]);
  166. }
  167. points[poly->m_count] = points[0];
  168. return VAR(Tuple({
  169. VAR("polygon"), VAR(std::move(points))
  170. }));
  171. }
  172. case b2Shape::e_circle:{
  173. b2CircleShape* circle = static_cast<b2CircleShape*>(shape);
  174. return VAR(Tuple({
  175. VAR("circle"), VAR(circle->m_radius)
  176. }));
  177. }
  178. case b2Shape::e_chain:{
  179. b2ChainShape* chain = static_cast<b2ChainShape*>(shape);
  180. Tuple points(chain->m_count);
  181. for(int i = 0; i < chain->m_count; ++i){
  182. points[i] = VAR(chain->m_vertices[i]);
  183. }
  184. return VAR(Tuple({
  185. VAR("chain"), VAR(std::move(points))
  186. }));
  187. }
  188. default:
  189. vm->ValueError("unsupported shape type");
  190. return vm->None;
  191. }
  192. });
  193. }
  194. // This class captures the closest hit shape.
  195. class MyRayCastCallback : public b2RayCastCallback
  196. {
  197. VM* vm;
  198. public:
  199. List result;
  200. MyRayCastCallback(VM* vm): vm(vm) {}
  201. float ReportFixture(b2Fixture* fixture, const b2Vec2& point,
  202. const b2Vec2& normal, float fraction)
  203. {
  204. auto userdata = fixture->GetBody()->GetUserData().pointer;
  205. Body* body = reinterpret_cast<Body*>(userdata);
  206. result.push_back(VAR_T(PyBody, body));
  207. // if(only_one) return 0;
  208. return fraction;
  209. }
  210. };
  211. void PyWorld::_register(VM* vm, PyObject* mod, PyObject* type){
  212. vm->bind(type, "__new__(cls)", [](VM* vm, ArgsView args){
  213. b2World* w = new b2World(b2Vec2(0, 0));
  214. w->SetAllowSleeping(true);
  215. w->SetAutoClearForces(true);
  216. // the contact listener will leak memory after the world is destroyed
  217. // but it's not a big deal since the world is only destroyed when the game exits
  218. w->SetContactListener(new PyContactListener(vm));
  219. w->SetDebugDraw(new PyDebugDraw(vm));
  220. return VAR_T(PyWorld, w);
  221. });
  222. // gravity
  223. vm->bind_property(type, "gravity", "vec2", [](VM* vm, ArgsView args){
  224. PyWorld& self = _CAST(PyWorld&, args[0]);
  225. return VAR(self->GetGravity());
  226. }, [](VM* vm, ArgsView args){
  227. PyWorld& self = _CAST(PyWorld&, args[0]);
  228. self->SetGravity(CAST(b2Vec2, args[1]));
  229. return vm->None;
  230. });
  231. // body
  232. vm->bind(type, "create_body(self, obj) -> Body", [](VM* vm, ArgsView args){
  233. PyWorld& self = _CAST(PyWorld&, args[0]);
  234. return VAR_T(PyBody, new Body(self.ptr, args[1]));
  235. });
  236. vm->bind(type, "get_bodies(self) -> list[Body]", [](VM* vm, ArgsView args){
  237. PyWorld& self = _CAST(PyWorld&, args[0]);
  238. List list;
  239. b2Body* p = self->GetBodyList();
  240. while(p != nullptr){
  241. Body* body = (Body*)p->GetUserData().pointer;
  242. list.push_back(VAR_T(PyBody, body));
  243. p = p->GetNext();
  244. }
  245. return VAR(std::move(list));
  246. });
  247. // step
  248. vm->bind(type, "step(self, dt: float, velocity_iterations: int, position_iterations: int)",
  249. [](VM* vm, ArgsView args){
  250. PyWorld& self = _CAST(PyWorld&, args[0]);
  251. float dt = CAST(float, args[1]);
  252. int velocity_iterations = CAST(int, args[2]);
  253. int position_iterations = CAST(int, args[3]);
  254. auto f = [](VM* vm, b2Body* p, StrName name){
  255. while(p != nullptr){
  256. Body* body = (Body*)p->GetUserData().pointer;
  257. vm->call_method(body->obj, name);
  258. p = p->GetNext();
  259. }
  260. };
  261. DEF_SNAME(on_box2d_pre_step);
  262. DEF_SNAME(on_box2d_post_step);
  263. f(vm, self->GetBodyList(), on_box2d_pre_step);
  264. self->Step(dt, velocity_iterations, position_iterations);
  265. f(vm, self->GetBodyList(), on_box2d_post_step);
  266. return vm->None;
  267. });
  268. // raycast
  269. vm->bind(type, "raycast(self, start: vec2, end: vec2) -> list[Body]", [](VM* vm, ArgsView args){
  270. auto _lock = vm->heap.gc_scope_lock();
  271. PyWorld& self = _CAST(PyWorld&, args[0]);
  272. b2Vec2 start = CAST(b2Vec2, args[1]);
  273. b2Vec2 end = CAST(b2Vec2, args[2]);
  274. MyRayCastCallback callback(vm);
  275. self->RayCast(&callback, start, end);
  276. return VAR(std::move(callback.result));
  277. });
  278. }
  279. }
  280. } // namespace pkpy