Search code examples
pythonpyqtbox2d

How to use the Box2D's b2Draw class in PyQt6 with OpenGL3? DrawSegment is not called


I am trying to draw colliders of Box2D. Now I have only physics in this example without graphics for simplicity. The DrawSegment() method must be called to print hello. I inherited the DebugDrawer class from the b2Draw class:

debug_drawer.py

from Box2D import b2Draw


class DebugDrawer(b2Draw):

    def DrawSegment(self, p1, p2, color):
        print("hello")

    def DrawSolidPolygon(self, vertices, color):
        pass
    def DrawPoint(self, p, size, color):
        pass
    def DrawPolygon(self, vertices, color):
        pass
    def DrawCircle(self, center, radius, color, drawwidth=1):
        pass
    def DrawSolidCircle(self, center, radius, axis, color):
        pass
    def DrawTransform(self, xf):
        pass

I created one object with the box shape. I have the animationLoop() method that I call with timer. Inside of the animationLoop() method I the self.world.Step() method and I call the paintGL() method by calling the self.update() method. Inside of the paintGL() method I call the self.world.DrawDebugData() method. I expect that the DrawSegment() will be called but it does not happen.

widget.py


from Box2D import (b2_staticBody, b2Body, b2BodyDef, b2FixtureDef,
                   b2PolygonShape, b2Vec2, b2World)
from OpenGL import GL as gl
from PyQt6.QtCore import QElapsedTimer, QSize, QTimer
from PyQt6.QtOpenGLWidgets import QOpenGLWidget

from debug_drawer import DebugDrawer


class Widget(QOpenGLWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Box2D, OpenGL3, PyQt6")
        self.setFixedSize(QSize(500, 500))
        self.deltaTime = 0

        self.WORLD_SCALE = 30.0
        self.world = b2World(gravity=b2Vec2(0.0, 9.8))

    def initializeGL(self):
        gl.glClearColor(0.2, 0.2, 0.2, 1.0)
        gl.glEnable(gl.GL_DEPTH_TEST)

        self.debugDrawer = DebugDrawer()
        self.world.renderer = self.debugDrawer

        self.debugDrawer.flags = { 'drawShapes': True,
            'drawJoints': True, 'drawAABBs': True, 'drawPairs': True }
        # print(self.debugDrawer.flags)

        shape = b2PolygonShape()
        shape.SetAsBox(50.0 / self.WORLD_SCALE, 50.0 / self.WORLD_SCALE)

        bodyDef = b2BodyDef()
        bodyDef.type = b2_staticBody

        self.body: b2Body = self.world.CreateBody(bodyDef)
        fixtureDef = b2FixtureDef()
        fixtureDef.shape = shape
        fixtureDef.density = 2
        self.body.CreateFixture(fixtureDef)

        self.timer = QTimer()
        self.timer.timeout.connect(self.animationLoop)
        self.elapsedTimer = QElapsedTimer()
        self.elapsedTimer.start()
        self.timer.start(1000//60)
    
    def paintGL(self):
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        self.world.DrawDebugData()
    
    def resizeGL(self, w: int, h: int):
        gl.glViewport(0, 0, w, h)

    def animationLoop(self):
        self.deltaTime = self.elapsedTimer.elapsed() / 1000.0
        self.elapsedTimer.restart()
        self.world.Step(self.deltaTime, 8, 3)
        self.update()

main.py

import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QSurfaceFormat
from PyQt6.QtWidgets import QApplication

from widget import Widget


def main():
    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)

    format = QSurfaceFormat()
    format.setSamples(8)
    
    w = Widget()
    w.setFormat(format)
    w.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    main()

Solution

  • I should use DrawPolygon to draw segments of colliders when I use boxes to draw borders around game objects. DrawSegment() will be called when an instance of b2EdgeShape is created:

            edgeShape = b2EdgeShape()
            edgeShape.vertices = [(0.0, 0.0), (1.0, 0.0)]
            self.edgeBody: b2Body = self.world.CreateBody(bodyDef)
            edgeFixtureDef = b2FixtureDef()
            edgeFixtureDef.shape = edgeShape
            edgeFixtureDef.density = 2
            self.edgeBody.CreateFixture(edgeFixtureDef)