python, gtk and opengl: render after mouseclick

36 Views Asked by At

I have an old program written in python2, gtk2 and opengl1.2. It needs to be updated to modern standards. So I try to learn these. So far I have this example code that does render, but does not update on a mouseclick: the triangle should turn when clicked inside the triangle, but it does not. Why? Can someone please help to modify the code to make the triangle turn?

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib
from OpenGL.GL import *
from OpenGL.GL import shaders
import numpy as np


# Define your vertex and fragment shader source code
vertex_shader_source = """ 
#version 330 core
layout(location = 0) in vec3 a_position;
void main()
{
    gl_Position = vec4(a_position, 1.0);
}
"""

fragment_shader_source = """ 
#version 330 core
out vec4 frag_color;
void main()
{
    frag_color = vec4(1.0, 0.5, 0.2, 1.0);
}
"""

class OpenGLDrawingArea(Gtk.GLArea):
    def __init__(self):
        super().__init__()
        self.connect("realize", self.on_realize)
        self.connect("render", self.on_render)
        self.connect("button-press-event", self.on_button_press_event)
        self.set_has_depth_buffer(False)
        self.set_has_stencil_buffer(False)
        self.set_auto_render(True)
        self.angle = 0.0
        self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)

    def on_realize(self, area):
        ctx = self.get_context()
        ctx.make_current()
        # Compile shaders
        vertex_shader = shaders.compileShader(vertex_shader_source, GL_VERTEX_SHADER)
        fragment_shader = shaders.compileShader(fragment_shader_source, GL_FRAGMENT_SHADER)
        self.shader_program = shaders.compileProgram(vertex_shader, fragment_shader)

        # Define triangle vertices
        self.vertices = np.array([
            -0.5, -0.5, 0.0,
             0.5, -0.5, 0.0,
             0.0,  0.5, 0.0
        ], dtype=np.float32)

        # Create and bind Vertex Array Object
        self.VAO = glGenVertexArrays(1)
        glBindVertexArray(self.VAO)

        # Create and bind Vertex Buffer Object
        self.VBO = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.VBO)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)

        # Specify vertex attribute pointers
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)

        # Unbind buffers and VAO
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindVertexArray(0)

    def on_render(self, area, ctx):
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glUseProgram(self.shader_program)
        glBindVertexArray(self.VAO)

        # Apply rotation
        rotation_matrix = np.array([
            [np.cos(np.radians(self.angle)), -np.sin(np.radians(self.angle)), 0.0],
            [np.sin(np.radians(self.angle)), np.cos(np.radians(self.angle)), 0.0],
            [0.0, 0.0, 1.0]
        ], dtype=np.float32)
        glUniformMatrix3fv(glGetUniformLocation(self.shader_program, "rotation"), 1, GL_TRUE, rotation_matrix)

        glDrawArrays(GL_TRIANGLES, 0, 3)
        glBindVertexArray(0)


    def on_button_press_event(self, area, event):
        if event.button == Gdk.BUTTON_PRIMARY:  # Left mouse button
            # Convert mouse coordinates to OpenGL coordinates
            x, y = event.x, event.y
            print(x,y)
            width, height = area.get_allocated_width(), area.get_allocated_height()
            gl_x = (2.0 * x / width - 1.0)
            gl_y = (1.0 - 2.0 * y / height)

            # Check if the mouse click is inside the triangle
            if self.point_in_triangle(gl_x, gl_y):
                print(gl_x,gl_y)
                self.angle += 10.0  # Rotate clockwise by 10 degrees
                self.queue_render()  # Request redraw


    def point_in_triangle(self, x, y):
        # Check if the point (x, y) is inside the triangle
        def sign(p1, p2, p3):
            return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])

        b1 = sign((x, y), self.vertices[:2], self.vertices[3:]) < 0.0
        b2 = sign((x, y), self.vertices[3:], self.vertices[6:]) < 0.0
        b3 = sign((x, y), self.vertices[6:], self.vertices[:2]) < 0.0

        return (b1 == b2) and (b2 == b3)

class MainWindow(Gtk.Window):
    def __init__(self):
        super().__init__(title="OpenGL in GTK Window")
        self.set_default_size(300, 300)
        self.set_resizable(False)  # Set window to be non-resizable

        self.drawing_area = OpenGLDrawingArea()
        self.add(self.drawing_area)

        self.connect("destroy", Gtk.main_quit)

def main():
    win = MainWindow()
    win.show_all()
    Gtk.main()

if __name__ == "__main__":
    main()
1

There are 1 best solutions below

0
frits On

Solved: the shader was wrong, did not rotate:

vertex_shader_source = """ 
#version 330 core
layout(location = 0) in vec3 a_position;
uniform mat3 rotation;  // Rotation matrix
void main()
{
    vec3 rotated_position = rotation * a_position;  // Apply rotation
    gl_Position = vec4(rotated_position, 1.0);
}
"""