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 buildTree(float minx, float maxx, float miny, float maxy, float minz, float maxz, BuildTask task, int depth, List <int> tempTree, int offset, List <int> tempList, BuildStats stats) { // get node bounding box extents if (task.numObjects > maxPrims && depth < MAX_DEPTH) { float dx = maxx - minx; float dy = maxy - miny; float dz = maxz - minz; // search for best possible split float bestCost = INTERSECT_COST * task.numObjects; int bestAxis = -1; int bestOffsetStart = -1; int bestOffsetEnd = -1; float bestSplit = 0; bool bestPlanarLeft = false; int bnl = 0, bnr = 0; // inverse area of the bounding box (factor of 2 ommitted) float area = (dx * dy + dy * dz + dz * dx); float ISECT_COST = INTERSECT_COST / area; // setup counts for each axis int[] nl = { 0, 0, 0 }; int[] nr = { task.numObjects, task.numObjects, task.numObjects }; // setup bounds for each axis float[] dp = { dy *dz, dz *dx, dx *dy }; float[] ds = { dy + dz, dz + dx, dx + dy }; float[] nodeMin = { minx, miny, minz }; float[] nodeMax = { maxx, maxy, maxz }; // search for best cost int nSplits = task.n; long[] splits = task.splits; byte[] lrtable = task.leftRightTable; for (int i = 0; i < nSplits;) { // extract current split long ptr = splits[i]; float split = unpackSplit(ptr); int axis = unpackAxis(ptr); // mark current position int currentOffset = i; // count number of primitives start/stopping/lying on the // current plane int pClosed = 0, pPlanar = 0, pOpened = 0; long ptrMasked = (long)((ulong)ptr & (~((ulong)TYPE_MASK) & 0xFFFFFFFFF0000000L)); long ptrClosed = ptrMasked | CLOSED; long ptrPlanar = ptrMasked | PLANAR; long ptrOpened = ptrMasked | OPENED; while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrClosed) { int obj = unpackObject(splits[i]); lrtable[(uint)obj >> 2] = 0;//>>> pClosed++; i++; } while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrPlanar) { int obj = unpackObject(splits[i]); lrtable[(uint)obj >> 2] = 0;//>>> pPlanar++; i++; } while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrOpened) { int obj = unpackObject(splits[i]); lrtable[(uint)obj >> 2] = 0;//>>> pOpened++; i++; } // now we have summed all contributions from this plane nr[axis] -= pPlanar + pClosed; // compute cost if (split >= nodeMin[axis] && split <= nodeMax[axis]) { // left and right surface area (factor of 2 ommitted) float dl = split - nodeMin[axis]; float dr = nodeMax[axis] - split; float lp = dp[axis] + dl * ds[axis]; float rp = dp[axis] + dr * ds[axis]; // planar prims go to smallest cell always bool planarLeft = dl < dr; int numLeft = nl[axis] + (planarLeft ? pPlanar : 0); int numRight = nr[axis] + (planarLeft ? 0 : pPlanar); float eb = ((numLeft == 0 && dl > 0) || (numRight == 0 && dr > 0)) ? EMPTY_BONUS : 0; float cost = TRAVERSAL_COST + ISECT_COST * (1 - eb) * (lp * numLeft + rp * numRight); if (cost < bestCost) { bestCost = cost; bestAxis = axis; bestSplit = split; bestOffsetStart = currentOffset; bestOffsetEnd = i; bnl = numLeft; bnr = numRight; bestPlanarLeft = planarLeft; } } // move objects left nl[axis] += pOpened + pPlanar; } // debug check for correctness of the scan for (int axis = 0; axis < 3; axis++) { int numLeft = nl[axis]; int numRight = nr[axis]; if (numLeft != task.numObjects || numRight != 0) { UI.printError(UI.Module.ACCEL, "Didn't scan full range of objects @depth={0}. Left overs for axis {1}: [L: {2}] [R: {3}]", depth, axis, numLeft, numRight); } } // found best split? if (bestAxis != -1) { // allocate space for child nodes BuildTask taskL = new BuildTask(bnl, task); BuildTask taskR = new BuildTask(bnr, task); int lk = 0, rk = 0; for (int i = 0; i < bestOffsetStart; i++) { long ptr = splits[i]; if (unpackAxis(ptr) == bestAxis) { if (unpackSplitType(ptr) != CLOSED) { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>> lk++; } } } for (int i = bestOffsetStart; i < bestOffsetEnd; i++) { long ptr = splits[i]; Debug.Assert(unpackAxis(ptr) == bestAxis); if (unpackSplitType(ptr) == PLANAR) { if (bestPlanarLeft) { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>> lk++; } else { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>> rk++; } } } for (int i = bestOffsetEnd; i < nSplits; i++) { long ptr = splits[i]; if (unpackAxis(ptr) == bestAxis) { if (unpackSplitType(ptr) != OPENED) { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>> rk++; } } } // output new splits while maintaining order long[] splitsL = taskL.splits; long[] splitsR = taskR.splits; int nsl = 0, nsr = 0; for (int i = 0; i < nSplits; i++) { long ptr = splits[i]; int obj = unpackObject(ptr); int idx = (int)((uint)obj >> 2);//>>> int mask = 1 << ((obj & 3) << 1); if ((lrtable[idx] & mask) != 0) { splitsL[nsl] = ptr; nsl++; } if ((lrtable[idx] & (mask << 1)) != 0) { splitsR[nsr] = ptr; nsr++; } } taskL.n = nsl; taskR.n = nsr; // free more memory task.splits = splits = splitsL = splitsR = null; task = null; // allocate child nodes int nextOffset = tempTree.Count; tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); // create current node tempTree[offset + 0] = (bestAxis << 30) | nextOffset; tempTree[offset + 1] = ByteUtil.floatToRawIntBits(bestSplit); // recurse for child nodes - free object arrays after each step stats.updateInner(); switch (bestAxis) { case 0: buildTree(minx, bestSplit, miny, maxy, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats); taskL = null; buildTree(bestSplit, maxx, miny, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats); taskR = null; return; case 1: buildTree(minx, maxx, miny, bestSplit, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats); taskL = null; buildTree(minx, maxx, bestSplit, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats); taskR = null; return; case 2: buildTree(minx, maxx, miny, maxy, minz, bestSplit, taskL, depth + 1, tempTree, nextOffset, tempList, stats); taskL = null; buildTree(minx, maxx, miny, maxy, bestSplit, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats); taskR = null; return; default: Debug.Assert(false); break; } } } // create leaf node int listOffset = tempList.Count; int n = 0; for (int i = 0; i < task.n; i++) { long ptr = task.splits[i]; if (unpackAxis(ptr) == 0 && unpackSplitType(ptr) != CLOSED) { tempList.Add(unpackObject(ptr)); n++; } } stats.updateLeaf(depth, n); if (n != task.numObjects) { UI.printError(UI.Module.ACCEL, "Error creating leaf node - expecting {0} found {1}", task.numObjects, n); } tempTree[offset + 0] = (3 << 30) | listOffset; tempTree[offset + 1] = task.numObjects; // free some memory task.splits = null; }
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); } }
private void buildTree(float minx, float maxx, float miny, float maxy, float minz, float maxz, BuildTask task, int depth, List<int> tempTree, int offset, List<int> tempList, BuildStats stats) { // get node bounding box extents if (task.numObjects > maxPrims && depth < MAX_DEPTH) { float dx = maxx - minx; float dy = maxy - miny; float dz = maxz - minz; // search for best possible split float bestCost = INTERSECT_COST * task.numObjects; int bestAxis = -1; int bestOffsetStart = -1; int bestOffsetEnd = -1; float bestSplit = 0; bool bestPlanarLeft = false; int bnl = 0, bnr = 0; // inverse area of the bounding box (factor of 2 ommitted) float area = (dx * dy + dy * dz + dz * dx); float ISECT_COST = INTERSECT_COST / area; // setup counts for each axis int[] nl = { 0, 0, 0 }; int[] nr = { task.numObjects, task.numObjects, task.numObjects }; // setup bounds for each axis float[] dp = { dy * dz, dz * dx, dx * dy }; float[] ds = { dy + dz, dz + dx, dx + dy }; float[] nodeMin = { minx, miny, minz }; float[] nodeMax = { maxx, maxy, maxz }; // search for best cost int nSplits = task.n; long[] splits = task.splits; byte[] lrtable = task.leftRightTable; for (int i = 0; i < nSplits; ) { // extract current split long ptr = splits[i]; float split = unpackSplit(ptr); int axis = unpackAxis(ptr); // mark current position int currentOffset = i; // count number of primitives start/stopping/lying on the // current plane int pClosed = 0, pPlanar = 0, pOpened = 0; long ptrMasked = (long)((ulong)ptr & (~((ulong)TYPE_MASK) & 0xFFFFFFFFF0000000L)); long ptrClosed = ptrMasked | CLOSED; long ptrPlanar = ptrMasked | PLANAR; long ptrOpened = ptrMasked | OPENED; while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrClosed) { int obj = unpackObject(splits[i]); lrtable[(uint)obj >> 2] = 0;//>>> pClosed++; i++; } while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrPlanar) { int obj = unpackObject(splits[i]); lrtable[(uint)obj >> 2] = 0;//>>> pPlanar++; i++; } while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrOpened) { int obj = unpackObject(splits[i]); lrtable[(uint)obj >> 2] = 0;//>>> pOpened++; i++; } // now we have summed all contributions from this plane nr[axis] -= pPlanar + pClosed; // compute cost if (split >= nodeMin[axis] && split <= nodeMax[axis]) { // left and right surface area (factor of 2 ommitted) float dl = split - nodeMin[axis]; float dr = nodeMax[axis] - split; float lp = dp[axis] + dl * ds[axis]; float rp = dp[axis] + dr * ds[axis]; // planar prims go to smallest cell always bool planarLeft = dl < dr; int numLeft = nl[axis] + (planarLeft ? pPlanar : 0); int numRight = nr[axis] + (planarLeft ? 0 : pPlanar); float eb = ((numLeft == 0 && dl > 0) || (numRight == 0 && dr > 0)) ? EMPTY_BONUS : 0; float cost = TRAVERSAL_COST + ISECT_COST * (1 - eb) * (lp * numLeft + rp * numRight); if (cost < bestCost) { bestCost = cost; bestAxis = axis; bestSplit = split; bestOffsetStart = currentOffset; bestOffsetEnd = i; bnl = numLeft; bnr = numRight; bestPlanarLeft = planarLeft; } } // move objects left nl[axis] += pOpened + pPlanar; } // debug check for correctness of the scan for (int axis = 0; axis < 3; axis++) { int numLeft = nl[axis]; int numRight = nr[axis]; if (numLeft != task.numObjects || numRight != 0) UI.printError(UI.Module.ACCEL, "Didn't scan full range of objects @depth={0}. Left overs for axis {1}: [L: {2}] [R: {3}]", depth, axis, numLeft, numRight); } // found best split? if (bestAxis != -1) { // allocate space for child nodes BuildTask taskL = new BuildTask(bnl, task); BuildTask taskR = new BuildTask(bnr, task); int lk = 0, rk = 0; for (int i = 0; i < bestOffsetStart; i++) { long ptr = splits[i]; if (unpackAxis(ptr) == bestAxis) { if (unpackSplitType(ptr) != CLOSED) { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>> lk++; } } } for (int i = bestOffsetStart; i < bestOffsetEnd; i++) { long ptr = splits[i]; Debug.Assert(unpackAxis(ptr) == bestAxis); if (unpackSplitType(ptr) == PLANAR) { if (bestPlanarLeft) { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>> lk++; } else { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>> rk++; } } } for (int i = bestOffsetEnd; i < nSplits; i++) { long ptr = splits[i]; if (unpackAxis(ptr) == bestAxis) { if (unpackSplitType(ptr) != OPENED) { int obj = unpackObject(ptr); lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>> rk++; } } } // output new splits while maintaining order long[] splitsL = taskL.splits; long[] splitsR = taskR.splits; int nsl = 0, nsr = 0; for (int i = 0; i < nSplits; i++) { long ptr = splits[i]; int obj = unpackObject(ptr); int idx = (int)((uint)obj >> 2);//>>> int mask = 1 << ((obj & 3) << 1); if ((lrtable[idx] & mask) != 0) { splitsL[nsl] = ptr; nsl++; } if ((lrtable[idx] & (mask << 1)) != 0) { splitsR[nsr] = ptr; nsr++; } } taskL.n = nsl; taskR.n = nsr; // free more memory task.splits = splits = splitsL = splitsR = null; task = null; // allocate child nodes int nextOffset = tempTree.Count; tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); tempTree.Add(0); // create current node tempTree[offset + 0] = (bestAxis << 30) | nextOffset; tempTree[offset + 1] = ByteUtil.floatToRawIntBits(bestSplit); // recurse for child nodes - free object arrays after each step stats.updateInner(); switch (bestAxis) { case 0: buildTree(minx, bestSplit, miny, maxy, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats); taskL = null; buildTree(bestSplit, maxx, miny, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats); taskR = null; return; case 1: buildTree(minx, maxx, miny, bestSplit, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats); taskL = null; buildTree(minx, maxx, bestSplit, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats); taskR = null; return; case 2: buildTree(minx, maxx, miny, maxy, minz, bestSplit, taskL, depth + 1, tempTree, nextOffset, tempList, stats); taskL = null; buildTree(minx, maxx, miny, maxy, bestSplit, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats); taskR = null; return; default: Debug.Assert(false); break; } } } // create leaf node int listOffset = tempList.Count; int n = 0; for (int i = 0; i < task.n; i++) { long ptr = task.splits[i]; if (unpackAxis(ptr) == 0 && unpackSplitType(ptr) != CLOSED) { tempList.Add(unpackObject(ptr)); n++; } } stats.updateLeaf(depth, n); if (n != task.numObjects) UI.printError(UI.Module.ACCEL, "Error creating leaf node - expecting {0} found {1}", task.numObjects, n); tempTree[offset + 0] = (3 << 30) | listOffset; tempTree[offset + 1] = task.numObjects; // free some memory task.splits = null; }
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); }