//public void Subdivide(KdTreeNode node, AxisAlignedBox box, int depth, int prims) { // // recycle used split list nodes // _list = null; // // determine split axis // Vector3D s = box.Size; // if ((s.X >= s.Y) && (s.X >= s.Z)) // node.Axis = 0; // else if ((s.Y >= s.X) && (s.Y >= s.Z)) // node.Axis = 1; // int axis = node.Axis; // // make a list of the split position candidates // ObjectList l = node.List; // double p1, p2; // double pos1 = box.Position[axis]; // double pos2 = box.Position[axis] + box.Size[axis]; // bool[] pright = new bool[prims]; // double[] eleft = new double[prims]; // double[] eright = new double[prims]; // Primitive[] parray = new Primitive[prims]; // int aidx = 0; // while (l != null) { // parray[aidx] = l.Primitive; // Primitive p = parray[aidx]; // pright[aidx] = true; // p.CalculateRange(ref eleft[aidx], ref eright[aidx], axis); // aidx++; // if (p.Type == PrimitiveType.Sphere) { // p1 = p.Center[axis] - p.Radius; // p2 = p.Center[axis] + p.Radius; // if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos( p1 ); // if ((p2 >= pos1) && (p2 <= pos2)) InsertSplitPos( p2 ); // } // else { // for (int i = 0; i < 3; i++ ) { // p1 = p.GetVertex(i).Position[axis]; // if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos( p1 ); // } // } // l = l.Next; // } // // determine n1count / n2count for each split position // AxisAlignedBox b1 = null, b2 = null, b3, b4; // b3 = box; // b4 = box; // SplitList splist = _list; // double b3p1 = b3.Position[axis]; // double b4p2 = b4.Position[axis] + b4.Size[axis]; // while (splist != null) { // b4.Position[axis] = splist.SplitPos; // b4.Size[axis] = pos2 - splist.SplitPos; // b3.Size[axis] = splist.SplitPos - pos1; // double b3p2 = b3.Position[axis] + b3.Size[axis]; // double b4p1 = b4.Position[axis]; // for (int i = 0; i < prims; i++) { // if (pright[i]) { // Primitive p = parray[i]; // if ((eleft[i] <= b3p2) && (eright[i] >= b3p1)) // if (p.IntersectBox(b3)) // splist.N1Count++; // if ((eleft[i] <= b4p2) && (eright[i] >= b4p1)) // if (p.IntersectBox(b4)) // splist.N2Count++; // else // pright[i] = false; // } // else // splist.N1Count++; // } // splist = splist.Next; // } // // calculate surface area for current node // double SAV = 0.5f / (box.W * box.D + box.W * box.H + box.D * box.H); // // calculate cost for not splitting // double Cleaf = prims * 1.0f; // // determine optimal split plane position // splist = _list; // double lowcost = 10000; // double bestpos = 0; // while (splist != null) { // // calculate child node extends // b4.Position[axis] = splist.SplitPos; // b4.Size[axis] = pos2 - splist.SplitPos; // b3.Size[axis] = splist.SplitPos - pos1; // // calculate child node cost // double SA1 = 2 * (b3.W * b3.D + b3.W * b3.H + b3.D * b3.H); // double SA2 = 2 * (b4.W * b4.D + b4.W * b4.H + b4.D * b4.H); // double splitcost = 0.3f + 1.0f * (SA1 * SAV * splist.N1Count + SA2 * SAV * splist.N2Count); // // update best cost tracking variables // if (splitcost < lowcost) { // lowcost = splitcost; // bestpos = splist.SplitPos; // b1 = b3; // b2 = b4; // } // splist = splist.Next; // } // if (lowcost > Cleaf) // return; // node.SplitPosition = bestpos; // // construct child nodes // KdTreeNode tmpLeft = new KdTreeNode(); // KdTreeNode tmpRight = new KdTreeNode(); // int n1count = 0, n2count = 0, total = 0; // // assign primitives to both sides // double b1p1 = b1.Position[axis]; // double b2p2 = b2.Position[axis] + b2.Size[axis]; // double b1p2 = b1.Position[axis] + b1.Size[axis]; // double b2p1 = b2.Position[axis]; // for ( int i = 0; i < prims; i++ ) { // Primitive p = parray[i]; // total++; // if (eleft[i] <= b1p2 && eright[i] >= b1p1) { // if (p.IntersectBox(b1)) { // tmpLeft.Add(p); // n1count++; // } // } // if (eleft[i] <= b2p2 && eright[i] >= b2p1) { // if (p.IntersectBox(b2)) { // tmpRight.Add(p); // n2count++; // } // } // } // node.List = null; // if (n1count > 0) node.Left = tmpLeft; // if (n2count > 0) node.Right = tmpRight; // if (depth < Constants.MaxTreeDepth) { // if (n1count > 2) Subdivide(tmpLeft, b1, depth + 1, n1count); // if (n2count > 2) Subdivide(tmpRight, b2, depth + 1, n2count); // } //} public void Subdivide(KdTreeNode node, AxisAlignedBox box, int depth, int prims) { // recycle used split list nodes _sortedList = null; // determine split axis Vector3D s = box.Size; if ((s.X >= s.Y) && (s.X >= s.Z)) node.Axis = 0; else if ((s.Y >= s.X) && (s.Y >= s.Z)) node.Axis = 1; int axis = node.Axis; // make a list of the split position candidates ObjectList l = node.List; double p1, p2; double pos1 = box.Position[axis]; double pos2 = box.Position[axis] + box.Size[axis]; bool[] pright = new bool[prims]; double[] eleft = new double[prims]; double[] eright = new double[prims]; Primitive[] parray = new Primitive[prims]; int aidx = 0; while (l != null) { parray[aidx] = l.Primitive; Primitive p = parray[aidx]; pright[aidx] = true; p.CalculateRange(ref eleft[aidx], ref eright[aidx], axis); aidx++; if (p.Type == PrimitiveType.Sphere) { p1 = p.Center[axis] - p.Radius; p2 = p.Center[axis] + p.Radius; if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos(p1); if ((p2 >= pos1) && (p2 <= pos2)) InsertSplitPos(p2); } else { for (int i = 0; i < 3; i++) { p1 = p.GetVertex(i).Position[axis]; if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos(p1); } } l = l.Next; } // determine n1count / n2count for each split position AxisAlignedBox b1 = null, b2 = null, b3, b4; b3 = box; b4 = box; double b3p1 = b3.Position[axis]; double b4p2 = b4.Position[axis] + b4.Size[axis]; if (_sortedList == null) return; foreach (KeyValuePair<double, SplitListItem> pair in _sortedList) { SplitListItem spListItem = pair.Value; b4.Position[axis] = spListItem.SplitPos; b4.Size[axis] = pos2 - spListItem.SplitPos; b3.Size[axis] = spListItem.SplitPos - pos1; double b3p2 = b3.Position[axis] + b3.Size[axis]; double b4p1 = b4.Position[axis]; for (int i = 0; i < prims; i++) { if (pright[i]) { Primitive p = parray[i]; if ((eleft[i] <= b3p2) && (eright[i] >= b3p1)) if (p.IntersectBox(b3)) spListItem.N1Count++; if ((eleft[i] <= b4p2) && (eright[i] >= b4p1)) if (p.IntersectBox(b4)) spListItem.N2Count++; else pright[i] = false; } else spListItem.N1Count++; } } // calculate surface area for current node double SAV = 0.5f / (box.W * box.D + box.W * box.H + box.D * box.H); // calculate cost for not splitting double Cleaf = prims * 1.0f; // determine optimal split plane position double lowcost = 10000; double bestpos = 0; foreach (KeyValuePair<double, SplitListItem> pair in _sortedList) { SplitListItem spListItem = pair.Value; // calculate child node extends b4.Position[axis] = spListItem.SplitPos; b4.Size[axis] = pos2 - spListItem.SplitPos; b3.Size[axis] = spListItem.SplitPos - pos1; // calculate child node cost double SA1 = 2 * (b3.W * b3.D + b3.W * b3.H + b3.D * b3.H); double SA2 = 2 * (b4.W * b4.D + b4.W * b4.H + b4.D * b4.H); double splitcost = 0.3f + 1.0f * (SA1 * SAV * spListItem.N1Count + SA2 * SAV * spListItem.N2Count); // update best cost tracking variables if (splitcost < lowcost) { lowcost = splitcost; bestpos = spListItem.SplitPos; b1 = b3; b2 = b4; } } if (lowcost > Cleaf) return; node.SplitPosition = bestpos; // construct child nodes KdTreeNode tmpLeft = new KdTreeNode(); KdTreeNode tmpRight = new KdTreeNode(); int n1count = 0, n2count = 0, total = 0; // assign primitives to both sides double b1p1 = b1.Position[axis]; double b2p2 = b2.Position[axis] + b2.Size[axis]; double b1p2 = b1.Position[axis] + b1.Size[axis]; double b2p1 = b2.Position[axis]; for (int i = 0; i < prims; i++) { Primitive p = parray[i]; total++; if (eleft[i] <= b1p2 && eright[i] >= b1p1) { if (p.IntersectBox(b1)) { tmpLeft.Add(p); n1count++; } } if (eleft[i] <= b2p2 && eright[i] >= b2p1) { if (p.IntersectBox(b2)) { tmpRight.Add(p); n2count++; } } } node.List = null; if (n1count > 0) node.Left = tmpLeft; if (n2count > 0) node.Right = tmpRight; if (depth < Constants.MaxTreeDepth) { if (n1count > 2) Subdivide(tmpLeft, b1, depth + 1, n1count); if (n2count > 2) Subdivide(tmpRight, b2, depth + 1, n2count); } }
public KdTree() { Root = new KdTreeNode(); }
public static KdTreeNode makeInternal(Axis axis, int aboveChildIndex, float split) { KdTreeNode node = new KdTreeNode(); node.axis = axis; node.splitPos = split; node.aboveChild = aboveChildIndex; return node; }
public static KdTreeNode makeLeaf(int[] primIndices, int primIndicesBase, int numPrims) { KdTreeNode node = new KdTreeNode(); node.axis = Axis.none; node.nPrimitives = numPrims; if(numPrims == 0) { node.primitiveIndex = 0; } else if (numPrims == 1) { node.primitiveIndex = primIndices[primIndicesBase]; } else { node.primitiveIndices = new int[numPrims]; Array.Copy(primIndices, primIndicesBase, node.primitiveIndices, 0, numPrims); } return node; }
private void buildTree( int nodeNum, BBox nodeBounds, BBox[] allPrimBounds, int[] primNums, int primNumsBase, int nPrimitives, int depth, BoundEdge[][] edges, int[] prims0, int[] prims1, int prims1base, int badRefines) { string indent = "".PadLeft(_maxDepth - depth); if (nextFreeNode == nAllocedNodes) { int nAlloc = Math.Max(2*nAllocedNodes, 512); KdTreeNode[] n = new KdTreeNode[nAlloc]; if (nAllocedNodes > 0) Array.Copy(_nodes, n, nAllocedNodes); _nodes = n; nAllocedNodes = nAlloc; } ++nextFreeNode; // *** Termination: Stop recursion if we have few enough nodes or the max depth has been reached *** if (nPrimitives <= _maxPrims || depth == 0) { _nodes[nodeNum] = KdTreeNode.makeLeaf(primNums, primNumsBase, nPrimitives); #if DEBUG Console.WriteLine(indent + _nodes[nodeNum]); #endif return; } // *** Not reached termination, so pick split plane and continue recursion *** Axis bestAxis = Axis.none; int bestOffset = -1; float bestCost = float.PositiveInfinity; float oldCost = iCost * (float)nPrimitives; float totalSA = nodeBounds.surfaceArea(); float invTotalSA = 1.0f/totalSA; Vector3 d = nodeBounds.pMax - nodeBounds.pMin; Axis axis = nodeBounds.longestAxis(); // Start by checking the longest axis int retries = 0; retrySplit: // Fill edge lists for (int i=0; i<nPrimitives; i++) { int primIndex = primNums[primNumsBase + i]; BBox bbox = allPrimBounds[primIndex]; edges[(int)axis][2*i] = new BoundEdge( getAxisVal(bbox.pMin, axis), primIndex, true); edges[(int)axis][2*i+1] = new BoundEdge(getAxisVal(bbox.pMax, axis), primIndex, false); } Array.Sort(edges[(int)axis], 0, 2*nPrimitives); // sort the edges of the first n primitives (2*n edges) // Find the best split by checking cost of every possible split plane int nBelow = 0, nAbove = nPrimitives; for (int i=0; i<2*nPrimitives; i++) { if( edges[(int)axis][i].type == BoundType.End) --nAbove; float edgeT = edges[(int)axis][i].t; if (edgeT > getAxisVal(nodeBounds.pMin, axis) && edgeT < getAxisVal(nodeBounds.pMax, axis) ) { // Compute split cost for the ith edge Axis otherAxis0 = (Axis) ( ((int)axis+1) % 3); Axis otherAxis1 = (Axis) ( ((int)axis+2) % 3); float belowSA = 2* (getAxisVal(d, otherAxis0) * getAxisVal(d, otherAxis1) + (edgeT - getAxisVal(nodeBounds.pMin, axis)) * (getAxisVal(d, otherAxis0) + getAxisVal(d, otherAxis1))); float aboveSA = 2*(getAxisVal(d, otherAxis0) * getAxisVal(d, otherAxis1) + (getAxisVal(nodeBounds.pMax, axis) - edgeT) * (getAxisVal(d, otherAxis0) + getAxisVal(d, otherAxis1))); float pBelow = belowSA * invTotalSA; float pAbove = aboveSA * invTotalSA; float eb = (nAbove == 0 || nBelow == 0) ? emptyBonus : 0; float cost = tCost + iCost * (1-eb) * (pBelow * nBelow + pAbove * nAbove); // Save this split if it is better the previous split position if(cost < bestCost) { bestCost = cost; bestAxis = axis; bestOffset = i; } } if (edges[(int)axis][i].type == BoundType.Start) ++nBelow; } // Create a leaf node if no good split was found if(bestAxis == Axis.none && retries < 2) { ++retries; axis = (Axis) ( ((int)axis+1) % 3); goto retrySplit; } if (bestCost > oldCost) ++badRefines; if (( bestCost > 4.0f * oldCost && nPrimitives < 16) || bestAxis == Axis.none || badRefines == 3) { _nodes[nodeNum] = KdTreeNode.makeLeaf(primNums, primNumsBase, nPrimitives); #if DEBUG Console.WriteLine(indent + _nodes[nodeNum] + " (split failure)"); #endif return; } // Classifiy primitives with respect to split int n0=0, n1=0; for (int i=0; i<bestOffset; i++) { if(edges[(int)bestAxis][i].type == BoundType.Start) prims0[n0++] = edges[(int)bestAxis][i].primNum; } for (int i=bestOffset+1; i<2*nPrimitives; i++) { if(edges[(int)bestAxis][i].type == BoundType.End) prims1[prims1base + n1++] = edges[(int)bestAxis][i].primNum; } // Ititalize child nodes float tsplit = edges[(int)bestAxis][bestOffset].t; BBox bounds0 = nodeBounds, bounds1 = nodeBounds; setAxisVal(ref bounds0.pMax, bestAxis, tsplit); setAxisVal(ref bounds1.pMin, bestAxis, tsplit); #if DEBUG KdTreeNode testNode = KdTreeNode.makeInternal(bestAxis, nextFreeNode, tsplit); int[] aboveIndices = new int[n1]; int[] belowIndices = new int[n0]; Array.Copy(prims0, belowIndices, n0); Array.Copy(prims1, prims1base, aboveIndices, 0, n1); Console.WriteLine(indent + testNode); Console.WriteLine(indent + " Above: { " + String.Join(",", aboveIndices) + " }"); Console.WriteLine(indent + " Below: { " + String.Join(",", belowIndices) + " }"); #endif buildTree(nodeNum+1, bounds0, allPrimBounds, prims0, 0, n0, depth-1, edges, prims0, prims1, prims1base+nPrimitives-1, badRefines); int aboveChild = nextFreeNode; _nodes[nodeNum] = KdTreeNode.makeInternal(bestAxis, aboveChild, tsplit); buildTree(aboveChild, bounds1, allPrimBounds, prims1, prims1base, n1, depth-1, edges, prims0, prims1, prims1base+nPrimitives-1, badRefines); }