blueloveTH 2 jaren geleden
bovenliggende
commit
e979ab7107

+ 119 - 23
3rd/box2d/include/box2d_bindings.hpp

@@ -3,28 +3,123 @@
 #include "box2d/b2_world.h"
 #include "box2d/box2d.h"
 #include "pocketpy/pocketpy.h"
-#include <cstdlib>
 
 namespace pkpy{
+    template<>
+    inline b2Vec2 py_cast<b2Vec2>(VM* vm, PyObject* obj){
+        Vec2 v = py_cast<Vec2>(vm, obj);
+        return b2Vec2(v.x, v.y);
+    }
 
-template<>
-inline b2Vec2 py_cast<b2Vec2>(VM* vm, PyObject* obj){
-    Vec2 v = py_cast<Vec2>(vm, obj);
-    return b2Vec2(v.x, v.y);
-}
+    template<>
+    inline b2Vec2 _py_cast<b2Vec2>(VM* vm, PyObject* obj){
+        Vec2 v = _py_cast<Vec2>(vm, obj);
+        return b2Vec2(v.x, v.y);
+    }
 
-template<>
-inline b2Vec2 _py_cast<b2Vec2>(VM* vm, PyObject* obj){
-    Vec2 v = _py_cast<Vec2>(vm, obj);
-    return b2Vec2(v.x, v.y);
+    inline PyObject* py_var(VM* vm, b2Vec2 v){
+        return py_var(vm, Vec2(v.x, v.y));
+    }
 }
 
-inline PyObject* py_var(VM* vm, b2Vec2 v){
-    return py_var(vm, Vec2(v.x, v.y));
-}
+using namespace pkpy;
 
 namespace imbox2d{
 
+// maybe we will use this class later
+struct PyDebugDraw: b2Draw{
+    PK_ALWAYS_PASS_BY_POINTER(PyDebugDraw)
+
+    VM* vm;
+    PyObject* draw_like;
+
+    PyDebugDraw(VM* vm): vm(vm){}
+
+    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
+    }
+
+    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
+    }
+
+    void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) override{
+    }
+
+    void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) override{
+    }
+
+    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override{
+    }
+
+    void DrawTransform(const b2Transform& xf) override{
+    }
+
+    void DrawPoint(const b2Vec2& p, float size, const b2Color& color) override{
+    }
+};
+
+struct PyContactListener: b2ContactListener{
+    PK_ALWAYS_PASS_BY_POINTER(PyContactListener)
+    VM* vm;
+    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<Body*>(a);
+        Body* bodyB = reinterpret_cast<Body*>(b);
+        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));
+    }
+
+	void BeginContact(b2Contact* contact) override {
+        DEF_SNAME(on_contact_begin);
+        _contact_f(contact, on_contact_begin);
+    }
+
+    void EndContact(b2Contact* contact) override {
+        DEF_SNAME(on_contact_end);
+        _contact_f(contact, on_contact_end);
+    }
+};
+
+struct PyBody{
+    PY_CLASS(PyBody, box2d, Body)
+    PK_ALWAYS_PASS_BY_POINTER(PyBody)
+
+    b2Body* body;
+    b2Fixture* fixture;
+    PyObject* node_like;
+
+    PyBody() = default;
+
+    void _gc_mark() {
+        PK_OBJ_MARK(node_like);
+    }
+
+    static void _register(VM* vm, PyObject* mod, PyObject* type);
+};
+
+struct PyWorld {
+    PY_CLASS(PyWorld, box2d, World)
+    PK_ALWAYS_PASS_BY_POINTER(PyWorld)
+
+    b2World world;
+    PyContactListener _contact_listener;
+    PyDebugDraw _debug_draw;
+
+    PyWorld(VM* vm);
+
+    void _gc_mark(){
+        PK_OBJ_MARK(_debug_draw.draw_like);
+    }
+
+    static void _register(VM* vm, PyObject* mod, PyObject* type);
+};
+
 
 struct Body final{
     b2Body* body;
@@ -128,17 +223,18 @@ struct Body final{
     }
 };
 
-struct PyBody: OpaquePointer<Body>{
-    PY_CLASS(PyBody, box2d, Body)
-
-    using OpaquePointer<Body>::OpaquePointer;
-    static void _register(VM* vm, PyObject* mod, PyObject* type);
-};
-
 
+inline PyObject* get_body_object(b2Body* p){
+    auto userdata = p->GetUserData().pointer;
+    return reinterpret_cast<PyObject*>(userdata);
+}
 
 }   // namespace imbox2d
 
-void add_module_box2d(VM* vm);
-
-}   // namespace pkpy
+namespace pkpy{
+    inline void add_module_box2d(VM* vm){
+        PyObject* mod = vm->new_module("box2d");
+        imbox2d::PyBody::register_class(vm, mod);
+        imbox2d::PyWorld::register_class(vm, mod);
+    }
+}

+ 28 - 0
3rd/box2d/src/box2d_Body.cpp

@@ -0,0 +1,28 @@
+#include "box2d/b2_world.h"
+#include "box2d/b2_world_callbacks.h"
+#include "box2d_bindings.hpp"
+
+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){
+            PyWorld& world = CAST(PyWorld&, args[1]);
+            PyObject* node = args[2];
+            PyObject* obj = VAR_T(PyBody, PyBody());
+            PyBody& body = _CAST(PyBody&, obj);
+            b2BodyDef def;
+            def.type = b2_dynamicBody;
+            // a weak reference to this object
+            def.userData.pointer = reinterpret_cast<uintptr_t>(obj);
+            body.body = world.world.CreateBody(&def);
+            body.fixture = nullptr;
+            body.node_like = node;
+            return obj;
+        });
+}
+
+}   // namespace imbox2d

+ 51 - 112
3rd/box2d/src/box2d_World.cpp

@@ -2,122 +2,47 @@
 #include "box2d/b2_world_callbacks.h"
 #include "box2d_bindings.hpp"
 
-namespace pkpy{
-    namespace imbox2d{
+using namespace pkpy;
+
+namespace imbox2d{
+
+struct MyRayCastCallback: b2RayCastCallback{
+    PK_ALWAYS_PASS_BY_POINTER(MyRayCastCallback)
 
-// This class captures the closest hit shape.
-class MyRayCastCallback : public b2RayCastCallback
-{
     VM* vm;
-public:
     List result;
     MyRayCastCallback(VM* vm): vm(vm) {}
  
-    float ReportFixture(b2Fixture* fixture, const b2Vec2& point,
-                        const b2Vec2& normal, float fraction)
-    {
-        auto userdata = fixture->GetBody()->GetUserData().pointer;
-        Body* body = reinterpret_cast<Body*>(userdata);
-        result.push_back(VAR_T(PyBody, body));
+    float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction){
+        result.push_back(get_body_object(fixture->GetBody()));
         // if(only_one) return 0;
         return fraction;
     }
 };
 
-class MyBoxCastCallback: public b2QueryCallback{
+struct MyBoxCastCallback: b2QueryCallback{
+    PK_ALWAYS_PASS_BY_POINTER(MyBoxCastCallback)
+
     VM* vm;
-public:
     List result;
     MyBoxCastCallback(VM* vm): vm(vm) {}
 
     bool ReportFixture(b2Fixture* fixture) override{
-        auto userdata = fixture->GetBody()->GetUserData().pointer;
-        Body* body = reinterpret_cast<Body*>(userdata);
-        result.push_back(VAR_T(PyBody, body));
+        result.push_back(get_body_object(fixture->GetBody()));
         return true;
     }
 };
 
-// maybe we will use this class later
-class PyDebugDraw: public b2Draw{
-    VM* vm;
-public:
-    PyDebugDraw(VM* vm): vm(vm){}
-
-    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
-    }
-
-    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
-    }
-
-    void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) override{
-    }
-
-    void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) override{
-    }
-
-    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override{
-    }
-
-    void DrawTransform(const b2Transform& xf) override{
-    }
-
-    void DrawPoint(const b2Vec2& p, float size, const b2Color& color) override{
-    }
-};
-
-class PyContactListener : public b2ContactListener{
-    VM* vm;
-public:
-    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<Body*>(a);
-        Body* bodyB = reinterpret_cast<Body*>(b);
-        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));
-    }
-
-	void BeginContact(b2Contact* contact) override {
-        DEF_SNAME(on_contact_begin);
-        _contact_f(contact, on_contact_begin);
-    }
-
-    void EndContact(b2Contact* contact) override {
-        DEF_SNAME(on_contact_end);
-        _contact_f(contact, on_contact_end);
-    }
-};
-
-// implement placement VAR_T...!!!!
-struct PyWorld {
-    PY_CLASS(PyWorld, box2d, World)
-
-    // this object is too large, so we use unique_ptr
-    std::unique_ptr<b2World> world;
-    std::unique_ptr<PyContactListener> _contact_listener;
-    std::unique_ptr<PyDebugDraw> _debug_draw;
-
-    PyWorld(VM* vm):
-            world(new b2World(b2Vec2(0, 0))),
-            _contact_listener(new PyContactListener(vm)),
-            _debug_draw(new PyDebugDraw(vm)){
-        world->SetAllowSleeping(true);
-        world->SetAutoClearForces(true);
-        world->SetContactListener(_contact_listener.get());
-        world->SetDebugDraw(_debug_draw.get());
-    }
-
-    PyWorld(const PyWorld&) = delete;
-    PyWorld& operator=(const PyWorld&) = delete;
+/****************** PyWorld ******************/
+PyWorld::PyWorld(VM* vm): world(b2Vec2(0, 0)), _contact_listener(vm), _debug_draw(vm){
+    _debug_draw.draw_like = vm->None;
+    world.SetAllowSleeping(true);
+    world.SetAutoClearForces(true);
+    world.SetContactListener(&_contact_listener);
+    world.SetDebugDraw(&_debug_draw);
+}
 
-    static void _register(VM* vm, PyObject* mod, PyObject* type){
+void PyWorld::_register(VM* vm, PyObject* mod, PyObject* type){
     vm->bind(type, "__new__(cls)", [](VM* vm, ArgsView args){
         return VAR_T(PyWorld, PyWorld(vm));
     });
@@ -125,20 +50,19 @@ struct PyWorld {
     // gravity
     vm->bind_property(type, "gravity", "vec2", [](VM* vm, ArgsView args){
         PyWorld& self = _CAST(PyWorld&, args[0]);
-        return VAR(self.world->GetGravity());
+        return VAR(self.world.GetGravity());
     }, [](VM* vm, ArgsView args){
         PyWorld& self = _CAST(PyWorld&, args[0]);
-        self.world->SetGravity(CAST(b2Vec2, args[1]));
+        self.world.SetGravity(CAST(b2Vec2, args[1]));
         return vm->None;
     });
 
     vm->bind(type, "get_bodies(self) -> list[Body]", [](VM* vm, ArgsView args){
         PyWorld& self = _CAST(PyWorld&, args[0]);
         List list;
-        b2Body* p = self.world->GetBodyList();
+        b2Body* p = self.world.GetBodyList();
         while(p != nullptr){
-            Body* body = (Body*)p->GetUserData().pointer;
-            list.push_back(VAR_T(PyBody, body));
+            list.push_back(get_body_object(p));
             p = p->GetNext();
         }
         return VAR(std::move(list));
@@ -150,7 +74,7 @@ struct PyWorld {
         b2Vec2 start = CAST(b2Vec2, args[1]);
         b2Vec2 end = CAST(b2Vec2, args[2]);
         MyRayCastCallback callback(vm);
-        self.world->RayCast(&callback, start, end);
+        self.world.RayCast(&callback, start, end);
         return VAR(std::move(callback.result));
     });
 
@@ -161,7 +85,7 @@ struct PyWorld {
         aabb.lowerBound = CAST(b2Vec2, args[1]);
         aabb.upperBound = CAST(b2Vec2, args[2]);
         MyBoxCastCallback callback(vm);
-        self.world->QueryAABB(&callback, aabb);
+        self.world.QueryAABB(&callback, aabb);
         return VAR(std::move(callback.result));
     });
 
@@ -174,21 +98,36 @@ struct PyWorld {
 
             auto f = [](VM* vm, b2Body* p, StrName name){
                 while(p != nullptr){
-                    Body* body = (Body*)p->GetUserData().pointer;
-                    vm->call_method(body->obj, name);
+                    PyObject* body_obj = get_body_object(p);
+                    PyBody& body = _CAST(PyBody&, body_obj);
+                    if(body.node_like != vm->None){
+                        vm->call_method(body.node_like, name);
+                    }
                     p = p->GetNext();
                 }
             };
 
             DEF_SNAME(on_box2d_pre_step);
             DEF_SNAME(on_box2d_post_step);
-            f(vm, self.world->GetBodyList(), on_box2d_pre_step);
-            self.world->Step(dt, velocity_iterations, position_iterations);
-            f(vm, self.world->GetBodyList(), on_box2d_post_step);
+            f(vm, self.world.GetBodyList(), on_box2d_pre_step);
+            self.world.Step(dt, velocity_iterations, position_iterations);
+            f(vm, self.world.GetBodyList(), on_box2d_post_step);
             return vm->None;
         });
-    }
-};
 
-    }   // namespace imbox2d
-}   // namespace pkpy
+    vm->bind(type, "debug_draw(self, flags: int)", [](VM* vm, ArgsView args){
+        PyWorld& self = _CAST(PyWorld&, args[0]);
+        int flags = CAST(int, args[1]);
+        self._debug_draw.SetFlags(flags);
+        self.world.DebugDraw();
+        return vm->None;
+    });
+
+    vm->bind(type, "set_debug_draw(self, draw: _DrawLike)", [](VM* vm, ArgsView args){
+        PyWorld& self = _CAST(PyWorld&, args[0]);
+        self._debug_draw.draw_like = args[1];
+        return vm->None;
+    });
+}
+
+}   // namespace imbox2d

+ 0 - 6
3rd/box2d/src/box2d_bindings.cpp

@@ -2,12 +2,6 @@
 
 namespace pkpy{
 
-void add_module_box2d(VM *vm){
-    PyObject* mod = vm->new_module("box2d");
-    imbox2d::PyBody::register_class(vm, mod);
-    imbox2d::PyWorld::register_class(vm, mod);
-}
-
 namespace imbox2d{
 
 

+ 6 - 0
include/pocketpy/common.h

@@ -164,4 +164,10 @@ struct is_pod {
 	static constexpr bool value = std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
 };
 
+#define PK_ALWAYS_PASS_BY_POINTER(T) \
+	T(const T&) = delete; \
+	T& operator=(const T&) = delete; \
+	T(T&&) = delete; \
+	T& operator=(T&&) = delete;
+
 } // namespace pkpy

+ 2 - 0
include/pocketpy/vm.h

@@ -107,6 +107,8 @@ struct FrameId{
 typedef void(*PrintFunc)(VM*, const Str&);
 
 class VM {
+    PK_ALWAYS_PASS_BY_POINTER(VM)
+    
     VM* vm;     // self reference for simplify code
 public:
     ManagedHeap heap;