Tuesday, November 13, 2007

Example 3-2 : Using Modeling Transformations

I'm tired.

I scratched my head over the Z-axis stuff; be sure to read the paragraph called "Troubleshooting Transformations" in chapter 3. It'll help prevent a lot of scratch-marks.

In this case, the following phrase made me realise the mistake I made:
Remember that with the projection commands, the near and far coordinates measure distance from the viewpoint and that (by default) you're looking down the negative z axis. Thus, if the near value is 1.0 and the far 3.0, objects must have z coordinates between -1.0 and -3.0 in order to be visible.

The vertices I drew were all on the positive side of the z-axis, while I had to give them negative z-coordinates ofcourse.

(Note to self: first read the whole chapter before trying out all the examples in the book).

The code:

# Example 3-2 : Using Modeling Transformations

from pyglet.gl import *
from pyglet import window
from OpenGL.GLUT import *

win = window.Window()

def init():
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_FLAT)

def draw_triangle():
glBegin(GL_LINES)
glVertex3f(50.0, 50.0, -2.0)
glVertex3f(150.0, 50.0, -2.0)
glVertex3f(150.0, 50.0, -2.0)
glVertex3f(150.0, 150.0, -2.0)
glVertex3f(150.0, 150.0, -2.0)
glVertex3f(50.0, 50.0, -2.0)
glEnd()

def reshape (w, h):
glViewport (0, 0, w, h)
print w, h
glMatrixMode (GL_PROJECTION)
glLoadIdentity ()
glFrustum (-150.0, 150.0, -150.0, 150.0, 1.0, 20.0)
glMatrixMode (GL_MODELVIEW)

def display():
glClear (GL_COLOR_BUFFER_BIT)
glLoadIdentity()

glColor3f(1.0, 1.0, 1.0)
draw_triangle() # solid lines
glEnable(GL_LINE_STIPPLE) # dashed lines
glLineStipple(1, 0xF0F0)
glLoadIdentity()
glTranslatef(-20.0, 0.0, 0.0)
draw_triangle()

glLineStipple(1, 0xF00F) #long dashed lines
glLoadIdentity()
glScalef(1.5, 0.5, 1.0)
draw_triangle()

glLineStipple(1, 0x8888) # dotted lines
glLoadIdentity()
glRotatef (90.0, 0.0, 0.0, 1.0)
draw_triangle ()
glDisable (GL_LINE_STIPPLE);
glFlush ()

init()

win.on_resize = reshape

while not win.has_exit:
win.dispatch_events()
win.clear()
display()
win.flip()


(As a side-note, I've used GL_LINES instead of GL_TRIANGLES to draw the triangles, so that the original glEnable(GL_LINE_STIPPLE) would remain unaltered).

Example 3-1 : Transformed Cube

I've made a small jump ahead and went on to chapter 3, "Viewing". Some code from chapter 2 will be posted here at a later date (it's just that that code resides on another system now).

This is the code that sets off Chapter 3:

# Example 3-1 : Transformed Cube

from pyglet.gl import *
from pyglet import window
from OpenGL.GLUT import *

win = window.Window()

def init():
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_FLAT)

def reshape (w, h):
glViewport (0, 0, w, h)
glMatrixMode (GL_PROJECTION)
glLoadIdentity ()
glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0)
glMatrixMode (GL_MODELVIEW)

def display():
glClear (GL_COLOR_BUFFER_BIT)
glColor3f (1.0, 1.0, 1.0)
glLoadIdentity () # clear the matrix
# viewing transformation
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
glScalef (1.0, 2.0, 1.0) # modeling transformation
glutWireCube (1.0)
glFlush ()

init()

win.on_resize = reshape

while not win.has_exit:
win.dispatch_events()
win.clear()
display()
win.flip()


As the GLUT library is not being wrapped in pyglet, we'll have to import it from within OpenGL (get it here). Mind that GLUT are but helper libraries (such as the famous teapot), and the pyglet people have good reasons not to include it in their module.

Also, with what has been learned in chapter 2 of the red book, a WireCube() function shouldn't be too hard to write.

Wednesday, November 07, 2007

Example 2-10 using Stride: another way to draw Elements

Instead of defining the vertex and colour arrays one by one, you can also define one large array holding (in this case) vertex and colour data. The data is then referenced by pointing to the different "starting points" within the array.
The parts in bold are changed from the previous 2-10 example.

Have a look at the code:

# Example 2-10 : Using glArrayElement() to Define Colors and Vertices

from pyglet.gl import *
from pyglet import window

win = window.Window()

def display():

intertwined = [
1.0, 0.2, 1.0, 100.0, 100.0, 0.0,
1.0, 0.2, 0.2, 0.0, 200.0, 0.0,
1.0, 1.0, 0.2, 100.0, 300.0, 0.0,
0.2, 1.0, 0.2, 200.0, 300.0, 0.0,
0.2, 1.0, 1.0, 300.0, 200.0, 0.0,
0.2, 0.2, 1.0, 200.0, 100.0, 0.0]

glEnableClientState (GL_COLOR_ARRAY)
glEnableClientState (GL_VERTEX_ARRAY)

glColorPointer (3, GL_FLOAT, 6 * sizeof(GLfloat), (GLfloat * len(intertwined))(*intertwined))
glVertexPointer (3, GL_FLOAT, 6 * sizeof(GLfloat), (GLfloat * len(intertwined[3:]))(*intertwined[3:]))

glBegin(GL_TRIANGLES)
glArrayElement (2)
glArrayElement (3)
glArrayElement (5)
glEnd()

def init():
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_FLAT)

while not win.has_exit:
win.dispatch_events()
win.clear()
display()
win.flip()


glVertexPointer() is being told that

  • each vertex is composed of 3 coordinates
  • the coordinates are of the type GL_FLOAT
  • the next vertex is to be found at 6 * the size of a GL_FLOAT variable further on in the array
  • the vertex data starts at the 4th element of the intertwined array.

Example 2-10 : Using glArrayElement() to Define Colors and Vertices

Vertex arrays! Huzzah, now I finally know what they mean.

The code goes like this:

# Example 2-10 : Using glArrayElement() to Define Colors and Vertices
# (Using vertices & colors variables defined in Example 2-9)

from pyglet.gl import *
from pyglet import window

win = window.Window()

def display():

vertices = [25, 25,
100, 325,
175, 25,
175, 325,
250, 25,
325, 325]
colors = [1.0, 0.2, 0.2,
0.2, 0.2, 1.0,
0.8, 1.0, 0.2,
0.75, 0.75, 0.75,
0.35, 0.35, 0.35,
0.5, 0.5, 0.5]

glEnableClientState (GL_COLOR_ARRAY)
glEnableClientState (GL_VERTEX_ARRAY)

glColorPointer (3, GL_FLOAT, 0, (GLfloat * len(colors))(*colors));
glVertexPointer (2, GL_INT, 0, (GLint * len(vertices))(*vertices));

glBegin(GL_TRIANGLES)
glArrayElement (2)
glArrayElement (3)
glArrayElement (5)
glEnd()

def init():
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_FLAT)

while not win.has_exit:
win.dispatch_events()
win.clear()
display()
win.flip()


glArrayElement() allows you to use a vertex predefined in an array via glVertexPointer(). An easy example, but it an important one, as vertex arrays allow us to drastically optimise our code by reducing the number of function calls.

Tuesday, November 06, 2007

Example 2-7: Marking Polygon Boundary Edges

An easy one this, especially since I now have learned how to create and pass suitable arrays to the gl*() functions.

Here's the code:

# Example 2-7 : Marking Polygon Boundary Edges

from pyglet.gl import *
from pyglet import window

# The OpenGL context is created only when you create a window. Any calls
# to OpenGL before a window is created will fail.
win = window.Window()

def display():
# vertices undefined in example
# adding a Z value of 1.0 (simply to remain in sync with the example,
# where glVertex3fv is called,
# i.e. 3 variables are required by the function

v0 = [0.0, 0.0, 1.0]
v1 = [100.0, 100.0, 1.0]
v2 = [150.0, 50.0, 1.0]

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
glBegin(GL_POLYGON)
glEdgeFlag(GL_TRUE)
glVertex3fv((GLfloat * len(v0))(*v0))
glEdgeFlag(GL_FALSE)
glVertex2fv((GLfloat * len(v1))(*v1))
glEdgeFlag(GL_TRUE)
glVertex2fv((GLfloat * len(v2))(*v2))
glEnd()

def init():
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_FLAT)

while not win.has_exit:
win.dispatch_events()
win.clear()
display()
win.flip()


One thing to note perhaps is that instead of passing the values directly, we have used a pointer to an array this time.

The Red Book, example 2-6

I had some trouble translating example 2-6 from the red book (the code resides under the enlarged grid, to be found a few lines below this link). But here it is:

from pyglet.gl import *
from pyglet import window

# The OpenGL context is created only when you create a window. Any calls
# to OpenGL before a window is created will fail.
win = window.Window()

def drawOneLine(x1 , y1, x2, y2):
glBegin(GL_LINES)
glVertex2f ((x1),(y1))
glVertex2f ((x2),(y2))
glEnd()

def display():
fly = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08]

halftone = [
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55]

glClear (GL_COLOR_BUFFER_BIT)
glColor3f (1.0, 1.0, 1.0)

# draw one solid, unstippled rectangle,
# then two stippled rectangles
glRectf (25.0, 25.0, 125.0, 125.0)
glEnable (GL_POLYGON_STIPPLE)

# convert the data to ctypes:
# Many thanks, Alex! (http://groups.google.be/group/pyglet-users/browse_thread/thread/10e304d4a5e77d97)
# Long version:
# array_type = (GLubyte * len(fly))
# fly = array_type(*fly)
# glPolygonStipple(fly)

# Short version:
glPolygonStipple( (GLubyte * len(fly))(*fly) )

glRectf (125.0, 25.0, 225.0, 125.0)
glPolygonStipple ( (GLubyte * len(halftone))(*halftone))

glRectf (225.0, 25.0, 325.0, 125.0)
glDisable (GL_POLYGON_STIPPLE)

# glFlush() unnecessary, as we call flip() during the main loop.
# glFlush ()

def init():
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_FLAT)

while not win.has_exit:
win.dispatch_events()
win.clear()
display()
win.flip()

As I have but a fleeting knowledge of how C handles variables, I was at a loss at how to pass an array of unsigned bytes to the glPolygonStipple() function. Help came via the people at the pyglet newsgroup. The solution boils down to (also see the comments in the code above):

  • Defining the array type (GLubyte * lenght of data list)

  • Converting the (elements of the) list to an array of the above defined type

  • Passing this array to the glPolygonStipple() function

As you see in the above code, these three steps can be easily factored into one single line: glPolygonStipple((GLubyte * len(fly))(*fly)).

Monday, November 05, 2007

First steps in OpenGL with Python

I have always wanted to sink my teeth in OpenGL (ever since the days of the first Voodoo graphics cards). It always seemed to be the best tool for graphics programming on the PC (but boy, how I still miss my A1200's blitter chip!).

My main problem has always been my penchant for scripting languages ever since I switched from the Amiga (AmigaE and 68k assembler) to the PC. Which can be translated as: I never learned C, or C++ for that matter.

Python's been my latest love for the last few years now. First as a simple scripting language, then some GUI stuff with Tkinter, later Django came along and now I feel confident enough to tackle OpenGL.

As I started reading the red book (the online version for OpenGL 1.1 can be found here), I tried to convert some of the examples found there to Python, using pyglet (I like its easy way of setting up screens and windows compared to pygame's).

Here's a translation of example 2-5 (found near here):

from pyglet import window
from pyglet.gl import *

def drawOneLine(x1,y1,x2,y2):
glBegin(GL_LINES)
glVertex2f ((x1),(y1))
glVertex2f ((x2),(y2))
glEnd()

def init():
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_FLAT)

def display():
i = 0

glClear (GL_COLOR_BUFFER_BIT)
# select white for all lines
glColor3f (1.0, 1.0, 1.0)

# in 1st row, 3 lines, each with a different stipple
glEnable (GL_LINE_STIPPLE)

glLineStipple (1, 0x0101) # dotted
drawOneLine (50.0, 125.0, 150.0, 125.0)
glLineStipple (1, 0x00FF) # dashed
drawOneLine (150.0, 125.0, 250.0, 125.0)
glLineStipple (1, 0x1C47) # dash/dot/dash
drawOneLine (250.0, 125.0, 350.0, 125.0)
# in 2nd row, 3 wide lines, each with different stipple
glLineWidth (5.0)
glLineStipple (1, 0x0101) # dotted
drawOneLine (50.0, 100.0, 150.0, 100.0)
glLineStipple (1, 0x00FF) # dashed
drawOneLine (150.0, 100.0, 250.0, 100.0)
glLineStipple (1, 0x1C47) # dash/dot/dash
drawOneLine (250.0, 100.0, 350.0, 100.0)
glLineWidth (1.0)

# in 3rd row, 6 lines, with dash/dot/dash stipple
# as part of a single connected line strip
glLineStipple (1, 0x1C47) # dash/dot/dash

glBegin (GL_LINE_STRIP)
for i in range(0, 7):
glVertex2f (50.0 + (i * 50.0), 75.0)
glEnd ()

# in 4th row, 6 independent lines with same stipple
for i in range(0, 5):
drawOneLine (50.0 + (i * 50.0), 50.0, 50.0 + ((i+1) * 50.0), 50.0)


# in 5th row, 1 line, with dash/dot/dash stipple
# and a stipple repeat factor of 5
glLineStipple (5, 0x1C47) # dash/dot/dash
drawOneLine (50.0, 25.0, 350.0, 25.0)

glDisable (GL_LINE_STIPPLE)

# glFlush ()
# glFlush() is unnecessary, as flip() is called on window (ie double buffered window).

win = window.Window()
init()

while not win.has_exit:
win.dispatch_events()
win.clear()
display()
win.flip()