Sunday, September 7, 2008

Back Again

Life is hectic, of course. Nevertheless I had a couple of spare hours today, so I finished separating the framework and updated all of the code to use it. Also, as you will see below, I wrote up a quick appendix about the framework. All comments appreciated.

Hopefully it won't be so long until I get the next item (The first tutorial!) up.

By the way, price, if you could shoot your modified code for pyglet back, that would be great.

Till Next Time,
Naltai

Appendix B: Framework

This appendix describes the framework code that interacts with the operating system in order to give us somewhere to draw with OpenGL, as well as a way to interact with the user. The framework for pybsp is very simple, doing the minimum to show a window and allow the user some measure of camera control. This is intentional, to keep the focus on the OpenGL code. For this reason basic features you would normally find in a framework such as window resizing, mouse handling and display loops are not supported.

The pybsp framework is written against the wxwindows library for python. This library is freely available, cross platform, and relatively easy to use. In addition it is easy to set up an OpenGL window for use. The library and its documentation can be found at www.wxpython.org.

The main function pretty much says it all:

def main(pybsp):
"""Create a simple wx application to show our opengl drawing and allow simple camera commands"""
app = wx.PySimpleApp()
frame = wx.Frame(None,-1,'PyBSP',wx.DefaultPosition,(400,400))
canvas = myGLCanvas(frame)
canvas.pybsp = pybsp
frame.Show()

Create a wx application. Create a 400 by 400 window with the title PyBSP. Create a Custom GL Canvas to fill the window. Tell the custom canvas the pybsp object to use (contains the tutorial code). Show the window.

The custom GL Canvas is basically a wrapper around the GLCanvas object that calls out to the pybsp object for drawing and GL initialization. It handles two events: the paint event and the keydown event.

On the paint event the framework checks if the pybsp object has initialised OpenGL. If not it calls the OnInit function in the pybsp object. On normal execution it sets up the canvas for drawing using wx calls, calls the OnDraw function in the pybsp object to draw the screen, then calls wx to finish the drawing. In addition it times the OnDraw function as a simple performance metric.

On the keydown event, keystrokes are interpreted to move the camera. The camera is tracked with a simple x,y,z position, a direction angle that indicates the direction the camera is pointing in the xy plane, and an updown angle which indicates the direction above or below the horizon that the camera is pointing. The key press routine interprets the arrow keys to modify the camera's direction by 5 degrees on the appropriate axis, whilst the w,a,s and d keys move it forwards, left, backwards and right along the line of sight of the camera respectively. After updating the camera position, wx is asked to repaint the display, thus invoking the OnDraw function in the pybsp object again from the paint event handler.

Till Next time,
Naltai

Friday, August 8, 2008

Life Intrudes Again

I've been very busy at work and at home for the past week, so not much has been done. About the only progress is some refactoring of the code to separate all of the wx windows GUI code from the opengl code. This is in preparation to writing the tutorials so that the tutorials can completely focus on the opengl code. When the refactoring is complete (I've only done one file so far) I will write an explanation of the framework code similar to the parser code.

The next two weeks don't look good for progress either with the olympics on TV.

Oh Well,

Till next time,
Naltai

Sunday, August 3, 2008

Appendix A: BSP Parser

This Appendix explains the BSP parser code. This code is used by the tutorials to obtain data out of a BSP file. The code is completely self contained in the ibsp.py file.

The BSP parser code, like the BSP file format itself, is based around the concept of lumps. A lump is basically an array of one type of data. There are seventeen lumps in the Quake3 BSP file format, each of which contains a different type of data. Whilst the parser handles all of the Lump types, only a couple are used as examples in this document. For complete details of all of the lumps see Unoffical Quake3 Map Specs.

The parser extensively uses the python struct module. This module takes binary data in python strings (such as that you get from reading a binary file) and turns it into python base types such as integers and floating point numbers. For example, a plane object is read with the following line:


x, y, z, self.dist = struct.unpack("ffff", data)


The first argument specifies that we are reading 4 floating point numbers, the second argument is the string containing the data, which must be of the appropriate length, in this case 16 bytes (each float is 4 bytes long). The function returns a tuple containing the read values as the appropriate type, here we are using pythons ability to automatically unpack tuples with an assignment to place the values exactly where we want them. All uses of the struct module in this parser fall into this general usage pattern.

The parser is structured as a single class object that contains seventeen lump objects, named after their types. For example IBSP vectors is a lump of vectors. An IBSP object is created through its constructor which takes a standard python file object. It reads this file and makes a local copy of all the data which it holds internally. After the IBSP object is created, if the file is deleted or modified it will not affect the functioning of the program.

A lump is represented as a pseudo python list object. There are two ways to obtain data from each lump, either use standard list access syntax which will return a helper object representing the data. It is also possible to access the raw data in each lump using the .data property. This is useful for passing to functions which require the raw data, such as glVertexPointer.

There is a helper class for each type of data in the BSP file, for example the Plane class represents the objects in the planes lump. Helper classes are very simple, they have a constructor which reads the raw object data and converts it into object attributes of more useful types. They also have a pretty printing function which allows for relatively simple debugging.

The Leaffaces, leafbrushes and meshverts lumps have the type of a simple integer. Rather than make a simple integer helper class, a special lump class IntLump handles these three types. This will return the data as python integers which is a naturally useful interface to these types of data.

The final part of the file is a small test script that is executed when this script is run as a standalone script. This simply reads a supplied BSP file and pretty prints the first item of data from each lump as a test of the script.

Enjoy,
Naltai