Pythonstuff GLSL in English Pythonstuff GLSL auf Deutsch Pythonstuff GLSL Pythonstuff
PythonStuff Home
 

 

Example 1 - making Pyglet's graphics.py interactive

This is the series of Python programs that I made to understand the Pyglet library, OpenGL and GLSL.

This is the first example - I started with graphics.py by Alex Holkner. You can find the original in the Pyglet Repository. I expanded the functionality to have a base for all my GLSL-demo programs.

and a few screenshots:

Wireframe Sphere Shaded Torus

Wireframe Torus Shaded Sphere

To run this demo, you need Pyglet and the “euclid.py” module by Alex Holkner, you will find everything on the installation page.

(If you are looking for “euclid.py”: the file is in the directory App/Lib/site-packages/shader of the installation package glslpythonpack.zip).

The enhanced version shows:

  • HTML-Text to tell you what the demo does (it automatically vanishes after 10 seconds and can be turned on/off by the “H”-key)
  • a frames-per-second counter display to show the (in-)efficiency of the rendering
  • key-controlled rotation of the object - stop, rotate manually in addition to automatic rotation
  • key-controlled light - the primary light can be moved around
  • a key to turn on/off wireframe display (primarily to show the next feature :-))
  • a sphere with equidistant vertices in addition to the torus

HTML-Text

A nice feature of the Pyglet library - you put html-code in a string, make it an object by

label = pyglet.text.HTMLLabel(html, # location=location,
                              width=window.width//2,
                              multiline=True, anchor_x='center', anchor_y='center')

and display it by

@window.event
def on_draw():
  label.draw()

in the “on_draw()” handler whenever the window needs refreshing.

I remove the text after 10 seconds by a pyglet-event:

def dismiss_dialog(dt):
    global showdialog
    showdialog = False
pyglet.clock.schedule_once(dismiss_dialog, 10.0)

Keyboad Handling

This is also quite easy - the toggle the “showdialog”-Variable with the “H”-key I write

@window.event
def on_key_press(symbol, modifiers):
    global showdialog
  if symbol == key.H:
      showdialog = not showdialog

One of the nice things about Python - easy to read and enhance :-)

FPS counter display

This is a Pyglet standard function. The only thing to be aware of is that this is rendered into a texture, so you should have one active to see it.

fps_display = pyglet.clock.ClockDisplay() # see programming guide pg 48
@window.event
def on_draw():
    fps_display.draw()  

Sphere with equidistant vertices

In addition to “batch1” that has the vertex arrays for a torus:

batch1  = pyglet.graphics.Batch()
torus = Torus(1, 0.3, 80, 25, batch=batch1)

I wanted a “batch2” (toggle between the the figures with the “F”-key) with a regular sphere:

batch2  = pyglet.graphics.Batch()
sphere = Sphere(1.2, 4, batch=batch2)

Why a regular sphere ? If you think of a globe with an equator, parallels and meridians, you will see that strange things happen to the coordinates on the north and south poles. To avoid them, the easiest method is triangle subdivision:

Find a regular (platonic) figure that can be inscribed in a sphere and consist only if equilateral triangles - choose one of:

  • tetrahedron (4 triangles, 4 vertices, 6 edges)
  • octahedron (8 triangles, 6 vertices, 12 edges)
  • icosahedron (20 triangles, 12 vertices, 30 edges)

I start with an octahedron, because the vertex coordinates are the most trivial.

        self.vv.append( Vector3( 1.0, 0.0, 0.0 ) ) # North
        self.vv.append( Vector3(-1.0, 0.0, 0.0 ) ) # South
        self.vv.append( Vector3( 0.0, 1.0, 0.0 ) ) # A
        self.vv.append( Vector3( 0.0, 0.0, 1.0 ) ) # B
        self.vv.append( Vector3( 0.0,-1.0, 0.0 ) ) # C
        self.vv.append( Vector3( 0.0, 0.0,-1.0 ) ) # D

Then I (recursively) subdivide every triangle into 4 smaller equilateral triangles. The new (three) vertices are “pushed out” radially to the surface of the sphere. Repeat until you have enough :-)

Here is a picture of different recursion levels. The vertices of one triangle are marked by red dots, so you can easily see the subdivision algorithm.

The implementation of this algorithm in Python would be easy and elegant, but in its simple form it creates shared vertices - they end up as duplicates in the list of triangle vertices. To avoid them, I keep track of “already processed” edges:

If the edge is new (“N”), the vertex is put onto the vertex list. If the edge is already done (“X”), I search for the vertex with equal coordinates and reuse it's index.

    def myindex( self, list, value ):
        for idx, obj in enumerate(list):
            if abs(obj-value) < 0.0001:
              return idx
        raise ValueError # not found

Keeping track of new/done edges for every triangle makes the code rather ugly - I have the faint feeling that there is a better way (by cleverly numbering the triangles so I can immediately calculate the index instead of searching through a list of floating point vectors).

Well, but here we are. Look at the Pyglet Demo Base Code for details.

The whole thing is the base for my GLSL experiments - Let's start with proper GLSL lighting (including a beautiful highlight).


Deutschsprachige Version, Start
Impressum & Disclaimer