Exemple #1
0
class SquareObject(QObject):
    def __init__(self, parent=None):
        super(SquareObject, self).__init__(parent=parent)
        self.initialize_shader_program()
        self.initialize_geometry()
        self.initialize_geometry_on_gpu()
        self.initialize_texture_on_gpu()

    def destroy(self):
        self.vao.release()
        self.vao.destroy()

        self.vbo.release()
        self.vbo.destroy()

        self.ebo.release()
        self.ebo.destroy()

        self.texture0.release()
        self.texture0.destroy()

        self.texture1.release()
        self.texture1.destroy()

    def initialize_shader_program(self):
        self.vertex_shader = """
            #version 330 core
              layout (location = 0) in vec3 in_coords;
              layout (location = 1) in vec3 in_color;
              layout (location = 2) in vec2 in_tex_coords;

              uniform mat4 transform;

              out vec3 out_color;
              out vec2 out_tex_coords;

              void main()
              {
                gl_Position = transform * vec4(in_coords, 1.0);
                out_color = in_color;
                out_tex_coords = vec2(in_tex_coords.x, in_tex_coords.y);
              }
        """

        self.fragment_shader = """
            #version 330 core
              in vec3 out_color;
              in vec2 out_tex_coords;
              out vec4 frag_color;

              uniform sampler2D texture0;
              uniform sampler2D texture1;

              void main()
              {
                if (out_tex_coords.x > 0.5)
                  frag_color = texture(texture0, out_tex_coords);
                else
                  frag_color = texture(texture1, out_tex_coords);
                //frag_color = mix(texture(texture0, out_tex_coords),
                //                 texture(texture1, out_tex_coords), 0.5)
                //           * vec4(out_color, 1.0);
              }
        """

        self.arg_pos = {
            'in_coords': 0,
            'in_color': 1,
            'in_tex_coords': 2,
            'out_color': 0
        }

        self.program = QOpenGLShaderProgram(parent=self.parent())
        self.program.addCacheableShaderFromSourceCode(QOpenGLShader.Vertex,
                                                      self.vertex_shader)
        self.program.addCacheableShaderFromSourceCode(QOpenGLShader.Fragment,
                                                      self.fragment_shader)
        self.program.link()
        self.program.bind()

    def initialize_geometry(self):
        self.vertices = np.array(
            [
                # coords            color            texture coords
                [0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0],  # bottom-right
                [0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0],  # top-right
                [-0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0],  # top-let
                [-0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]  # bottom-let
            ],
            dtype=np.float32)

        self.triangles = np.array([[0, 1, 2], [2, 3, 0]], dtype=np.int32)

    def initialize_geometry_on_gpu(self):
        self.vao = QOpenGLVertexArrayObject(self.parent())
        if not self.vao.create():
            raise ValueError('Could not create VAO')
        self.vao.bind()

        self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        if not self.vbo.create():
            raise ValueError('Could not create VBO')
        self.vbo.bind()
        self.vbo.setUsagePattern(QOpenGLBuffer.StaticDraw)
        vertices_data = self.vertices.tostring()
        self.vbo.allocate(len(vertices_data))
        self.vbo.write(0, vertices_data, len(vertices_data))

        self.ebo = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        if not self.ebo.create():
            raise ValueError('Could not create EBO')
        self.ebo.bind()
        self.ebo.setUsagePattern(QOpenGLBuffer.StaticDraw)
        triangles_data = self.triangles.tostring()
        self.ebo.allocate(len(triangles_data))
        self.ebo.write(0, triangles_data, len(triangles_data))

        self.program.enableAttributeArray(self.arg_pos['in_coords'])
        self.program.setAttributeBuffer(
            self.arg_pos['in_coords'], gl.GL_FLOAT, 0, 3,
            self.vertices.shape[1] * self.vertices.dtype.itemsize)

        self.program.enableAttributeArray(self.arg_pos['in_color'])
        self.program.setAttributeBuffer(
            self.arg_pos['in_color'], gl.GL_FLOAT,
            3 * self.vertices.dtype.itemsize, 3,
            self.vertices.shape[1] * self.vertices.dtype.itemsize)

        self.program.enableAttributeArray(self.arg_pos['in_tex_coords'])
        self.program.setAttributeBuffer(
            self.arg_pos['in_tex_coords'], gl.GL_FLOAT,
            6 * self.vertices.dtype.itemsize, 2,
            self.vertices.shape[1] * self.vertices.dtype.itemsize)

        self.vao.release()
        self.vbo.release()
        self.ebo.release()

    def initialize_texture_on_gpu(self):
        # Texture 0.
        image0 = QImage(path.join(DATA_DIR, 'ksmall.jpg')).mirrored()
        self.texture0 = QOpenGLTexture(image0)
        self.texture0.setMinificationFilter(QOpenGLTexture.LinearMipMapLinear)
        self.texture0.setMagnificationFilter(QOpenGLTexture.Linear)
        self.texture0.setWrapMode(QOpenGLTexture.Repeat)
        self.texture0.bind(0)
        self.program.setUniformValue(self.program.uniformLocation('texture0'),
                                     0)

        # Texture 1.
        image1 = QImage(path.join(DATA_DIR, 'sunflowerField.jpg')).mirrored()
        self.texture1 = QOpenGLTexture(image1)
        self.texture1.setMinificationFilter(QOpenGLTexture.LinearMipMapLinear)
        self.texture1.setMagnificationFilter(QOpenGLTexture.Linear)
        self.texture1.setWrapMode(QOpenGLTexture.Repeat)
        self.texture1.bind(1)
        self.program.setUniformValue(self.program.uniformLocation('texture1'),
                                     1)

    def render(self, transform):
        self.program.bind()
        self.program.setUniformValue('transform', transform)
        self.vao.bind()
        gl.glDrawElements(gl.GL_TRIANGLES, self.triangles.size,
                          gl.GL_UNSIGNED_INT, None)
        self.program.release()
Exemple #2
0
class TGLViewport(QOpenGLWidget, QOpenGLFunctions):
    def __init__(self, parent=None):
        QOpenGLWidget.__init__(self, parent)
        QOpenGLFunctions.__init__(self)
        self.setMinimumSize(32, 32)

        self.info = ""
        self._supported_images = [
            "TGA", "PNG", "JPG", "JPEG", "TIF", "TIFF", "BMP", "DDS"
        ]

        # indices
        indices = [0, 1, 3, 1, 2, 3]
        self._indices = array('I', indices)

        # vertices
        # 3 position | 2 texture coord
        vertex = [
            1.0,
            1.0,
            0.0,
            1.0,
            1.0,  # top right
            1.0,
            -1.0,
            0.0,
            1.0,
            0.0,  # bottom right
            -1.0,
            -1.0,
            0.0,
            0.0,
            0.0,  # bottom left
            -1.0,
            1.0,
            0.0,
            0.0,
            1.0  # top left
        ]
        self._vertex = array('f', vertex)

        # opengl data related
        self._program = QOpenGLShaderProgram()
        self._program_bg = QOpenGLShaderProgram()
        self._vao = QOpenGLVertexArrayObject()
        self._vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        self._texture = None
        self._texture_size = (1, 1)
        self._location = ()

        self._colors_default = (QColor.fromRgbF(0.65, 0.65, 0.65, 1.0),
                                QColor.fromRgbF(0.90, 0.90, 0.90, 1.0))
        self._u_colors = self._colors_default
        self._height = QVector4D(0, self.height(), 0, 0)

        self._u_channels = QMatrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
                                      0, 0)

    def initializeGL(self):
        # Set up the rendering context, define display lists etc.
        self.initializeOpenGLFunctions()
        self.glClearColor(0.2, 0.0, 0.2, 0.0)
        self.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)

        # shader code (OpenGL ES)
        # texture
        vs_source_es = """
		attribute highp vec3 Pos;
		attribute highp vec2 UV;
		uniform highp vec2 Scale;
		varying highp vec2 oUV;
		void main() {
			gl_Position = vec4(Pos * vec3(Scale, 1.0), 1.0);
			oUV = UV;
		}
		"""
        ps_source_es = """
		varying highp vec2 oUV;
		uniform sampler2D Texture;
		uniform highp mat4 Channels;
		uniform highp vec4 Add;
		void main() {
			gl_FragColor = texture2D(Texture, oUV * vec2(1.0, -1.0)) * Channels;
			gl_FragColor.a += (1.0 - Channels[3][3]);
		}
		"""
        # background
        vs_grid_es = """
		attribute highp vec3 Pos;
		void main() {
			gl_Position = vec4(Pos, 1.0);
		}
		"""
        ps_grid_es = """
		uniform highp vec4 Color1;
		uniform highp vec4 Color2;
		uniform highp vec4 Height;
		void main() {
			highp vec2 a = floor((Height.xy - gl_FragCoord.xy) / 64.0);
			highp float even = mod(a.x + a.y, 2.0);
			highp vec3 c = mix(Color1.rgb, Color2.rgb, even);
			gl_FragColor = vec4(c, 1);
		}
		"""

        # program - texture
        # shader
        vs = self.__create_shader(QOpenGLShader.Vertex, vs_source_es)
        fs = self.__create_shader(QOpenGLShader.Fragment, ps_source_es)
        # program
        self._program = QOpenGLShaderProgram(self.context())
        self._program.addShader(vs)
        self._program.addShader(fs)
        # attribute location
        self._program.bindAttributeLocation("Pos", 0)
        self._program.bindAttributeLocation("UV", 1)
        # link program
        r = self._program.link()

        # program - background
        # shader
        vs = self.__create_shader(QOpenGLShader.Vertex, vs_grid_es)
        fs = self.__create_shader(QOpenGLShader.Fragment, ps_grid_es)
        # program
        self._program_bg = QOpenGLShaderProgram(self.context())
        self._program_bg.addShader(vs)
        self._program_bg.addShader(fs)
        # attribute location
        self._program_bg.bindAttributeLocation("Pos", 0)
        # link program
        r = self._program_bg.link()

        # uniform locations
        self._location = (
            self._program.uniformLocation("Scale"),
            self._program.uniformLocation("Channels"),
            self._program_bg.uniformLocation("Color1"),
            self._program_bg.uniformLocation("Color2"),
            self._program_bg.uniformLocation("Height"),
        )

        # vao
        r = self._vao.create()
        r = self._vao.bind()
        # vbo
        r = self._vbo.create()
        self._vbo.setUsagePattern(QOpenGLBuffer.StaticDraw)
        r = self._vbo.bind()
        sz_float = ctypes.sizeof(ctypes.c_float)
        self._vbo.allocate(self._vertex.tobytes(),
                           sz_float * len(self._vertex))
        self._vao.release()
        # texture
        self.set_texture(r"C:")

    def resizeGL(self, width, height):
        self.__update_scale(width, height)
        self._height = QVector4D(0, self.height(), 0, 0)
        self.glViewport(0, 0, width, height)

    def paintGL(self):
        SZ_FLOAT = ctypes.sizeof(ctypes.c_float)

        # draw the scene
        self.glClear(GL.GL_COLOR_BUFFER_BIT)

        self._vao.bind()

        # background
        self._program_bg.bind()
        self._program.setAttributeBuffer(0, GL.GL_FLOAT, 0, 3, 5 * SZ_FLOAT)
        self._program.enableAttributeArray(0)
        self._program.setUniformValue(self._location[2], self._u_colors[0])
        self._program.setUniformValue(self._location[3], self._u_colors[1])
        self._program.setUniformValue(self._location[4], self._height)
        self.glDrawElements(GL.GL_TRIANGLES, len(self._indices),
                            GL.GL_UNSIGNED_INT, self._indices.tobytes())

        # texture
        if self._texture is None:
            return

        self.glEnable(GL.GL_BLEND)
        self._program.bind()
        self._program.setAttributeBuffer(1, GL.GL_FLOAT, 3 * SZ_FLOAT, 2,
                                         5 * SZ_FLOAT)
        self._program.enableAttributeArray(1)
        self._program.setUniformValue(self._location[0], *self._scale)
        self._program.setUniformValue(self._location[1], self._u_channels)
        self._texture.bind()
        self.glDrawElements(GL.GL_TRIANGLES, len(self._indices),
                            GL.GL_UNSIGNED_INT, self._indices.tobytes())
        self.glDisable(GL.GL_BLEND)

    @staticmethod
    def __create_shader(type_: QOpenGLShader.ShaderType, source):
        shader = QOpenGLShader(type_)
        r = shader.compileSourceCode(source)
        if not r:
            print(shader.log())
        return shader

    def __update_scale(self, width, height):
        # calc texture scale

        if self._texture_size[0] < width and self._texture_size[1] < height:
            self._scale = (self._texture_size[0] / width,
                           self._texture_size[1] / height)
        else:
            tw = width
            th = self._texture_size[1] / self._texture_size[0] * tw
            if th > height:
                th = height
                tw = th * self._texture_size[0] / self._texture_size[1]
            self._scale = (tw / width, th / height)

    def get_gl_info(self):
        self.makeCurrent()
        info = """
			Vendor: {0}
			Renderer: {1}
			OpenGL Version: {2}
			Shader Version: {3}
			""".format(self.glGetString(GL.GL_VENDOR), self.glGetString(GL.GL_RENDERER),
              self.glGetString(GL.GL_VERSION),
              self.glGetString(GL.GL_SHADING_LANGUAGE_VERSION))
        return info

    def set_channels(self, r, g, b, a):
        r, g, b, a = float(r), float(g), float(b), float(a)
        s = r + g + b
        if s > 1.1:
            self._u_channels = QMatrix4x4(r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0,
                                          0, 0, 0, a)
        elif s < 0.1:
            self._u_channels = QMatrix4x4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                          a, a, a, 0)
        elif a:
            self._u_channels = QMatrix4x4(r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0,
                                          0, 0, 0, a)
        else:
            self._u_channels = QMatrix4x4(r, r, r, 0, g, g, g, 0, b, b, b, 0,
                                          0, 0, 0, a)
        # redraw
        self.update()

    def set_texture(self, filename):
        # load image
        p = Path(filename)
        suffix = p.suffix[1:].upper()
        try:
            if p.is_file() and suffix in self._supported_images:
                pim = Image.open(filename)
                img = ImageQt(pim)
                self.info = f"{pim.format} - {pim.size} - {pim.mode} "
            else:
                ico = QFileIconProvider().icon(QFileInfo(filename))
                pix = ico.pixmap(256, 256)
                img = pix.toImage()
                self.info = "not an image "
        except UnidentifiedImageError as e:
            print("UnidentifiedImageError:\n", e, flush=True)
            return

        self._texture_size = img.width(), img.height()
        self.__update_scale(self.width(), self.height())

        # create texture
        self.makeCurrent()
        self._texture = QOpenGLTexture(QOpenGLTexture.Target2D)
        self._texture.create()
        self._texture.bind()
        self._texture.setMinMagFilters(QOpenGLTexture.LinearMipMapLinear,
                                       QOpenGLTexture.Linear)
        self._texture.setWrapMode(QOpenGLTexture.DirectionS,
                                  QOpenGLTexture.Repeat)
        self._texture.setWrapMode(QOpenGLTexture.DirectionT,
                                  QOpenGLTexture.Repeat)
        self._texture.setData(img)
        self._texture.release()

        # redraw
        self.update()

    def set_colors(self, checkerboard, color1, color2):
        if checkerboard:
            self._u_colors = self._colors_default
        else:
            self._u_colors = (color1, color2)

        self.update()
Exemple #3
0
class EventsGL(QOpenGLWidget):
    "Cube gl widget"

    def __init__(self, parent=None):
        QOpenGLWidget.__init__(self, parent)

        # camera
        self.camera = QtCamera()
        self.camera.position = QVector3D(0.0, 0.0, 3.0)
        self.camera.front = QVector3D(0.0, 0.0, -1.0)
        self.camera.up = QVector3D(0.0, 1.0, 0.0)
        self.camera.movementSensitivity = 0.05

        # shaders etc
        tutoTutoDir = os.path.dirname(__file__)
        tutoPardir = os.path.join(tutoTutoDir, os.pardir)
        tutoPardir = os.path.realpath(tutoPardir)
        mediaDir = os.path.join(tutoPardir, "media")
        shaderDir = os.path.join(mediaDir, "shaders")

        availableShaders = ["cube"]
        self.shaders = {
            name: {
                "fragment": os.path.join(shaderDir, name + ".frag"),
                "vertex": os.path.join(shaderDir, name + ".vert")
            }
            for name in availableShaders
        }
        self.core = "--coreprofile" in QCoreApplication.arguments()
        imdir = os.path.join(mediaDir, "images")
        imFName = "im"
        imageFile1 = os.path.join(imdir, imFName + "0.png")
        self.image1 = QImage(imageFile1).mirrored()
        imageFile2 = os.path.join(imdir, imFName + "1.png")
        self.image2 = QImage(imageFile2).mirrored()

        # opengl data related
        self.context = QOpenGLContext()
        self.vao = QOpenGLVertexArrayObject()
        self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        self.program = QOpenGLShaderProgram()
        self.texture1 = None
        self.texture2 = None
        self.texUnit1 = 0
        self.texUnit2 = 1

        # vertex data
        self.cubeVertices = np.array(
            [
                # pos vec3 || texcoord vec2
                -0.5,
                -0.5,
                -0.5,
                0.0,
                0.0,
                0.5,
                -0.5,
                -0.5,
                1.0,
                0.0,
                0.5,
                0.5,
                -0.5,
                1.0,
                1.0,
                0.5,
                0.5,
                -0.5,
                1.0,
                1.0,
                -0.5,
                0.5,
                -0.5,
                0.0,
                1.0,
                -0.5,
                -0.5,
                -0.5,
                0.0,
                0.0,
                -0.5,
                -0.5,
                0.5,
                0.0,
                0.0,
                0.5,
                -0.5,
                0.5,
                1.0,
                0.0,
                0.5,
                0.5,
                0.5,
                1.0,
                1.0,
                0.5,
                0.5,
                0.5,
                1.0,
                1.0,
                -0.5,
                0.5,
                0.5,
                0.0,
                1.0,
                -0.5,
                -0.5,
                0.5,
                0.0,
                0.0,
                -0.5,
                0.5,
                0.5,
                1.0,
                0.0,
                -0.5,
                0.5,
                -0.5,
                1.0,
                1.0,
                -0.5,
                -0.5,
                -0.5,
                0.0,
                1.0,
                -0.5,
                -0.5,
                -0.5,
                0.0,
                1.0,
                -0.5,
                -0.5,
                0.5,
                0.0,
                0.0,
                -0.5,
                0.5,
                0.5,
                1.0,
                0.0,
                0.5,
                0.5,
                0.5,
                1.0,
                0.0,
                0.5,
                0.5,
                -0.5,
                1.0,
                1.0,
                0.5,
                -0.5,
                -0.5,
                0.0,
                1.0,
                0.5,
                -0.5,
                -0.5,
                0.0,
                1.0,
                0.5,
                -0.5,
                0.5,
                0.0,
                0.0,
                0.5,
                0.5,
                0.5,
                1.0,
                0.0,
                -0.5,
                -0.5,
                -0.5,
                0.0,
                1.0,
                0.5,
                -0.5,
                -0.5,
                1.0,
                1.0,
                0.5,
                -0.5,
                0.5,
                1.0,
                0.0,
                0.5,
                -0.5,
                0.5,
                1.0,
                0.0,
                -0.5,
                -0.5,
                0.5,
                0.0,
                0.0,
                -0.5,
                -0.5,
                -0.5,
                0.0,
                1.0,
                -0.5,
                0.5,
                -0.5,
                0.0,
                1.0,
                0.5,
                0.5,
                -0.5,
                1.0,
                1.0,
                0.5,
                0.5,
                0.5,
                1.0,
                0.0,
                0.5,
                0.5,
                0.5,
                1.0,
                0.0,
                -0.5,
                0.5,
                0.5,
                0.0,
                0.0,
                -0.5,
                0.5,
                -0.5,
                0.0,
                1.0
            ],
            dtype=ctypes.c_float)
        # cube worldSpace coordinates
        self.cubeCoords = [
            QVector3D(0.2, 1.1, -1.0),
            QVector3D(2.0, 5.0, -15.0),
            QVector3D(-1.5, -2.2, -2.5),
            QVector3D(-3.8, -2.0, -12.3),
            QVector3D(2.4, -0.4, -3.5),
            QVector3D(-1.7, 3.0, -7.5),
            QVector3D(1.3, -2.0, -2.5),
            QVector3D(1.5, 2.0, -2.5),
            QVector3D(1.5, 0.2, -1.5),
            QVector3D(-1.3, 1.0, -1.5)
        ]
        self.rotateVector = QVector3D(0.7, 0.2, 0.5)

    def loadShader(self, shaderName: str, shaderType: str):
        "Load shader"
        shader = self.shaders[shaderName]
        shaderSourcePath = shader[shaderType]
        if shaderType == "vertex":
            shader = QOpenGLShader(QOpenGLShader.Vertex)
        else:
            shader = QOpenGLShader(QOpenGLShader.Fragment)
        #
        isCompiled = shader.compileSourceFile(shaderSourcePath)

        if isCompiled is False:
            print(shader.log())
            raise ValueError(
                "{0} shader {2} known as {1} is not compiled".format(
                    shaderType, shaderName, shaderSourcePath))
        return shader

    def useShaders(self, shaderProgram: QOpenGLShaderProgram,
                   shaders: {"shaderName": ["shaderType"]}, attrLocs: dict):
        ""
        print("program shaders: ", shaderProgram.shaders())
        for shaderName, shaderTypes in shaders.items():
            #
            if len(shaderTypes) == 2:
                self.useShaderSingleName(shaderProgram=shaderProgram,
                                         shaderName=shaderName,
                                         attrLocs=attrLocs)
            elif len(shaderTypes) == 1:
                shaderType = shaderTypes[0]
                if shaderType == "vertex":
                    shader = self.loadVertexShader(shaderName)
                else:
                    shader = self.loadFragmentShader(shaderName)

                shaderProgram.addShader(shader)
                # adding shader
                self.bindLinkProgram(shaderProgram, attrLocs)

    def loadVertexShader(self, shaderName: str):
        "load vertex shader"
        return self.loadShader(shaderName, "vertex")

    def loadFragmentShader(self, shaderName: str):
        "load fragment shader"
        return self.loadShader(shaderName, "fragment")

    def getGlInfo(self):
        "Get opengl info"
        info = """
            Vendor: {0}
            Renderer: {1}
            OpenGL Version: {2}
            Shader Version: {3}
            """.format(pygl.glGetString(pygl.GL_VENDOR),
                       pygl.glGetString(pygl.GL_RENDERER),
                       pygl.glGetString(pygl.GL_VERSION),
                       pygl.glGetString(pygl.GL_SHADING_LANGUAGE_VERSION))
        return info

    def moveCamera(self, direction: str):
        "Move camera to certain direction and update gl widget"
        self.camera.move(direction, deltaTime=0.05)
        self.update()

    def turnAround(self, x: float, y: float):
        ""
        self.camera.lookAround(xoffset=x, yoffset=y, pitchBound=True)
        self.update()

    def rotateCubes(self, xval: float, yval: float, zval: float):
        ""
        self.rotateVector.setZ(zval)
        self.rotateVector.setY(yval)
        self.rotateVector.setX(xval)
        self.update()

    def cleanUpGl(self):
        "Clean up everything"
        self.context.makeCurrent()
        self.vbo.destroy()
        self.texture1.destroy()
        self.texture2.destroy()
        self.vao.destroy()
        del self.program
        self.program = None
        self.doneCurrent()

    def resizeGL(self, width: int, height: int):
        "Resize the viewport"
        funcs = self.context.functions()
        funcs.glViewport(0, 0, width, height)

    def initializeGL(self):
        print('gl initial')
        print(self.getGlInfo())

        # create context and make it current
        self.context.create()
        self.context.aboutToBeDestroyed.connect(self.cleanUpGl)

        # initialize functions
        funcs = self.context.functions()
        funcs.initializeOpenGLFunctions()
        funcs.glClearColor(0.0, 0.4, 0.4, 0)
        funcs.glEnable(pygl.GL_DEPTH_TEST)
        funcs.glEnable(pygl.GL_TEXTURE_2D)

        # create uniform values for shaders
        # deal with shaders

        # cube shader
        self.program = QOpenGLShaderProgram(self.context)
        vshader = self.loadVertexShader("cube")
        fshader = self.loadFragmentShader("cube")
        self.program.addShader(vshader)  # adding vertex shader
        self.program.addShader(fshader)  # adding fragment shader
        self.program.bindAttributeLocation("aPos", 0)
        self.program.bindAttributeLocation("aTexCoord", 1)

        isLinked = self.program.link()
        print("cube shader program is linked: ", isLinked)
        # bind the program
        self.program.bind()

        self.program.setUniformValue('myTexture1', self.texUnit1)
        self.program.setUniformValue('myTexture2', self.texUnit2)
        #
        # deal with vaos and vbo
        # vbo
        isVbo = self.vbo.create()
        isVboBound = self.vbo.bind()

        floatSize = ctypes.sizeof(ctypes.c_float)

        # allocate space on vbo buffer
        self.vbo.allocate(self.cubeVertices.tobytes(),
                          floatSize * self.cubeVertices.size)
        self.vao.create()
        vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
        funcs.glEnableVertexAttribArray(0)  # viewport
        funcs.glVertexAttribPointer(0, 3, int(pygl.GL_FLOAT),
                                    int(pygl.GL_FALSE), 5 * floatSize,
                                    VoidPtr(0))
        funcs.glEnableVertexAttribArray(1)
        funcs.glVertexAttribPointer(1, 2, int(pygl.GL_FLOAT),
                                    int(pygl.GL_FALSE), 5 * floatSize,
                                    VoidPtr(3 * floatSize))
        # deal with textures
        # first texture
        self.texture1 = QOpenGLTexture(QOpenGLTexture.Target2D)
        self.texture1.create()
        self.texture1.bind(self.texUnit1)
        self.texture1.setData(self.image1)
        self.texture1.setMinMagFilters(QOpenGLTexture.Nearest,
                                       QOpenGLTexture.Nearest)
        self.texture1.setWrapMode(QOpenGLTexture.DirectionS,
                                  QOpenGLTexture.Repeat)
        self.texture1.setWrapMode(QOpenGLTexture.DirectionT,
                                  QOpenGLTexture.Repeat)

        # second texture
        self.texture2 = QOpenGLTexture(QOpenGLTexture.Target2D)
        self.texture2.create()
        self.texture2.bind(self.texUnit2)
        self.texture2.setData(self.image2)
        self.texture2.setMinMagFilters(QOpenGLTexture.Linear,
                                       QOpenGLTexture.Linear)
        self.texture2.setWrapMode(QOpenGLTexture.DirectionS,
                                  QOpenGLTexture.Repeat)
        self.texture2.setWrapMode(QOpenGLTexture.DirectionT,
                                  QOpenGLTexture.Repeat)

        self.vbo.release()
        vaoBinder = None
        print("gl initialized")

    def paintGL(self):
        "drawing loop"
        funcs = self.context.functions()

        # clean up what was drawn
        funcs.glClear(pygl.GL_COLOR_BUFFER_BIT | pygl.GL_DEPTH_BUFFER_BIT)
        self.vao.bind()
        self.vbo.bind()

        # actual drawing
        self.program.bind()
        # set projection matrix
        projectionMatrix = QMatrix4x4()
        projectionMatrix.perspective(self.camera.zoom,
                                     self.width() / self.height(), 0.2, 100.0)

        self.program.setUniformValue('projection', projectionMatrix)

        # set view/camera matrix
        viewMatrix = self.camera.getViewMatrix()
        self.program.setUniformValue('view', viewMatrix)

        # bind textures
        for i, pos in enumerate(self.cubeCoords):
            #
            cubeModel = QMatrix4x4()
            cubeModel.translate(pos)
            angle = 30 * i
            cubeModel.rotate(angle, self.rotateVector)
            self.program.setUniformValue("model", cubeModel)
            self.texture1.bind(self.texUnit1)
            self.texture2.bind(self.texUnit2)
            funcs.glDrawArrays(pygl.GL_TRIANGLES, 0, 36)
        self.vbo.release()
        self.program.release()
        self.texture1.release()
        self.texture2.release()
Exemple #4
0
class Viewport(View, QOpenGLWidget):
    def __init__(self,
                 scene,
                 updateFpsDisplay,
                 renderSettings=Rendersettings(),
                 parent=None):
        View.__init__(self, scene)
        QOpenGLWidget.__init__(self, parent)
        self.renderImage = QImage()
        self.renderSettings = renderSettings
        self.updateFpsDisplay = updateFpsDisplay
        self.shaderProgram = QOpenGLShaderProgram()
        self.viewPortShaderProgram = QOpenGLShaderProgram()
        self.lightShaderProgram = QOpenGLShaderProgram()
        self.eyeLoc = QVector3D(0.0, 0.0, 5.0)
        self.pressLoc = QVector2D()
        self.isRendering = False
        self.viewportPlane = ViewportPlane()
        self.viewportTexture = None
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.checkRender)
        self.scene.registerView(self, [
            UpdateType.MATERIAL_CHANGE, UpdateType.MATERIAL_CREATE,
            UpdateType.MATERIAL_DELETE, UpdateType.OBJECT_CREATE,
            UpdateType.OBJECT_DELETE, UpdateType.OBJECT_TRANSFORM,
            UpdateType.CAMERA_CHANGE, UpdateType.LIGHT_CHANGE,
            UpdateType.SCENE_LOAD
        ])
        self.timestamps = None
        self.fpsWindow = 10
        self.renderer = None
        self.renderStartTime = None

    def initializeGL(self):
        self.glFunctions = self.context().functions()
        self.glFunctions.glEnable(GL.GL_DEPTH_TEST)
        self.glFunctions.glEnable(GL.GL_CULL_FACE)
        self.glFunctions.glClearColor(0.15, 0.25, 0.3, 1.0)

        vertSuccess = self.shaderProgram.addShaderFromSourceFile(
            QOpenGLShader.Vertex, "editor/ui/shaders/vertex.vert")
        fragSuccess = self.shaderProgram.addShaderFromSourceFile(
            QOpenGLShader.Fragment, "editor/ui/shaders/fragment.frag")

        print("Vertex shader compilation {}".format(
            "successful" if vertSuccess else "failed"))
        print("Fragment shader compilation {}".format(
            "successful" if fragSuccess else "failed"))
        print("Program linking {}".format(
            "successful" if self.shaderProgram.link() else "failed"))

        vertSuccess = self.viewPortShaderProgram.addShaderFromSourceFile(
            QOpenGLShader.Vertex, "editor/ui/shaders/viewportvertex.vert")
        fragSuccess = self.viewPortShaderProgram.addShaderFromSourceFile(
            QOpenGLShader.Fragment, "editor/ui/shaders/viewportfragment.frag")

        print("Viewport vertex shader compilation {}".format(
            "successful" if vertSuccess else "failed"))
        print("Viewport fragment shader compilation {}".format(
            "successful" if fragSuccess else "failed"))
        print("Viewport program linking {}".format(
            "successful" if self.viewPortShaderProgram.link() else "failed"))

        vertSuccess = self.lightShaderProgram.addShaderFromSourceFile(
            QOpenGLShader.Vertex, "editor/ui/shaders/vertex.vert")
        fragSuccess = self.lightShaderProgram.addShaderFromSourceFile(
            QOpenGLShader.Fragment, "editor/ui/shaders/lightfragment.frag")

        print("Light vertex shader compilation {}".format(
            "successful" if vertSuccess else "failed"))
        print("Light fragment shader compilation {}".format(
            "successful" if fragSuccess else "failed"))
        print("Light program linking {}".format(
            "successful" if self.viewPortShaderProgram.link() else "failed"))

        self.shaderProgram.release()
        self.viewPortShaderProgram.release()

        # Call all drawable Objects initGL
        for c in drawableClasses:
            if isinstance(c, ViewportPlane):
                c.initGL(self.viewPortShaderProgram)
            else:
                c.initGL(self.shaderProgram)

    def resizeGL(self, w, h):
        self.scene.getCamera().setAspect(w, h)
        if self.viewportTexture and self.context().isValid():
            self.viewportTexture.destroy()
        if self.context().isValid():
            self.renderImage = QImage(w, h, QImage.Format_RGB32)
            self.renderImage.fill(Qt.black)
            self.viewportTexture = QOpenGLTexture(QOpenGLTexture.Target2D)
            self.viewportTexture.setSize(w, h)
            self.viewportTexture.setBorderColor(0, 0, 0, 255)
            self.viewportTexture.setWrapMode(QOpenGLTexture.ClampToBorder)
            self.viewportTexture.setMinificationFilter(QOpenGLTexture.Nearest)
            self.viewportTexture.setMagnificationFilter(QOpenGLTexture.Nearest)
            self.viewportTexture.setData(self.renderImage,
                                         QOpenGLTexture.DontGenerateMipMaps)
            self.updateRenderResolution(w, h)

    def paintGL(self):
        self.glFunctions.glClear(GL.GL_COLOR_BUFFER_BIT
                                 | GL.GL_DEPTH_BUFFER_BIT)
        if not self.isRendering:
            self.shaderProgram.bind()
            self.shaderProgram.setUniformValue(
                "eyePos",
                self.scene.getCamera().getTranslation())
            self.shaderProgram.release()

            for obj in self.scene.getObjectIterator():
                if isinstance(obj, Light):
                    obj.draw(self.lightShaderProgram, self.glFunctions)
                else:
                    obj.draw(self.shaderProgram, self.glFunctions)
        else:
            self.viewportTexture.bind()
            self.viewportPlane.draw(self.viewPortShaderProgram,
                                    self.glFunctions)
            self.viewportTexture.release()

    def mousePressEvent(self, e):
        self.pressLoc = QVector2D(e.localPos())
        super().mousePressEvent(e)

    def mouseMoveEvent(self, e):
        if e.buttons() == Qt.LeftButton:
            currPos = QVector2D(e.localPos())
            diff = currPos - self.pressLoc
            diff *= 0.5
            self.pressLoc = currPos
            self.scene.getCamera().rotate(diff.x(), diff.y())

    def wheelEvent(self, e):
        if e.modifiers() == Qt.ShiftModifier:
            self.scene.getCamera().zoom(-e.angleDelta().y() / 45.0)
        else:
            self.scene.getCamera().track(-e.angleDelta().y() / 600.0)

    def notify(self, updateType):
        if updateType == UpdateType.SCENE_LOAD:
            self.scene.getCamera().setAspect(self.width(), self.height())
        if self.isRendering:
            if updateType == UpdateType.CAMERA_CHANGE:
                self.cameraToRenderer()
            elif updateType in [
                    UpdateType.OBJECT_TRANSFORM, UpdateType.OBJECT_CREATE,
                    UpdateType.OBJECT_DELETE, UpdateType.MATERIAL_CREATE,
                    UpdateType.MATERIAL_DELETE, UpdateType.MATERIAL_CHANGE,
                    UpdateType.LIGHT_CHANGE
            ]:
                self.renderer.stop()
                self.renderer.clearMaterials()
                self.renderer.clearObjects()
                for mat in self.scene.getMaterialIterator():
                    self.materialToRenderer(mat)
                for obj in self.scene.getObjectIterator():
                    self.sceneObjectToRenderer(obj)
                self.cameraToRenderer()
                self.renderer.start()
            self.timestamps = [[time.time(), 0]]
            self.renderStartTime = time.time()

        self.update()

    def startRender(self):
        if self.isRendering:
            return
        self.isRendering = True
        self.renderer = PyRenderer(self.width(), self.height())
        self.renderer.setRenderSettings(self.renderSettings)
        self.timestamps = [[time.time(), 0]]
        self.cameraToRenderer()
        self.update()
        for mat in self.scene.getMaterialIterator():
            self.materialToRenderer(mat)
        for obj in self.scene.getObjectIterator():
            self.sceneObjectToRenderer(obj)
        self.renderer.start()
        self.timer.setInterval(20)
        self.timer.start()
        self.renderStartTime = time.time()

    def stopRender(self):
        if not self.isRendering:
            return
        self.timer.stop()
        self.isRendering = False
        self.renderer = None
        self.resizeGL(self.width(), self.height())
        self.update()
        self.updateFpsDisplay('')

    def updateRenderResolution(self, width, height):
        if not self.isRendering:
            return
        self.renderer.setResolution(width, height)
        self.timestamps = [[time.time(), 0]]

    def checkRender(self):
        if not self.isRendering:
            return
        imageArray, numIterations = self.renderer.getData(
            self.width(), self.height())
        if numIterations < 0 or imageArray.shape != (self.height(),
                                                     self.width()):
            self.timer.start()
            return
        if self.viewportTexture and self.context().isValid():
            self.viewportTexture.destroy()
        if self.context().isValid():
            self.renderImage = QImage(self.width(), self.height(),
                                      QImage.Format_RGB32)
            copyArray = np.ndarray(shape=(self.height(), self.width()),
                                   dtype=np.uint32,
                                   buffer=self.renderImage.bits())
            copyArray[0:, 0:] = imageArray[0:, 0:]
            self.viewportTexture = QOpenGLTexture(QOpenGLTexture.Target2D)
            self.viewportTexture.setSize(self.width(), self.height())
            self.viewportTexture.setBorderColor(0, 0, 0, 255)
            self.viewportTexture.setWrapMode(QOpenGLTexture.ClampToBorder)
            self.viewportTexture.setMinificationFilter(QOpenGLTexture.Nearest)
            self.viewportTexture.setMagnificationFilter(QOpenGLTexture.Nearest)
            self.viewportTexture.setData(self.renderImage,
                                         QOpenGLTexture.DontGenerateMipMaps)
            if len(self.timestamps) == self.fpsWindow:
                self.timestamps.pop()
            self.timestamps.insert(0, [time.time(), numIterations])
            diffs = [(self.timestamps[i][1] - self.timestamps[i + 1][1]) /
                     (self.timestamps[i][0] - self.timestamps[i + 1][0])
                     for i in range(len(self.timestamps) - 1)]
            secs = int(time.time() - self.renderStartTime)
            self.updateFpsDisplay(
                '{: 4.1f} iterations/sec     {:02.0f}:{:02.0f} min    {:7d} iterations    {:4d} x {:4d} pixel'
                .format(
                    sum(diffs) / len(diffs), secs // 60, secs % 60,
                    numIterations, self.width(), self.height()))
        self.update()
        self.timer.start()

    def closeViewport(self):
        self.stopRender()
        if self.viewportTexture and self.context().isValid():
            self.viewportTexture.destroy()

    def cameraToRenderer(self):
        cam = self.scene.getCamera()
        worldPos = cam.getTranslation()
        targetPos = self.scene.getFocusObject().getTranslation()
        self.renderer.updateCamera(
            [worldPos.x(), worldPos.y(),
             worldPos.z()],
            [targetPos.x(), targetPos.y(),
             targetPos.z()], cam.fovY, cam.fStop, cam.focusDistance,
            cam.stratificationLevel)

    def sceneObjectToRenderer(self, sceneObject):
        objType = None
        materialId = None
        lightIntensity = 0.0
        lightColor = [0.0, 0.0, 0.0]
        isVisible = True
        if isinstance(sceneObject, Camera):
            objType = SceneObjectType.CAMERA
            materialId = -1
        if isinstance(sceneObject, Sphere):
            objType = SceneObjectType.SPHERE
            materialId = sceneObject.material.id
        if isinstance(sceneObject, Cube):
            objType = SceneObjectType.CUBE
            materialId = sceneObject.material.id
        if isinstance(sceneObject, Plane):
            objType = SceneObjectType.PLANE
            materialId = sceneObject.material.id
        if isinstance(sceneObject, Disc):
            objType = SceneObjectType.DISC
            materialId = sceneObject.material.id
        if isinstance(sceneObject, Light):
            objType = sceneObject.getEmitterShape()
            materialId = sceneObject.material.id
            lightIntensity = sceneObject.getIntensity()
            colVec = sceneObject.getColor()
            lightColor = [colVec.x(), colVec.y(), colVec.z()]
            isVisible = sceneObject.getVisibility()

        matrixArray = sceneObject.calculateModel().transposed().copyDataTo()
        self.renderer.addObject(sceneObject.id, materialId, objType,
                                matrixArray, lightIntensity, lightColor,
                                sceneObject.area(), isVisible)

    def materialToRenderer(self, material):
        if type(material) == DiffuseMaterial:
            col = material.getEditorColor()
            self.renderer.addDiffuseMaterial(
                material.id, [col.x(), col.y(), col.z()])
        if type(material) == SpecularMaterial:
            reflectionColor = material.getReflectionColor()
            transmissionColor = material.getTransmissionColor()
            self.renderer.addSpecularMaterial(material.id, [
                reflectionColor.x(),
                reflectionColor.y(),
                reflectionColor.z()
            ], [
                transmissionColor.x(),
                transmissionColor.y(),
                transmissionColor.z()
            ], material.getIOR())

    def setRenderSettings(self, renderSettings):
        self.renderSettings = renderSettings
        if self.isRendering:
            self.renderer.setRenderSettings(self.renderSettings)

    def getRenderSettings(self):
        return self.renderSettings

    def saveImage(self):
        if not self.isRendering:
            return

        saveImage = self.renderImage.mirrored(horizontal=True)
        fileTuple = QFileDialog.getSaveFileName(
            self,
            'Save destination',
            dir="./images",
            filter='Images (*.png *.jpg);;All files (*)')
        if fileTuple[0]:
            filename = fileTuple[0]
            if '.' not in filename:
                filename += '.png'
            saveImage.save(filename, quality=100)
class TextureGL(QOpenGLWidget):
    "Texture loading opengl widget"

    def __init__(self, parent=None):
        "Constructor"
        QOpenGLWidget.__init__(self, parent)
        tutoTutoDir = os.path.dirname(__file__)
        tutoPardir = os.path.join(tutoTutoDir, os.pardir)
        tutoPardir = os.path.realpath(tutoPardir)
        mediaDir = os.path.join(tutoPardir, "media")
        shaderDir = os.path.join(mediaDir, "shaders")
        #
        availableShaders = ["texture"]
        self.shaders = {
            name: {
                "fragment": os.path.join(shaderDir, name + ".frag"),
                "vertex": os.path.join(shaderDir, name + ".vert")
            }
            for name in availableShaders
        }
        imdir = os.path.join(mediaDir, "images")
        imFName = "im"
        imageFile = os.path.join(imdir, imFName + "0.png")
        self.image = QImage(imageFile).mirrored()
        self.core = "--coreprofile" in QCoreApplication.arguments()

        # opengl data related
        self.context = QOpenGLContext()
        self.program = QOpenGLShaderProgram()
        self.vao = QOpenGLVertexArrayObject()
        self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        self.texture = None
        self.indices = np.array(
            [
                0,
                1,
                3,  # first triangle
                1,
                2,
                3  # second triangle
            ],
            dtype=ctypes.c_uint)

        # vertex data of the panel that would hold the image
        self.vertexData = np.array(
            [
                # viewport position || texture coords
                0.5,
                0.5,
                0.0,
                1.0,
                1.0,  # top right
                0.5,
                -0.5,
                0.0,
                1.0,
                0.0,  # bottom right
                -0.5,
                -0.5,
                0.0,
                0.0,
                0.0,  # bottom left
                -0.5,
                0.5,
                0.0,
                0.0,
                1.0  # top left
            ],
            dtype=ctypes.c_float)

    def loadShader(self, shaderName: str, shaderType: str):
        "Load shader"
        shader = self.shaders[shaderName]
        shaderSourcePath = shader[shaderType]
        if shaderType == "vertex":
            shader = QOpenGLShader(QOpenGLShader.Vertex)
        else:
            shader = QOpenGLShader(QOpenGLShader.Fragment)
        #
        isCompiled = shader.compileSourceFile(shaderSourcePath)

        if isCompiled is False:
            print(shader.log())
            raise ValueError(
                "{0} shader {2} known as {1} is not compiled".format(
                    shaderType, shaderName, shaderSourcePath))
        return shader

    def loadVertexShader(self, shaderName: str):
        "load vertex shader"
        return self.loadShader(shaderName, "vertex")

    def loadFragmentShader(self, shaderName: str):
        "load fragment shader"
        return self.loadShader(shaderName, "fragment")

    def getGlInfo(self):
        "Get opengl info"
        info = """
            Vendor: {0}
            Renderer: {1}
            OpenGL Version: {2}
            Shader Version: {3}
            """.format(pygl.glGetString(pygl.GL_VENDOR),
                       pygl.glGetString(pygl.GL_RENDERER),
                       pygl.glGetString(pygl.GL_VERSION),
                       pygl.glGetString(pygl.GL_SHADING_LANGUAGE_VERSION))
        return info

    def cleanUpGl(self):
        "Clean up everything"
        self.context.makeCurrent()
        del self.program
        self.program = None
        self.texture.release()
        self.doneCurrent()

    def resizeGL(self, width: int, height: int):
        "Resize the viewport"
        funcs = self.context.functions()
        funcs.glViewport(0, 0, width, height)

    def initializeGL(self):
        "Initialize opengl "
        print('gl initial')
        print(self.getGlInfo())
        # create context and make it current
        self.context.create()
        self.context.aboutToBeDestroyed.connect(self.cleanUpGl)

        # initialize functions
        funcs = self.context.functions()
        funcs.initializeOpenGLFunctions()
        funcs.glClearColor(1, 0, 1, 1)

        # shader
        shaderName = "texture"
        vshader = self.loadVertexShader(shaderName)
        fshader = self.loadFragmentShader(shaderName)

        # create shader program
        self.program = QOpenGLShaderProgram(self.context)
        self.program.addShader(vshader)
        self.program.addShader(fshader)

        # bind attribute location
        self.program.bindAttributeLocation("aPos", 0)
        self.program.bindAttributeLocation("aTexCoord", 1)

        # link shader program
        isLinked = self.program.link()
        print("shader program is linked: ", isLinked)

        # activate shader program to set uniform an attribute values
        self.program.bind()
        self.program.setUniformValue('myTexture', 0)

        # vbo
        isVbo = self.vbo.create()
        isVboBound = self.vbo.bind()

        floatSize = ctypes.sizeof(ctypes.c_float)

        # allocate vbo
        self.vbo.allocate(self.vertexData.tobytes(),
                          floatSize * self.vertexData.size)

        # texture new school
        self.texture = QOpenGLTexture(QOpenGLTexture.Target2D)
        self.texture.create()
        # new school
        self.texture.bind()
        self.texture.setData(self.image)
        self.texture.setMinMagFilters(QOpenGLTexture.Linear,
                                      QOpenGLTexture.Linear)
        self.texture.setWrapMode(QOpenGLTexture.DirectionS,
                                 QOpenGLTexture.Repeat)
        self.texture.setWrapMode(QOpenGLTexture.DirectionT,
                                 QOpenGLTexture.Repeat)

    def paintGL(self):
        "paint gl"
        funcs = self.context.functions()
        # clean up what was drawn
        funcs.glClear(pygl.GL_COLOR_BUFFER_BIT)

        self.program.bind()
        self.program.enableAttributeArray(0)
        self.program.enableAttributeArray(1)
        floatSize = ctypes.sizeof(ctypes.c_float)

        # set attribute values
        self.program.setAttributeBuffer(
            0,  # viewport position
            pygl.GL_FLOAT,  # coord type
            0,  # offset
            3,
            5 * floatSize)
        self.program.setAttributeBuffer(
            1,  # viewport position
            pygl.GL_FLOAT,  # coord type
            3 * floatSize,  # offset
            2,
            5 * floatSize)
        # bind texture
        self.texture.bind()
        funcs.glDrawElements(pygl.GL_TRIANGLES, self.indices.size,
                             pygl.GL_UNSIGNED_INT, self.indices.tobytes())