rename Python module from 'raytracer' to 'pyrit' pyrit
authorRadek Brich <radek.brich@devl.cz>
Tue, 29 Apr 2008 23:31:08 +0200
branchpyrit
changeset 90 f6a72eb99631
parent 89 fcf1487b398b
child 91 9d66d323c354
rename Python module from 'raytracer' to 'pyrit' improve Python binding: - new objects: Container, Octree, KdTree, Shape, Pixmap, Sampler, DefaultSampler - all shapes are now subclasses of Shape - clean, remove redundant Getattr's - Raytracer render method now just wraps C++ method without doing any additional work adjust all demos for changes in Python binding, remove PIL dependency add demo_PIL.py as a example of pyrit + PIL usage render_nff.py now either loads file given as a argument or reads input from stdin otherwise fix bug in pixmap float to char conversion
ccdemos/common_sdl.h
demos/SConscript
demos/boxes.py
demos/buddha.py
demos/bunny.py
demos/car.py
demos/demo_PIL.py
demos/dragon.py
demos/lworeader.py
demos/objreader.py
demos/plyreader.py
demos/render_nff.py
demos/spheres_ao.py
demos/spheres_glass.py
demos/spheres_shadow.py
demos/triangles_monkey.py
demos/triangles_sphere.py
include/pixmap.h
include/raytracer.h
include/sampler.h
src/SConscript
src/pixmap.cc
src/raytracer.cc
src/raytracermodule.cc
--- a/ccdemos/common_sdl.h	Tue Apr 29 13:56:29 2008 +0200
+++ b/ccdemos/common_sdl.h	Tue Apr 29 23:31:08 2008 +0200
@@ -41,9 +41,9 @@
 		}
 		*bufp = SDL_MapRGB(screen->format, c[0], c[1], c[2]);
 #else
-		__m64 m = _mm_cvtps_pi8(_mm_mul_ps(_mm_set_ps1(255.0),
+		__m64 m = _mm_cvtps_pi16(_mm_mul_ps(_mm_set_ps1(255.0),
 			_mm_min_ps(mOne, _mm_set_ps(0, fd[2],fd[1],fd[0]))));
-		*bufp = SDL_MapRGB(screen->format, ((char*)&m)[0], ((char*)&m)[1], ((char*)&m)[2]);
+		*bufp = SDL_MapRGB(screen->format, ((char*)&m)[0], ((char*)&m)[2], ((char*)&m)[4]);
 #endif
 		bufp++;
 	}
--- a/demos/SConscript	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/SConscript	Tue Apr 29 23:31:08 2008 +0200
@@ -7,7 +7,7 @@
 	'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']
+	'vector.py', 'render_nff.py', 'demo_PIL.py']
 
 l = []
 for file in files:
--- a/demos/boxes.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/boxes.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,9 +1,11 @@
 #!/usr/bin/python
 
-from raytracer import Raytracer, Material, Box, Light
-import Image
+from pyrit import *
 
 rt = Raytracer()
+top = Octree()
+rt.setTop(top)
+rt.setCamera(Camera())
 
 light1 = Light(position=(0.0, 5.0, -5.0), colour=(0.7, 0.3, 0.6))
 rt.addLight(light1)
@@ -19,7 +21,9 @@
 			box = Box(L=(-4.3+x, -4.6+y, -8.6+z), H=(-3.7+x, -4.0+y, -8.0+z), material=mat0)
 			rt.addShape(box)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('boxes.png')
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('boxes.png')
--- a/demos/buddha.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/buddha.py	Tue Apr 29 23:31:08 2008 +0200
@@ -3,20 +3,24 @@
 # this demo needs buddha model from
 # http://graphics.stanford.edu/data/3Dscanrep/
 
-from raytracer import Raytracer, Light, Sphere, Triangle, Material
+from pyrit import *
 from plyreader import LoadStanfordPlyFile
-import Image
 
 rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
+
+light = Light(position=(-5.0, 2.0, 8.0), colour=(0.9, 0.3, 0.6))
+rt.addLight(light)
+
 mat = Material(colour=(0.9, 0.9, 0.9))
 mat.setSmooth(True)
 LoadStanfordPlyFile(rt, "../models/ply/happy/happy_vrip_res2.ply",
 	mat, scale=20.0, trans=(0,-3,0))
-
-light = Light(position=(-5.0, 2.0, 8.0), colour=(0.9, 0.3, 0.6))
-rt.addLight(light)
+top.optimize()
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('buddha.png')
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('buddha.png')
--- a/demos/bunny.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/bunny.py	Tue Apr 29 23:31:08 2008 +0200
@@ -3,11 +3,14 @@
 # this demo needs bunny model from
 # http://graphics.stanford.edu/data/3Dscanrep/
 
-from raytracer import Raytracer, Light, Box, Triangle, Material
+from pyrit import *
 from plyreader import LoadStanfordPlyFile
-import Image
 
 rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
+
 #rt.ambientocclusion(samples=100, distance=16.0, angle=0.5)
 
 mat = Material(colour=(0.9, 0.9, 0.9))
@@ -33,7 +36,9 @@
 #light2.castshadows(0)
 rt.addLight(light2)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('bunny.png')
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('bunny.png')
--- a/demos/car.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/car.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,12 +1,12 @@
 #!/usr/bin/python
 
-from raytracer import Raytracer, Light, Sphere, Triangle, Material, Camera
+from pyrit import *
 from lworeader import LoadLightwaveLwoFile
-import Image
 from math import *
 
 rt = Raytracer()
-
+top = KdTree()
+rt.setTop(top)
 cam = Camera(eye=(0.,2.,8.))
 rotx=0.15
 cam.rotate((cos(rotx),-sin(rotx),0.,0.))
@@ -19,7 +19,9 @@
 light2 = Light(position=(5.0, 10.0, 10.0), colour=(0.9, 0.7, 0.7))
 rt.addLight(light2)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('car.png')
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('car.png')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/demo_PIL.py	Tue Apr 29 23:31:08 2008 +0200
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+from pyrit import *
+import Image
+
+rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
+
+light1 = Light(position=(0.0, 5.0, -5.0), colour=(0.7, 0.3, 0.6))
+rt.addLight(light1)
+
+light2 = Light(position=(-2.0, 10.0, -2.0), colour=(0.4, 0.6, 0.3))
+rt.addLight(light2)
+
+mat0 = Material(colour=(0.7, 0.7, 0.7))
+
+box = Box(L=(-20.0, -1.2, -20.0), H=(20.0, -1.0, 20.0), material=mat0)
+rt.addShape(box)
+
+mat1 = Material(colour=(1.0, 0.0, 0.0))
+bigsphere = Sphere(centre=(3.0, 2.0, -7.0), radius=3.0, material=mat1)
+rt.addShape(bigsphere)
+
+mat2 = Material(colour=(0.0, 1.0, 0.0))
+smallsphere = Sphere(centre=(-5.5, 1.5, -8.0), radius=2.0, material=mat2)
+rt.addShape(smallsphere)
+
+mat3 = Material(colour=(0.0, 0.0, 1.0))
+tinysphere = Sphere(centre=(-1.2, 0.0, -2.0), radius=0.5, material=mat3)
+rt.addShape(tinysphere)
+
+top.optimize()
+imagesize = (800, 600)
+sampler = DefaultSampler(imagesize)
+rt.setSampler(sampler)
+rt.render()
+pixmap = sampler.getPixmap()
+data = pixmap.getCharData()
+img = Image.fromstring("RGB", imagesize, data)
+img.save('demo_PIL.png')
--- a/demos/dragon.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/dragon.py	Tue Apr 29 23:31:08 2008 +0200
@@ -3,11 +3,14 @@
 # this demo needs dragon model from
 # http://graphics.stanford.edu/data/3Dscanrep/
 
-from raytracer import Raytracer, Light, Sphere, Triangle, Material
+from pyrit import *
 from plyreader import LoadStanfordPlyFile
-import Image
 
 rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
+
 mat = Material(colour=(0.9, 0.9, 0.9))
 mat.setSmooth(True)
 LoadStanfordPlyFile(rt, "../models/ply/dragon/dragon_vrip_res2.ply",
@@ -19,7 +22,9 @@
 light2 = Light(position=(3.0, 0.0, 9.0), colour=(0.0, 1.0, 0.2))
 rt.addLight(light2)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('dragon.png')
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('dragon.png')
--- a/demos/lworeader.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/lworeader.py	Tue Apr 29 23:31:08 2008 +0200
@@ -2,7 +2,7 @@
 
 from math import *
 from struct import *
-from raytracer import Triangle, NormalVertex, Material
+from pyrit import Triangle, NormalVertex, Material
 from vector import dot
 
 def read_int4(f):
--- a/demos/objreader.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/objreader.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,6 +1,6 @@
 # Wavefron .obj file loader
 
-from raytracer import Triangle, NormalVertex
+from pyrit import Triangle, NormalVertex
 
 def LoadWavefrontObjFile(rt, filename, mat, scale):
 	vertices = []
--- a/demos/plyreader.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/plyreader.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,6 +1,6 @@
 # Stanford .ply file loader
 
-from raytracer import Triangle, NormalVertex
+from pyrit import Triangle, NormalVertex
 
 def LoadStanfordPlyFile(rt, filename, mat, scale=(1,1,1), trans=(0,0,0)):
 	if (type(scale) == float or type(scale) == int):
--- a/demos/render_nff.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/render_nff.py	Tue Apr 29 23:31:08 2008 +0200
@@ -4,16 +4,25 @@
 # see http://tog.acm.org/resources/SPD/
 # cylinders are not implemented
 
-from raytracer import Raytracer, Camera, Light, Material, Sphere, NormalVertex, Triangle
+from pyrit import *
 from math import pi
-import sys, Image
+import sys
 
 rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
+
 imagesize = (800, 600)
 
 mat = Material(colour=(1.0, 1.0, 1.0))
 
 f = sys.stdin
+fbase = "render_nff"
+if len(sys.argv) > 1:
+	f = open(sys.argv[1])
+	fbase = sys.argv[1].rsplit('.',1)[0]
+
 while True:
 	line = f.readline()
 	if line == "":
@@ -90,6 +99,9 @@
 		print "Not implemented:", line
 f.close()
 
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('render_nff.png')
+top.optimize()
+
+sampler = DefaultSampler(imagesize)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG(fbase+'.png')
--- a/demos/spheres_ao.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/spheres_ao.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,9 +1,11 @@
 #!/usr/bin/python
 
-from raytracer import Raytracer, Material, Box, Sphere, Light
-import Image
+from pyrit import *
 
 rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
 rt.ambientOcclusion(samples=100, distance=16.0, angle=0.5)
 
 light1 = Light(position=(0.0, 5.0, -5.0), colour=(0.7, 0.3, 0.6))
@@ -31,7 +33,9 @@
 tinysphere = Sphere(centre=(-1.2, 0.0, -2.0), radius=0.5, material=mat3)
 rt.addShape(tinysphere)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('spheres_ao.png')
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('spheres_ao.png')
--- a/demos/spheres_glass.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/spheres_glass.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,10 +1,11 @@
 #!/usr/bin/python
 
-from raytracer import Raytracer, Material, Box, Sphere, Light
-#, SphericalLight
-import Image
+from pyrit import *
 
 rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
 
 light1 = Light(position=(0.0, 4.0, -3.0), colour=(0.9, 0.3, 0.6))
 rt.addLight(light1)
@@ -38,7 +39,9 @@
 	sph = Sphere(centre=(-5.0+i, -1.5, -4.0), radius=0.5, material=mat3)
 	rt.addShape(sph)
 
-rendersize = (800, 600)
-data = rt.render(rendersize)
-img = Image.fromstring("RGB", rendersize, data)
-img.save('spheres_glass.png')
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('spheres_glass.png')
--- a/demos/spheres_shadow.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/spheres_shadow.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,9 +1,11 @@
 #!/usr/bin/python
 
-from raytracer import Raytracer, Material, Box, Sphere, Light
-import Image
+from pyrit import *
 
 rt = Raytracer()
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
 
 light1 = Light(position=(0.0, 5.0, -5.0), colour=(0.7, 0.3, 0.6))
 rt.addLight(light1)
@@ -28,7 +30,9 @@
 tinysphere = Sphere(centre=(-1.2, 0.0, -2.0), radius=0.5, material=mat3)
 rt.addShape(tinysphere)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('spheres_shadow.png')
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('spheres_shadow.png')
--- a/demos/triangles_monkey.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/triangles_monkey.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,17 +1,21 @@
 #!/usr/bin/python
 
-from raytracer import Raytracer, Light, Sphere, Triangle, NormalVertex, Material
+from pyrit import *
 from objreader import LoadWavefrontObjFile
-import Image
 
 rt = Raytracer()
-mat = Material(colour=(0.9, 0.9, 0.9))
-LoadWavefrontObjFile(rt, "../models/obj/monkey.obj", mat, 1.5)
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
 
 light = Light(position=(-5.0, 2.0, 8.0), colour=(0.9, 0.3, 0.6))
 rt.addLight(light)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('triangles_monkey.png')
+mat = Material(colour=(0.9, 0.9, 0.9))
+LoadWavefrontObjFile(rt, "../models/obj/monkey.obj", mat, 1.5)
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('triangles_monkey.png')
--- a/demos/triangles_sphere.py	Tue Apr 29 13:56:29 2008 +0200
+++ b/demos/triangles_sphere.py	Tue Apr 29 23:31:08 2008 +0200
@@ -1,12 +1,12 @@
 #!/usr/bin/python
 
-from raytracer import Raytracer, Light, Sphere, Triangle, NormalVertex, Material
+from pyrit import *
 from objreader import LoadWavefrontObjFile
-import Image
 
 rt = Raytracer()
-mat = Material(colour=(0.9, 0.9, 0.9))
-LoadWavefrontObjFile(rt, "../models/obj/sphere.obj", mat, 1.5)
+top = KdTree()
+rt.setTop(top)
+rt.setCamera(Camera())
 
 light1 = Light(position=(0.0, 2.0, 6.0), colour=(0.9, 0.3, 0.6))
 light1.castShadows(False);
@@ -16,7 +16,11 @@
 light2.castShadows(False);
 rt.addLight(light2)
 
-imagesize = (800, 600)
-data = rt.render(imagesize)
-img = Image.fromstring("RGB", imagesize, data)
-img.save('triangles_sphere.png')
+mat = Material(colour=(0.9, 0.9, 0.9))
+LoadWavefrontObjFile(rt, "../models/obj/sphere.obj", mat, 1.5)
+top.optimize()
+
+sampler = DefaultSampler(800, 600)
+rt.setSampler(sampler)
+rt.render()
+sampler.getPixmap().writePNG('triangles_sphere.png')
--- a/include/pixmap.h	Tue Apr 29 13:56:29 2008 +0200
+++ b/include/pixmap.h	Tue Apr 29 23:31:08 2008 +0200
@@ -53,8 +53,8 @@
 	void setData(Float *afdata, int aw, int ah)
 		{ fdata = afdata; w = aw; h = ah; };
 	Colour get(int x, int y) { return data[y*w + x]; };
-	const int &getWidth() { return w; };
-	const int &getHeight() { return h; };
+	const int &getWidth() const { return w; };
+	const int &getHeight() const { return h; };
 	Float*& getFloatData() { return fdata; };
 	unsigned char *getCharData() const;
 	int writePNG(const char *fname) const;
--- a/include/raytracer.h	Tue Apr 29 13:56:29 2008 +0200
+++ b/include/raytracer.h	Tue Apr 29 23:31:08 2008 +0200
@@ -93,9 +93,11 @@
 	void addShape(Shape *shape) { top->addShape(shape); };
 	void addLight(Light *light) { lights.push_back(light); };
 	void setSampler(Sampler *sampl) { sampler = sampl; };
+	Sampler *&getSampler() { return sampler; };
 	void setCamera(Camera *cam) { camera = cam; };
+	Camera *&getCamera() { return camera; };
 	void setTop(Container *atop) { top = atop; };
-	Container *getTop() { return top; };
+	Container *&getTop() { return top; };
 
 	void setBgColour(const Colour &bg) { bg_colour = bg; };
 	void setMaxDepth(int newdepth) { max_depth = newdepth; };
--- a/include/sampler.h	Tue Apr 29 13:56:29 2008 +0200
+++ b/include/sampler.h	Tue Apr 29 23:31:08 2008 +0200
@@ -73,7 +73,7 @@
 	virtual bool nextSample(Sample *s) = 0;
 	virtual void saveSample(Sample &samp, Colour &col) = 0;
 	bool packetableSamples() { return packetable; };
-	const Pixmap &getPixmap() { return pixmap; };
+	const Pixmap &getPixmap() const { return pixmap; };
 };
 
 /**
--- a/src/SConscript	Tue Apr 29 13:56:29 2008 +0200
+++ b/src/SConscript	Tue Apr 29 23:31:08 2008 +0200
@@ -26,7 +26,7 @@
 	objs.append( env.Object(src) )
 	shared_objs.append( env.SharedObject(src) )
 
-pymodule = pyenv.SharedLibrary('raytracer',
+pymodule = pyenv.SharedLibrary('pyrit',
 	['raytracermodule.cc']+shared_objs,
 	SHLIBPREFIX = '',
 	CCFLAGS = '$CCFLAGS -Wno-write-strings')
--- a/src/pixmap.cc	Tue Apr 29 13:56:29 2008 +0200
+++ b/src/pixmap.cc	Tue Apr 29 23:31:08 2008 +0200
@@ -48,9 +48,10 @@
 	__m64 m;
 	for (unsigned char *cd = cdata; cd < cdata + w*h*3; cd += 4, fd += 4)
 	{
-		m = _mm_cvtps_pi8(_mm_mul_ps(cmax,
+		m = _mm_cvtps_pi16(_mm_mul_ps(cmax,
 			_mm_min_ps(mOne, _mm_set_ps(fd[3],fd[2],fd[1],fd[0]))));
-		memcpy(cd, &m, 4);
+		for (int i = 0; i < 4; i++)
+			cd[i] = ((unsigned char *)&m)[i<<1];
 	}
 #endif
 
--- a/src/raytracer.cc	Tue Apr 29 13:56:29 2008 +0200
+++ b/src/raytracer.cc	Tue Apr 29 23:31:08 2008 +0200
@@ -372,7 +372,7 @@
 			}
 		}
 
-		dbgmsg(2, "phase %d:  0%% done", phase);
+		dbgmsg(2, "- phase %d:  0%% done", phase);
 
 		pthread_mutex_lock(&sampler_mutex);
 
--- a/src/raytracermodule.cc	Tue Apr 29 13:56:29 2008 +0200
+++ b/src/raytracermodule.cc	Tue Apr 29 23:31:08 2008 +0200
@@ -28,8 +28,53 @@
 
 #include <vector>
 #include "raytracer.h"
+#include "octree.h"
 #include "kdtree.h"
 
+#define TYPE_OBJECT(name, basicsize, dealloc, flags, doc, methods, members, base, init) \
+{ \
+	PyObject_HEAD_INIT(NULL) \
+	0,                          /* ob_size */ \
+	(name),                     /* tp_name */ \
+	(basicsize),                /* tp_basicsize*/ \
+	0,                          /* tp_itemsize*/ \
+	(dealloc),                  /* tp_dealloc*/ \
+	0,                          /* tp_print*/\
+	0,                          /* tp_getattr*/\
+	0,                          /* tp_setattr*/\
+	0,                          /* tp_compare*/\
+	0,                          /* tp_repr*/\
+	0,                          /* tp_as_number*/\
+	0,                          /* tp_as_sequence*/\
+	0,                          /* tp_as_mapping*/\
+	0,                          /* tp_hash */\
+	0,                          /* tp_call*/\
+	0,                          /* tp_str*/\
+	PyObject_GenericGetAttr,    /* tp_getattro*/\
+	0,                          /* tp_setattro*/\
+	0,                          /* tp_as_buffer*/\
+	(flags),                    /* tp_flags*/\
+	(doc),                      /* tp_doc */\
+	0,                          /* tp_traverse */\
+	0,                          /* tp_clear */\
+	0,                          /* tp_richcompare */\
+	0,                          /* tp_weaklistoffset */\
+	0,                          /* tp_iter */\
+	0,                          /* tp_iternext */\
+	(methods),                  /* tp_methods */\
+	(members),                  /* tp_members */\
+	0,                          /* tp_getset */\
+	(base),                     /* tp_base */\
+	0,                          /* tp_dict */ \
+	0,                          /* tp_descr_get */ \
+	0,                          /* tp_descr_set */ \
+	0,                          /* tp_dictoffset */ \
+	(init),                     /* tp_init */ \
+	0,                          /* tp_alloc */ \
+	0,                          /* tp_new */ \
+	0,                          /* tp_free */ \
+}
+
 //=========================== Light Source Object ===========================
 
 typedef struct {
@@ -39,33 +84,26 @@
 
 static PyObject *Light_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
 static void Light_Destructor(PyObject* self);
-static PyObject *Light_Getattr(PyObject *self, char *name);
 static PyObject *Light_castShadows(PyObject* self, PyObject* args);
 
-static PyTypeObject LightType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"Light",			/*tp_name*/
-	sizeof(LightObject),		/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	Light_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	Light_Getattr,			/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
-};
-
 static PyMethodDef LightMethods[] = {
 	{"castShadows", (PyCFunction)Light_castShadows, METH_VARARGS, "Enable or disable shadows from this light."},
 	{NULL, NULL}
 };
 
+static PyTypeObject LightType =
+TYPE_OBJECT(
+	"Light",                    /* tp_name */
+	sizeof(LightObject),        /* tp_basicsize */
+	Light_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Light type",               /* tp_doc */
+	LightMethods,               /* tp_methods */
+	0,                          /* tp_members */
+	0,                          /* tp_base */
+	0                           /* tp_init */
+);
+
 static PyObject* Light_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
 	LightObject *v;
@@ -91,12 +129,7 @@
 static void Light_Destructor(PyObject* self)
 {
 	delete ((LightObject *)self)->light;
-	PyObject_Del(self);
-}
-
-static PyObject *Light_Getattr(PyObject *self, char *name)
-{
-	return Py_FindMethod(LightMethods, self, name);
+	self->ob_type->tp_free(self);
 }
 
 static PyObject *Light_castShadows(PyObject* self, PyObject* args)
@@ -121,30 +154,10 @@
 
 static PyObject *Camera_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
 static void Camera_Destructor(PyObject* self);
-static PyObject *Camera_Getattr(PyObject *self, char *name);
 static PyObject *Camera_setEye(PyObject* self, PyObject* args);
 static PyObject *Camera_setAngle(PyObject* self, PyObject* args);
 static PyObject *Camera_rotate(PyObject* self, PyObject* args);
 
-static PyTypeObject CameraType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"Camera",			/*tp_name*/
-	sizeof(CameraObject),		/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	Camera_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	Camera_Getattr,			/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
-};
-
 static PyMethodDef CameraMethods[] = {
 	{"setEye", (PyCFunction)Camera_setEye, METH_VARARGS, "Set eye of the camera."},
 	{"setAngle", (PyCFunction)Camera_setAngle, METH_VARARGS, "Set vertical angle of view."},
@@ -152,6 +165,19 @@
 	{NULL, NULL}
 };
 
+static PyTypeObject CameraType =
+TYPE_OBJECT(
+	"Camera",                    /* tp_name */
+	sizeof(CameraObject),        /* tp_basicsize */
+	Camera_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Camera type",               /* tp_doc */
+	CameraMethods,               /* tp_methods */
+	0,                           /* tp_members */
+	0,                           /* tp_base */
+	0                            /* tp_init */
+);
+
 static PyObject* Camera_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
 	CameraObject *v;
@@ -207,15 +233,9 @@
 static void Camera_Destructor(PyObject* self)
 {
 	delete ((CameraObject *)self)->camera;
-	PyObject_Del(self);
+	self->ob_type->tp_free(self);
 }
 
-static PyObject *Camera_Getattr(PyObject *self, char *name)
-{
-	return Py_FindMethod(CameraMethods, self, name);
-}
-
-
 static PyObject *Camera_setEye(PyObject* self, PyObject* args)
 {
 	PyObject *TEye = NULL;
@@ -274,31 +294,11 @@
 
 static PyObject *Material_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
 static void Material_Destructor(PyObject* self);
-static PyObject *Material_Getattr(PyObject *self, char *name);
 static PyObject *Material_setPhong(PyObject* self, PyObject* args);
 static PyObject *Material_setReflectivity(PyObject* self, PyObject* args);
 static PyObject *Material_setTransmissivity(PyObject* self, PyObject* args);
 static PyObject *Material_setSmooth(PyObject* self, PyObject* args);
 
-static PyTypeObject MaterialType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"Material",			/*tp_name*/
-	sizeof(MaterialObject),		/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	Material_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	Material_Getattr,			/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
-};
-
 static PyMethodDef MaterialMethods[] = {
 	{"setPhong", (PyCFunction)Material_setPhong, METH_VARARGS, "Set ambient, diffuse, specular and shininess Phong model constants."},
 	{"setReflectivity", (PyCFunction)Material_setReflectivity, METH_VARARGS, "Set reflectivity."},
@@ -307,6 +307,19 @@
 	{NULL, NULL}
 };
 
+static PyTypeObject MaterialType =
+TYPE_OBJECT(
+	"Material",                    /* tp_name */
+	sizeof(MaterialObject),        /* tp_basicsize */
+	Material_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Material type",               /* tp_doc */
+	MaterialMethods,               /* tp_methods */
+	0,                             /* tp_members */
+	0,                             /* tp_base */
+	0                              /* tp_init */
+);
+
 static PyObject* Material_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
 	MaterialObject *v;
@@ -330,12 +343,7 @@
 static void Material_Destructor(PyObject* self)
 {
 	delete ((MaterialObject *)self)->material;
-	PyObject_Del(self);
-}
-
-static PyObject *Material_Getattr(PyObject *self, char *name)
-{
-	return Py_FindMethod(MaterialMethods, self, name);
+	self->ob_type->tp_free(self);
 }
 
 static PyObject *Material_setPhong(PyObject* self, PyObject* args)
@@ -400,33 +408,26 @@
 
 static PyObject *NormalVertex_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
 static void NormalVertex_Destructor(PyObject* self);
-static PyObject *NormalVertex_Getattr(PyObject *self, char *name);
 static PyObject *NormalVertex_setNormal(PyObject* self, PyObject* args);
 
-static PyTypeObject NormalVertexType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"NormalVertex",			/*tp_name*/
-	sizeof(NormalVertexObject),		/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	NormalVertex_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	NormalVertex_Getattr,			/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
-};
-
 static PyMethodDef NormalVertexMethods[] = {
 	{"setNormal", (PyCFunction)NormalVertex_setNormal, METH_VARARGS, "Set normal of this vertex."},
 	{NULL, NULL}
 };
 
+static PyTypeObject NormalVertexType =
+TYPE_OBJECT(
+	"NormalVertex",                    /* tp_name */
+	sizeof(NormalVertexObject),        /* tp_basicsize */
+	NormalVertex_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"NormalVertex type",               /* tp_doc */
+	NormalVertexMethods,               /* tp_methods */
+	0,                                 /* tp_members */
+	0,                                 /* tp_base */
+	0                                  /* tp_init */
+);
+
 static PyObject* NormalVertex_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
 	NormalVertexObject *v;
@@ -462,12 +463,7 @@
 static void NormalVertex_Destructor(PyObject* self)
 {
 	delete ((NormalVertexObject *)self)->nvertex;
-	PyObject_Del(self);
-}
-
-static PyObject *NormalVertex_Getattr(PyObject *self, char *name)
-{
-	return Py_FindMethod(NormalVertexMethods, self, name);
+	self->ob_type->tp_free(self);
 }
 
 static PyObject *NormalVertex_setNormal(PyObject* self, PyObject* args)
@@ -487,40 +483,121 @@
 	return Py_None;
 }
 
+//=========================== Shape Object (abstract) ===========================
+
+typedef struct {
+	PyObject_HEAD
+	Shape *shape;
+} ShapeObject;
+
+static void Shape_Destructor(PyObject* self);
+
+static PyMethodDef ShapeMethods[] = {
+	{NULL, NULL}
+};
+
+static PyTypeObject ShapeType =
+TYPE_OBJECT(
+	"Shape",                    /* tp_name */
+	sizeof(ShapeObject),        /* tp_basicsize */
+	Shape_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Shape type (abstract)",    /* tp_doc */
+	ShapeMethods,               /* tp_methods */
+	0,                          /* tp_members */
+	0,                          /* tp_base */
+	0                           /* tp_init */
+);
+
+static void Shape_Destructor(PyObject* self)
+{
+	delete ((ShapeObject *)self)->shape;
+	self->ob_type->tp_free(self);
+}
+
+//=========================== Triangle Object ===========================
+
+typedef struct {
+	ShapeObject shape;
+} TriangleObject;
+
+static PyObject *Triangle_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
+static PyObject *Triangle_getNormal(PyObject* self, PyObject* args);
+
+static PyMethodDef TriangleMethods[] = {
+	{"getNormal", (PyCFunction)Triangle_getNormal, METH_NOARGS, "Get normal of whole triangle."},
+	{NULL, NULL}
+};
+
+static PyTypeObject TriangleType =
+TYPE_OBJECT(
+	"Triangle",                    /* tp_name */
+	sizeof(TriangleObject),        /* tp_basicsize */
+	0,                             /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Triangle type",               /* tp_doc */
+	TriangleMethods,               /* tp_methods */
+	0,                             /* tp_members */
+	&ShapeType,                    /* tp_base */
+	0                              /* tp_init */
+);
+
+static PyObject* Triangle_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
+{
+	TriangleObject *v;
+	MaterialObject *material;
+	static char *kwdlist[] = {"A", "B", "C", "material", NULL};
+	NormalVertexObject *A, *B, *C;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwd, "O!O!O!O!", kwdlist,
+		&NormalVertexType, &A, &NormalVertexType, &B, &NormalVertexType, &C,
+		&MaterialType, &material))
+		return NULL;
+
+	v = PyObject_New(TriangleObject, &TriangleType);
+	v->shape.shape = new Triangle(A->nvertex, B->nvertex, C->nvertex, material->material);
+	Py_INCREF(material);
+	Py_INCREF(A);
+	Py_INCREF(B);
+	Py_INCREF(C);
+	return (PyObject*)v;
+}
+
+static PyObject* Triangle_getNormal(PyObject* self, PyObject* args)
+{
+	PyObject *obj;
+
+	Vector3 N = ((Triangle*)((TriangleObject *)self)->shape.shape)->getNormal();
+
+	obj = Py_BuildValue("(fff)", N.x, N.y, N.z);
+	return obj;
+}
+
 //=========================== Sphere Object ===========================
 
 typedef struct {
-	PyObject_HEAD
-	Sphere *shape;
+	ShapeObject shape;
 } SphereObject;
 
 static PyObject *Sphere_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
-static void Sphere_Destructor(PyObject* self);
-static PyObject *Sphere_Getattr(PyObject *self, char *name);
-
-static PyTypeObject SphereType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"Sphere",			/*tp_name*/
-	sizeof(SphereObject),		/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	Sphere_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	Sphere_Getattr,			/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
-};
 
 static PyMethodDef SphereMethods[] = {
 	{NULL, NULL}
 };
 
+static PyTypeObject SphereType =
+TYPE_OBJECT(
+	"Sphere",                    /* tp_name */
+	sizeof(SphereObject),        /* tp_basicsize */
+	0,                           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Sphere type",               /* tp_doc */
+	SphereMethods,               /* tp_methods */
+	0,                           /* tp_members */
+	&ShapeType,                  /* tp_base */
+	0                            /* tp_init */
+);
+
 static PyObject* Sphere_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
 	SphereObject *v;
@@ -537,56 +614,36 @@
 		return NULL;
 
 	v = PyObject_New(SphereObject, &SphereType);
-	v->shape = new Sphere(Vector3(cx, cy, cz), radius, material->material);
+	v->shape.shape = new Sphere(Vector3(cx, cy, cz), radius, material->material);
 	Py_INCREF(material);
 	return (PyObject*)v;
 }
 
-static void Sphere_Destructor(PyObject* self)
-{
-	delete ((SphereObject *)self)->shape;
-	PyObject_Del(self);
-}
-
-static PyObject *Sphere_Getattr(PyObject *self, char *name)
-{
-	return Py_FindMethod(SphereMethods, self, name);
-}
-
 //=========================== Box Object ===========================
 
 typedef struct {
-	PyObject_HEAD
-	Box *shape;
+	ShapeObject shape;
 } BoxObject;
 
 static PyObject *Box_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
-static void Box_Destructor(PyObject* self);
-static PyObject *Box_Getattr(PyObject *self, char *name);
-
-static PyTypeObject BoxType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"Box",			/*tp_name*/
-	sizeof(BoxObject),		/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	Box_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	Box_Getattr,			/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
-};
 
 static PyMethodDef BoxMethods[] = {
 	{NULL, NULL}
 };
 
+static PyTypeObject BoxType =
+TYPE_OBJECT(
+	"Box",                    /* tp_name */
+	sizeof(BoxObject),        /* tp_basicsize */
+	0,                        /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Box type",               /* tp_doc */
+	BoxMethods,               /* tp_methods */
+	0,                        /* tp_members */
+	&ShapeType,               /* tp_base */
+	0                         /* tp_init */
+);
+
 static PyObject* Box_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
 	BoxObject *v;
@@ -607,98 +664,325 @@
 		return NULL;
 
 	v = PyObject_New(BoxObject, &BoxType);
-	v->shape = new Box(Vector3(lx, ly, lz), Vector3(hx, hy, hz), material->material);
+	v->shape.shape = new Box(Vector3(lx, ly, lz), Vector3(hx, hy, hz), material->material);
 	Py_INCREF(material);
 	return (PyObject*)v;
 }
 
-static void Box_Destructor(PyObject* self)
+//=========================== Pixmap Object ===========================
+
+typedef struct {
+	PyObject_HEAD
+	const Pixmap *pixmap;
+} PixmapObject;
+
+static PyObject* Pixmap_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
+static PyObject* Pixmap_getWidth(PyObject* self, PyObject* args);
+static PyObject* Pixmap_getHeight(PyObject* self, PyObject* args);
+static PyObject* Pixmap_getCharData(PyObject* self, PyObject* args);
+static PyObject* Pixmap_writePNG(PyObject* self, PyObject* args);
+
+static PyMethodDef PixmapMethods[] = {
+	{"getWidth", (PyCFunction)Pixmap_getWidth, METH_NOARGS, "Get width of pixmap."},
+	{"getHeight", (PyCFunction)Pixmap_getHeight, METH_NOARGS, "Get height of pixmap."},
+	{"getCharData", (PyCFunction)Pixmap_getCharData, METH_NOARGS, "Get raw byte data."},
+	{"writePNG", (PyCFunction)Pixmap_writePNG, METH_VARARGS, "Write pixmap to PNG file."},
+	{NULL, NULL}
+};
+
+static PyTypeObject PixmapType =
+TYPE_OBJECT(
+	"Pixmap",                    /* tp_name */
+	sizeof(PixmapObject),        /* tp_basicsize */
+	0,                           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Pixmap type",               /* tp_doc */
+	PixmapMethods,               /* tp_methods */
+	0,                           /* tp_members */
+	0,                           /* tp_base */
+	0                            /* tp_init */
+);
+
+static PyObject* Pixmap_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
-	delete ((BoxObject *)self)->shape;
-	PyObject_Del(self);
+	int w = 0, h = 0;
+	PixmapObject *v;
+
+	if (!PyArg_ParseTuple(args, "(ii)", &w, &h))
+		return NULL;
+
+	v = PyObject_New(PixmapObject, &PixmapType);
+	v->pixmap = new Pixmap(w, h);
+	return (PyObject*)v;
 }
 
-static PyObject *Box_Getattr(PyObject *self, char *name)
+static PyObject* Pixmap_getWidth(PyObject* self, PyObject* args)
 {
-	return Py_FindMethod(BoxMethods, self, name);
+	return Py_BuildValue("i", ((PixmapObject *)self)->pixmap->getWidth());
+}
+
+static PyObject* Pixmap_getHeight(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("i", ((PixmapObject *)self)->pixmap->getHeight());
 }
 
-//=========================== Triangle Object ===========================
+static PyObject *Pixmap_getCharData(PyObject* self, PyObject* args)
+{
+	unsigned char *chardata;
+	int w,h;
+	PyObject *o;
+
+	chardata = ((PixmapObject *)self)->pixmap->getCharData();
+	w = ((PixmapObject *)self)->pixmap->getWidth();
+	h = ((PixmapObject *)self)->pixmap->getHeight();
+	o = Py_BuildValue("s#", chardata, w*h*3);
+	delete[] chardata;
+	return o;
+}
+
+static PyObject *Pixmap_writePNG(PyObject* self, PyObject* args)
+{
+	const char *fname;
+	int res;
+
+	if (!PyArg_ParseTuple(args, "s", &fname))
+		return NULL;
+
+	res = ((PixmapObject *)self)->pixmap->writePNG(fname);
+	return Py_BuildValue("i", res);
+}
+
+//=========================== Sampler Object (abstract) ===========================
 
 typedef struct {
 	PyObject_HEAD
-	Triangle *triangle;
-} TriangleObject;
-
-static PyObject *Triangle_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
-static void Triangle_Destructor(PyObject* self);
-static PyObject *Triangle_Getattr(PyObject *self, char *name);
-static PyObject *Triangle_getNormal(PyObject* self, PyObject* args);
+	Sampler *sampler;
+} SamplerObject;
 
-static PyTypeObject TriangleType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"Triangle",			/*tp_name*/
-	sizeof(TriangleObject),		/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	Triangle_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	Triangle_Getattr,			/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
+static void Sampler_Destructor(PyObject* self);
+static PyObject* Sampler_getPixmap(PyObject* self, PyObject* args);
+
+static PyMethodDef SamplerMethods[] = {
+	{"getPixmap", (PyCFunction)Sampler_getPixmap, METH_NOARGS, "Get sampler's pixmap."},
+	{NULL, NULL}
 };
 
-static PyMethodDef TriangleMethods[] = {
-	{"getNormal", (PyCFunction)Triangle_getNormal, METH_NOARGS, "Get normal of whole triangle."},
+static PyTypeObject SamplerType =
+TYPE_OBJECT(
+	"Sampler",                    /* tp_name */
+	sizeof(SamplerObject),        /* tp_basicsize */
+	Sampler_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Sampler type (abstract)",    /* tp_doc */
+	SamplerMethods,               /* tp_methods */
+	0,                            /* tp_members */
+	0,                            /* tp_base */
+	0                             /* tp_init */
+);
+
+static void Sampler_Destructor(PyObject* self)
+{
+	delete ((SamplerObject *)self)->sampler;
+	self->ob_type->tp_free(self);
+}
+
+static PyObject* Sampler_getPixmap(PyObject* self, PyObject* args)
+{
+	PixmapObject *v = PyObject_New(PixmapObject, &PixmapType);
+	v->pixmap = &((SamplerObject *)self)->sampler->getPixmap();
+	return (PyObject*)v;
+}
+
+//=========================== DefaultSampler Object ===========================
+
+typedef struct {
+	SamplerObject sampler;
+} DefaultSamplerObject;
+
+static PyObject *DefaultSampler_Constructor(PyObject* self, PyObject* args, PyObject *kwd);
+static PyObject* DefaultSampler_setSubsample(PyObject* self, PyObject* args);
+static PyObject* DefaultSampler_setOversample(PyObject* self, PyObject* args);
+
+static PyMethodDef DefaultSamplerMethods[] = {
+	{"setSubsample", (PyCFunction)DefaultSampler_setSubsample, METH_VARARGS, "Set subsampling mode."},
+	{"setOversample", (PyCFunction)DefaultSampler_setOversample, METH_VARARGS, "Set oversampling mode."},
 	{NULL, NULL}
 };
 
-static PyObject* Triangle_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
+static PyTypeObject DefaultSamplerType =
+TYPE_OBJECT(
+	"DefaultSampler",                    /* tp_name */
+	sizeof(DefaultSamplerObject),        /* tp_basicsize */
+	0,                                   /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"DefaultSampler type",               /* tp_doc */
+	DefaultSamplerMethods,               /* tp_methods */
+	0,                                   /* tp_members */
+	&SamplerType,                        /* tp_base */
+	0                                    /* tp_init */
+);
+
+static PyObject* DefaultSampler_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
 {
-	TriangleObject *v;
-	MaterialObject *material;
-	static char *kwdlist[] = {"A", "B", "C", "material", NULL};
-	NormalVertexObject *A, *B, *C;
+	int w = 0, h = 0;
+	PyObject *o1, *o2;
+	DefaultSamplerObject *v;
 
-	if (!PyArg_ParseTupleAndKeywords(args, kwd, "O!O!O!O!", kwdlist,
-		&NormalVertexType, &A, &NormalVertexType, &B, &NormalVertexType, &C,
-		&MaterialType, &material))
+	if (!PyArg_ParseTuple(args, "O|O", &o1, &o2))
 		return NULL;
 
-	v = PyObject_New(TriangleObject, &TriangleType);
-	v->triangle = new Triangle(A->nvertex, B->nvertex, C->nvertex, material->material);
-	Py_INCREF(material);
-	Py_INCREF(A);
-	Py_INCREF(B);
-	Py_INCREF(C);
+	if (PyTuple_Check(o1))
+	{
+		if (!PyArg_ParseTuple(o1, "ii", &w, &h))
+			return NULL;
+	}
+	else
+		if (!PyArg_ParseTuple(args, "ii", &w, &h))
+			return NULL;
+
+	v = PyObject_New(DefaultSamplerObject, &DefaultSamplerType);
+	v->sampler.sampler = new DefaultSampler(w, h);
 	return (PyObject*)v;
 }
 
-static void Triangle_Destructor(PyObject* self)
+static PyObject* DefaultSampler_setSubsample(PyObject* self, PyObject* args)
+{
+	int size = 0;
+
+	if (!PyArg_ParseTuple(args, "i", &size))
+		return NULL;
+
+	((DefaultSampler *)((DefaultSamplerObject *)self)->sampler.sampler)->setSubsample(size);
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject* DefaultSampler_setOversample(PyObject* self, PyObject* args)
 {
-	delete ((TriangleObject *)self)->triangle;
-	PyObject_Del(self);
+	int osa = 0;
+
+	if (!PyArg_ParseTuple(args, "i", &osa))
+		return NULL;
+
+	((DefaultSampler *)((DefaultSamplerObject *)self)->sampler.sampler)->setOversample(osa);
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+//=========================== Container Object ===========================
+
+typedef struct {
+	PyObject_HEAD
+	Container *container;
+} ContainerObject;
+
+static PyObject *Container_Constructor(PyObject* self, PyObject* args);
+static void Container_Destructor(PyObject* self);
+static PyObject* Container_optimize(PyObject* self, PyObject* args);
+
+static PyMethodDef ContainerMethods[] = {
+	{"optimize", (PyCFunction)Container_optimize, METH_NOARGS, "Build acceleration structures."},
+	{NULL, NULL}
+};
+
+static PyTypeObject ContainerType =
+TYPE_OBJECT(
+	"Container",                    /* tp_name */
+	sizeof(ContainerObject),        /* tp_basicsize */
+	Container_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Container type",               /* tp_doc */
+	ContainerMethods,               /* tp_methods */
+	0,                              /* tp_members */
+	0,                              /* tp_base */
+	0                               /* tp_init */
+);
+
+static PyObject* Container_Constructor(PyObject* self, PyObject* args)
+{
+	ContainerObject *v;
+	v = PyObject_New(ContainerObject, &ContainerType);
+	v->container = new Container();
+	return (PyObject*)v;
+}
+
+static void Container_Destructor(PyObject* self)
+{
+	delete ((ContainerObject *)self)->container;
+	self->ob_type->tp_free(self);
 }
 
-static PyObject *Triangle_Getattr(PyObject *self, char *name)
+static PyObject* Container_optimize(PyObject* self, PyObject* args)
 {
-	return Py_FindMethod(TriangleMethods, self, name);
+	((ContainerObject *)self)->container->optimize();
+	Py_INCREF(Py_None);
+	return Py_None;
 }
 
-static PyObject* Triangle_getNormal(PyObject* self, PyObject* args)
-{
-	PyObject *obj;
+//=========================== Octree Object ===========================
+
+typedef struct {
+	ContainerObject container;
+} OctreeObject;
+
+static PyObject* Octree_Constructor(PyObject* self, PyObject* args);
+
+static PyMethodDef OctreeMethods[] = {
+	{NULL, NULL}
+};
+
+static PyTypeObject OctreeType =
+TYPE_OBJECT(
+	"Octree",                    /* tp_name */
+	sizeof(OctreeObject),        /* tp_basicsize */
+	0,                           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Octree type",               /* tp_doc */
+	OctreeMethods,               /* tp_methods */
+	0,                           /* tp_members */
+	&ContainerType,              /* tp_base */
+	0                            /* tp_init */
+);
 
-	Vector3 N = ((TriangleObject *)self)->triangle->getNormal();
+static PyObject* Octree_Constructor(PyObject* self, PyObject* args)
+{
+	OctreeObject *v;
+	v = PyObject_New(OctreeObject, &OctreeType);
+	v->container.container = new Octree();
+	return (PyObject*)v;
+}
+
+//=========================== KdTree Object ===========================
+
+typedef struct {
+	ContainerObject container;
+} KdTreeObject;
+
+static PyObject* KdTree_Constructor(PyObject* self, PyObject* args);
+
+static PyMethodDef KdTreeMethods[] = {
+	{NULL, NULL}
+};
 
-	obj = Py_BuildValue("(fff)", N.x, N.y, N.z);
-	return obj;
+static PyTypeObject KdTreeType =
+TYPE_OBJECT(
+	"KdTree",                    /* tp_name */
+	sizeof(KdTreeObject),        /* tp_basicsize */
+	0,                           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"KdTree type",               /* tp_doc */
+	KdTreeMethods,               /* tp_methods */
+	0,                           /* tp_members */
+	&ContainerType,              /* tp_base */
+	0                            /* tp_init */
+);
+
+static PyObject* KdTree_Constructor(PyObject* self, PyObject* args)
+{
+	KdTreeObject *v;
+	v = PyObject_New(KdTreeObject, &KdTreeType);
+	v->container.container = new KdTree();
+	return (PyObject*)v;
 }
 
 //=========================== Raytracer Object ===========================
@@ -711,36 +995,20 @@
 
 static PyObject *Raytracer_Constructor(PyObject* self, PyObject* args);
 static void Raytracer_Destructor(PyObject* self);
-static PyObject *Raytracer_Getattr(PyObject *self, char *name);
 static PyObject *Raytracer_render(PyObject* self, PyObject* args);
+static PyObject *Raytracer_setSampler(PyObject* self, PyObject* args);
 static PyObject *Raytracer_setCamera(PyObject* self, PyObject* args);
+static PyObject *Raytracer_setTop(PyObject* self, PyObject* args);
 static PyObject *Raytracer_setBgColour(PyObject* self, PyObject* args);
 static PyObject *Raytracer_addShape(PyObject* self, PyObject* args);
 static PyObject *Raytracer_addLight(PyObject* self, PyObject* args);
 static PyObject *Raytracer_ambientOcclusion(PyObject* self, PyObject* args, PyObject *kwd);
 
-static PyTypeObject RaytracerType = {
-	PyObject_HEAD_INIT(NULL)
-	0,				/*ob_size*/
-	"Raytracer",			/*tp_name*/
-	sizeof(RaytracerObject),	/*tp_basicsize*/
-	0,				/*tp_itemsize*/
-	/* methods */
-	Raytracer_Destructor,		/*tp_dealloc*/
-	0,				/*tp_print*/
-	Raytracer_Getattr,		/*tp_getattr*/
-	0,				/*tp_setattr*/
-	0,				/*tp_compare*/
-	0,				/*tp_repr*/
-	0,				/*tp_as_number*/
-	0,				/*tp_as_sequence*/
-	0,				/*tp_as_mapping*/
-	0,				/*tp_hash */
-};
-
 static PyMethodDef RaytracerMethods[] = {
-	{"render", (PyCFunction)Raytracer_render, METH_VARARGS, "Render scene and return image data."},
+	{"render", (PyCFunction)Raytracer_render, METH_NOARGS, "Render scene."},
+	{"setSampler", (PyCFunction)Raytracer_setSampler, METH_VARARGS, "Set sampler."},
 	{"setCamera", (PyCFunction)Raytracer_setCamera, METH_VARARGS, "Set camera for the scene."},
+	{"setTop", (PyCFunction)Raytracer_setTop, METH_VARARGS, "Set top container for shapes."},
 	{"setBgColour", (PyCFunction)Raytracer_setBgColour, METH_VARARGS, "Set background colour."},
 	{"addShape", (PyCFunction)Raytracer_addShape, METH_VARARGS, "Add new shape to scene."},
 	{"addLight", (PyCFunction)Raytracer_addLight, METH_VARARGS, "Add new light source to scene."},
@@ -749,6 +1017,19 @@
 	{NULL, NULL}
 };
 
+static PyTypeObject RaytracerType =
+TYPE_OBJECT(
+	"Raytracer",                    /* tp_name */
+	sizeof(RaytracerObject),        /* tp_basicsize */
+	Raytracer_Destructor,           /* tp_dealloc */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
+	"Raytracer type",               /* tp_doc */
+	RaytracerMethods,               /* tp_methods */
+	0,                              /* tp_members */
+	0,                              /* tp_base */
+	0                               /* tp_init */
+);
+
 static PyObject* Raytracer_Constructor(PyObject* self, PyObject* args)
 {
 	RaytracerObject *v;
@@ -759,9 +1040,6 @@
 	v = PyObject_New(RaytracerObject, &RaytracerType);
 	v->raytracer = new Raytracer();
 	v->children = new vector<PyObject*>();
-	v->raytracer->setCamera(new Camera());
-	v->raytracer->setTop(new KdTree());
-
 	return (PyObject*)v;
 }
 
@@ -772,41 +1050,28 @@
 		o != ((RaytracerObject *)self)->children->end(); o++)
 		Py_DECREF(*o);
 	delete ((RaytracerObject *)self)->raytracer;
-	PyObject_Del(self);
-}
-
-static PyObject *Raytracer_Getattr(PyObject *self, char *name)
-{
-	return Py_FindMethod(RaytracerMethods, self, name);
+	self->ob_type->tp_free(self);
 }
 
 static PyObject* Raytracer_render(PyObject* self, PyObject* args)
 {
-	int w = 0, h = 0;
-	unsigned char *chardata;
-	Float *data;
-	PyObject *o;
+	((RaytracerObject *)self)->raytracer->render();
+	Py_INCREF(Py_None);
+	return Py_None;
+}
 
-	if (!PyArg_ParseTuple(args, "(ii)", &w, &h))
+static PyObject* Raytracer_setSampler(PyObject* self, PyObject* args)
+{
+	SamplerObject *samp;
+
+	if (!PyArg_ParseTuple(args, "O!", &SamplerType, &samp))
 		return NULL;
 
-	printf("[pyrit] Running ray tracer\n");
-	((RaytracerObject *)self)->raytracer->getTop()->optimize();
-	data = (Float *) malloc(w*h*3*sizeof(Float));
-	DefaultSampler sampler(data, w, h);
-	((RaytracerObject *)self)->raytracer->setSampler(&sampler);
-	((RaytracerObject *)self)->raytracer->render();
-	if (!data) {
-		Py_INCREF(Py_None);
-		return Py_None;
-	}
+	((RaytracerObject *)self)->raytracer->setSampler(samp->sampler);
 
-	printf("[pyrit] Converting image data (float to char)\n");
-	chardata = sampler.getPixmap().getCharData();
-	o = Py_BuildValue("s#", chardata, w*h*3);
-	delete[] chardata;
-	printf("[pyrit] Done.\n");
-	return o;
+	Py_INCREF(samp);
+	Py_INCREF(Py_None);
+	return Py_None;
 }
 
 static PyObject* Raytracer_setCamera(PyObject* self, PyObject* args)
@@ -823,6 +1088,20 @@
 	return Py_None;
 }
 
+static PyObject* Raytracer_setTop(PyObject* self, PyObject* args)
+{
+	ContainerObject *cont;
+
+	if (!PyArg_ParseTuple(args, "O!", &ContainerType, &cont))
+		return NULL;
+
+	((RaytracerObject *)self)->raytracer->setTop(cont->container);
+
+	Py_INCREF(cont);
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
 static PyObject* Raytracer_setBgColour(PyObject* self, PyObject* args)
 {
 	Float r,g,b;
@@ -838,16 +1117,14 @@
 
 static PyObject* Raytracer_addShape(PyObject* self, PyObject* args)
 {
-	PyObject *obj;
+	ShapeObject *shape;
 
-	if (!PyArg_ParseTuple(args, "O", &obj))
+	if (!PyArg_ParseTuple(args, "O!", &ShapeType, &shape))
 		return NULL;
 
-	((RaytracerObject *)self)->raytracer->addShape(
-		((BoxObject*)obj)->shape);
-
-	((RaytracerObject *)self)->children->push_back(obj);
-	Py_INCREF(obj);
+	((RaytracerObject *)self)->raytracer->addShape(shape->shape);
+	((RaytracerObject *)self)->children->push_back((PyObject*)shape);
+	Py_INCREF(shape);
 	Py_INCREF(Py_None);
 	return Py_None;
 }
@@ -893,17 +1170,50 @@
 		METH_VARARGS | METH_KEYWORDS, "Material object constructor."},
 	{"NormalVertex", (PyCFunction) NormalVertex_Constructor,
 		METH_VARARGS | METH_KEYWORDS, "NormalVertex object constructor."},
+	{"Triangle", (PyCFunction) Triangle_Constructor,
+		METH_VARARGS | METH_KEYWORDS, "Triangle object constructor."},
 	{"Sphere", (PyCFunction) Sphere_Constructor,
 		METH_VARARGS | METH_KEYWORDS, "Sphere object constructor."},
 	{"Box", (PyCFunction) Box_Constructor,
 		METH_VARARGS | METH_KEYWORDS, "Box object constructor."},
-	{"Triangle", (PyCFunction) Triangle_Constructor,
-		METH_VARARGS | METH_KEYWORDS, "Triangle object constructor."},
+	{"Pixmap", (PyCFunction) Pixmap_Constructor,
+		METH_VARARGS | METH_KEYWORDS, "Pixmap object constructor."},
+	{"DefaultSampler", (PyCFunction) DefaultSampler_Constructor,
+		METH_VARARGS | METH_KEYWORDS, "DefaultSampler object constructor."},
+	{"Container", (PyCFunction) Container_Constructor,
+		METH_NOARGS, "Container object constructor."},
+	{"Octree", (PyCFunction) Octree_Constructor,
+		METH_NOARGS, "Octree object constructor."},
+	{"KdTree", (PyCFunction) KdTree_Constructor,
+		METH_NOARGS, "KdTree object constructor."},
 	{NULL, NULL}
 };
 
 
-extern "C" void initraytracer(void)
+extern "C" void initpyrit(void)
 {
-	Py_InitModule("raytracer", ModuleMethods);
+	PyObject* m;
+
+	if (PyType_Ready(&RaytracerType) < 0
+	|| PyType_Ready(&LightType) < 0
+	|| PyType_Ready(&CameraType) < 0
+	|| PyType_Ready(&MaterialType) < 0
+	|| PyType_Ready(&NormalVertexType) < 0
+	|| PyType_Ready(&ShapeType) < 0
+	|| PyType_Ready(&TriangleType) < 0
+	|| PyType_Ready(&SphereType) < 0
+	|| PyType_Ready(&BoxType) < 0
+	|| PyType_Ready(&PixmapType) < 0
+	|| PyType_Ready(&SamplerType) < 0
+	|| PyType_Ready(&DefaultSamplerType) < 0
+	|| PyType_Ready(&ContainerType) < 0
+	|| PyType_Ready(&OctreeType) < 0
+	|| PyType_Ready(&KdTreeType) < 0
+	)
+		return;
+
+	m = Py_InitModule3("pyrit", ModuleMethods, "Pyrit ray tracer.");
+
+	if (m == NULL)
+		return;
 }