src/kdtree.cc
branchpyrit
changeset 12 f4fcabf05785
parent 11 4d192e13ee84
child 13 fbd1d2f7d94e
--- a/src/kdtree.cc	Fri Nov 23 16:14:38 2007 +0100
+++ b/src/kdtree.cc	Sat Nov 24 21:55:41 2007 +0100
@@ -1,7 +1,63 @@
 #include <algorithm>
+#include <stack>
 
 #include "kdtree.h"
 
+class SortableShape
+{
+public:
+	Shape *shape;
+	BBox bbox;
+	short axis;
+	short mark;
+
+	SortableShape(Shape *aShape, short &aAxis): shape(aShape), axis(aAxis), mark(0)
+		{ bbox = shape->get_bbox(); };
+	friend bool operator<(const SortableShape& a, const SortableShape& b)
+		{ return a.bbox.L.cell[a.axis] < b.bbox.L.cell[b.axis]; };
+	void setAxis(short aAxis) { axis = aAxis; };
+	void setMark() { mark = 1; };
+	short hasMark() { return mark; };
+};
+
+class SortableShapeList: public vector<SortableShape>
+{
+public:
+	SortableShapeList(ShapeList &shapes, short axis)
+	{
+		ShapeList::iterator shape;
+		for (shape = shapes.begin(); shape != shapes.end(); shape++)
+			push_back(SortableShape(*shape, axis));
+	};
+};
+
+class SplitPos
+{
+public:
+	float pos;
+	int lnum, rnum;
+	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>
+{
+};
+
+// stack element for kd-tree traversal
+struct StackElem
+{
+	KdNode* node; /* pointer to far child */
+	float t; /* the entry/exit signed distance */
+	Vector3 pb; /* the coordinates of entry/exit point */
+};
+
+// ----------------------------------------
+
 void Container::addShape(Shape* aShape)
 {
 	shapes.push_back(aShape);
@@ -238,6 +294,132 @@
 	built = true;
 }
 
+/* algorithm by Vlastimil Havran, Heuristic Ray Shooting Algorithms, appendix C */
+Shape *KdTree::nearest_intersection(const Shape *origin_shape, const Ray &ray,
+	float &nearest_distance)
+{
+	float a, b; /* entry/exit signed distance */
+	float t;    /* signed distance to the splitting plane */
+
+	/* if we have no tree, fall back to naive test */
+	if (!built)
+		return Container::nearest_intersection(origin_shape, ray, nearest_distance);
+
+	if (!bbox.intersect(ray, a, b))
+		return NULL;
+
+	stack<StackElem*> st;
+
+	/* pointers to the far child node and current node */
+	KdNode *farchild, *node;
+	node = root; /* start from the kd-tree root node */
+
+	StackElem *enPt = new StackElem();
+	enPt->t = a; /* set the signed distance */
+	enPt->node = NULL;
+
+	/* distinguish between internal and external origin */
+	if (a >= 0.0) /* a ray with external origin */
+		enPt->pb = ray.a + ray.dir * a;
+	else /* a ray with internal origin */
+		enPt->pb = ray.a;
+
+	/* setup initial exit point in the stack */
+	StackElem *exPt = new StackElem();
+	exPt->t = b;
+	exPt->pb = ray.a + ray.dir * b;
+	exPt->node = NULL; /* set termination flag */
+
+	st.push(enPt);
+
+	/* loop, traverse through the whole kd-tree, until an object is intersected or ray leaves the scene */
+	while (node)
+	{
+		/* loop until a leaf is found */
+		while (!node->isLeaf())
+		{
+			/* retrieve position of splitting plane */
+			float splitVal = node->getSplit();
+			short axis = node->getAxis();
+
+			if (enPt->pb[axis] <= splitVal)
+			{
+				if (exPt->pb[axis] <= splitVal)
+				{ /* case N1, N2, N3, P5, Z2, and Z3 */
+					node = node->getLeftChild();
+					continue;
+				}
+				if (exPt->pb[axis] == splitVal)
+				{ /* case Z1 */
+					node = node->getRightChild();
+					continue;
+				}
+				/* case N4 */
+				farchild = node->getRightChild();
+				node = node->getLeftChild();
+			}
+			else
+			{ /* (enPt->pb[axis] > splitVal) */
+				if (splitVal < exPt->pb[axis])
+				{
+					/* case P1, P2, P3, and N5 */
+					node = node->getRightChild();
+					continue;
+				}
+				/* case P4*/
+				farchild = node->getLeftChild();
+				node = node->getRightChild();
+			}
+
+			/* case P4 or N4 . . . traverse both children */
+
+			/* signed distance to the splitting plane */
+			t = (splitVal - ray.a.cell[axis]) / ray.dir.cell[axis];
+
+			/* setup the new exit point */
+			st.push(exPt);
+			exPt = new StackElem();
+
+			/* push values onto the stack */
+			exPt->t = t;
+			exPt->node = farchild;
+			exPt->pb[axis] = splitVal;
+			exPt->pb[(axis+1)%3] = ray.a.cell[(axis+1)%3] + t * ray.dir.cell[(axis+1)%3];
+			exPt->pb[(axis+2)%3] = ray.a.cell[(axis+2)%3] + t * ray.dir.cell[(axis+2)%3];
+		} /* while */
+
+		/* current node is the leaf . . . empty or full */
+		/* "intersect ray with each object in the object list, discarding "
+		"those lying before stack[enPt].t or farther than stack[exPt].t" */
+		Shape *nearest_shape = NULL;
+		float dist = exPt->t;
+		ShapeList::iterator shape;
+		for (shape = node->shapes.begin(); shape != node->shapes.end(); shape++)
+			if (*shape != origin_shape && (*shape)->intersect(ray, dist)
+			&& dist >= enPt->t)
+				nearest_shape = *shape;
+
+		if (nearest_shape)
+		{
+			nearest_distance = dist;
+			return nearest_shape;
+		}
+
+		/* pop from the stack */
+		enPt = exPt; /* the signed distance intervals are adjacent */
+
+		/* retrieve the pointer to the next node, it is possible that ray traversal terminates */
+		node = enPt->node;
+
+		// pop
+		exPt = st.top();
+		st.pop();
+	} /* while */
+
+	/* ray leaves the scene */
+	return NULL;
+}
+
 // this should save whole kd-tree with triangles distributed into leaves
 void KdTree::save(ostream &str, KdNode *node)
 {
@@ -260,5 +442,5 @@
 // load kd-tree from file/stream
 void KdTree::load(istream &str, KdNode *node)
 {
-	
+
 }