smooth triangles (aka Phong shading) pyrit
authorRadek Brich <radek.brich@devl.cz>
Sat, 08 Dec 2007 12:37:45 +0100
branchpyrit
changeset 28 ffe83ca074f3
parent 27 e9bb83c2b8b9
child 29 574c34441a1c
smooth triangles (aka Phong shading) extend Python binding to support vertex normals and smooth triangles make bunny.py and realtime_dragon smooth, and fix other demos for new triangle constructor add Vector::operator/=
TODO
ccdemos/realtime_dragon.cc
demos/buddha.py
demos/bunny.py
demos/dragon.py
demos/triangles_monkey.py
demos/triangles_sphere.py
include/scene.h
include/vector.h
src/container.cc
src/kdtree.cc
src/raytracermodule.cc
src/scene.cc
--- a/TODO	Fri Dec 07 16:39:42 2007 +0100
+++ b/TODO	Sat Dec 08 12:37:45 2007 +0100
@@ -2,9 +2,8 @@
    - optimize structures
    - optimize construction: do not use bounding boxes of shapes, instead implement box-shape intersection
    - save/load
- * update Python binding, Camera, new classes
+ * update Python binding: Camera, new classes
  * more complex demos
- * check/update triangle routines
  * namespace
 
 New Classes?
--- a/ccdemos/realtime_dragon.cc	Fri Dec 07 16:39:42 2007 +0100
+++ b/ccdemos/realtime_dragon.cc	Sat Dec 08 12:37:45 2007 +0100
@@ -13,7 +13,9 @@
 
 void load_ply(const char *filename, Material *mat, Float scale)
 {
-	vector<Vector3> vertices;
+	vector<NormalVertex*> vertices;
+	vector<Vector3> normals;
+	vector<int> vertex_face_num;
 	ifstream f(filename);
 	string token = "a";
 	if (!f.is_open())
@@ -37,19 +39,22 @@
 	}
 
 	// read vertices
-	Vector3 v;
-	while (vertex_num--)
+	Vector3 P;
+	int num = vertex_num;
+	while (num--)
 	{
-		f >> v.x >> v.y >> v.z;
-		v.x = -scale*v.x;
-		v.y = scale*v.y - 3.6;
-		v.z = -scale*v.z;
-		vertices.push_back(v);
+		f >> P.x >> P.y >> P.z;
+		P.x = -scale*P.x;
+		P.y = scale*P.y - 3.6;
+		P.z = -scale*P.z;
+		vertices.push_back(new NormalVertex(P));
+		normals.push_back(Vector3());
+		vertex_face_num.push_back(0);
 	}
 
 	// read faces
 	Triangle *face;
-	int num, v1, v2, v3;
+	int v1, v2, v3;
 	while (face_num--)
 	{
 		f >> num;
@@ -61,6 +66,21 @@
 		f >> v1 >> v2 >> v3;
 		face = new Triangle(vertices.at(v1), vertices.at(v2), vertices.at(v3), mat);
 		rt.addshape(face);
+		face->setSmooth();
+
+		normals.at(v1) += face->getNormal();
+		vertex_face_num.at(v1)++;
+		normals.at(v2) += face->getNormal();
+		vertex_face_num.at(v2)++;
+		normals.at(v3) += face->getNormal();
+		vertex_face_num.at(v3)++;
+	}
+
+	for (int i; i < vertex_num; i++)
+	{
+		normals.at(i) /= vertex_face_num.at(i);
+		normals.at(i).normalize();
+		vertices.at(i)->N = normals.at(i);
 	}
 
 	f.close();
@@ -119,7 +139,7 @@
 	/* initialize raytracer and prepare scene */
 	render_buffer = (Float *) malloc(w*h*3*sizeof(Float));
 
-	rt.setThreads(2);
+	rt.setThreads(1);
 	rt.setMaxDepth(3);
 
 	KdTree top;
--- a/demos/buddha.py	Fri Dec 07 16:39:42 2007 +0100
+++ b/demos/buddha.py	Sat Dec 08 12:37:45 2007 +0100
@@ -36,7 +36,7 @@
 		if (tokens[0] != "3"):
 			print "ply warning: faces of %d vertices not supported" % tokens[0]
 		f = [vertices[int(x)] for x in tokens[1:4]]
-		face = Triangle(f[0], f[1], f[2], mat)
+		face = Triangle(NormalVertex(f[0]), NormalVertex(f[1]), NormalVertex(f[2]), mat)
 		rt.addshape(face)
 		face_num -= 1
 
--- a/demos/bunny.py	Fri Dec 07 16:39:42 2007 +0100
+++ b/demos/bunny.py	Sat Dec 08 12:37:45 2007 +0100
@@ -6,11 +6,13 @@
 import sys
 sys.path.append(open('ModulePath').read().strip())
 
-from raytracer import Raytracer, Light, Sphere, Triangle, Material
+from raytracer import Raytracer, Light, Sphere, Triangle, NormalVertex, Material
 import Image
 
 def LoadStanfordPlyFile(rt, mat, filename, scale):
 	vertices = []
+	normals = []
+	vertex_face_num = []
 	fp = file(filename)
 	# read header
 	tokens = (0,)
@@ -23,27 +25,45 @@
 				face_num = int(tokens[2])
 
 	# read vertices
-	while (vertex_num):
+	num = vertex_num
+	while (num):
 		tokens = fp.readline().split()
 		v = [scale*float(x) for x in tokens[0:3]]
 		v[0] = -v[0]-1
 		v[1] = v[1]-3
-		vertices.append(tuple(v))
-		vertex_num -= 1
+		vertices.append(NormalVertex(tuple(v)))
+		normals.append([0.,0.,0.])
+		vertex_face_num.append(0)
+		num -= 1
 
 	# read faces
 	while (face_num):
 		tokens = fp.readline().split()
 		if (tokens[0] != "3"):
 			print "ply warning: faces of %d vertices not supported" % tokens[0]
-		f = [vertices[int(x)] for x in tokens[1:4]]
-		face = Triangle(f[0], f[2], f[1], mat)
+		v = [vertices[int(x)] for x in tokens[1:4]]
+		face = Triangle(v[0], v[2], v[1], mat)
+		n = face.getNormal()
+		for x in tokens[1:4]:
+			for i in range(3):
+				normals[int(x)][i] += n[i]
+			vertex_face_num[int(x)] += 1
+		face.setSmooth()
 		rt.addshape(face)
 		face_num -= 1
 
+	# interpolate normals at vertices
+	num = 0
+	while (num < vertex_num):
+		if (vertex_face_num[num] > 0):
+			for i in range(3):
+				normals[num][i] /= vertex_face_num[num]
+		vertices[num].setNormal(tuple(normals[num]))
+		num += 1
+
 rt = Raytracer()
 mat = Material(colour=(0.9, 0.9, 0.9))
-LoadStanfordPlyFile(rt, mat, "../models/bunny/bun_zipper_res3.ply", 29.0)
+LoadStanfordPlyFile(rt, mat, "../models/bunny/bun_zipper.ply", 29.0)
 
 light = Light(position=(-5.0, 2.0, 10.0), colour=(0.9, 0.3, 0.6))
 rt.addlight(light)
--- a/demos/dragon.py	Fri Dec 07 16:39:42 2007 +0100
+++ b/demos/dragon.py	Sat Dec 08 12:37:45 2007 +0100
@@ -38,13 +38,13 @@
 		if (tokens[0] != "3"):
 			print "ply warning: faces of %d vertices not supported" % tokens[0]
 		f = [vertices[int(x)] for x in tokens[1:4]]
-		face = Triangle(f[0], f[1], f[2], mat)
+		face = Triangle(NormalVertex(f[0]), NormalVertex(f[1]), NormalVertex(f[2]), mat)
 		rt.addshape(face)
 		face_num -= 1
 
 rt = Raytracer()
 mat = Material(colour=(0.9, 0.9, 0.9))
-LoadStanfordPlyFile(rt, mat, "../models/dragon/dragon_vrip_res4.ply", 29.0)
+LoadStanfordPlyFile(rt, mat, "../models/dragon/dragon_vrip_res2.ply", 29.0)
 
 light1 = Light(position=(-5.0, 2.0, 8.0), colour=(0.9, 0.3, 0.2))
 rt.addlight(light1)
--- a/demos/triangles_monkey.py	Fri Dec 07 16:39:42 2007 +0100
+++ b/demos/triangles_monkey.py	Sat Dec 08 12:37:45 2007 +0100
@@ -19,7 +19,7 @@
 			vertices.append(tuple(v))
 		if ln[0] == "f":
 			f = [vertices[int(x)-1] for x in ln[1:4]]
-			face = Triangle(f[0], f[1], f[2], mat)
+			face = Triangle(NormalVertex(f[0]), NormalVertex(f[1]), NormalVertex(f[2]), mat)
 			rt.addshape(face)
 
 rt = Raytracer()
--- a/demos/triangles_sphere.py	Fri Dec 07 16:39:42 2007 +0100
+++ b/demos/triangles_sphere.py	Sat Dec 08 12:37:45 2007 +0100
@@ -3,7 +3,7 @@
 import sys
 sys.path.append(open('ModulePath').read().strip())
 
-from raytracer import Raytracer, Light, Sphere, Triangle, Material
+from raytracer import Raytracer, Light, Sphere, Triangle, NormalVertex, Material
 import Image
 
 def LoadWavefrontObjFile(rt, mat, filename):
@@ -19,7 +19,7 @@
 			vertices.append(tuple(v))
 		if ln[0] == "f":
 			f = [vertices[int(x)-1] for x in ln[1:4]]
-			face = Triangle(f[0], f[1], f[2], mat)
+			face = Triangle(NormalVertex(f[0]), NormalVertex(f[1]), NormalVertex(f[2]), mat)
 			rt.addshape(face)
 
 rt = Raytracer()
--- a/include/scene.h	Fri Dec 07 16:39:42 2007 +0100
+++ b/include/scene.h	Sat Dec 08 12:37:45 2007 +0100
@@ -9,6 +9,7 @@
 #define SCENE_H
 
 #include <vector>
+#include <typeinfo>
 
 #include "noise.h"
 #include "vector.h"
@@ -156,7 +157,7 @@
 	virtual bool intersect_all(const Ray &ray, Float dist, vector<Float> &allts) const = 0;
 
 	// normal at point P
-	virtual Vector3 normal(Vector3 &P) const = 0;
+	virtual const Vector3 normal(const Vector3 &P) const = 0;
 
 	virtual BBox get_bbox() const = 0;
 };
@@ -178,7 +179,7 @@
 		center(acenter), radius(aradius) { material = amaterial; }
 	bool intersect(const Ray &ray, Float &dist) const;
 	bool intersect_all(const Ray &ray, Float dist, vector<Float> &allts) const;
-	Vector3 normal(Vector3 &P) const { return (P - center) * inv_radius; };
+	const Vector3 normal(const Vector3 &P) const { return (P - center) * inv_radius; };
 	BBox get_bbox() const;
 };
 
@@ -196,10 +197,28 @@
 	};
 	bool intersect(const Ray &ray, Float &dist) const;
 	bool intersect_all(const Ray &ray, Float dist, vector<Float> &allts) const { return false; };
-	Vector3 normal(Vector3 &P) const;
+	const Vector3 normal(const Vector3 &P) const;
 	BBox get_bbox() const { return BBox(L, H); };
 };
 
+class Vertex
+{
+public:
+	Vector3 P;
+	Vertex(const Vector3 &aP): P(aP) {};
+};
+
+class NormalVertex: public Vertex
+{
+public:
+	Vector3 N;
+	NormalVertex(const Vector3 &aP): Vertex(aP) {};
+	NormalVertex(const Vector3 &aP, const Vector3 &aN): Vertex(aP), N(aN) {};
+	const Vector3 &getNormal() { return N; };
+	void setNormal(const Vector3 &aN) { N = aN; };
+};
+
+
 class Triangle: public Shape
 {
 #ifdef TRI_BARI_PRE
@@ -214,13 +233,39 @@
 #ifdef TRI_PLUCKER
 	Float pla[6], plb[6], plc[6];
 #endif
+	Vector3 N;
+	bool smooth;
+	const Vector3 smooth_normal(const Vector3 &P) const
+	{
+#ifdef TRI_BARI_PRE
+		const Vector3 &NA = static_cast<NormalVertex*>(A)->N;
+		const Vector3 &NB = static_cast<NormalVertex*>(B)->N;
+		const Vector3 &NC = static_cast<NormalVertex*>(C)->N;
+		static const int modulo3[5] = {0,1,2,0,1};
+		register const int ku = modulo3[k+1];
+		register const int kv = modulo3[k+2];
+		const Float pu = P[ku] - A->P[ku];
+		const Float pv = P[kv] - A->P[kv];
+		const Float u = pv * bnu + pu * bnv;
+		const Float v = pu * cnv + pv * cnu;
+		Vector3 n = NA + u * (NB - NA) + v * (NC - NA);
+		n.normalize();
+		return n;
+#else
+		return N; // not implemented for other algorithms
+#endif
+	};
 public:
-	Vector3 A, B, C, N;
+	Vertex *A, *B, *C;
 
-	Triangle(const Vector3 &aA, const Vector3 &aB, const Vector3 &aC, Material *amaterial);
+	Triangle(Vertex *aA, Vertex *aB, Vertex *aC, Material *amaterial);
 	bool intersect(const Ray &ray, Float &dist) const;
 	bool intersect_all(const Ray &ray, Float dist, vector<Float> &allts) const {return false;};
-	Vector3 normal(Vector3 &) const { return N; };
+	const Vector3 normal(const Vector3 &P) const { return (smooth ? smooth_normal(P) : N); };
+	const Vector3 getNormal() const { return N; };
+	void setSmooth() { smooth = true; };//(typeid(*A) == typeid(*B) == typeid(*C) == typeid(NormalVertex)); };
+	void setFlat() { smooth = false; };
+	bool getSmooth() const { return smooth; };
 	BBox get_bbox() const;
 };
 
--- a/include/vector.h	Fri Dec 07 16:39:42 2007 +0100
+++ b/include/vector.h	Sat Dec 08 12:37:45 2007 +0100
@@ -69,6 +69,15 @@
 		return *this;
 	};
 
+	// cut
+	Vector3 operator/=(const Float &f)
+	{
+		x /= f;
+		y /= f;
+		z /= f;
+		return *this;
+	};
+
 	// sum
 	friend Vector3 operator+(const Vector3 &a, const Vector3 &b)
 	{
--- a/src/container.cc	Fri Dec 07 16:39:42 2007 +0100
+++ b/src/container.cc	Sat Dec 08 12:37:45 2007 +0100
@@ -3,29 +3,29 @@
 
 void Container::addShape(Shape* aShape)
 {
-        shapes.push_back(aShape);
-        if (shapes.size() == 0) {
-                /* initialize bounding box */
-                bbox = aShape->get_bbox();
-        } else {
-                /* adjust bounding box */
-                BBox shapebb = aShape->get_bbox();
-                if (shapebb.L.x < bbox.L.x)  bbox.L.x = shapebb.L.x;
-                if (shapebb.L.y < bbox.L.y)  bbox.L.y = shapebb.L.y;
-                if (shapebb.L.z < bbox.L.z)  bbox.L.z = shapebb.L.z;
-                if (shapebb.H.x > bbox.H.x)  bbox.H.x = shapebb.H.x;
-                if (shapebb.H.y > bbox.H.y)  bbox.H.y = shapebb.H.y;
-                if (shapebb.H.z > bbox.H.z)  bbox.H.z = shapebb.H.z;
-        }
+	shapes.push_back(aShape);
+	if (shapes.size() == 0) {
+		/* initialize bounding box */
+		bbox = aShape->get_bbox();
+	} else {
+		/* adjust bounding box */
+		BBox shapebb = aShape->get_bbox();
+		if (shapebb.L.x < bbox.L.x)  bbox.L.x = shapebb.L.x;
+		if (shapebb.L.y < bbox.L.y)  bbox.L.y = shapebb.L.y;
+		if (shapebb.L.z < bbox.L.z)  bbox.L.z = shapebb.L.z;
+		if (shapebb.H.x > bbox.H.x)  bbox.H.x = shapebb.H.x;
+		if (shapebb.H.y > bbox.H.y)  bbox.H.y = shapebb.H.y;
+		if (shapebb.H.z > bbox.H.z)  bbox.H.z = shapebb.H.z;
+	}
 };
 
 Shape *Container::nearest_intersection(const Shape *origin_shape, const Ray &ray,
         Float &nearest_distance)
 {
-        Shape *nearest_shape = NULL;
-        ShapeList::iterator shape;
-        for (shape = shapes.begin(); shape != shapes.end(); shape++)
-                if (*shape != origin_shape && (*shape)->intersect(ray, nearest_distance))
-                        nearest_shape = *shape;
-        return nearest_shape;
+	Shape *nearest_shape = NULL;
+	ShapeList::iterator shape;
+	for (shape = shapes.begin(); shape != shapes.end(); shape++)
+		if (*shape != origin_shape && (*shape)->intersect(ray, nearest_distance))
+			nearest_shape = *shape;
+	return nearest_shape;
 }
--- a/src/kdtree.cc	Fri Dec 07 16:39:42 2007 +0100
+++ b/src/kdtree.cc	Sat Dec 08 12:37:45 2007 +0100
@@ -183,13 +183,13 @@
 		return;
 	}
 
-#if 0
+#if 1
 // export kd-tree as .obj for visualization
 // this would be hard to reconstruct later
 	static ofstream F("kdtree.obj");
 	Vector3 v;
 	static int f=0;
-	v.cell[axis] = splitpos->pos;
+	v.cell[axis] = split;
 	v.cell[(axis+1)%3] = bbox.L.cell[(axis+1)%3];
 	v.cell[(axis+2)%3] = bbox.L.cell[(axis+2)%3];
 	F << "v " << v.x << " " << v.y << " " << v.z << endl;
--- a/src/raytracermodule.cc	Fri Dec 07 16:39:42 2007 +0100
+++ b/src/raytracermodule.cc	Sat Dec 08 12:37:45 2007 +0100
@@ -139,8 +139,9 @@
 		&PyTuple_Type, &TCol))
 		return NULL;
 
-	if (!PyArg_ParseTuple(TCol, "fff", &cr, &cg, &cb))
-		return NULL;
+	if (TCol)
+		if (!PyArg_ParseTuple(TCol, "fff", &cr, &cg, &cb))
+			return NULL;
 
 	v = PyObject_New(MaterialObject, &MaterialType);
 	v->material = new Material(Colour(cr, cg, cb));
@@ -158,6 +159,94 @@
 	return Py_FindMethod(MaterialMethods, self, name);
 }
 
+//=========================== NormalVertex Object ===========================
+
+typedef struct {
+	PyObject_HEAD
+	NormalVertex *nvertex;
+} NormalVertexObject;
+
+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 PyObject* NormalVertex_Constructor(PyObject* self, PyObject* args, PyObject *kwd)
+{
+	NormalVertexObject *v;
+	static char *kwdlist[] = {"vector", "normal", NULL};
+	PyObject *TVer = NULL;
+	PyObject *TNor = NULL;
+	Float vx, vy, vz, nx=0, ny=0, nz=0;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwd, "O!|O!", kwdlist,
+		&PyTuple_Type, &TVer, &PyTuple_Type, &TNor))
+		return NULL;
+
+	if (!PyArg_ParseTuple(TVer, "fff", &vx, &vy, &vz))
+		return NULL;
+
+	if (TNor)
+		if (!PyArg_ParseTuple(TNor, "fff", &nx, &ny, &nz))
+			return NULL;
+
+	v = PyObject_New(NormalVertexObject, &NormalVertexType);
+	v->nvertex = new NormalVertex(Vector3(vx, vy, vz), Vector3(nx, ny, nz));
+	return (PyObject*)v;
+}
+
+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);
+}
+
+static PyObject *NormalVertex_setNormal(PyObject* self, PyObject* args)
+{
+	PyObject *TNor = NULL;
+	Float nx, ny, nz;
+
+	if (!PyArg_ParseTuple(args, "O!", &PyTuple_Type, &TNor))
+		return NULL;
+
+	if (!PyArg_ParseTuple(TNor, "fff", &nx, &ny, &nz))
+		return NULL;
+
+	((NormalVertexObject *)self)->nvertex->setNormal(Vector3(nx,ny,nz).normalize());
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
 //=========================== Sphere Object ===========================
 
 typedef struct {
@@ -298,12 +387,14 @@
 
 typedef struct {
 	PyObject_HEAD
-	Triangle *shape;
+	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);
+static PyObject *Triangle_setSmooth(PyObject* self, PyObject* args);
 
 static PyTypeObject TriangleType = {
 	PyObject_HEAD_INIT(NULL)
@@ -325,6 +416,8 @@
 };
 
 static PyMethodDef TriangleMethods[] = {
+	{"getNormal", (PyCFunction)Triangle_getNormal, METH_NOARGS, "Get normal of whole triangle."},
+	{"setSmooth", (PyCFunction)Triangle_setSmooth, METH_NOARGS, "Set the triangle smooth."},
 	{NULL, NULL}
 };
 
@@ -333,33 +426,25 @@
 	TriangleObject *v;
 	MaterialObject *material;
 	static char *kwdlist[] = {"A", "B", "C", "material", NULL};
-	PyObject *A = NULL, *B = NULL, *C = NULL;
-	Float ax, ay, az, bx, by, bz, cx, cy, cz;
+	NormalVertexObject *A, *B, *C;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwd, "O!O!O!O!", kwdlist,
-		&PyTuple_Type, &A, &PyTuple_Type, &B, &PyTuple_Type, &C,
+		&NormalVertexType, &A, &NormalVertexType, &B, &NormalVertexType, &C,
 		&MaterialType, &material))
 		return NULL;
 
-	if (!PyArg_ParseTuple(A, "fff", &ax, &ay, &az))
-		return NULL;
-
-	if (!PyArg_ParseTuple(B, "fff", &bx, &by, &bz))
-		return NULL;
-
-	if (!PyArg_ParseTuple(C, "fff", &cx, &cy, &cz))
-		return NULL;
-
 	v = PyObject_New(TriangleObject, &TriangleType);
-	v->shape = new Triangle(Vector3(ax, ay, az), Vector3(bx, by, bz),
-		Vector3(cx, cy, cz), material->material);
+	v->triangle = 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 void Triangle_Destructor(PyObject* self)
 {
-	delete ((TriangleObject *)self)->shape;
+	delete ((TriangleObject *)self)->triangle;
 	PyObject_Del(self);
 }
 
@@ -368,6 +453,23 @@
 	return Py_FindMethod(TriangleMethods, self, name);
 }
 
+static PyObject* Triangle_getNormal(PyObject* self, PyObject* args)
+{
+	PyObject *obj;
+
+	Vector3 N = ((TriangleObject *)self)->triangle->getNormal();
+
+	obj = Py_BuildValue("(fff)", N.x, N.y, N.z);
+	return obj;
+}
+
+static PyObject* Triangle_setSmooth(PyObject* self, PyObject* args)
+{
+	((TriangleObject *)self)->triangle->setSmooth();
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
 //=========================== Raytracer Object ===========================
 
 typedef struct {
@@ -532,6 +634,8 @@
 		METH_VARARGS | METH_KEYWORDS, "Light source object constructor."},
 	{"Material", (PyCFunction) Material_Constructor,
 		METH_VARARGS | METH_KEYWORDS, "Material object constructor."},
+	{"NormalVertex", (PyCFunction) NormalVertex_Constructor,
+		METH_VARARGS | METH_KEYWORDS, "NormalVertex object constructor."},
 	{"Sphere", (PyCFunction) Sphere_Constructor,
 		METH_VARARGS | METH_KEYWORDS, "Sphere object constructor."},
 	{"Box", (PyCFunction) Box_Constructor,
--- a/src/scene.cc	Fri Dec 07 16:39:42 2007 +0100
+++ b/src/scene.cc	Sat Dec 08 12:37:45 2007 +0100
@@ -165,7 +165,7 @@
 		return false;
 }
 
-Vector3 Box::normal(Vector3 &P) const
+const Vector3 Box::normal(const Vector3 &P) const
 {
 	Vector3 N;
 	for (int i = 0; i < 3; i++)
@@ -203,22 +203,22 @@
 }
 #endif
 
-Triangle::Triangle(const Vector3 &aA, const Vector3 &aB, const Vector3 &aC, Material *amaterial)
-	: A(aA), B(aB), C(aC)
+Triangle::Triangle(Vertex *aA, Vertex *aB, Vertex *aC, Material *amaterial)
+	: smooth(false), A(aA), B(aB), C(aC)
 {
 	material = amaterial;
 	material->reflection = 0;
 
-	const Vector3 c = B - A;
-	const Vector3 b = C - A;
+	const Vector3 c = B->P - A->P;
+	const Vector3 b = C->P - A->P;
 
-	N = -cross(c, b);
+	N = cross(c, b);
 	N.normalize();
 
 #ifdef TRI_PLUCKER
-	Plucker(B,C,pla);
-	Plucker(C,A,plb);
-	Plucker(A,B,plc);
+	Plucker(B->P,C->P,pla);
+	Plucker(C->P,A->P,plb);
+	Plucker(A->P,B->P,plc);
 #endif
 
 #if defined(TRI_BARI) || defined(TRI_BARI_PRE)
@@ -244,7 +244,7 @@
 	Float krec = 1.0 / N[k];
 	nu = N[u] * krec;
 	nv = N[v] * krec;
-	nd = dot(N, A) * krec;
+	nd = dot(N, A->P) * krec;
 
 	// first line equation
 	Float reci = 1.0f / (b[u] * c[v] - b[v] * c[u]);
@@ -269,7 +269,7 @@
 	const bool side2 = Side(plr, plc) >= 0.0;
 	if (side0 != side2)
 		return false;
-	const Float t = - dot( (ray.o-A), N) / dot(ray.dir,N);
+	const Float t = - dot( (ray.o - A->P), N) / dot(ray.dir,N);
 	if(t <= Eps || t >= dist)
 		return false;
 	dist = t;
@@ -289,15 +289,15 @@
 	if (t >= dist || t < Eps)
 		return false;
 
-	const Float hu = O[u] + t * D[u] - A[u];
-	const Float hv = O[v] + t * D[v] - A[v];
+	const Float hu = O[u] + t * D[u] - A->P[u];
+	const Float hv = O[v] + t * D[v] - A->P[v];
 	const Float beta = hv * bnu + hu * bnv;
 
 	if (beta < 0.)
 		return false;
 
 	const Float gamma = hu * cnv + hv * cnu;
-	if (gamma < 0. || beta + gamma > 1)
+	if (gamma < 0. || beta + gamma > 1.)
 		return false;
 
 	dist = t;
@@ -333,19 +333,19 @@
 BBox Triangle::get_bbox() const
 {
 	BBox bbox = BBox();
-	bbox.L = A;
-	if (B.x < bbox.L.x)  bbox.L.x = B.x;
-	if (C.x < bbox.L.x)  bbox.L.x = C.x;
-	if (B.y < bbox.L.y)  bbox.L.y = B.y;
-	if (C.y < bbox.L.y)  bbox.L.y = C.y;
-	if (B.z < bbox.L.z)  bbox.L.z = B.z;
-	if (C.z < bbox.L.z)  bbox.L.z = C.z;
-	bbox.H = A;
-	if (B.x > bbox.H.x)  bbox.H.x = B.x;
-	if (C.x > bbox.H.x)  bbox.H.x = C.x;
-	if (B.y > bbox.H.y)  bbox.H.y = B.y;
-	if (C.y > bbox.H.y)  bbox.H.y = C.y;
-	if (B.z > bbox.H.z)  bbox.H.z = B.z;
-	if (C.z > bbox.H.z)  bbox.H.z = C.z;
+	bbox.L = A->P;
+	if (B->P.x < bbox.L.x)  bbox.L.x = B->P.x;
+	if (C->P.x < bbox.L.x)  bbox.L.x = C->P.x;
+	if (B->P.y < bbox.L.y)  bbox.L.y = B->P.y;
+	if (C->P.y < bbox.L.y)  bbox.L.y = C->P.y;
+	if (B->P.z < bbox.L.z)  bbox.L.z = B->P.z;
+	if (C->P.z < bbox.L.z)  bbox.L.z = C->P.z;
+	bbox.H = A->P;
+	if (B->P.x > bbox.H.x)  bbox.H.x = B->P.x;
+	if (C->P.x > bbox.H.x)  bbox.H.x = C->P.x;
+	if (B->P.y > bbox.H.y)  bbox.H.y = B->P.y;
+	if (C->P.y > bbox.H.y)  bbox.H.y = C->P.y;
+	if (B->P.z > bbox.H.z)  bbox.H.z = B->P.z;
+	if (C->P.z > bbox.H.z)  bbox.H.z = C->P.z;
 	return bbox;
 };