void KdTreeAccelerator::initializeInteriorNode(int node, const BBox& nodeBounds,
		const std::vector<BBox>& primitiveBounds, uint* overlappingPrimitives,
		int numOverlappingPrimitives, int depth, BoundEdge* edges[3],
		uint* prims0, uint* prims1, int badRefines)
{
	// Choose split axis and position for interior node
	int bestAxis = -1;
	int bestOffset = -1;
	float bestCost = INFINITY;
	float oldCost = m_intersectionCost * float(numOverlappingPrimitives);
	float totalSA = nodeBounds.getSurfaceArea();
	float invTotalSA = 1.f / totalSA;
	glm::vec3 d = nodeBounds.getMax() - nodeBounds.getMin();

	uint axis = nodeBounds.getAxisMaximumExtent();
	int retries = 0;

	// label for jump to choose another split
	retrySplit:

	// Initialize edges for choosen axis
	for(int i = 0; i < numOverlappingPrimitives; ++i)
	{
		int primitive = overlappingPrimitives[i];
		const BBox& bbox = primitiveBounds[primitive];
		edges[axis][2 * i + 0] = BoundEdge(bbox.getMin()[axis], primitive, true);
		edges[axis][2 * i + 1] = BoundEdge(bbox.getMax()[axis], primitive, false);
	}
	std::sort(&edges[axis][0], &edges[axis][2*numOverlappingPrimitives]);

	// Compute cost of all splits for the choosen axis to find best
	int nBelow = 0;
	int nAbove = numOverlappingPrimitives;
	for(int i = 0; i < 2 * numOverlappingPrimitives; ++i)
	{
		if(edges[axis][i].m_type == BoundEdge::END) --nAbove;

		float split = edges[axis][i].m_t;
		if(split > nodeBounds.getMin()[axis] && split < nodeBounds.getMax()[axis])
		{
			// compute cost of split at i-th edge
			uint oA0 = (axis + 1) % 3; // first other axis 
			uint oA1 = (axis + 2) % 3; // second other axis
			float belowSA = 2 * (d[oA0] * d[oA1] + (split - nodeBounds.getMin()[axis]) * (d[oA0] + d[oA1]));
			float aboveSA = 2 * (d[oA0] * d[oA1] + (nodeBounds.getMax()[axis] - split) * (d[oA0] + d[oA1]));
			float pBelow = belowSA * invTotalSA;
			float pAbove = aboveSA * invTotalSA;
			float bonus = (nAbove==0 || nBelow==0) ? m_emptyBonus : 0.f;
			float cost = m_intersectionCost * (1.f - bonus) * (pBelow * nBelow + pAbove * nAbove)
				+ m_traversalCost;

			// update best split if this split has lower costs
			if(cost < bestCost)
			{
				bestCost = cost;
				bestAxis = axis;
				bestOffset = i;
			}
		}

		if(edges[axis][i].m_type == BoundEdge::START) ++nBelow;
	}

	// try another axis of no good slit was found
	if(bestAxis == -1 && retries < 2)
	{
		++retries;
		axis = (axis + 1) % 3;
		goto retrySplit;
	}
	
	// Create lead if no good splits were found
	if (bestCost > oldCost) ++badRefines;
	if ((bestCost > 4.f * oldCost && numOverlappingPrimitives < 16) ||
		bestAxis == -1 || badRefines == 3) {
		m_nodes[node].initLeaf(overlappingPrimitives, numOverlappingPrimitives);
		return;
	}
	
	// Classify overlapping primitives with respect to split
	int n0 = 0, n1 = 0;
	for (int i = 0; i < bestOffset; ++i) {
		if (edges[bestAxis][i].m_type == BoundEdge::START) {
			prims0[n0++] = edges[bestAxis][i].m_primitive;
		}
	}
	for (int i = bestOffset+1; i < 2*numOverlappingPrimitives; ++i) {
		if (edges[bestAxis][i].m_type == BoundEdge::END) {
			prims1[n1++] = edges[bestAxis][i].m_primitive;
		}
	}

	// Recursively initialize child nodes
	float split = edges[bestAxis][bestOffset].m_t;
	glm::vec3 min = nodeBounds.getMax();
	glm::vec3 max = nodeBounds.getMin();
	min[bestAxis] = split;
	max[bestAxis] = split;
	BBox bounds0 = nodeBounds;
	BBox bounds1 = nodeBounds;
	bounds0.setMax(max);
	bounds1.setMin(min);

	buildTreeRecursive(node + 1, bounds0, primitiveBounds, prims0, n0, depth - 1, 
		edges, prims0, prims1 + numOverlappingPrimitives, badRefines);

	uint aboveChild = m_nextFreeNode;
	m_nodes[node].initInterior(bestAxis, aboveChild, split);

	buildTreeRecursive(aboveChild, bounds1, primitiveBounds, prims1, n1, depth - 1, 
		edges, prims0, prims1 + numOverlappingPrimitives, badRefines);
}