Search code examples
openglgraphicslinepyopenglopengl-compat

Draw variable number of lines - PyOpenGL


I want to create a function that user can call multiple times, say drawLine(x,y), and these all lines should be displayed at once(no replacement). I am new to PyOpenGL (and OpenGL) and I'm not sure how to go about doing this. As of now, I know how to draw a fixed number of lines using something like this :

def main_loop(window):
    while (
        glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS and
        not glfw.window_should_close(window)
    ):
        glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        glBegin(GL_LINES)
        glVertex2f(0.0,0.0)
        glVertex2f(1.0,1.0)
        glEnd()

        glfw.swap_buffers(window)
        glfw.poll_events()

Here, I can repeat the glBegin(GL_LINES) - glEnd() blocks multiple times to draw a fixed number of lines with fixed parameters. But how to do the variable line task?

In other words, I want to create a function, which upon being called with x and y coordinates, adds a line to the bunch of lines already being displayed on the screen. This function may be called multiple times according to user interaction. The only way I can think of adding lines is inserting glBegin-glEnd blocks in this main_loop function(shown in code above), but how to do that at runtime?


Solution

  • You have to redraw the entire scene in every frame. Hence you need a list where you store the points for the lines.

    Create a function which can draw a GL_LINE_STRIP. The argument to the function is a list of vertices:

    def draw_line(vertices):
        glBegin(GL_LINE_STRIP)
        for vertex in vertices:
            glVertex2f(*vertex)
        glEnd()
    

    Define an empty list for the verices:

    line_vertices = []
    

    Add a new point to the line by user interaction. For instnace when the mouse button is pressed:

    def onMouseButton(win, button, action, mods):
        global line_vertices
        
        if button == glfw.MOUSE_BUTTON_LEFT:
            if action == glfw.PRESS:
                line_vertices.append(glfw.get_cursor_pos(win))
    

    Draw the line in the main application loop:

    while not glfwWindowShouldClose(window):
        # [...]
    
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        draw_line(line_vertices + [glfw.get_cursor_pos(window)])  
    

    Minimal example:

    import glfw
    from glfw.GLFW import *
    from OpenGL.GL import *
    
    def draw_line(vertices):
        glBegin(GL_LINE_STRIP)
        for vertex in vertices:
            glVertex2f(*vertex)
        glEnd()
    
    line_vertices = []
    
    def onMouseButton(win, button, action, mods):
        global line_vertices
        
        if button == glfw.MOUSE_BUTTON_LEFT:
            if action == glfw.PRESS:
                line_vertices.append(glfw.get_cursor_pos(win))
    
    glfw.init()
    display_size = (640, 480)
    window = glfw.create_window(*display_size, "OpenGL window", None, None)
    
    glfw.make_context_current(window)
    glfw.set_mouse_button_callback(window, onMouseButton)
    
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0, display_size[0], display_size[1], 0, -1, 1)
    
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    
    while not glfwWindowShouldClose(window):
        
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        draw_line(line_vertices + [glfw.get_cursor_pos(window)])  
    
        glfwSwapBuffers(window)
        glfwPollEvents()
    
    glfw.terminate()