added bench.py demo which prints tree build and render times
added Vertex to python binding, NormalVertex is now its subclass
--- a/demos/SConscript	Thu May 15 00:07:25 2008 +0200
+++ b/demos/SConscript	Thu May 15 19:15:57 2008 +0200
@@ -7,7 +7,8 @@
 	'spheres_ao.py', 'spheres_glass.py', 'spheres_shadow.py',
 	'triangles_monkey.py', 'triangles_sphere.py',
 	'objreader.py', 'plyreader.py', 'lworeader.py',
-	'vector.py', 'render_nff.py', 'demo_PIL.py', 'texture.py']
+	'vector.py', 'render_nff.py', 'demo_PIL.py',
+	'texture.py', 'bench.py']
 
 l = []
 for file in files:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/bench.py	Thu May 15 19:15:57 2008 +0200
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+from pyrit import *
+from lworeader import LoadLightwaveLwoFile
+from plyreader import LoadStanfordPlyFile
+from math import *
+from time import time
+
+rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setBgColour((0.2,0.3,0.8))
+cam = Camera(eye=(-8,3,4),lookat=(0,-0.5,1))
+rt.setCamera(cam)
+
+carmodel = "../models/lwo/Nissan300ZX.lwo"
+bunnymodel = "../models/ply/bunny/bun_zipper.ply"
+
+print "Loading file", carmodel
+facenum = LoadLightwaveLwoFile(rt, carmodel, scale=0.4)
+print "Loaded", facenum, "faces"
+
+matbunny = Material(colour=(0.4, 0.5, 0.9))
+matbunny.setTransmissivity(0.65, 1.3)
+matbunny.setReflectivity(0.3)
+matbunny.setSmooth(True)
+
+print "Loading file", bunnymodel
+facenum = LoadStanfordPlyFile(rt, bunnymodel,
+	matbunny, scale=(20.0, 20.0, -20.0), trans=(-1,-1.8,3.5))
+print "Loaded", facenum, "faces"
+
+mat = Material(colour=(0.5, 0.5, 0.5))
+ground = Box(L=(-10,-2,-10), H=(10,-1.2,10), material=mat)
+rt.addShape(ground)
+
+lwall = Box(L=(-10,-2,-4), H=(10,10,-3), material=mat)
+rt.addShape(lwall)
+
+mat = Material(colour=(0.5, 0.5, 0.5))
+mat.setReflectivity(0.7)
+backwall = Box(L=(8,-2, -10), H=(10,10,10), material=mat)
+rt.addShape(backwall)
+
+light1 = Light(position=(-5.0, 20.0, 8.0), colour=(0.9, 0.9, 0.9))
+rt.addLight(light1)
+light2 = Light(position=(5.0, 10.0, 10.0), colour=(0.9, 0.7, 0.7))
+rt.addLight(light2)
+
+t1 = time()
+top.optimize()
+t2 = time()
+print "kd-tree built in", (t2-t1), "s"
+
+sampler = DefaultSampler(1024, 768)
+sampler.setOversample(1)
+rt.setSampler(sampler)
+
+t1 = time()
+rt.render()
+t2 = time()
+print "rendered in", (t2-t1), "s"
+
+sampler.getPixmap().writePNG('bench.png')
--- a/demos/lworeader.py	Thu May 15 00:07:25 2008 +0200
+++ b/demos/lworeader.py	Thu May 15 19:15:57 2008 +0200
@@ -271,3 +271,5 @@
 		matidx = f[3]-1
 		face = Triangle(v[0], v[1], v[2], materials[matidx])
 		rt.addShape(face)
+
+	return len(faces)
--- a/demos/plyreader.py	Thu May 15 00:07:25 2008 +0200
+++ b/demos/plyreader.py	Thu May 15 19:15:57 2008 +0200
@@ -33,7 +33,8 @@
 		num -= 1
 
 	# read faces
-	while (face_num):
+	num = face_num
+	while (num):
 		tokens = fp.readline().split()
 		if (tokens[0] != "3"):
 			print "ply warning: faces of %d vertices not supported" % tokens[0]
@@ -45,7 +46,7 @@
 				normals[int(x)][i] += n[i]
 			vertex_face_num[int(x)] += 1
 		rt.addShape(face)
-		face_num -= 1
+		num -= 1
 
 	# interpolate normals at vertices
 	num = 0
@@ -55,3 +56,5 @@
 				normals[num][i] /= vertex_face_num[num]
 		vertices[num].setNormal(tuple(normals[num]))
 		num += 1
+
+	return face_num
--- a/include/raytracermodule.h	Thu May 15 00:07:25 2008 +0200
+++ b/include/raytracermodule.h	Thu May 15 19:15:57 2008 +0200
@@ -166,10 +166,15 @@
 	Material *material;
 } MaterialObject;
 
-/** NormalVertex object */
+/** Vertex object */
 typedef struct {
 	PyObject_HEAD
-	NormalVertex *nvertex;
+	Vertex *vertex;
+} VertexObject;
+
+/** NormalVertex object - inherits Vertex*/
+typedef struct {
+	VertexObject vertex;
 } NormalVertexObject;
 
 /** abstract Shape object */
--- a/src/raytracermodule.cc	Thu May 15 00:07:25 2008 +0200
+++ b/src/raytracermodule.cc	Thu May 15 19:15:57 2008 +0200
@@ -914,10 +914,37 @@
 }
 
 
+//=========================== Vertex Object (abstract) ===========================
+
+static void Vertex_Destructor(PyObject* self);
+
+static PyMethodDef VertexMethods[] = {
+	{NULL, NULL}
+};
+
+static PyTypeObject VertexType =
+TYPE_OBJECT(
+	"Vertex",                    /* tp_name */
+	sizeof(VertexObject),        /* tp_basicsize */
+	Vertex_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Vertex type (abstract)",    /* tp_doc */
+	VertexMethods,               /* tp_methods */
+	0,                           /* tp_members */
+	0,                           /* tp_base */
+	0                            /* tp_init */
+);
+
+static void Vertex_Destructor(PyObject* self)
+{
+	delete ((VertexObject *)self)->vertex;
+	self->ob_type->tp_free(self);
+}
+
+
 //=========================== NormalVertex Object ===========================
 
 static PyObject *NormalVertex_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
-static void NormalVertex_Destructor(PyObject* self);
 static PyObject *NormalVertex_setNormal(PyObject* self, PyObject* args);
 
 static PyMethodDef NormalVertexMethods[] = {
@@ -929,12 +956,12 @@
 TYPE_OBJECT(
 	"NormalVertex",                    /* tp_name */
 	sizeof(NormalVertexObject),        /* tp_basicsize */
-	NormalVertex_Destructor,           /* tp_dealloc */
+	0,                                 /* tp_dealloc */
 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
 	"NormalVertex type",               /* tp_doc */
 	NormalVertexMethods,               /* tp_methods */
 	0,                                 /* tp_members */
-	0,                                 /* tp_base */
+	&VertexType,                       /* tp_base */
 	0                                  /* tp_init */
 );
 
@@ -953,7 +980,7 @@
 	if (!TNor && TVer->ob_type == &NormalVertexType)
 	{
 		v = PyObject_New(NormalVertexObject, &NormalVertexType);
-		v->nvertex = new NormalVertex(((NormalVertexObject*)TVer)->nvertex);
+		v->vertex.vertex = new NormalVertex((NormalVertex*)((NormalVertexObject*)TVer)->vertex.vertex);
 	}
 	else
 	{
@@ -965,17 +992,11 @@
 				return NULL;
 
 		v = PyObject_New(NormalVertexObject, &NormalVertexType);
-		v->nvertex = new NormalVertex(Vector(vx, vy, vz), Vector(nx, ny, nz));
+		v->vertex.vertex = new NormalVertex(Vector(vx, vy, vz), Vector(nx, ny, nz));
 	}
 	return (PyObject*)v;
 }
 
-static void NormalVertex_Destructor(PyObject* self)
-{
-	delete ((NormalVertexObject *)self)->nvertex;
-	self->ob_type->tp_free(self);
-}
-
 static PyObject *NormalVertex_setNormal(PyObject* self, PyObject* args)
 {
 	PyObject *TNor = NULL;
@@ -987,7 +1008,7 @@
 	if (!PyArg_ParseTuple(TNor, "fff", &nx, &ny, &nz))
 		return NULL;
 
-	((NormalVertexObject *)self)->nvertex->setNormal(Vector(nx,ny,nz).normalize());
+	((NormalVertex*)((VertexObject *)self)->vertex)->setNormal(Vector(nx,ny,nz).normalize());
 
 	Py_INCREF(Py_None);
 	return Py_None;
@@ -1048,15 +1069,15 @@
 	TriangleObject *v;
 	MaterialObject *material;
 	static char *kwdlist[] = {"A", "B", "C", "material", NULL};
-	NormalVertexObject *A, *B, *C;
+	VertexObject *A, *B, *C;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwd, "O!O!O!O!", kwdlist,
-		&NormalVertexType, &A, &NormalVertexType, &B, &NormalVertexType, &C,
+		&VertexType, &A, &VertexType, &B, &VertexType, &C,
 		&MaterialType, &material))
 		return NULL;
 
 	v = PyObject_New(TriangleObject, &TriangleType);
-	v->shape.shape = new Triangle(A->nvertex, B->nvertex, C->nvertex, material->material);
+	v->shape.shape = new Triangle(A->vertex, B->vertex, C->vertex, material->material);
 	Py_INCREF(material);
 	Py_INCREF(A);
 	Py_INCREF(B);
@@ -1605,6 +1626,7 @@
 	|| PyType_Ready(&LightType) < 0
 	|| PyType_Ready(&CameraType) < 0
 	|| PyType_Ready(&MaterialType) < 0
+	|| PyType_Ready(&VertexType) < 0
 	|| PyType_Ready(&NormalVertexType) < 0
 	|| PyType_Ready(&ShapeType) < 0
 	|| PyType_Ready(&TriangleType) < 0