Wednesday 28 May 2014

Pyglet - moving, rotating and translating images in 2D

Continuing on from the previous tutorial, now we can dig into moving things around in 2D. Why bother learning this when I've covered the same thing in Tkinter tut and when I'm going to cover it again in a pygame tut? Because in pyglet, the same commands for movement and rotation in 2D ALSO APPLY TO 3D, so once we make the jump to 3D we will automatically have access to all the colours, motion and movement actions we're already learned rather than having to grapple with all of them together at once. *

Translation

The translation command takes an object you've created and then moves it by a certain amount. In the previous tut, we controlled the shape and size of our triangle by where we placed it, but imagine if we wanted to move it after we had created it. Then we would have to calculate the location of all the vertices of the object every time it moved. So instead, we create an image close to our origin and use that to control its size and shape and then we move it to where we want in a separate command. This comes in really handy in 3D when figuring out where things are in 3D space and describing that in detail does my head in - so i cheat and use this command instead:

glTranslatef()

The three numbers we pass the method tell OpenGL how far and in what direction we want to move our shape in the order x,y,z. Some helpful tips to remember:
  • x = left or right motion. Negative numbers move left, positive moves right.
  • y = up or down motion. Negative numbers move down, positive moves up.
  • z = motion into or out of the screen (3D only), Negative numbers move into the screen, positive moves out of it.
And yes, because we're in 2D all our transformations currently will leave z =0.0. Try changing it if you like and see what happens, but it's likely your image will disappear because we haven't quite set our system up to display 3D well yet.

Rotation

Rotation is similar to translation except it requires some serious math. So once again, rather than bothering with the details we use an OpenGL command to do all the heavy thinking for us. This time though we have 4 input options:

glRotatef(10.0,0.0,0.0,1.0)

They're:
  1. the number of degrees to move
  2. 1.0 if rotating around the x axis, 0.0 if not
  3. 1.0 if rotating around the y axis, 0.0 if not
  4. 1.0 if rotating around the z axis, 0.0 if not
So a full program making use of both translation and rotation would looks something like:


Note: We've also included 2 other commands 

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

These aren't explicitly necessary - take them out and everything will still work fine. However, once we move into more complicated motion these will become really important, because we're going to have to change between viewpoints and these are the command that 'bring us home' (i.e. back to where we started). For those that are interested, OpenGL works by using a matrix to transform/move/rotate our objects depending of our starting position. If we move to a new location then the same matrix will have a different effect - so we 'come home' by loading the identity matrix in case we've accidentally changed location without realising it. Yes this is why linear algebra is important for computer graphics. No it's not the only reason - but it's one of the prettiest. :) 

Challenge!
Does the order of the translation and the rotation commands matter? Is that weird? Can you figure out why or why not? What happens to your image if you rotate it around the x or y axis? Why?

Movement

Time for another method that I mentioned earlier - update().

def update(dt):
    global rz
    rz += dt * 10
    rz %= 360

ry=0
pyglet.clock.schedule(update)

The update() command every time pyglet's internal clock 'ticks', so if we change the position just a little with every 'tick' as long as it happens fast enough this will look like movement. Here's we're probably better to explain by example:

The details are - we create a global variable called rz (the rotation around the z direction)

Challenge!
What happens if you set rz to be constant??? Why? What would you need to add to get translation working?

*I'm certain this can also be achieved in pygame with or without external libraries (see examples here) and if I ever learn how with that I'll post a tutorial on it.

1 comment:

  1. Here's some nice code a student used to generate an up-spinning fan effect with the triangles described above
    import pyglet
    from pyglet.gl import *

    win = pyglet.window.Window()


    def make_triangle(vertex1, vertex2, vertex3, colour):
    glColor3f(colour[0], colour[1], colour[2])
    pyglet.graphics.draw(3, GL_TRIANGLES, ('v2f', (vertex1[0],vertex1[1],vertex2[0],vertex2[1], vertex3[0], vertex3[1])))
    @win.event
    def on_draw():
    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT)
    glMatrixMode(GL_MODELVIEW)
    #glLoadIdentity()
    glRotatef(ry,0,0,1)
    glTranslatef(rt,rt,0)
    make_triangle((1.0, 1.0), (640.0, 480.0), (640.0, 0.0),(0,1,0))
    make_triangle((480.0, 0.0), (640.0, 480.0), (0.0, 0.0),(1,0,0))
    make_triangle((640.0, 1.0), (480.0, 0.0), (0.0, 0.0),(0,0,100))


    def update(dt):
    global ry
    global rt
    ry += dt * 10
    ry %= 360
    rt += dt * 10

    ry=0
    rt=0
    pyglet.clock.schedule(update)
    pyglet.app.run()

    ReplyDelete