PyOpenGL for OpenGL Programmers

This document describes those features of PyOpenGL which are likely to be unfamiliar to OpenGL programmers.  It also explains various features of PyOpenGL which are not covered in general OpenGL documentation.

Method Signatures vs. C OpenGL

Most of the functions which appear in PyOpenGL 2 are identical in calling method and functionality to that of the appropriate C specification.  There are a few exceptions because of the differences between C and Python. Most of these exceptions are due to the difference between C and Python in the way that they access arrays.  For example, a C function like this:

void foo(int count, const int *args);

will have the Python binding:

foo(args) -> None

Also C functions which write array data to a function argument like:

void bar(int args[4]);

will have the Python binding:

bar() -> args[]

The following sections will document changes other than simple changes like the above examples.

Errors raise Exceptions

PyOpenGL always uses "strict" OpenGL operation, which is closer to the operation of Python itself, i.e. errors are raised as exceptions, rather than silently being passed back to the user. glGetError is not available as it would never return anything save GL_NO_ERROR.

To convert code which uses glGetError, you will want to search for each instance of glGetError and introduce the appropriate exception handling mechanisms. The exceptions raised are:

GL.GLerror is a subclass of EnvironmentError and has a errno and msg like EnvironmentError. In the case of multiple errors the errno attribute will be set to a tuple of error numbers and msg will be a concatenation of the error messages for each error.

Array Handling Routines

Each call which sets an array pointer, such as glVertexPointer, will have many variants. First there will a function which is identical that of the specification. For the pointer argument one should pass a string. Also note that the stride values are used.

Next there will a set of functions named:

glXPointer{ub|b|us|s|ui|i|f|d}

These will usually take as a single argument a multidimensional array of values. The type argument is controlled by the suffix of the function (ub is unsigned byte, b is byte, f is float, d is double etc.) Most other arguments are derived from the dimensions of the array.

So for glColorPointer we have:

glColorPointer(size, type, stride, pointer) -> None
glColorPointerub(pointer[][]) -> None
glColorPointerb(pointer[][]) -> None
glColorPointerus(pointer[][]) -> None
glColorPointers(pointer[][]) -> None
glColorPointerui(pointer[][]) -> None
glColorPointeri(pointer[][]) -> None
glColorPointerf(pointer[][]) -> None
glColorPointerd(pointer[][]) -> None

This same decoration strategy is used for other array functions besides glXPointer.

For instance, glDrawElements has the Python binding:

glDrawElements(mode, count, type, indices) -> None

where indices is expected to be a string.  There are also the decorated bindings:

glDrawElementsub(mode, indices[]) -> None
glDrawElementsus(mode, indices[]) -> None
glDrawElementsui(mode, indices[]) -> None

where "indices" is now a single dimensional array.

When calling a glColorPointer, glVertexPointer, etc. Python needs to allocate memory to store the values that OpenGL needs. This memory is reference counted and takes into account function calls like glPushClientAttrib and glPopClientAttrib. To force this memory to be released one just need to make a call glColorPointerub(None).

Currently glPushClientAttrib will always set the GL_CLIENT_VERTEX_ARRAY_BIT flag as glPopClientAttrib has no way of knowing that flag was set and the state of the flag is needed to know whether or not to decrement the pointer locks on the allocated memory.

This may change in the future. That said, surrounding array use glPushClientAttrib/glPopClientAttrib is a good way to force the release of any allocated memory, but make sure that all calls to glXPointer, etc. are within the ClientAttrib block if you chose to use this scheme.

Note that since all the memory allocation is automatic there is no need for glGetPointerv function, so it is excluded.

Note that the function glInterleavedArrays is also present, but it does not have the variants that the others do (i.e., no glInterleavedArraysf)

Warning if you write an extension module which makes calls to the pointer functions and expects to interact with PyOpenGL safely it should push the client state (from within the extension) before and after array use.

Image Routines

glDrawPixels and the other image/texturing functions have much the same decoration scheme as the array functions. For glDrawPixels there is the standard function which expects a string as the pixel data:

glDrawPixels(width, height, format, type, pixels) -> None

This function will respect the parameters set by glPixelStore{i|f}.

There is also a collection of variants which take a multidimensional array as the data source and set glPixelStore{i|f} automatically. For example:

glDrawPixelsub(format, pixels) -> None

Notice that width and height are inferred from the pixel data and the type is GLubyte.

Selection and Feedback Buffers

Normally in OpenGL to use a selection buffer one would do the following:

GLuint buffer[SIZE];
glSelectBuffer(SIZE, buffer);
glRenderMode(GL_SELECT);
/* draw some stuff */
GLint count = glRenderMode(GL_RENDER);
/* parse the selection buffer */

In Python this accomplished like this:

glSelectBuffer(SIZE) # allocate a selection buffer of SIZE elements
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)
for hit_record in buffer:
    min_depth, max_depth, names = hit_record
    # do something with the record

Feedback buffers are used in the same way except that each item in the buffer is tuple (token, value), where value is either a passthrough token or a list of vertices.

Note that if glRenderMode returns a buffer than it also resets OpenGL's pointer for the corresponding buffer. This means that the buffer returned by glRenderMode is independent of future calls to glRenderMode, i.e. it will not be overwritten by any such future calls. This makes the returned buffer somewhat thread-safe. It also means that every call to glRenderMode(GL_SELECT | GL_FEEDBACK) needs to preceded by a call to glSelectBuffer or glFeedbackBuffer first, i.e. the following code will not work:

### THIS DOESN'T WORK!!!
glSelectBuffer(SIZE) # allocate a selection buffer of SIZE elements
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)
# do another selection
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)

Instead one must do:

glSelectBuffer(SIZE) # allocate a selection buffer of SIZE elements
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)
# do another selection
glSelectBuffer(SIZE) allocate a new selection buffer
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)

Extensions and Conditional Functionality

PyOpenGL includes support for many GL, GLU and WGL extensions. These extensions are implemented as sub-modules of the GL, GLU or WGL package. For example, the GL_ARB_multitexture extension would be implemented in the module GL.ARB.multitexture.

For every extension that PyOpenGL supports the corresponding module will exist regardless of whether or not that extension is implemented by the user's OpenGL library. A placeholder module will also exist even if the extension defines no new tokens or functions. If the extension is not supported by the current context than any attempt to use that extension will throw a GLerror exception with a code of GL_INVALID_OPERATION.

Each OpenGL implementation has its' own method for linking to OpenGL extensions.  These methods vary from dynamic linking to static linking and even some dynamic/static combinations.  Regardless of which case applies to a specific implementation of OpenGL, one still needs to verify that a particular extension is supported before attempting to use any functions or tokens that it declares.  This is usually done by calling glGetString(GL_EXTENSIONS) and looking for extension name in the returned string.

Extensions are potentially context dependent, which means that an extension supported by one OpenGL context may not be supported by another.  This means that verification that extension is supported and loading of the extension procedure addresses (if the OpenGL extension mechanism is dynamic) needs to be done for each context that intends to use a particular extension.  PyOpenGL simplifies this somewhat by providing a single initialization function for each extension which does the following:

  1. verify the extension is supported by the current context
  2. load all procedure addresses for extension if needed
  3. return a boolean indicating success of the previous steps

The naming scheme of the this initialization is designed to be consistent with the naming scheme used with OpenGL extensions. For instance, here is a function which takes an extension name and returns the initialization function name:

def init_name(extension_name):
    parts = string.split(extension_name, '_')
    return string.join([string.lower(parts[0]), 'Init'] +
                       map(string.capitalize, parts[2:]) +
                       [parts[1]], '')
>>> init_name('GL_ARB_multitexture')
'glInitMultitextureARB'
>>> init_name('GLU_SGI_filter4_parameters')
'gluInitFilter4ParametersSGI'

As an example of extension usage, here is a GLUT program that needs the GL_ARB_multitexture extension:

from OpenGL.GLUT import *
from OpenGL.GL.ARB.multitexture import *
import sys

# initialize GLUT
argv = glutInit(sys.argv)

# create a window, needs to be done before glInitMultitextureARB
glutCreateWindow('foo')

# see if GL_ARB_multitexture is supported
if not glInitMultitextureARB():
    # it's not supported...panic!
    print "Help, I'm lost without GL_ARB_multitexture!"
    sys.exit(1)
# do something with it...

It is important to note that the initialization function for an extension must be called by each context that intends to use that extension.  Failure to do so will result in a GL.GLerror with a code of GL_INVALID_OPERATION even if the extension is actually supported by that context.

Core Module Versions

For many modules PyOpenGL is designed to support multiple versions of the API exposed by that particular module. For instance, the GLU module supports GLU API versions 1.0, 1.1, 1.2 and 1.3. In many cases different versions of an API have different functions and constants.

There are several ways for Python code to determine at runtime which API is exposed.

All PyOpenGL modules have an attribute named __api_version__ which is set to an integer which defines the current version. Usually this a combination of the major version number in the high word and the minor in the low word. So GLU has __api_version__ = 0x100 | 0x101 | 0x102 | 0x103

Currently the only exception to this is the GLUT module which only uses the low word to store the Xlib implementation number (see your GLUT header for information about this.)

Often times a C header which defines an API has some macro or macros which define the version number. PyOpenGL includes the same macros as module attributes to make porting C code easier. For the GLU module these macros are GLU_VERSION_1_1, GLU_VERSION_1_2, and GLU_VERSION_1_3. So if your Python code needs GLU 1.2 to run then you could test for this at runtime by doing the following:

from OpenGL.GLU import *
try:
    GLU_VERSION_1_2
except:
    print "Help! I'm lost without GLU 1.2!"
    sys.exit(1)

Function Aliases

PyOpenGL has historically provided a number of aliases for functions.  For backwards compatibility, these aliases continue to be provided: