1
\$\begingroup\$

I've been steadily working through building a basic 2D tile framework in python.

I'm trying to use modern openGL so I have a simple shader and some vertex buffers and it's mostly doing what I want so far. I have a Scene with a method for centering the viewport on a Sprite:

def centre_viewport(self, x, y):
    x = -x+self.viewport_width//2
    y = y-self.viewport_height//2
    if x > 0:
        x = 0
    if y < 0:
        y = 0
    if x - self.viewport_width < -self.width:
        x = -self.width + self.viewport_width
    if y + self.viewport_height > self.height:
        y = self.height - self.viewport_height
    self.viewport_x = x
    self.viewport_y = y
    glViewport(x, y, self.viewport_width, self.viewport_height)

self.viewport_width and _height are set when a scene is added to the window, so they have the same dimensions as whatever window is created.

In my test game, the scene width and height are twice the window dimensions of 704x512. Everything works fine on my main mac development machine. On my work windows machine though, when the sprite moves and causes the scene to scroll, the previously hidden parts of the scene are not drawn:

viewport issue

My draw loop is very simple:

def _draw(self):
    glBindBuffer(GL_ARRAY_BUFFER, self.sprite_vertex_buffer)
    glVertexAttribPointer(self.vertices, 3, GL_FLOAT, GL_FALSE, 0, None)

    glBindBuffer(GL_ARRAY_BUFFER, self.sprite_texture_buffer)
    glVertexAttribPointer(self.tex_coords, 2, GL_FLOAT, GL_TRUE, 0, None)

    glDrawArrays(GL_TRIANGLES, 0, self.sprite_vertex_count)


    glBindBuffer(GL_ARRAY_BUFFER, self.tile_vertex_buffer)
    glVertexAttribPointer(self.vertices, 3, GL_FLOAT, GL_FALSE, 0, None)

    glBindBuffer(GL_ARRAY_BUFFER, self.tile_texture_buffer)
    glVertexAttribPointer(self.tex_coords, 2, GL_FLOAT, GL_TRUE, 0, None)

    glDrawArrays(GL_TRIANGLES, 0, self.tile_vertex_count)

and as mentioned, it works fine on my mac.

Is glViewport() the appropriate way to be doing this, and why isn't it working properly on windows?

\$\endgroup\$
  • \$\begingroup\$ Don't tell me you create a new array buffer every time you draw something \$\endgroup\$ – Bálint Jan 3 '17 at 13:53
  • \$\begingroup\$ Nope, the buffers are created when the scene is first created, then each one is bound and drawn in the _draw() method. There is an _update() method that runs through each sprite to update the positions/tex coords for each sprite (hence the separate tile/sprite buffers) and they are each loaded with new data using glBufferSubData() so that the buffers aren't recreated. That part was all learnt from this question :) \$\endgroup\$ – MalphasWats Jan 3 '17 at 14:00
  • \$\begingroup\$ Please don't take this as a personal attack, but I'm really new to all this and I'm seeing so many comments like this, but so little explanation of how I should be doing it instead, I really have no idea. I've diligently worked through various examples and tutorials and whatnot, trying to piece together everything as best I can. Once I try to go beyond drawing a rotating cube though, everything falls apart because it's really hard to find concrete this is the best way to do this because. I know every application is different, but there seems to be a set of unwritten rules. \$\endgroup\$ – MalphasWats Jan 3 '17 at 14:54
  • 1
    \$\begingroup\$ Okay, then just 1 thing, glGetAttribLocation is very costy, you should only call it once and save the result somewhere \$\endgroup\$ – Bálint Jan 3 '17 at 14:56
  • \$\begingroup\$ Thank you - I'm planning to write all my findings up eventually for clueless noobs that follow :) I've updated my _draw() method to take those out! \$\endgroup\$ – MalphasWats Jan 3 '17 at 15:12
1
\$\begingroup\$

So I solved my problem by doing things differently. I don't really want this to be the accepted answer because it doesn't answer why glViewport() isn't working properly on my Windows machine. However, I have a working viewport now, using a translation matrix:

first I modified my shader to accept a matrix and apply it to each vertex:

        attribute vec3 a_position;
        attribute vec2 a_texCoords;

        uniform mat4 t_matrix;

        varying vec2 v_texCoords; 

        void main() 
        { 
            gl_Position = t_matrix * vec4(a_position, 1); 

            v_texCoords = a_texCoords; 
        }

in my Scene class, I hook into the matrix as part of my initialisation method:

self.transform_matrix = glGetUniformLocation(self.shader_program, "t_matrix")

and then whenever the viewport is moved, I update the matrix:

def centre_viewport(self, x, y):
    x = -x+self.viewport_width//2
    y = y-self.viewport_height//2
    if x > 0:
        x = 0
    if y < 0:
        y = 0
    if x - self.viewport_width < -self.width:
        x = -self.width + self.viewport_width
    if y + self.viewport_height > self.height:
        y = self.height - self.viewport_height
    self.viewport_x = x
    self.viewport_y = y
    #glViewport(x, y, self.viewport_width, self.viewport_height)

    self.viewport_matrix = [
                                1, 0, 0, self.viewport_x * 2.0 / self.viewport_width,
                                0, 1, 0, self.viewport_y * 2.0 / self.viewport_height,
                                0, 0, 1, 0, 
                                0, 0, 0, 1,
                           ]

The matrix then gets uploaded to the GPU each time around the _draw() loop:

def _draw(self):

    glUniformMatrix4fv(self.transform_matrix, 1, True, self.viewport_matrix)

    ...

Notice that the matrix is transposed (3rd parameter set True), I could have constructed the matrix the right way around I suppose, but I think it looks tidier this way!

Ultimately this is a better way to do what I want anyway, because it gives me the option of translating the z axis too, if I want to do things with very tall towers later or something. I can probably offload pixel->ndc coordinate calculations by adding a projection matrix later if things need to be faster (not sure it'll make enough difference to worry about, but the option is there).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.