private void subdivide(int left, int right, List <int> tempTree, int[] indices, float[] gridBox, float[] nodeBox, int nodeIndex, int depth, BuildStats stats) { if ((right - left + 1) <= maxPrims || depth >= 64) { // write leaf node stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } // calculate extents int axis = -1, prevAxis, rightOrig; float clipL = float.NaN, clipR = float.NaN, prevClip = float.NaN; float split = float.NaN, prevSplit; bool wasLeft = true; while (true) { prevAxis = axis; prevSplit = split; // perform quick consistency checks float[] d = { gridBox[1] - gridBox[0], gridBox[3] - gridBox[2], gridBox[5] - gridBox[4] }; if (d[0] < 0 || d[1] < 0 || d[2] < 0) { throw new Exception("negative node extents"); } for (int i = 0; i < 3; i++) { if (nodeBox[2 * i + 1] < gridBox[2 * i] || nodeBox[2 * i] > gridBox[2 * i + 1]) { UI.printError(UI.Module.ACCEL, "Reached tree area in error - discarding node with: {0} objects", right - left + 1); throw new Exception("invalid node overlap"); } } // find longest axis if (d[0] > d[1] && d[0] > d[2]) { axis = 0; } else if (d[1] > d[2]) { axis = 1; } else { axis = 2; } split = 0.5f * (gridBox[2 * axis] + gridBox[2 * axis + 1]); // partition L/R subsets clipL = float.NegativeInfinity; clipR = float.PositiveInfinity; rightOrig = right; // save this for later float nodeL = float.PositiveInfinity; float nodeR = float.NegativeInfinity; for (int i = left; i <= right;) { int obj = indices[i]; float minb = primitives.getPrimitiveBound(obj, 2 * axis + 0); float maxb = primitives.getPrimitiveBound(obj, 2 * axis + 1); float center = (minb + maxb) * 0.5f; if (center <= split) { // stay left i++; if (clipL < maxb) { clipL = maxb; } } else { // move to the right most int t = indices[i]; indices[i] = indices[right]; indices[right] = t; right--; if (clipR > minb) { clipR = minb; } } if (nodeL > minb) { nodeL = minb; } if (nodeR < maxb) { nodeR = maxb; } } // check for empty space if (nodeL > nodeBox[2 * axis + 0] && nodeR < nodeBox[2 * axis + 1]) { float nodeBoxW = nodeBox[2 * axis + 1] - nodeBox[2 * axis + 0]; float nodeNewW = nodeR - nodeL; // node box is too big compare to space occupied by primitives? if (1.3f * nodeNewW < nodeBoxW) { stats.updateBVH2(); int nextIndex = tempTree.Count; // allocate child tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); // write bvh2 clip node stats.updateInner(); tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex; tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(nodeL); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(nodeR); // update nodebox and recurse nodeBox[2 * axis + 0] = nodeL; nodeBox[2 * axis + 1] = nodeR; subdivide(left, rightOrig, tempTree, indices, gridBox, nodeBox, nextIndex, depth + 1, stats); return; } } // ensure we are making progress in the subdivision if (right == rightOrig) { // all left if (clipL <= split) { // keep looping on left half gridBox[2 * axis + 1] = split; prevClip = clipL; wasLeft = true; continue; } if (prevAxis == axis && prevSplit == split) { // we are stuck here - create a leaf stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } gridBox[2 * axis + 1] = split; prevClip = float.NaN; } else if (left > right) { // all right right = rightOrig; if (clipR >= split) { // keep looping on right half gridBox[2 * axis + 0] = split; prevClip = clipR; wasLeft = false; continue; } if (prevAxis == axis && prevSplit == split) { // we are stuck here - create a leaf stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } gridBox[2 * axis + 0] = split; prevClip = float.NaN; } else { // we are actually splitting stuff if (prevAxis != -1 && !float.IsNaN(prevClip)) { // second time through - lets create the previous split // since it produced empty space int nextIndex = tempTree.Count; // allocate child node tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); if (wasLeft) { // create a node with a left child // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex; tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(prevClip); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(float.PositiveInfinity); } else { // create a node with a right child // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3); tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(float.NegativeInfinity); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(prevClip); } // count stats for the unused leaf depth++; stats.updateLeaf(depth, 0); // now we keep going as we are, with a new nodeIndex: nodeIndex = nextIndex; } break; } } // compute index of child nodes int nextIndex1 = tempTree.Count; // allocate left node int nl = right - left + 1; int nr = rightOrig - (right + 1) + 1; if (nl > 0) { tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); } else { nextIndex1 -= 3; } // allocate right node if (nr > 0) { tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); } // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (axis << 30) | nextIndex1; tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(clipL); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(clipR); // prepare L/R child boxes float[] gridBoxL = new float[6]; float[] gridBoxR = new float[6]; float[] nodeBoxL = new float[6]; float[] nodeBoxR = new float[6]; for (int i = 0; i < 6; i++) { gridBoxL[i] = gridBoxR[i] = gridBox[i]; nodeBoxL[i] = nodeBoxR[i] = nodeBox[i]; } gridBoxL[2 * axis + 1] = gridBoxR[2 * axis] = split; nodeBoxL[2 * axis + 1] = clipL; nodeBoxR[2 * axis + 0] = clipR; // free memory gridBox = nodeBox = null; // recurse if (nl > 0) { subdivide(left, right, tempTree, indices, gridBoxL, nodeBoxL, nextIndex1, depth + 1, stats); } else { stats.updateLeaf(depth + 1, 0); } if (nr > 0) { subdivide(right + 1, rightOrig, tempTree, indices, gridBoxR, nodeBoxR, nextIndex1 + 3, depth + 1, stats); } else { stats.updateLeaf(depth + 1, 0); } }
void subdivide(int left, int right, List <uint> tempTree, buildData dat, AABound gridBox, AABound nodeBox, int nodeIndex, int depth, BuildStats stats) { if ((right - left + 1) <= dat.maxPrims || depth >= 64) { // write leaf node stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } // calculate extents int axis = -1, prevAxis, rightOrig; float clipL = float.NaN, clipR = float.NaN, prevClip = float.NaN; float split = float.NaN, prevSplit; bool wasLeft = true; while (true) { prevAxis = axis; prevSplit = split; // perform quick consistency checks Vector3 d = gridBox.hi - gridBox.lo; for (int i = 0; i < 3; i++) { if (nodeBox.hi[i] < gridBox.lo[i] || nodeBox.lo[i] > gridBox.hi[i]) { Log.outError(LogFilter.Server, "Reached tree area in error - discarding node with: {0} objects", right - left + 1); } } // find longest axis axis = (int)d.primaryAxis(); split = 0.5f * (gridBox.lo[axis] + gridBox.hi[axis]); // partition L/R subsets clipL = float.NegativeInfinity; clipR = float.PositiveInfinity; rightOrig = right; // save this for later float nodeL = float.PositiveInfinity; float nodeR = float.NegativeInfinity; for (int i = left; i <= right;) { int obj = (int)dat.indices[i]; float minb = dat.primBound[obj].Lo[axis]; float maxb = dat.primBound[obj].Hi[axis]; float center = (minb + maxb) * 0.5f; if (center <= split) { // stay left i++; if (clipL < maxb) { clipL = maxb; } } else { // move to the right most int t = (int)dat.indices[i]; dat.indices[i] = dat.indices[right]; dat.indices[right] = (uint)t; right--; if (clipR > minb) { clipR = minb; } } nodeL = Math.Min(nodeL, minb); nodeR = Math.Max(nodeR, maxb); } // check for empty space if (nodeL > nodeBox.lo[axis] && nodeR < nodeBox.hi[axis]) { float nodeBoxW = nodeBox.hi[axis] - nodeBox.lo[axis]; float nodeNewW = nodeR - nodeL; // node box is too big compare to space occupied by primitives? if (1.3f * nodeNewW < nodeBoxW) { stats.updateBVH2(); int nextIndex1 = tempTree.Count; // allocate child tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); // write bvh2 clip node stats.updateInner(); tempTree[nodeIndex + 0] = (uint)((axis << 30) | (1 << 29) | nextIndex1); tempTree[nodeIndex + 1] = floatToRawIntBits(nodeL); tempTree[nodeIndex + 2] = floatToRawIntBits(nodeR); // update nodebox and recurse nodeBox.lo[axis] = nodeL; nodeBox.hi[axis] = nodeR; subdivide(left, rightOrig, tempTree, dat, gridBox, nodeBox, nextIndex1, depth + 1, stats); return; } } // ensure we are making progress in the subdivision if (right == rightOrig) { // all left if (prevAxis == axis && MathFunctions.fuzzyEq(prevSplit, split)) { // we are stuck here - create a leaf stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } if (clipL <= split) { // keep looping on left half gridBox.hi[axis] = split; prevClip = clipL; wasLeft = true; continue; } gridBox.hi[axis] = split; prevClip = float.NaN; } else if (left > right) { // all right right = rightOrig; if (prevAxis == axis && MathFunctions.fuzzyEq(prevSplit, split)) { // we are stuck here - create a leaf stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } if (clipR >= split) { // keep looping on right half gridBox.lo[axis] = split; prevClip = clipR; wasLeft = false; continue; } gridBox.lo[axis] = split; prevClip = float.NaN; } else { // we are actually splitting stuff if (prevAxis != -1 && !float.IsNaN(prevClip)) { // second time through - lets create the previous split // since it produced empty space int nextIndex0 = tempTree.Count; // allocate child node tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); if (wasLeft) { // create a node with a left child // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (uint)((prevAxis << 30) | nextIndex0); tempTree[nodeIndex + 1] = floatToRawIntBits(prevClip); tempTree[nodeIndex + 2] = floatToRawIntBits(float.PositiveInfinity); } else { // create a node with a right child // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (uint)((prevAxis << 30) | (nextIndex0 - 3)); tempTree[nodeIndex + 1] = floatToRawIntBits(float.NegativeInfinity); tempTree[nodeIndex + 2] = floatToRawIntBits(prevClip); } // count stats for the unused leaf depth++; stats.updateLeaf(depth, 0); // now we keep going as we are, with a new nodeIndex: nodeIndex = nextIndex0; } break; } } // compute index of child nodes int nextIndex = tempTree.Count; // allocate left node int nl = right - left + 1; int nr = rightOrig - (right + 1) + 1; if (nl > 0) { tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); } else { nextIndex -= 3; } // allocate right node if (nr > 0) { tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); } // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (uint)((axis << 30) | nextIndex); tempTree[nodeIndex + 1] = floatToRawIntBits(clipL); tempTree[nodeIndex + 2] = floatToRawIntBits(clipR); // prepare L/R child boxes AABound gridBoxL = gridBox; AABound gridBoxR = gridBox; AABound nodeBoxL = nodeBox; AABound nodeBoxR = nodeBox; gridBoxL.hi[axis] = gridBoxR.lo[axis] = split; nodeBoxL.hi[axis] = clipL; nodeBoxR.lo[axis] = clipR; // recurse if (nl > 0) { subdivide(left, right, tempTree, dat, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats); } else { stats.updateLeaf(depth + 1, 0); } if (nr > 0) { subdivide(right + 1, rightOrig, tempTree, dat, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats); } else { stats.updateLeaf(depth + 1, 0); } }
private void subdivide(int left, int right, List<int> tempTree, int[] indices, float[] gridBox, float[] nodeBox, int nodeIndex, int depth, BuildStats stats) { if ((right - left + 1) <= maxPrims || depth >= 64) { // write leaf node stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } // calculate extents int axis = -1, prevAxis, rightOrig; float clipL = float.NaN, clipR = float.NaN, prevClip = float.NaN; float split = float.NaN, prevSplit; bool wasLeft = true; while (true) { prevAxis = axis; prevSplit = split; // perform quick consistency checks float[] d = { gridBox[1] - gridBox[0], gridBox[3] - gridBox[2], gridBox[5] - gridBox[4] }; if (d[0] < 0 || d[1] < 0 || d[2] < 0) throw new Exception("negative node extents"); for (int i = 0; i < 3; i++) { if (nodeBox[2 * i + 1] < gridBox[2 * i] || nodeBox[2 * i] > gridBox[2 * i + 1]) { UI.printError(UI.Module.ACCEL, "Reached tree area in error - discarding node with: {0} objects", right - left + 1); throw new Exception("invalid node overlap"); } } // find longest axis if (d[0] > d[1] && d[0] > d[2]) axis = 0; else if (d[1] > d[2]) axis = 1; else axis = 2; split = 0.5f * (gridBox[2 * axis] + gridBox[2 * axis + 1]); // partition L/R subsets clipL = float.NegativeInfinity; clipR = float.PositiveInfinity; rightOrig = right; // save this for later float nodeL = float.PositiveInfinity; float nodeR = float.NegativeInfinity; for (int i = left; i <= right; ) { int obj = indices[i]; float minb = primitives.getPrimitiveBound(obj, 2 * axis + 0); float maxb = primitives.getPrimitiveBound(obj, 2 * axis + 1); float center = (minb + maxb) * 0.5f; if (center <= split) { // stay left i++; if (clipL < maxb) clipL = maxb; } else { // move to the right most int t = indices[i]; indices[i] = indices[right]; indices[right] = t; right--; if (clipR > minb) clipR = minb; } if (nodeL > minb) nodeL = minb; if (nodeR < maxb) nodeR = maxb; } // check for empty space if (nodeL > nodeBox[2 * axis + 0] && nodeR < nodeBox[2 * axis + 1]) { float nodeBoxW = nodeBox[2 * axis + 1] - nodeBox[2 * axis + 0]; float nodeNewW = nodeR - nodeL; // node box is too big compare to space occupied by primitives? if (1.3f * nodeNewW < nodeBoxW) { stats.updateBVH2(); int nextIndex = tempTree.Count; // allocate child tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); // write bvh2 clip node stats.updateInner(); tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex; tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(nodeL); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(nodeR); // update nodebox and recurse nodeBox[2 * axis + 0] = nodeL; nodeBox[2 * axis + 1] = nodeR; subdivide(left, rightOrig, tempTree, indices, gridBox, nodeBox, nextIndex, depth + 1, stats); return; } } // ensure we are making progress in the subdivision if (right == rightOrig) { // all left if (clipL <= split) { // keep looping on left half gridBox[2 * axis + 1] = split; prevClip = clipL; wasLeft = true; continue; } if (prevAxis == axis && prevSplit == split) { // we are stuck here - create a leaf stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } gridBox[2 * axis + 1] = split; prevClip = float.NaN; } else if (left > right) { // all right right = rightOrig; if (clipR >= split) { // keep looping on right half gridBox[2 * axis + 0] = split; prevClip = clipR; wasLeft = false; continue; } if (prevAxis == axis && prevSplit == split) { // we are stuck here - create a leaf stats.updateLeaf(depth, right - left + 1); createNode(tempTree, nodeIndex, left, right); return; } gridBox[2 * axis + 0] = split; prevClip = float.NaN; } else { // we are actually splitting stuff if (prevAxis != -1 && !float.IsNaN(prevClip)) { // second time through - lets create the previous split // since it produced empty space int nextIndex = tempTree.Count; // allocate child node tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); if (wasLeft) { // create a node with a left child // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex; tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(prevClip); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(float.PositiveInfinity); } else { // create a node with a right child // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3); tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(float.NegativeInfinity); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(prevClip); } // count stats for the unused leaf depth++; stats.updateLeaf(depth, 0); // now we keep going as we are, with a new nodeIndex: nodeIndex = nextIndex; } break; } } // compute index of child nodes int nextIndex1 = tempTree.Count; // allocate left node int nl = right - left + 1; int nr = rightOrig - (right + 1) + 1; if (nl > 0) { tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); } else nextIndex1 -= 3; // allocate right node if (nr > 0) { tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); } // write leaf node stats.updateInner(); tempTree[nodeIndex + 0] = (axis << 30) | nextIndex1; tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(clipL); tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(clipR); // prepare L/R child boxes float[] gridBoxL = new float[6]; float[] gridBoxR = new float[6]; float[] nodeBoxL = new float[6]; float[] nodeBoxR = new float[6]; for (int i = 0; i < 6; i++) { gridBoxL[i] = gridBoxR[i] = gridBox[i]; nodeBoxL[i] = nodeBoxR[i] = nodeBox[i]; } gridBoxL[2 * axis + 1] = gridBoxR[2 * axis] = split; nodeBoxL[2 * axis + 1] = clipL; nodeBoxR[2 * axis + 0] = clipR; // free memory gridBox = nodeBox = null; // recurse if (nl > 0) subdivide(left, right, tempTree, indices, gridBoxL, nodeBoxL, nextIndex1, depth + 1, stats); else stats.updateLeaf(depth + 1, 0); if (nr > 0) subdivide(right + 1, rightOrig, tempTree, indices, gridBoxR, nodeBoxR, nextIndex1 + 3, depth + 1, stats); else stats.updateLeaf(depth + 1, 0); }