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 printhello
. I inherited theDebugDrawer
class from theb2Draw
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 theanimationLoop()
method I theself.world.Step()
method and I call thepaintGL()
method by calling theself.update()
method. Inside of thepaintGL()
method I call theself.world.DrawDebugData()
method. I expect that theDrawSegment()
will be called but it does not happen.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()
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()
Plan:
- Add restart button
-
I have to use DrawPolygon to draw collider segments when I use blocks to draw borders around gameobjects. DrawSegment() will be called when b2EdgeShape is instantiated:
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)
-
Hi,
From a quick look at the repo, and if I understood correctly your question, it seems you would need to call SetDebugDraw from the b2World class to make use of your class.
However there's a bug opened about that method so it's not really clear if it's possible.
-
@SGaist said in How to use the Box2D's b2Draw class in PyQt6 with OpenGL3? DrawSegment is not called:
However there's a bug opened about that method so it's not really clear if it's possible.
This issue was opened by me. There is
self.world.renderer = self.debugDrawer
instead ofself.world.SetDebugDraw(self.debugDrawer)
. -
These are python bindings, so setting render like that might not work unless there is explicit support for that through the bindings.
You can add new attributes to python objects but that does not mean it will translate to the underlying C++ object.
-
This property exists in IntelliSense:
At first I found it in the documentation: https://github.com/pybox2d/pybox2d/wiki/manual#drawing
You can see there this example of usage:
draw = MyDraw() world = b2World() world.renderer = draw
DrawPolygon()
works in my example. It printshello
:from Box2D import b2Draw class DebugDrawer(b2Draw): def DrawSegment(self, p1, p2, color): print("hello") def DrawPolygon(self, vertices, color): print("hello") def DrawSolidPolygon(self, vertices, color): pass def DrawPoint(self, p, size, color): pass def DrawCircle(self, center, radius, color, drawwidth=1): pass def DrawSolidCircle(self, center, radius, axis, color): pass def DrawTransform(self, xf): pass
But
DrawSegment
does not work and I do not understand why. -
I can print polygons like this:
class DebugDrawer(b2Draw): def DrawSegment(self, p1, p2, color): print("hello") def DrawPolygon(self, vertices, color): print("Polygon. Begin") print(vertices[0][0] * 30, vertices[0][1] * 30) print(vertices[1][0] * 30, vertices[1][1] * 30) print(vertices[2][0] * 30, vertices[2][1] * 30) print(vertices[3][0] * 30, vertices[3][1] * 30) print("Polygon. End")
Output:
Polygon. Begin -53.29999923706055 -53.29999923706055 53.29999923706055 -53.29999923706055 53.29999923706055 53.29999923706055 -53.29999923706055 53.29999923706055 Polygon. End
But I need segments. Why
DrawSegment
does not work? -
Temporary I can get segments from polygon.
-
I have to use DrawPolygon to draw collider segments when I use blocks to draw borders around gameobjects. DrawSegment() will be called when b2EdgeShape is instantiated:
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)
-
Glad you found out and thanks for sharing !
It would be nice a have a complete example that shows how to use a debug drawer provided by Box2D. Did you consider submitting one ?
-
@SGaist said in How to use the Box2D's b2Draw class in PyQt6 with OpenGL3? DrawSegment is not called:
It would be nice a have a complete example that shows how to use a debug drawer provided by Box2D. Did you consider submitting one ?
I wrote this simple example using OpenGL version 1 for simplicity:
PyBox2D currently works at most with Python version 3.8, as written on GitHub:
Recent builds should be available for Windows, Linux, and OS X, with Python 3.6, 3.7, and 3.8.
You must have PyQt6, PyOpenGL, and PyBox2D installed to run this example:
pip install PyQt6 PyOpenGL Box2D
import sys from PyQt6.QtCore import Qt from PyQt6.QtGui import QSurfaceFormat from PyQt6.QtWidgets import QApplication from opengl_widget import OpenGLWidget def main(): QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL) app = QApplication(sys.argv) format = QSurfaceFormat() format.setSamples(8) w = OpenGLWidget() w.setFormat(format) w.show() sys.exit(app.exec()) if __name__ == "__main__": main()
opengl_widget.py
import math from Box2D import (b2_dynamicBody, b2_staticBody, b2Body, b2BodyDef, b2FixtureDef, b2PolygonShape, b2Vec2, b2World) from OpenGL.GL import * from PyQt6.QtCore import QElapsedTimer, QSize, QTimer from PyQt6.QtOpenGLWidgets import QOpenGLWidget from debug_drawer import DebugDrawer class OpenGLWidget(QOpenGLWidget): def __init__(self): super().__init__() self.setWindowTitle("Box2D, OpenGL1, PyQt6") self.setFixedSize(QSize(400, 400)) self.deltaTime = 0 self.WORLD_SCALE = 30.0 self.world = b2World(gravity=b2Vec2(0.0, -9.8)) def initializeGL(self): glClearColor(0.2, 0.2, 0.2, 1.0) self.debugDrawer = DebugDrawer() self.world.renderer = self.debugDrawer self.debugDrawer.flags = { 'drawShapes': True, 'drawJoints': True, 'drawAABBs': True, 'drawPairs': True } # print(self.debugDrawer.flags) boxShape = b2PolygonShape() boxShape.SetAsBox(10 / self.WORLD_SCALE, 10 / self.WORLD_SCALE) boxBodyDef = b2BodyDef() boxBodyDef.type = b2_dynamicBody self.boxBody = self.world.CreateBody(boxBodyDef) boxFixtureDef = b2FixtureDef() boxFixtureDef.shape = boxShape boxFixtureDef.density = 2 self.boxBody.CreateFixture(boxFixtureDef) self.boxBody.position = b2Vec2(100 / self.WORLD_SCALE, 100 / self.WORLD_SCALE) groundShape = b2PolygonShape() groundShape.SetAsBox(100 / self.WORLD_SCALE, 10 / self.WORLD_SCALE) groundBodyDef = b2BodyDef() groundBodyDef.type = b2_staticBody groundBody = self.world.CreateBody(groundBodyDef) groundFixtureDef = b2FixtureDef() groundFixtureDef.shape = groundShape groundFixtureDef.density = 2 groundBody.CreateFixture(groundFixtureDef) groundBody.position = b2Vec2(100 / self.WORLD_SCALE, 0 / self.WORLD_SCALE) platformShape = b2PolygonShape() platformShape.SetAsBox(30 / self.WORLD_SCALE, 5 / self.WORLD_SCALE) platformBodyDef = b2BodyDef() platformBodyDef.type = b2_staticBody platformBody = self.world.CreateBody(platformBodyDef) platformFixtureDef = b2FixtureDef() platformFixtureDef.shape = platformShape boxFixtureDef.density = 2 platformBody.CreateFixture(platformFixtureDef) platformBody.position = b2Vec2(100 / self.WORLD_SCALE, 50 / self.WORLD_SCALE) platformBody.angle = math.radians(-10) self.timer = QTimer() self.timer.timeout.connect(self.animationLoop) self.elapsedTimer = QElapsedTimer() self.elapsedTimer.start() self.timer.start(1000//60) def paintGL(self): glClear(GL_COLOR_BUFFER_BIT) self.world.DrawDebugData() def resizeGL(self, w, h): glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0, 200, 0, 200, -10, 10) def animationLoop(self): self.deltaTime = self.elapsedTimer.elapsed() / 1000.0 self.elapsedTimer.restart() self.world.Step(self.deltaTime, 8, 3) self.update()
debug_drawer.py
from Box2D import b2Draw from OpenGL.GL import * class DebugDrawer(b2Draw): 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") glLineWidth(3) glBegin(GL_LINES) glColor3f(color.r, color.g, color.b) glVertex2f(vertexes[0][0] * 30, vertexes[0][1] * 30) glVertex2f(vertexes[1][0] * 30, vertexes[1][1] * 30) glVertex2f(vertexes[1][0] * 30, vertexes[1][1] * 30) glVertex2f(vertexes[2][0] * 30, vertexes[2][1] * 30) glVertex2f(vertexes[2][0] * 30, vertexes[2][1] * 30) glVertex2f(vertexes[3][0] * 30, vertexes[3][1] * 30) glVertex2f(vertexes[3][0] * 30, vertexes[3][1] * 30) glVertex2f(vertexes[0][0] * 30, vertexes[0][1] * 30) glEnd() def DrawPolygon(self, vertexes, color): pass def DrawSegment(self, p1, p2, color): pass def DrawPoint(self, p, size, color): pass def DrawCircle(self, center, radius, color, drawwidth=1): pass def DrawSolidCircle(self, center, radius, axis, color): pass def DrawTransform(self, xf): pass
-
Added restart button:
Source code: https://github.com/8Observer8/edit-gravity-debug-drawer-opengl1-pyqt6