Sunday, November 30, 2008

New Site

I created a new site to which I am moving all the tutorials. It allows a much more logical layout for tutorials than a blog.

The URL for now is http://sites.google.com/site/naltai3/, but it may change in the future I guess.

This site will no longer be updated.

Thanks for your patience and sorry for the hiatus.

Naltai

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

Thursday, July 31, 2008

Source Code Available

I made the effort to figure out exactly how to get launchpad code hosting to work, and put the code up for the world to see.  As you can see it is still very messy, but serves as a simple example of an opengl program using vertex buffers.

It uses a map I downloaded for free from the internet, I do not have copyright to this map, but since it is available from many places I believe the author has placed it in the public domain.  If anyone is skilled at creating quake3 maps and wishes to donate one to this cause please let me know and I will use it in favour of the one I have at the moment.

To run the viewer run the command:
python map3.py

Once the viewer is running the left and right arrow keys can be used to rotate the view, and the w, a, s, and d keys can be used to move the camera in typical FPS style.  The script only redraws the screen on a keypress and prints the time it takes to redraw the screen to the console.  (approximately 0.05 seconds on my slow macbook).

Any comments are appreciated, but please understand that it is still in a very rough stage.

Have fun,
Naltai

Sick

Sorry to everybody, but I have been horribly sick for the last week or so, so there have been no updates.  I'm starting to feel better, so I might get around to writing the texturing code next week.

Cheers,
Naltai.

Monday, July 21, 2008

Speed Demon

Well, using the C like functions makes another huge difference to the performance, It is now hitting about 20 FPS without any kind of visibility culling algorithms.  Also, I don't know how optimized the string indexing functions I am using are, they might be causing additional performance overhead.

I think the next thing to do will be texturing and light mapping, to give more impressive screenshots :)

Cheers,
Naltai 

No More Troubles

Well, that was fast.  It seems merely writing about the problem allowed me to step back from it, and realize that vertex arrays have to be explicitly enabled using glClientStateEnable before using them.

So the demo now has the same functionality as the original, only it now runs at about 9 FPS rather than 3 FPS.  Next I will try going back to c-like calls since I now know I need to enable the arrays.

Till next time,
Naltai

Sunday, July 20, 2008

glVertexPointer Troubles

I decided to optimize the drawing first by using glVertexPointer and glDrawElements, however this has turned out to be more trouble than I expected.  It seems that no matter the way I play with the functions I cannot get them to work correctly.

I started off by using the C-like versions of the functions, but have since moved on to trying to get the pythonic versions to work.  Neither will draw anything to the screen.

I'll keep chipping away at it, but it is really annoying, and there are no good references on how this is meant to work in python.

On the plus side, it appears to have improved render times by at least 500%, of course - nothing is showing on the screen, so that could be misleading.

Cheers,
Naltai

Friday, July 18, 2008

It Works!


So a couple of hours of hacking today have gotten me over the initial OpenGL pains, and given me a basic render of the map. It aint pretty, it aint fast, but it works.

Put simply I took the vertex list from my previous post, and ran it through a GL_TRIANGLES call. Put in a simple lighting model, and some appropriate camera movement code and voila, a working map viewer. Of course it has it's problems, using such a simple scheme the viewer crawls (maybe 1 fps?), but it has been great for increasing my confidence that I am getting this stuff down pat.

anyway here is the core of the code and a screenshot to prove it works, I am not sure whether to work on the speed next, or get texturing and lighting working better to give better static screenshots :)

I am not sure why I can't pass the arrays as is to glColor and glNormal, maybe it is a bug in pyopengl, it needs further investigation.

Cheers,
Naltai


glBegin(GL_TRIANGLES)
for v in self.vertices:
glColor(*v.color)
glNormal(*v.normal)
glVertex(v.position)
glEnd()



Thursday, July 17, 2008

Open Dynamics Engine

Following some advice from the gamedev forums I have started looking at some physics engines to simulate the racing physics. The first one I have chosen to investigate is ODE, simply because it has python bindings which makes it much easier to play with.

According to the advice I should be able to get a cartoonish feel to the racing by messing with the simulation parameters of the car.

Once I get some working code I'll post it, but I am just exploring the documentation for now.

Wednesday, July 16, 2008

Extracting Vertices

The parser turns out to be even easier to use than I expected.  The attached python script will give all the vertices in order to be passed to OpenGL for rendering model zero (the static portion of the map).  This is of course a very dumb way to render the map, not taking any advantage of BSPs and visibility information, but it should do for a test render.  I hope to get a test render working in the next couple of days, but it may take a while given my general lack of OpenGL experience.




def get_vertices_from_model(ibsp, model):
vertices = []
face = ibsp.models[model].face
nfaces = ibsp.models[model].nfaces
for f in range(face, face+nfaces):
type = ibsp.faces[f].type
if type not in [1,3]:
continue
vertex = ibsp.faces[f].vertex
meshvert = ibsp.faces[f].meshvert
nmeshverts = ibsp.faces[f].nmeshverts
for m in range(meshvert, meshvert+nmeshverts):
vertices.append(ibsp.vertices[vertex + ibsp.meshverts[m]])
return vertices

Physics of Racing

I don't know if I mentioned it before, but the eventual game I want to create is a cartoonish type racing game in the tradition of Mario Kart. (I played that game to death).

Anyway some searching turned up this excellent treatise on the physics of racing.  I am a bit concerned that it is too in depth and may not lead to a particularly fun game.  Nevertheless it is engrossing reading, I would suggest anyone interested in cars or racing to check it out.

Till next time,
Naltai

Tuesday, July 15, 2008

Why Quake3 BSP?

So I was thinking today, why quake3 BSP as a starting point?

1. Id software has a good technical reputation, their format should be a good example
2. quake3 was hardware accelerated only, so the format won't be bogged down with software rendering cruft
3. There are lots of quake3 maps freely available

Anyway, I added a bit of documentation to the parser, I will look at putting the code up somewhere soon.

The next stage will be to read out the basic vectors in the order required to render a view of the entire map.  

Stay tuned.

Monday, July 14, 2008

Python Quake3 BSP Parser

So I spent a few hours working up a parser for quake3 bsp files in python. I intend to start writing a couple of simple opengl display modules using various methods to show the BSP files.  Maybe I will post this to google code or something when I am done.

The main thing I like about my parser is that it is very simple, only 200 lines or so including some basic test code.  Currently it handles all of the format except for the game specific information, such as spawn points, doors, etc.  I think I will need to modify it more when I get around to rendering through vertex buffers though, as it currently pulls all data out into classes before returning it, which is great for usability, but poor for performance.  

At the moment it is more meant as a self education tool while I continue to play with 3d graphics.  stay tuned for my findings :)

Saturday, July 12, 2008

Quake3 BSP

I am thinking of writing a simple 3d game.  Something fun and easy to play for 5 minutes and put down.

As an exploration of 3d programming, something I have had only limited experience with before, I have been looking at the Quake3 BSP format.  It's interesting how the format has been optimized for quick processing.

A simple example of this is all of the data organized in vertex buffers with double indirection for simple processing by 3d hardware.  Also more subtle things like the simplicity of the BSP algorithm and pre-computed visibility data, an algorithm that would probably entirely fit in the L1 i-cache of the processor.

Anyway for anyone looking some good  references on this, google says the best are this format description and this usage description.