piqnt / planck.js

2D JavaScript Physics Engine
http://piqnt.com/planck.js/
MIT License
4.87k stars 235 forks source link

Feature Request: Implementation of b2Draw, SetDebugDraw and DebugDraw #228

Closed 8Observer8 closed 12 months ago

8Observer8 commented 1 year ago

Please, implement b2Draw, SetDebugDraw, and DebugDraw like it was implemented in pybox2d

edit-gravity-debug-drawer-opengl3-pyqt6

from Box2D import b2Draw
from OpenGL.GL import *

class DebugDrawer(b2Draw):

    def __init__(self, program, worldScale):
        super().__init__()
        self.program = program
        self.WORLD_SCALE = worldScale

    def DrawSolidPolygon(self, vertexes, color):
        print("Polygon. Begin")
        print(vertexes[0][0] * 30, vertexes[0][1] * 30)
        print(vertexes[1][0] * 30, vertexes[1][1] * 30)
        print(vertexes[2][0] * 30, vertexes[2][1] * 30)
        print(vertexes[3][0] * 30, vertexes[3][1] * 30)
        print("Polygon. End")
        self.debugDrawer = DebugDrawer(self.program, self.WORLD_SCALE)
        self.world.renderer = self.debugDrawer
        self.debugDrawer.flags = { 'drawShapes': True,
            'drawJoints': True, 'drawAABBs': True, 'drawPairs': True }
    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT)
        glBindVertexArray(self.vao)
        self.projViewMatrix = self.projMatrix * self.viewMatrix

        if self.showColliders:
            self.world.DrawDebugData()
8Observer8 commented 1 year ago

@box2d/core already has the b2Draw class: https://github.com/Lusito/box2d.ts/issues/33#issuecomment-1387710668 but doesn't support JavaScript. I think you can get the b2Draw implementation from @box2d/core.

8Observer8 commented 1 year ago

box2d-wasm has the b2Draw class with JavaScript and ES6 module support.

Official example: https://github.com/Birch-san/box2d-wasm/tree/master/demo/modern/public

My example:

<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <!-- Since import maps are not yet supported by all browsers, it is
        necessary to add the polyfill es-module-shims.js
        Source: https://threejs.org/docs/index.html#manual/en/introduction/Installation
    -->
    <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

    <script type="importmap">
        {
            "imports": {
                "gl-matrix": "https://cdn.skypack.dev/gl-matrix@3.4.3",
                "box2d": "https://8observer8.github.io/lib/box2d-wasm-2.4.1/box2d-wasm-2.4.1.min.js"
            }
        }
    </script>

    <script type="module">
        import Box2DLib from "box2d";
        let box2d;

        function initBox2D() {
            return new Promise(resolve => {
                Box2DLib().then((re) => {
                    box2d = re;
                    resolve();
                });
            });
        }

        async function main() {
            await initBox2D();

            const {
                b2Draw: { e_shapeBit },
                b2Vec2,
                JSDraw
            } = box2d;

            const vec = new b2Vec2(1, 2);
            console.log(`vec = ${vec.x}, ${vec.y}`);

            function makeDebugDrawer() {
                const debugDrawer = Object.assign(new JSDraw(), {
                    DrawSegment(vert1_p, vert2_p, color_p) {},
                    DrawPolygon(vertices_p, vertexCount, color_p) {},
                    DrawSolidPolygon(vertices_p, vertexCount, color_p) {},
                    DrawCircle(center_p, radius, color_p) {},
                    DrawSolidCircle(center_p, radius, axis_p, color_p) {},
                    DrawTransform(transform_p) {},
                    DrawPoint(vertex_p, sizeMetres, color_p) {}
                });
                debugDrawer.SetFlags(e_shapeBit);
                return debugDrawer;
            }

            const debudDrawer = makeDebugDrawer();
        }

        main();
    </script>
</body>

</html>
shakiba commented 1 year ago

Hey @8Observer8 , I just came across this, following the discussion on the other post. I wonder what is the use-case for this? Is it not possible to do the same thing by iterating over objects in each frame and draw them?

8Observer8 commented 1 year ago

@shakiba What was the purpose of the b2Draw class, known in earlier versions of Box2D as b2DebugDraw? This is necessary for debugging the game. I can't know where the borders of the colliders are and what their real position is. All popular physics engines have this very necessary feature in game development:

This is my example where I placed tiles and colliders in Tiled. Packed tiles and sprites into one texture atlas using Free Texture Packer. Physics at box2d/core. Drawn with pure WebGL 1.0 and linear algebra with glMatrix:

Demo: https://8observer8.github.io/webgl10-js/mario-2d-jumps-box2dcore-webgl-js/

mario-2d-jumps-box2dcore-webgl-js

This tutorial does a good job of explaining why you need "debug drawing": https://www.iforce2d.net/b2dtut/debug-draw

The quote from the tutorial above:

Obviously if you are making a game you will want to have all kinds of eye-popping fancy graphics instead of these boring polygons, but the debug draw feature can be very useful when you are having trouble getting the physics scene to work right. Sometimes the problem can be in the rendering part of your game, for example a sprite may be drawn at the wrong position or rotation, giving the appearance of incorrect physics. I recommend keeping the debug draw ready to hand for times when you want to check exactly what is going on in the Box2D world.

The way it works is quite simple. Box2D tells you all the shapes it sees and where they are eg. "a circle of radius r at x,y", or "an edge from a to b", etc and you draw what you are told. You don't have to do any transforming or worry about where the actual body positions are or which fixtures belong to which bodies, you simply draw the geometry. The idea is that if all you have to do is draw a few lines, you can't mess it up :-)

The default debug draw used by the testbed is done by subclassing the b2DebugDraw class, which has a bunch of virtual functions that can be overridden. Here are the main players:

  virtual void DrawPolygon(b2Vec2* vertices, int32 vertexCount, b2Color& color) = 0;
  virtual void DrawSolidPolygon(b2Vec2* vertices, int32 vertexCount, b2Color& color) = 0;
  virtual void DrawCircle(b2Vec2& center, float32 radius, b2Color& color) = 0;
  virtual void DrawSolidCircle(b2Vec2& center, float32 radius, b2Vec2& axis, b2Color& color) = 0;
  virtual void DrawSegment(b2Vec2& p1, b2Vec2& p2, b2Color& color) = 0;
  virtual void DrawTransform(const b2Transform& xf) = 0;

Separating the rendering code into one location like this makes it easy to implement debug drawing for different APIs like DirectX or OpenGL ES, or switch between different methods easily.

shakiba commented 1 year ago

Thanks for doing the research and sharing that!

I think the same thing could be achieved by iterating over bodies/fixtures, and joints, so I didn't add it. But I agree it is good abstraction and more convenient to use.

Would you be interested in making a PR and adding this?

8Observer8 commented 1 year ago

Would you be interested in making a PR and adding this?

Sorry, definitely not, because I do not have the necessary qualifications, time and desire. If you don't have the next problems on Planck.js in the future, as on box2d.ts:

, then maybe I will switch to Planck.js.

shakiba commented 12 months ago

I will close this for now, since it doesn't seems there is enough interest, but please feel free to reopen or comment if anyone is interest.

This is also the link to Rendering documentation page, which could serve the same purpose.