
// This code written in 2010 by Forest Hale (darkplacesengine gmail com), and placed into public domain.

#include <stdlib.h>
#include <string.h>
#include "bih.h"

static int BIH_BuildNode(bih_t *bih, int numchildren, int *leaflist, float *totalmins, float *totalmaxs)
{
	int i;
	int j;
	int longestaxis;
	int axis = 0;
	int nodenum;
	int front = 0;
	int back = 0;
	bih_node_t *node;
	bih_leaf_t *child;
	float splitdist;
	float d;
	float mins[3];
	float maxs[3];
	float size[3];
	float frontmins[3];
	float frontmaxs[3];
	float backmins[3];
	float backmaxs[3];
	// calculate bounds of children
	child = bih->leafs + leaflist[0];
	mins[0] = child->mins[0];
	mins[1] = child->mins[1];
	mins[2] = child->mins[2];
	maxs[0] = child->maxs[0];
	maxs[1] = child->maxs[1];
	maxs[2] = child->maxs[2];
	for (i = 1;i < numchildren;i++)
	{
		child = bih->leafs + leaflist[i];
		if (mins[0] > child->mins[0]) mins[0] = child->mins[0];
		if (mins[1] > child->mins[1]) mins[1] = child->mins[1];
		if (mins[2] > child->mins[2]) mins[2] = child->mins[2];
		if (maxs[0] < child->maxs[0]) maxs[0] = child->maxs[0];
		if (maxs[1] < child->maxs[1]) maxs[1] = child->maxs[1];
		if (maxs[2] < child->maxs[2]) maxs[2] = child->maxs[2];
	}
	size[0] = maxs[0] - mins[0];
	size[1] = maxs[1] - mins[1];
	size[2] = maxs[2] - mins[2];
	// provide bounds to caller
	totalmins[0] = mins[0];
	totalmins[1] = mins[1];
	totalmins[2] = mins[2];
	totalmaxs[0] = maxs[0];
	totalmaxs[1] = maxs[1];
	totalmaxs[2] = maxs[2];
	// if we run out of nodes it's the caller's fault, but don't crash
	if (bih->numnodes == bih->maxnodes)
	{
		if (!bih->error)
			bih->error = BIHERROR_OUT_OF_NODES;
		return 0;
	}
	nodenum = bih->numnodes++;
	node = bih->nodes + nodenum;
	// store bounds for node
	node->mins[0] = mins[0];
	node->mins[1] = mins[1];
	node->mins[2] = mins[2];
	node->maxs[0] = maxs[0];
	node->maxs[1] = maxs[1];
	node->maxs[2] = maxs[2];
	node->front = 0;
	node->back = 0;
	node->frontmin = 0;
	node->backmax = 0;
	memset(node->children, -1, sizeof(node->children));
	// check if there are few enough children to store an unordered node
	if (numchildren <= BIH_MAXUNORDEREDCHILDREN)
	{
		node->type = BIH_UNORDERED;
		for (j = 0;j < numchildren;j++)
			node->children[j] = leaflist[j];
		return nodenum;
	}
	// pick longest axis
	longestaxis = 0;
	if (size[0] < size[1]) longestaxis = 1;
	if (size[longestaxis] < size[2]) longestaxis = 2;
	// iterate possible split axis choices, starting with the longest axis, if
	// all fail it means all children have the same bounds and we simply split
	// the list in half because each node can only have two children.
	for (j = 0;j < 3;j++)
	{
		// pick an axis
		axis = (longestaxis + j) % 3;
		// sort children into front and back lists
		splitdist = (node->mins[axis] + node->maxs[axis]) * 0.5f;
		front = 0;
		back = 0;
		for (i = 0;i < numchildren;i++)
		{
			child = bih->leafs + leaflist[i];
			d = (child->mins[axis] + child->maxs[axis]) * 0.5f;
			if (d < splitdist)
				bih->leafsortscratch[back++] = leaflist[i];
			else
				leaflist[front++] = leaflist[i];
		}
		// now copy the back ones into the space made in the leaflist for them
		if (back)
			memcpy(leaflist + front, bih->leafsortscratch, back*sizeof(leaflist[0]));
		// if both sides have some children, it's good enough for us.
		if (front && back)
			break;
	}
	if (j == 3)
	{
		// somewhat common case: no good choice, divide children arbitrarily
		axis = 0;
		back = numchildren >> 1;
		front = numchildren - back;
	}

	// we now have front and back children divided in leaflist...
	node->type = (bih_nodetype_t)((int)BIH_SPLITX + axis);
	node->front = BIH_BuildNode(bih, front, leaflist, frontmins, frontmaxs);
	node->frontmin = frontmins[axis];
	node->back = BIH_BuildNode(bih, back, leaflist + front, backmins, backmaxs);
	node->backmax = backmaxs[axis];
	return nodenum;
}

int BIH_Build(bih_t *bih, int numleafs, bih_leaf_t *leafs, int maxnodes, bih_node_t *nodes, int *temp_leafsort, int *temp_leafsortscratch)
{
	int i;

	memset(bih, 0, sizeof(*bih));
	bih->numleafs = numleafs;
	bih->leafs = leafs;
	bih->leafsort = temp_leafsort;
	bih->leafsortscratch = temp_leafsortscratch;
	bih->numnodes = 0;
	bih->maxnodes = maxnodes;
	bih->nodes = nodes;

	// clear things we intend to rebuild
	memset(bih->nodes, 0, sizeof(bih->nodes[0]) * bih->maxnodes);
	for (i = 0;i < bih->numleafs;i++)
		bih->leafsort[i] = i;

	bih->rootnode = BIH_BuildNode(bih, bih->numleafs, bih->leafsort, bih->mins, bih->maxs);
	return bih->error;
}

static void BIH_GetTriangleListForBox_Node(const bih_t *bih, int nodenum, int maxtriangles, int *trianglelist_idx, int *trianglelist_surf, int *numtrianglespointer, const float *mins, const float *maxs)
{
	int axis;
	bih_node_t *node;
	bih_leaf_t *leaf;
	for(;;)
	{
		node = bih->nodes + nodenum;
		// check if this is an unordered node (which holds an array of leaf numbers)
		if (node->type == BIH_UNORDERED)
		{
			for (axis = 0;axis < BIH_MAXUNORDEREDCHILDREN && node->children[axis] >= 0;axis++)
			{
				leaf = bih->leafs + node->children[axis];
				if (mins[0] > leaf->maxs[0] || maxs[0] < leaf->mins[0]
				 || mins[1] > leaf->maxs[1] || maxs[1] < leaf->mins[1]
				 || mins[2] > leaf->maxs[2] || maxs[2] < leaf->mins[2])
					continue;
				switch(leaf->type)
				{
				case BIH_RENDERTRIANGLE:
					if (*numtrianglespointer >= maxtriangles)
					{
						++*numtrianglespointer; // so the caller can detect overflow
						break;
					}
					if(trianglelist_surf)
						trianglelist_surf[*numtrianglespointer] = leaf->surfaceindex;
					trianglelist_idx[*numtrianglespointer] = leaf->itemindex;
					++*numtrianglespointer;
					break;
				default:
					break;
				}
			}
			return;
		}
		// splitting node
		axis = node->type - BIH_SPLITX;
		if (mins[axis] < node->backmax)
		{
			if (maxs[axis] > node->frontmin)
				BIH_GetTriangleListForBox_Node(bih, node->front, maxtriangles, trianglelist_idx, trianglelist_surf, numtrianglespointer, mins, maxs);
			nodenum = node->back;
			continue;
		}
		if (maxs[axis] > node->frontmin)
		{
			nodenum = node->front;
			continue;
		}
		// fell between the child groups, nothing here
		return;
	}
}

int BIH_GetTriangleListForBox(const bih_t *bih, int maxtriangles, int *trianglelist_idx, int *trianglelist_surf, const float *mins, const float *maxs)
{
	int numtriangles = 0;
	BIH_GetTriangleListForBox_Node(bih, bih->rootnode, maxtriangles, trianglelist_idx, trianglelist_surf, &numtriangles, mins, maxs);
	return numtriangles;
}
