From 1e8e9f171f2b1b8f5f4bcef406f9044a7d8fbe6f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 11 Jan 2018 19:47:32 +0100 Subject: Port OpenGL hellogl2 example which uses shaders Change-Id: I77ecf92e32ec1177f008b98c44b56567c3b11696 Reviewed-by: Friedemann Kleint --- examples/opengl/hellogl2.py | 473 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100755 examples/opengl/hellogl2.py (limited to 'examples/opengl') diff --git a/examples/opengl/hellogl2.py b/examples/opengl/hellogl2.py new file mode 100755 index 000000000..29a68269e --- /dev/null +++ b/examples/opengl/hellogl2.py @@ -0,0 +1,473 @@ +#!/usr/bin/env python + +############################################################################ +## +## Copyright (C) 2013 Riverbank Computing Limited. +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the PySide examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################ + +"""PySide2 port of the opengl/hellogl2 example from Qt v5.x""" + +import sys +import math +import numpy +import ctypes +from PySide2.QtCore import QCoreApplication, Signal, SIGNAL, SLOT, Qt, QSize, QPoint +from PySide2.QtGui import (QVector3D, QOpenGLFunctions, QOpenGLVertexArrayObject, QOpenGLBuffer, + QOpenGLShaderProgram, QMatrix4x4, QOpenGLShader, QOpenGLContext, QSurfaceFormat) +from PySide2.QtWidgets import (QApplication, QWidget, QMessageBox, QHBoxLayout, QSlider, + QOpenGLWidget) +from PySide2.shiboken2 import VoidPtr + +try: + from OpenGL import GL +except ImportError: + app = QApplication(sys.argv) + messageBox = QMessageBox(QMessageBox.Critical, "OpenGL hellogl", + "PyOpenGL must be installed to run this example.", + QMessageBox.Close) + messageBox.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate") + messageBox.exec_() + sys.exit(1) + + +class Window(QWidget): + def __init__(self, parent=None): + QWidget.__init__(self, parent) + + self.glWidget = GLWidget() + + self.xSlider = self.createSlider(SIGNAL("xRotationChanged(int)"), + self.glWidget.setXRotation) + self.ySlider = self.createSlider(SIGNAL("yRotationChanged(int)"), + self.glWidget.setYRotation) + self.zSlider = self.createSlider(SIGNAL("zRotationChanged(int)"), + self.glWidget.setZRotation) + + mainLayout = QHBoxLayout() + mainLayout.addWidget(self.glWidget) + mainLayout.addWidget(self.xSlider) + mainLayout.addWidget(self.ySlider) + mainLayout.addWidget(self.zSlider) + self.setLayout(mainLayout) + + self.xSlider.setValue(15 * 16) + self.ySlider.setValue(345 * 16) + self.zSlider.setValue(0 * 16) + + self.setWindowTitle(self.tr("Hello GL")) + + def createSlider(self, changedSignal, setterSlot): + slider = QSlider(Qt.Vertical) + + slider.setRange(0, 360 * 16) + slider.setSingleStep(16) + slider.setPageStep(15 * 16) + slider.setTickInterval(15 * 16) + slider.setTickPosition(QSlider.TicksRight) + + self.glWidget.connect(slider, SIGNAL("valueChanged(int)"), setterSlot) + self.connect(self.glWidget, changedSignal, slider, SLOT("setValue(int)")) + + return slider + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Escape: + self.close() + else: + super(Window, self).keyPressEvent(event) + +class Logo(): + def __init__(self): + self.m_count = 0 + self.i = 0 + self.m_data = numpy.empty(2500 * 6, dtype = ctypes.c_float) + + x1 = +0.06 + y1 = -0.14 + x2 = +0.14 + y2 = -0.06 + x3 = +0.08 + y3 = +0.00 + x4 = +0.30 + y4 = +0.22 + + self.quad(x1, y1, x2, y2, y2, x2, y1, x1) + self.quad(x3, y3, x4, y4, y4, x4, y3, x3) + + self.extrude(x1, y1, x2, y2) + self.extrude(x2, y2, y2, x2) + self.extrude(y2, x2, y1, x1) + self.extrude(y1, x1, x1, y1) + self.extrude(x3, y3, x4, y4) + self.extrude(x4, y4, y4, x4) + self.extrude(y4, x4, y3, x3) + + Pi = 3.14159265358979323846 + NumSectors = 100 + + for i in range(NumSectors): + angle = (i * 2 * Pi) / NumSectors + x5 = 0.30 * math.sin(angle) + y5 = 0.30 * math.cos(angle) + x6 = 0.20 * math.sin(angle) + y6 = 0.20 * math.cos(angle) + + angle = ((i + 1) * 2 * Pi) / NumSectors + x7 = 0.20 * math.sin(angle) + y7 = 0.20 * math.cos(angle) + x8 = 0.30 * math.sin(angle) + y8 = 0.30 * math.cos(angle) + + self.quad(x5, y5, x6, y6, x7, y7, x8, y8) + + self.extrude(x6, y6, x7, y7) + self.extrude(x8, y8, x5, y5) + + def constData(self): + return self.m_data.tobytes() + + def count(self): + return self.m_count + + def vertexCount(self): + return self.m_count / 6 + + def quad(self, x1, y1, x2, y2, x3, y3, x4, y4): + n = QVector3D.normal(QVector3D(x4 - x1, y4 - y1, 0), QVector3D(x2 - x1, y2 - y1, 0)) + + self.add(QVector3D(x1, y1, -0.05), n) + self.add(QVector3D(x4, y4, -0.05), n) + self.add(QVector3D(x2, y2, -0.05), n) + + self.add(QVector3D(x3, y3, -0.05), n) + self.add(QVector3D(x2, y2, -0.05), n) + self.add(QVector3D(x4, y4, -0.05), n) + + n = QVector3D.normal(QVector3D(x1 - x4, y1 - y4, 0), QVector3D(x2 - x4, y2 - y4, 0)) + + self.add(QVector3D(x4, y4, 0.05), n) + self.add(QVector3D(x1, y1, 0.05), n) + self.add(QVector3D(x2, y2, 0.05), n) + + self.add(QVector3D(x2, y2, 0.05), n) + self.add(QVector3D(x3, y3, 0.05), n) + self.add(QVector3D(x4, y4, 0.05), n) + + def extrude(self, x1, y1, x2, y2): + n = QVector3D.normal(QVector3D(0, 0, -0.1), QVector3D(x2 - x1, y2 - y1, 0)) + + self.add(QVector3D(x1, y1, 0.05), n) + self.add(QVector3D(x1, y1, -0.05), n) + self.add(QVector3D(x2, y2, 0.05), n) + + self.add(QVector3D(x2, y2, -0.05), n) + self.add(QVector3D(x2, y2, 0.05), n) + self.add(QVector3D(x1, y1, -0.05), n) + + def add(self, v, n): + self.m_data[self.i] = v.x() + self.i += 1 + self.m_data[self.i] = v.y() + self.i += 1 + self.m_data[self.i] = v.z() + self.i += 1 + self.m_data[self.i] = n.x() + self.i += 1 + self.m_data[self.i] = n.y() + self.i += 1 + self.m_data[self.i] = n.z() + self.i += 1 + self.m_count += 6 + +class GLWidget(QOpenGLWidget, QOpenGLFunctions): + xRotationChanged = Signal(int) + yRotationChanged = Signal(int) + zRotationChanged = Signal(int) + + def __init__(self, parent=None): + QOpenGLWidget.__init__(self, parent) + QOpenGLFunctions.__init__(self) + + self.core = "--coreprofile" in QCoreApplication.arguments() + self.xRot = 0 + self.yRot = 0 + self.zRot = 0 + self.lastPos = 0 + self.logo = Logo() + self.vao = QOpenGLVertexArrayObject() + self.logoVbo = QOpenGLBuffer() + self.program = QOpenGLShaderProgram() + self.projMatrixLoc = 0 + self.mvMatrixLoc = 0 + self.normalMatrixLoc = 0 + self.lightPosLoc = 0 + self.proj = QMatrix4x4() + self.camera = QMatrix4x4() + self.world = QMatrix4x4() + self.transparent = "--transparent" in QCoreApplication.arguments() + if self.transparent: + fmt = self.format() + fmt.setAlphaBufferSize(8) + self.setFormat(fmt) + + def xRotation(self): + return self.xRot + + def yRotation(self): + return self.yRot + + def zRotation(self): + return self.zRot + + def minimumSizeHint(self): + return QSize(50, 50) + + def sizeHint(self): + return QSize(400, 400) + + def normalizeAngle(self, angle): + while angle < 0: + angle += 360 * 16 + while angle > 360 * 16: + angle -= 360 * 16 + return angle + + def setXRotation(self, angle): + angle = self.normalizeAngle(angle) + if angle != self.xRot: + self.xRot = angle + self.emit(SIGNAL("xRotationChanged(int)"), angle) + self.update() + + def setYRotation(self, angle): + angle = self.normalizeAngle(angle) + if angle != self.yRot: + self.yRot = angle + self.emit(SIGNAL("yRotationChanged(int)"), angle) + self.update() + + def setZRotation(self, angle): + angle = self.normalizeAngle(angle) + if angle != self.zRot: + self.zRot = angle + self.emit(SIGNAL("zRotationChanged(int)"), angle) + self.update() + + def cleanup(self): + self.makeCurrent() + self.logoVbo.destroy() + del self.program + self.program = None + self.doneCurrent() + + def vertexShaderSourceCore(self): + return """#version 150 + in vec4 vertex; + in vec3 normal; + out vec3 vert; + out vec3 vertNormal; + uniform mat4 projMatrix; + uniform mat4 mvMatrix; + uniform mat3 normalMatrix; + void main() { + vert = vertex.xyz; + vertNormal = normalMatrix * normal; + gl_Position = projMatrix * mvMatrix * vertex; + }""" + + def fragmentShaderSourceCore(self): + return """#version 150 + in highp vec3 vert; + in highp vec3 vertNormal; + out highp vec4 fragColor; + uniform highp vec3 lightPos; + void main() { + highp vec3 L = normalize(lightPos - vert); + highp float NL = max(dot(normalize(vertNormal), L), 0.0); + highp vec3 color = vec3(0.39, 1.0, 0.0); + highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); + fragColor = vec4(col, 1.0); + }""" + + + def vertexShaderSource(self): + return """attribute vec4 vertex; + attribute vec3 normal; + varying vec3 vert; + varying vec3 vertNormal; + uniform mat4 projMatrix; + uniform mat4 mvMatrix; + uniform mat3 normalMatrix; + void main() { + vert = vertex.xyz; + vertNormal = normalMatrix * normal; + gl_Position = projMatrix * mvMatrix * vertex; + }""" + + def fragmentShaderSource(self): + return """varying highp vec3 vert; + varying highp vec3 vertNormal; + uniform highp vec3 lightPos; + void main() { + highp vec3 L = normalize(lightPos - vert); + highp float NL = max(dot(normalize(vertNormal), L), 0.0); + highp vec3 color = vec3(0.39, 1.0, 0.0); + highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); + gl_FragColor = vec4(col, 1.0); + }""" + + def initializeGL(self): + self.context().aboutToBeDestroyed.connect(self.cleanup) + self.initializeOpenGLFunctions() + self.glClearColor(0, 0, 0, 1) + + self.program = QOpenGLShaderProgram() + + if self.core: + self.vertexShader = self.vertexShaderSourceCore() + self.fragmentShader = self.fragmentShaderSourceCore() + else: + self.vertexShader = self.vertexShaderSource() + self.fragmentShader = self.fragmentShaderSource() + + self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, self.vertexShader) + self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, self.fragmentShader) + self.program.bindAttributeLocation("vertex", 0) + self.program.bindAttributeLocation("normal", 1) + self.program.link() + + self.program.bind() + self.projMatrixLoc = self.program.uniformLocation("projMatrix") + self.mvMatrixLoc = self.program.uniformLocation("mvMatrix") + self.normalMatrixLoc = self.program.uniformLocation("normalMatrix") + self.lightPosLoc = self.program.uniformLocation("lightPos") + + self.vao.create() + vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) + + self.logoVbo.create() + self.logoVbo.bind() + float_size = ctypes.sizeof(ctypes.c_float) + self.logoVbo.allocate(self.logo.constData(), self.logo.count() * float_size) + + self.setupVertexAttribs() + + self.camera.setToIdentity() + self.camera.translate(0, 0, -1) + + self.program.setUniformValue(self.lightPosLoc, QVector3D(0, 0, 70)) + self.program.release() + vaoBinder = None + + def setupVertexAttribs(self): + self.logoVbo.bind() + f = QOpenGLContext.currentContext().functions() + f.glEnableVertexAttribArray(0) + f.glEnableVertexAttribArray(1) + float_size = ctypes.sizeof(ctypes.c_float) + + null = VoidPtr(0) + pointer = VoidPtr(3 * float_size) + f.glVertexAttribPointer(0, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, null) + f.glVertexAttribPointer(1, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, pointer) + self.logoVbo.release() + + def paintGL(self): + self.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + self.glEnable(GL.GL_DEPTH_TEST) + self.glEnable(GL.GL_CULL_FACE) + + self.world.setToIdentity() + self.world.rotate(180 - (self.xRot / 16), 1, 0, 0) + self.world.rotate(self.yRot / 16, 0, 1, 0) + self.world.rotate(self.zRot / 16, 0, 0, 1) + + vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) + self.program.bind() + self.program.setUniformValue(self.projMatrixLoc, self.proj) + self.program.setUniformValue(self.mvMatrixLoc, self.camera * self.world) + normalMatrix = self.world.normalMatrix() + self.program.setUniformValue(self.normalMatrixLoc, normalMatrix) + + self.glDrawArrays(GL.GL_TRIANGLES, 0, self.logo.vertexCount()) + self.program.release() + vaoBinder = None + + def resizeGL(self, width, height): + self.proj.setToIdentity() + self.proj.perspective(45, width / height, 0.01, 100) + + def mousePressEvent(self, event): + self.lastPos = QPoint(event.pos()) + + def mouseMoveEvent(self, event): + dx = event.x() - self.lastPos.x() + dy = event.y() - self.lastPos.y() + + if event.buttons() & Qt.LeftButton: + self.setXRotation(self.xRot + 8 * dy) + self.setYRotation(self.yRot + 8 * dx) + elif event.buttons() & Qt.RightButton: + self.setXRotation(self.xRot + 8 * dy) + self.setZRotation(self.zRot + 8 * dx) + + self.lastPos = QPoint(event.pos()) + +if __name__ == '__main__': + app = QApplication(sys.argv) + + fmt = QSurfaceFormat() + fmt.setDepthBufferSize(24) + if "--multisample" in QCoreApplication.arguments(): + fmt.setSamples(4) + if "--coreprofile" in QCoreApplication.arguments(): + fmt.setVersion(3, 2) + fmt.setProfile(QSurfaceFormat.CoreProfile) + QSurfaceFormat.setDefaultFormat(fmt) + + mainWindow = Window() + if "--transparent" in QCoreApplication.arguments(): + mainWindow.setAttribute(Qt.WA_TranslucentBackground) + mainWindow.setAttribute(Qt.WA_NoSystemBackground, False) + + mainWindow.resize(mainWindow.sizeHint()) + mainWindow.show() + + res = app.exec_() + sys.exit(res) -- cgit v1.2.3