Created
January 29, 2023 12:52
-
-
Save 8Observer8/ac4d3cd1a41c9bb6c93449f6c7422e01 to your computer and use it in GitHub Desktop.
Click detection with AABB, Box2D, OpenGL 2.1, PyQt6, Python 3.8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
precision mediump float; | |
uniform vec3 uColor; | |
void main() | |
{ | |
gl_FragColor = vec4(uColor, 1.0); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
attribute vec2 aPosition; | |
uniform mat4 uMvpMatrix; | |
void main() | |
{ | |
gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from Box2D import b2Draw | |
from OpenGL.GL import * | |
from PyQt6.QtGui import QMatrix4x4, QQuaternion, QVector3D | |
class DebugDrawer(b2Draw): | |
def __init__(self, program, worldScale): | |
super().__init__() | |
self.program = program | |
self.WORLD_SCALE = worldScale | |
self.mvpMatrixLocation = self.program.uniformLocation("uMvpMatrix") | |
self.colorLocation = self.program.uniformLocation("uColor") | |
self.modelMatrix = QMatrix4x4() | |
self.projMatrix = None | |
self.viewMatrix = None | |
self.projViewMatrix = None | |
self.color = None | |
self.lineWidth = 4 | |
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.projViewMatrix = self.projMatrix * self.viewMatrix | |
self.color = QVector3D(color[0], color[1], color[2]) | |
self.drawLine(vertexes[0], vertexes[1]) | |
self.drawLine(vertexes[1], vertexes[2]) | |
self.drawLine(vertexes[2], vertexes[3]) | |
self.drawLine(vertexes[3], vertexes[0]) | |
def drawLine(self, pointA, pointB): | |
centerX, centerY = 0, 0 | |
tempVec = QVector3D() | |
fromX = pointA[0] * self.WORLD_SCALE | |
fromY = pointA[1] * self.WORLD_SCALE | |
toX = pointB[0] * self.WORLD_SCALE | |
toY = pointB[1] * self.WORLD_SCALE | |
if fromX > toX: | |
centerX = toX + abs(fromX - toX) / 2 | |
else: | |
centerX = fromX + abs(toX - fromX) / 2 | |
if fromY > toY: | |
centerY = toY + abs(fromY - toY) / 2 | |
else: | |
centerY = fromY + abs(toY - fromY) / 2 | |
tempVec.setX(toX - fromX) | |
tempVec.setY(toY - fromY) | |
length = tempVec.length() | |
tempVec.normalize() | |
quat = QQuaternion.rotationTo(QVector3D(1, 0, 0), tempVec) | |
self.modelMatrix.setToIdentity() | |
self.modelMatrix.translate(QVector3D(centerX, centerY, 0)) | |
self.modelMatrix.rotate(quat) | |
self.modelMatrix.scale(QVector3D(length, self.lineWidth, 1)) | |
self.mvpMatrix = self.projViewMatrix * self.modelMatrix | |
self.program.setUniformValue(self.mvpMatrixLocation, self.mvpMatrix) | |
self.program.setUniformValue(self.colorLocation, self.color) | |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) | |
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
import sys | |
import numpy as np | |
from Box2D import (b2_staticBody, b2AABB, b2BodyDef, b2FixtureDef, | |
b2PolygonShape, b2Vec2, b2World) | |
from OpenGL.GL import * | |
from PyQt6.QtCore import QFile, QIODevice, QSize, Qt | |
from PyQt6.QtGui import QMatrix4x4, QSurfaceFormat, QVector3D | |
from PyQt6.QtOpenGL import QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram | |
from PyQt6.QtOpenGLWidgets import QOpenGLWidget | |
from PyQt6.QtWidgets import QApplication | |
from debug_drawer import DebugDrawer | |
from query_callback import QueryCallback | |
class OpenGLWidget(QOpenGLWidget): | |
def __init__(self): | |
super().__init__() | |
self.setFixedSize(QSize(300, 300)) | |
self.setWindowTitle("OpenGL21 PyQt6 Python") | |
self.boxPosition = QVector3D(150, 150, 0) | |
self.boxAngle = 30 | |
self.boxSize = QVector3D(150, 150, 1) | |
self.world = b2World(gravity=(0, 0)) | |
self.pixelsPerMeter = 30 | |
boxShape = b2PolygonShape() | |
boxShape.SetAsBox(self.boxSize.x() / 2 / self.pixelsPerMeter, | |
self.boxSize.y() / 2 / self.pixelsPerMeter) | |
boxBodyDef = b2BodyDef(userData="box") | |
boxBodyDef.type = b2_staticBody | |
boxBody = self.world.CreateBody(boxBodyDef) | |
boxFixtureDef = b2FixtureDef() | |
boxFixtureDef.shape = boxShape | |
boxBody.CreateFixture(boxFixtureDef) | |
boxBody.position = b2Vec2(self.boxPosition.x() / self.pixelsPerMeter, | |
self.boxPosition.y() / self.pixelsPerMeter) | |
boxBody.angle = math.radians(self.boxAngle) | |
def initializeGL(self): | |
glClearColor(0.1, 0.3, 0.2, 1) | |
self.colorProgram = QOpenGLShaderProgram(self) | |
self.colorProgram.addShaderFromSourceFile( | |
QOpenGLShader.ShaderTypeBit.Vertex, "assets/shaders/color.vert") | |
self.colorProgram.addShaderFromSourceFile( | |
QOpenGLShader.ShaderTypeBit.Fragment, "assets/shaders/color.frag") | |
self.colorProgram.link() | |
self.colorProgram.bind() | |
vertPositions = np.array([ | |
-0.5, -0.5, | |
0.5, -0.5, | |
-0.5, 0.5, | |
0.5, 0.5], dtype=np.float32) | |
self.vertPosBuffer = QOpenGLBuffer() | |
self.vertPosBuffer.create() | |
self.vertPosBuffer.bind() | |
self.vertPosBuffer.allocate(vertPositions, len(vertPositions) * 4) | |
self.aPositionLocation = self.colorProgram.attributeLocation("aPosition") | |
self.colorProgram.setAttributeBuffer(self.aPositionLocation, GL_FLOAT, 0, 2) | |
self.colorProgram.enableAttributeArray(self.aPositionLocation) | |
self.mvpMatrix = QMatrix4x4() | |
self.modelMatrix = QMatrix4x4() | |
self.viewMatrix = QMatrix4x4() | |
self.projMatrix = QMatrix4x4() | |
self.projViewMatrix = QMatrix4x4() | |
self.viewMatrix.lookAt( | |
QVector3D(0, 0, 1), | |
QVector3D(0, 0, 0), | |
QVector3D(0, 1, 0)) | |
self.uMvpMatrixLocation = self.colorProgram.uniformLocation("uMvpMatrix") | |
self.uColorLocation = self.colorProgram.uniformLocation("uColor") | |
self.debugDrawer = DebugDrawer(self.colorProgram, self.pixelsPerMeter) | |
self.debugDrawer.flags = { "drawShapes": True } | |
self.world.renderer = self.debugDrawer | |
def resizeGL(self, w, h): | |
glViewport(0, 0, w, h) | |
self.projMatrix.setToIdentity() | |
self.projMatrix.ortho(0, 300, 0, 300, 1, -1) | |
self.debugDrawer.projMatrix = self.projMatrix | |
self.debugDrawer.viewMatrix = self.viewMatrix | |
def paintGL(self): | |
glClear(GL_COLOR_BUFFER_BIT) | |
self.projViewMatrix = self.projMatrix * self.viewMatrix | |
self.world.DrawDebugData() | |
self.modelMatrix.setToIdentity() | |
self.modelMatrix.translate(self.boxPosition) | |
self.modelMatrix.rotate(self.boxAngle, QVector3D(0, 0, 1)) | |
self.modelMatrix.scale(self.boxSize) | |
self.mvpMatrix = self.projViewMatrix * self.modelMatrix | |
self.colorProgram.setUniformValue(self.uMvpMatrixLocation, self.mvpMatrix) | |
self.colorProgram.setUniformValue(self.uColorLocation, QVector3D(1, 0, 0)) | |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) | |
def mousePressEvent(self, event): | |
if event.button() == Qt.MouseButton.LeftButton: | |
x = event.pos().x() / self.pixelsPerMeter | |
y = (self.width() - event.pos().y() - 1) / self.pixelsPerMeter | |
p = b2Vec2(x, y) | |
# Make a small box | |
aabb = b2AABB(lowerBound=p-(0.001, 0.001), upperBound=p+(0.001, 0.001)) | |
# Query the world for overlapping shapes | |
query = QueryCallback() | |
self.world.QueryAABB(query, aabb) | |
if __name__ == "__main__": | |
QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL) | |
app = QApplication(sys.argv) | |
w = OpenGLWidget() | |
format = QSurfaceFormat() | |
format.setSamples(4) | |
w.setFormat(format) | |
w.show() | |
sys.exit(app.exec()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from Box2D import b2QueryCallback | |
class QueryCallback(b2QueryCallback): | |
def __init__(self): | |
b2QueryCallback.__init__(self) | |
def ReportFixture(self, fixture): | |
name = fixture.body.userData | |
print(name) | |
# Continue the query by returning True | |
return True |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
At the moment, the Box2D module works with Python version 3.8 maximum.
The yellow rectangle was drawn after the screenshot was taken.