text: #!/usr/bin/env python
  
  import pyglet
  pyglet.options['debug_gl'] = True
  pyglet.options['debug_gl_trace'] = True
  pyglet.options['debug_gl_trace_args'] = True
  pyglet.options['debug_lib'] = True
  pyglet.options['debug_media'] = True
  pyglet.options['debug_trace'] = True
  pyglet.options['debug_trace_args'] = True
  #pyglet.options['debug_trace_depth'] = 1
  pyglet.options['debug_font'] = True
  pyglet.options['debug_x11'] = True
  pyglet.options['debug_trace'] = True
  
  from pyglet.gl import *
  from math import pi, sin, cos
  
  try:
      # Try and create a window with multisampling (antialiasing)
      config = Config(sample_buffers=1, samples=4, 
                      depth_size=16, double_buffer=True,)
      window = pyglet.window.Window(resizable=True, config=config)
  except pyglet.window.NoSuchConfigException:
      # Fall back to no multisampling for old hardware
      window = pyglet.window.Window(resizable=True)
  
  @window.event
  def on_resize(width, height):
      # Override the default on_resize handler to create a 3D projection
      glViewport(0, 0, width, height)
      glMatrixMode(GL_PROJECTION)
      glLoadIdentity()
      gluPerspective(60., width / float(height), .1, 1000.)
      glMatrixMode(GL_MODELVIEW)
      return pyglet.event.EVENT_HANDLED
  
  def update(dt):
      global rx, ry, rz
      rx += dt * 1
      ry += dt * 80
      rz += dt * 30
      rx %= 360
      ry %= 360
      rz %= 360
  pyglet.clock.schedule(update)
  
  @window.event
  def on_draw():
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
      glLoadIdentity()
      glTranslatef(0, 0, -4)
      glRotatef(rz, 0, 0, 1)
      glRotatef(ry, 0, 1, 0)
      glRotatef(rx, 1, 0, 0)
      torus.draw()
  
  def setup():
      # One-time GL setup
      glClearColor(1, 1, 1, 1)
      glColor3f(1, 0, 0)
      glEnable(GL_DEPTH_TEST)
      glEnable(GL_CULL_FACE)
      glCullFace(GL_BACK)
  
      # Uncomment this line for a wireframe view
      #glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
  
      # Simple light setup.  On Windows GL_LIGHT0 is enabled by default,
      # but this is not the case on Linux or Mac, so remember to always 
      # include it.
      glEnable(GL_LIGHTING)
      glEnable(GL_LIGHT0)
      #glEnable(GL_LIGHT1)
  
      # Define a simple function to create ctypes arrays of floats:
      def vec(*args):
          return (GLfloat * len(args))(*args)
  
      glLightfv(GL_LIGHT0, GL_POSITION, vec(.5, .5, 1, 0))
      glLightfv(GL_LIGHT0, GL_SPECULAR, vec(1, 1., 1., 1))
      glLightfv(GL_LIGHT0, GL_DIFFUSE, vec(.5, .1, .1, 1))
      #glLightfv(GL_LIGHT1, GL_POSITION, vec(1, 0, .5, 0))
      #glLightfv(GL_LIGHT1, GL_DIFFUSE, vec(.5, .5, .5, 1))
      #glLightfv(GL_LIGHT1, GL_SPECULAR, vec(1, 1, 1, 1))
  
      #glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.5, 0, 0.3, 1))
      glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, vec(0, 0, 0., 1))
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, vec(1, 0, 0., 1))
      #glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50)
  
  class Torus(object):
      def __init__(self, radius, inner_radius, slices, inner_slices):
          # Create the vertex and normal arrays.
          vertices = []
          normals = []
  
          u_step = 2 * pi / (slices - 1)
          v_step = 2 * pi / (inner_slices - 1)
          u = 0.
          for i in range(slices):
              cos_u = cos(u)
              sin_u = sin(u)
              v = 0.
              for j in range(inner_slices):
                  cos_v = cos(v)
                  sin_v = sin(v)
  
                  d = (radius + inner_radius * cos_v)
                  x = d * cos_u
                  y = d * sin_u
                  z = inner_radius * sin_v
  
                  nx = cos_u * cos_v
                  ny = sin_u * cos_v
                  nz = sin_v
  
                  vertices.extend([x, y, z])
                  normals.extend([nx, ny, nz])
                  v += v_step
              u += u_step
  
          # Create ctypes arrays of the lists
          vertices = (GLfloat * len(vertices))(*vertices)
          normals = (GLfloat * len(normals))(*normals)
  
          # Create a list of triangle indices.
          indices = []
          for i in range(slices - 1):
              for j in range(inner_slices - 1):
                  p = i * inner_slices + j
                  indices.extend([p, p + inner_slices, p + inner_slices + 1])
                  indices.extend([p,  p + inner_slices + 1, p + 1])
          indices = (GLuint * len(indices))(*indices)
  
          # Compile a display list
          self.list = glGenLists(1)
          glNewList(self.list, GL_COMPILE)
          glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
          glEnableClientState(GL_VERTEX_ARRAY)
          glEnableClientState(GL_NORMAL_ARRAY)
          glVertexPointer(3, GL_FLOAT, 0, vertices)
          glNormalPointer(GL_FLOAT, 0, normals)
          glDrawElements(GL_TRIANGLES, 
                          len(indices), 
                          GL_UNSIGNED_INT, 
                          indices)
          glPopClientAttrib()
          glEndList()
  
      def draw(self):
          glCallList(self.list)
  
  
  setup()
  torus = Torus(1, 0.3, 60, 30)
  rx = ry = rz = 0
  
  pyglet.app.run()
  