An Image Object


To make the new texture functions defined easier to use, I use an Image object that abstracts away some of the details, and adds some additional functionality.

Image.py:
    1 from OpenGL.GL import *
2 import gutil
3 import os
4
5
6 class Image:
7 def __init__(self, texname=None):
8 filename = os.path.join('data', texname)
9 filename += ".png"
10
11 self.texture, self.width, self.height = gutil.loadImage(filename)
12 self.displayList = gutil.createTexDL(self.texture, self.width, self.height)
13
14
15 def __del__(self):
16 if self.texture != None:
17 gutil.delTexture(self.texture)
18 self.texture = None
19 if self.displayList != None:
20 gutil.delDL(self.displayList)
21 self.displayList = None
Lets begin by looking at the constructor and destructor methods of our class.

In order to simplify things, the class makes some assumptions about the type of images that we want to load.  Namely, it assumes that the images are named with '.png' at the end, and are located in the 'data' directory.  You can see the code that handles these assumptions on lines 8 and 9.

This means that in order to load an image named 'data/alien.png' we pass the class constructor the string 'alien'.

Line 11 loads the texture, and captures it's id, width and heigh.  Line 12 then uses this data to get a display list id.

We have also defined a destructor that uses our cleanup functions.  With this in place, Python's memory management will automatically take care of destroying any stray OpenGL objects we created.

Now lets take a look at the draw method.

   24     def draw(self, pos=None, width = None, height = None, color=(1,1,1,1), rotation=0, rotationCenter=None):
25 glColor4fv(color)
26
27 if pos:
28 glLoadIdentity()
29 glTranslate(pos[0],pos[1],0)
30
31 if rotation != 0:
32 if rotationCenter == None:
33 rotationCenter = (self.width / 2, self.height / 2)
34 (w,h) = rotationCenter
35 glTranslate(rotationCenter[0],rotationCenter[1],0)
36 glRotate(rotation,0,0,-1)
37 glTranslate(-rotationCenter[0],-rotationCenter[1],0)
38
39 if width or height:
40 if not width:
41 width = self.width
42 elif not height:
43 height = self.height
44
45 glScalef(width/(self.width*1.0), height/(self.height*1.0), 1.0)
46
47
48 glCallList(self.displayList)
The method expands significantly on the glCallList technique that we used in the last example.  We add support of specifying the drawing position, the drawing color, a rotation, and a width and height for the image.

Lets start by looking at the arguments and how they are used.

Pos specifies the x, y coordinates that we will draw at.  Line 27 shows that including values is optional.  If a position isn't specified, the texture will draw at whatever location OpenGL is currently at.  If you do give it a position, lines 28 and 29 reset OpenGL, and then move to the coordinates to draw.

Color obviously specifes a 4 term color for the image.  By default it is set to all white with 100% opacity.

Width and height are also optional arguments.  If no arguments are given, the sprite is rendered as it's default size.  However if you adjust the width or the height or both, the glScalef call on line 45 ensures that the image fills the correct number of pixels.

Rotations can be done by specifies the number of degrees to rotate the image.  RotationCenter is an optional set of x, y coordinates that specify a position to rotate about.  These coordinates are relative to 0,0 being the bottom left corner of the image.  If a RotationCenter is not specified, the centre of the image is used.  You can see the logic for performing the rotation at the correct offset on lines 31 through 37.

The last line of the draw function simply calls the display list for the image, causing it to appear on screen with all the appropriate effects applied.

Here is our example program, using the new Image class.

example2b.py:
    1 #!/usr/bin/python
2
3 import gutil
4 import pygame
5 from pygame.locals import *
6 from OpenGL.GL import *
7
8
9 from Image import Image
10
11
12 def main():
13 pygame.init()
14 gutil.initializeDisplay(800, 600)
15
16 done = False
17
18 cow = Image('cow')
19 alien = Image('alien')
20
21 while not done:
22 cow.draw(pos=(100,100),width=128,height=128)
23 alien.draw(pos=(400, 400),rotation=-15,color=(.9,.3,.2,1))
24
25 pygame.display.flip()
26
27 eventlist = pygame.event.get()
28 for event in eventlist:
29 if event.type == QUIT \
30 or event.type == KEYDOWN and event.key == K_ESCAPE:
31 done = True
32
33 if __name__ == '__main__':
34 main()
As you can see, this new example is significantly simpler than the previous one, and it adds some new tricks.

Line 9 we import our Image class.  Then on lines 18 and 19 we create new Image objects for our cow and alien images.

Line 22 draws the cow graphic at screen coordinates 100, 100, with a width of 128 and a height of 128.
Line 23 draws the alien at 400, 400, at it's actual size, with a -15 degree rotation, and a reddish tint.



Up to Intro
Back to Example 2a
On to Example 3a