kd-tree: build algorithm tested and fixed pyrit
authorRadek Brich <radek.brich@devl.cz>
Fri, 23 Nov 2007 01:24:33 +0100
branchpyrit
changeset 10 f9fad94cd0cc
parent 9 3239f749e394
child 11 4d192e13ee84
kd-tree: build algorithm tested and fixed exporting kd-tree to wavefront obj file (visualisation!)
.bzrignore
src/kdtree.cc
src/kdtree.h
src/raytracer.cc
src/raytracer.h
--- a/.bzrignore	Thu Nov 22 21:46:09 2007 +0100
+++ b/.bzrignore	Fri Nov 23 01:24:33 2007 +0100
@@ -1,1 +1,2 @@
 demos/*.png
+demos/kdtree.obj
--- a/src/kdtree.cc	Thu Nov 22 21:46:09 2007 +0100
+++ b/src/kdtree.cc	Fri Nov 23 01:24:33 2007 +0100
@@ -22,18 +22,24 @@
 
 void KdNode::subdivide(BBox bbox, int depth)
 {
+	if (depth >= 10 || shapes.size() <= 2)
+	{
+		leaf = true;
+		return;
+	}
+
 	// choose split axis
 	axis = 0;
-	if (bbox.R.y - bbox.L.y > bbox.R.x - bbox.L.x)
+	if (bbox.h() > bbox.w() && bbox.h() > bbox.d())
 		axis = 1;
-	if (bbox.R.z - bbox.L.z > bbox.R.y - bbox.L.y)
+	if (bbox.d() > bbox.w() && bbox.d() > bbox.h())
 		axis = 2;
 
 	// *** find optimal split position
 	SortableShapeList sslist(shapes, axis);
 	sort(sslist.begin(), sslist.end());
 
-	SplitList splitlist = SplitList();
+	SplitList splitlist;
 
 	SortableShapeList::iterator sh;
 	for (sh = sslist.begin(); sh != sslist.end(); sh++)
@@ -42,6 +48,8 @@
 		splitlist.push_back(SplitPos(sh->bbox.R.cell[axis]));
 	}
 	sort(splitlist.begin(), splitlist.end());
+	// remove duplicate splits
+	splitlist.resize(unique(splitlist.begin(), splitlist.end()) - splitlist.begin());
 
 	// find all posible splits
 	SplitList::iterator spl;
@@ -51,7 +59,10 @@
 		for (sh = sslist.begin(), rest = sslist.size(); sh != sslist.end(); sh++, rest--)
 		{
 			if (sh->hasMark())
+			{
+				spl->lnum++;
 				continue;
+			}
 			// if shape is completely contained in split plane
 			if (spl->pos == sh->bbox.L.cell[axis] == sh->bbox.R.cell[axis])
 			{
@@ -69,12 +80,13 @@
 					else
 						spl->lnum++;
 				}
+				sh->setMark();
 			} else
 			// if shape is on left side of split plane
 			if (sh->bbox.R.cell[axis] <= spl->pos)
 			{
+				spl->lnum++;
 				sh->setMark();
-				spl->lnum++;
 			} else
 			// if shape occupies both sides of split plane
 			if (sh->bbox.L.cell[axis] < spl->pos && sh->bbox.R.cell[axis] > spl->pos)
@@ -105,8 +117,8 @@
 		lbb.R.cell[axis] = spl->pos;
 		rbb.L.cell[axis] = spl->pos;
 		float SAL = 2*(lbb.w()*lbb.h() + lbb.w()*lbb.d() + lbb.h()*lbb.d());
-        float SAR = 2*(rbb.w()*rbb.h() + rbb.w()*rbb.d() + rbb.h()*rbb.d());
-        float splitcost = K + SAL/SAV*(K+spl->lnum) + SAR/SAV*(K+spl->rnum);
+		float SAR = 2*(rbb.w()*rbb.h() + rbb.w()*rbb.d() + rbb.h()*rbb.d());
+		float splitcost = K + SAL/SAV*(K+spl->lnum) + SAR/SAV*(K+spl->rnum);
 
 		if (splitcost < cost)
 		{
@@ -119,6 +131,29 @@
 	if (leaf)
 		return;
 
+#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+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;
+	v.cell[(axis+1)%3] = bbox.L.cell[(axis+1)%3];
+	v.cell[(axis+2)%3] = bbox.R.cell[(axis+2)%3];
+	F << "v " << v.x << " " << v.y << " " << v.z << endl;
+	v.cell[(axis+1)%3] = bbox.R.cell[(axis+1)%3];
+	v.cell[(axis+2)%3] = bbox.R.cell[(axis+2)%3];
+	F << "v " << v.x << " " << v.y << " " << v.z << endl;
+	v.cell[(axis+1)%3] = bbox.R.cell[(axis+1)%3];
+	v.cell[(axis+2)%3] = bbox.L.cell[(axis+2)%3];
+	F << "v " << v.x << " " << v.y << " " << v.z << endl;
+	F << "f " << f+1 << " " << f+2 << " " << f+3 << " " << f+4 << endl;
+	f += 4;
+#endif
+
 	split = splitpos->pos;
 	float lnum = splitpos->lnum;
 	float rnum = splitpos->rnum;
@@ -184,9 +219,28 @@
 	children[1].subdivide(rbb, depth+1);
 }
 
-void KdTree::build()
+void KdTree::optimize()
 {
 	root = new KdNode();
 	root->shapes = shapes;
 	root->subdivide(bbox, 0);
+	built = true;
 }
+
+void KdTree::save(ostream &str, KdNode *node)
+{
+	if (!built)
+		return;
+	if (node == NULL)
+		node = root;
+	if (node->isLeaf())
+		str << "(leaf: " << node->shapes.size() << " shapes)";
+	else
+	{
+		str << "(split " << (char)('x'+node->getAxis()) << " at " << node->getSplit() << "; L=";
+		save(str, node->getLeftChild());
+		str << "; R=";
+		save(str, node->getRightChild());
+		str << ";)";
+	}
+}
--- a/src/kdtree.h	Thu Nov 22 21:46:09 2007 +0100
+++ b/src/kdtree.h	Fri Nov 23 01:24:33 2007 +0100
@@ -2,6 +2,8 @@
 #define KDTREE_H
 
 #include <vector>
+#include <iostream>
+#include <fstream>
 
 #include "scene.h"
 
@@ -44,9 +46,12 @@
 public:
 	float pos;
 	int lnum, rnum;
-	SplitPos(float &aPos): pos(aPos) {};
+	SplitPos(): pos(0.0), lnum(0), rnum(0) {};
+	SplitPos(float &aPos): pos(aPos), lnum(0), rnum(0) {};
 	friend bool operator<(const SplitPos& a, const SplitPos& b)
 		{ return a.pos < b.pos; };
+	friend bool operator==(const SplitPos& a, const SplitPos& b)
+		{ return a.pos == b.pos; };
 };
 
 class SplitList: public vector<SplitPos>
@@ -56,12 +61,13 @@
 class Container
 {
 protected:
-	ShapeList shapes;
 	BBox bbox;
 public:
-	Container(): shapes(), bbox() {};
-	void addShape(Shape* aShape);
+	ShapeList shapes;
+	Container(): bbox(), shapes() {};
+	virtual void addShape(Shape* aShape);
 	//void addShapeNoExtend(Shape* aShape) { shapes.push_back(aShape); };
+	virtual void optimize() {};
 };
 
 class KdNode
@@ -95,9 +101,12 @@
 class KdTree: public Container
 {
 	KdNode *root;
+	bool built;
 public:
-	KdTree() {};
-	void build();
+	KdTree() : Container(), built(false) {};
+	void addShape(Shape* aShape) { Container::addShape(aShape); built = false; };
+	void optimize(); // build kd-tree
+	void save(ostream &str, KdNode *node = NULL);
 };
 
 #endif
--- a/src/raytracer.cc	Thu Nov 22 21:46:09 2007 +0100
+++ b/src/raytracer.cc	Fri Nov 23 01:24:33 2007 +0100
@@ -57,7 +57,7 @@
 {
 	Shape *nearest_shape = NULL;
 	ShapeList::iterator shape;
-	for (shape = shapes.begin(); shape != shapes.end(); shape++)
+	for (shape = top->shapes.begin(); shape != top->shapes.end(); shape++)
 		if (*shape != origin_shape && (*shape)->intersect(ray, nearest_distance))
 			nearest_shape = *shape;
 	return nearest_shape;
@@ -221,6 +221,12 @@
 	float vy;
 	RenderrowData *d;
 
+	printf("* building kd-tree\n");
+	//cout << endl;
+	static_cast<KdTree*>(top)->optimize();
+	//static_cast<KdTree*>(top)->save(cout);
+	//cout << endl;
+
 	//srand(time(NULL));
 
 	// eye - viewing point
@@ -283,11 +289,6 @@
 	return data;
 }
 
-void Raytracer::addshape(Shape *shape)
-{
-	shapes.push_back(shape);
-}
-
 void Raytracer::addlight(Light *light)
 {
 	lights.push_back(light);
--- a/src/raytracer.h	Thu Nov 22 21:46:09 2007 +0100
+++ b/src/raytracer.h	Fri Nov 23 01:24:33 2007 +0100
@@ -27,7 +27,7 @@
 
 class Raytracer
 {
-	ShapeList shapes;
+	Container *top;
 	vector<Light*> lights;
 	Colour bg_colour;
 	int ao_samples;
@@ -37,11 +37,11 @@
 	inline Shape *nearest_intersection(const Shape *origin_shape, const Ray &ray,
 		float &nearest_distance);
 public:
-	Raytracer(): shapes(), lights(), bg_colour(0.0, 0.0, 0.0), ao_samples(0) {};
+	Raytracer(): lights(), bg_colour(0.0, 0.0, 0.0), ao_samples(0) { top = new KdTree(); };
 	~Raytracer() {};
 	float *render(int w, int h);
 	Colour raytrace(Ray &ray, int depth, Shape *origin_shape);
-	void addshape(Shape *shape);
+	void addshape(Shape *shape) { top->addShape(shape); };
 	void addlight(Light *light);
 
 	void ambientocclusion(int samples, float distance, float angle);