[ USING OPENGL VIA PYGAME ] [ By: Codeboy - codeboy@postmark.net ] [ OpenGL Comments: Willie Tang ] [ CAST OF CHARACTERS ] OpenGL a very widely adopted industry standard 3D graphics API with extensive support on different platforms. Hardware accelerated drivers for OpenGL are readily available for virtually all modern cards on Windows, Mac OS X, and Linux/BSD/*nix. See www.opengl.org Python a very popular, open-source, cross-platform, very high level scripting language (VHLL) with powerful features whose syntax is extremely readable and makes for easy-to-maintain programs. Python is available on just about every platform there is...from Unixes to Windows to Mac to PalmOS. See www.python.org SDL Simple DirectMedia Layer. SDL provides a simple, unified graphics/sound/input/CD API which works on the most popular platforms including Windows, Mac OS X, Linux, *BSD. It is built on top of the native APIs of these platforms thereby taking advantage of any hardware acceleration for graphics and sound that these APIs provide. For example, under Windows, SDL can use DirectX to accelerate its blit functionality. In lieu of its own 2D API, SDL also provides an OpenGL context which you can use in conjunction with the other non-graphics SDL APIs. See www.libsdl.org PyGame a Python binding/wrapper for SDL that makes SDL's already easy-to-understand API even simpler to use and exposes it to all the power of Python. See www.pygame.org PyOpenGL a Python binding for OpenGL that allows Python programs to use the OpenGL API. PyOpenGL works with many GUI libraries including wxPython, FxPy, Tkinter, etc....and of course, PyGame. See pyopengl.sf.net [ BENEFITS OF USING OPENGL UNDER PYGAME ] - Python's high level nature makes for very rapid development - Hardware accelerated OpenGL is widespread and readily exposed by PyOpenGL - Python's, SDL/Pygame's, and PyOpenGL's cross platform nature means that it is very possible to write a single piece of source code that will work on multiple platforms unchanged (as we shall see in the example) [ REQUIREMENTS ] You should have Python, Pygame (incl. the auxiliary libraries for sound mentioned in the pygame page), PyOpenGL, and the SDL libraries installed on your computer. You should also make sure that OpenGL is properly installed and running on your platform. Configuring these may take a bit of trial and error (esp. on Linux) and is beyond the scope of this article... but hopefully it is not that hard. [ THE SAMPLE CODE ] The following Python code has been tested under both Windows and Linux and work identically. In theory, the same should apply on any other platform where Pygame/PyOpenGL is available. One can see that it is absolutely free of any platform specific code including #ifdefs as well as of any code having to do with platform detection. In spite of this it will transparently take advantage of any 3d acceleration capability available (assuming you installed your OpenGL drivers properly) on your system! Python is very easy to understand so I will just let the source code and comments (they start with a '#') guide your understanding. [ CONTROLLING THE PROGRAM ] The 1, 2, and 3 keys control the rotation of the smaller inner square while the 4, 5, and 6 keys that of the outer square. The inner square demonstrates OpenGL color interpolation while the bigger, outer square demonstrates a texture with alpha transparency. Pressing the keys also plays a random wave file - this is to demonstrate how we use Pygame for sound playback. [ ABOUT THE CODE ] The code uses vertex arrays for speed. Note that PyOpenGL 2.0.0.44 will not handle vertex arrays properly unless you apply a patch to it. As far as I know, the PyOpenGL people have not applied this simple patch to the stable 2.0.0.44 release so you probably have to do this yourself. Search for 'glTexCoordPointer' in the PyOpenGL mailing list for a discussion of how to do the patch. [ START OF LISTING ] import sys # All we will use is the sys.exit() routine import pygame # Tell python we want to use the pygame module from random import choice # Imports the choice() function from the random module from pygame.locals import * # Imports the constant symbols from pygame.locals - like OPENGL, K_1, etc... from OpenGL.GL import * # This is so we can just do glXXXX() instead of OpenGL.GL.glXXXXX() rot1=0 rot2=0 direction1=0 direction2=0 square1Colors=[[1,0,0,1],[0,1,0,1],[0,0,1,1],[1,1,1,1]] square1Vertices=[[-64,-64],[64,-64],[64,64],[-64,64]] square2TexCoords=[[0,0],[1,0],[1,1],[0,1]] square2Vertices=[[-128,-128],[128,-128],[128,128],[-128,128]] def draw (): global rot1,rot2,direction1,direction2 global square1Colors,square1Vertices,square2TexCoords,square2Vertices glClearColor(0.0,0.0,0.0,0.0) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) glMatrixMode(GL_MODELVIEW) # Select GL_MODELVIEW matrix glPushMatrix() # Save current GL_MODELVIEW matrix state glTranslatef(200,200,0) # Translate 200 units to the right and back glEnableClientState(GL_COLOR_ARRAY) # Tell OpenGL that we will use a color-array glEnableClientState(GL_VERTEX_ARRAY) # Tell OpenGL that we will use a vertex-array glColorPointerf(square1Colors) # square1Colors contains colors for square1 glVertexPointerf(square1Vertices) # square1Vertices contains vertices for square1 glRotatef(rot2,0,0,1) # Rotate square1 around (0,0,1) rot2=(rot2+direction2)%360 # Update rot2 value glBegin(GL_QUADS) # Tell OpenGL that we will draw quads glArrayElement(0) # First element in vertex-array glArrayElement(1) # Second element in vertex-array glArrayElement(2) # Third element in vertex-array glArrayElement(3) # Fourth element in vertex-array glEnd() glDisableClientState(GL_COLOR_ARRAY) # Disable color-array glPopMatrix() # Restore last GL_MODELVIEW matrix state glPushMatrix() # Save current GL_MODELVIEW matrix state glTranslatef(200,200,0) # Translate 200 units to the right and back glEnable(GL_TEXTURE_2D) # Enable 2D texturemapping glEnable(GL_BLEND) # Enable blending glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) # Define blending function glEnableClientState(GL_TEXTURE_COORD_ARRAY) # Tell OpenGL that we will use a texture-coord-array glEnableClientState(GL_VERTEX_ARRAY) # Tell OpenGL that we will use a vertex-array glTexCoordPointerf(square2TexCoords) # square2TexCoords contains texture coordinates for square2 glVertexPointerf(square2Vertices) # square2Vertices contains vertices for square2 glRotatef(rot1,0,0,1) # Rotate squre2 around (0,0,1) rot1=(rot1+direction1)%360 # Update rot1 value glBegin(GL_QUADS) # Tell OpenGL that we will draw quads glArrayElement(0) # First element of vertex-array glArrayElement(1) # Second element of vertex-array glArrayElement(2) # Third element of vertex-array glArrayElement(3) # Fourth element of vertex-array glTexCoord2f(0,0) glEnd() glDisable(GL_BLEND) # Disable blending state glDisable(GL_TEXTURE_2D) # Disable 2D texturemapping glFlush() # Flush everything to screen ASAP glPopMatrix() # Restore last GL_MODELVIEW matrix state pygame.display.flip() # Flip the double-buffer pygame.init() # kickstart pygame pygame.display.set_mode((1280,1024),OPENGL|DOUBLEBUF|FULLSCREEN) # we want a fullscreen, 1280x1024, SDL surface that supports doublebuffered OpenGL # create the partially transparent texture tex="" for i in xrange(512): tex+="\xff\xff\xff\x7f\xff\xff\xff\x7f\xff\xff\xff\x7f\xff\xff\xff\x7f" tex+="\xff\xff\xff\x3f\xff\xff\xff\x3f\xff\xff\xff\x3f\xff\xff\xff\x3f" glTexImage2D(GL_TEXTURE_2D,0,4,64,64,0,GL_RGBA,GL_UNSIGNED_BYTE,tex) glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP) # Clamp our texture glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP) # Clamp our texture glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR) # Magnification filter is linear glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR) # Minification filter is linear glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) # Texture will not be affected by current color state glDisable(GL_DEPTH_TEST) # No depth testing glMatrixMode(GL_PROJECTION) # Select GL_PROJECTION matrix glLoadIdentity() # Reset GL_PROJECTION matrix to identity matrix glOrtho (0,1280,0,1024,-1,1) # Set up orthographic projection draw() # Call draw function # put random wave files in the soundList list # provide your own .wav files soundList=[] soundList.append(pygame.mixer.Sound("dingdong.wav")) soundList.append(pygame.mixer.Sound("trumpet1.wav")) soundList.append(pygame.mixer.Sound("boo.wav")) # the nice and simple event loop while 1: event=pygame.event.poll () if event.type is QUIT: sys.exit(0) draw() if event.type is KEYDOWN: if event.key is K_ESCAPE: sys.exit(0) if event.key is K_1: choice(soundList).play() direction2=2 if event.key is K_2: choice(soundList).play() direction2=-2 if event.key is K_3: choice(soundList).play() direction2=0 if event.key is K_4: choice(soundList).play() direction1=2 if event.key is K_5: choice(soundList).play() direction1=-2 if event.key is K_6: choice(soundList).play() direction1=0 <<------------------------------------------------------>> [ Written By: Codeboy ] [ Written On: 03.10.2003 ] [ Updated On: 03.11.2003 ]