public static Dictionary <long, BuildStats> FindBuildStatsPerPlayer() { Dictionary <long, BuildStats> stats = new Dictionary <long, BuildStats>(); foreach (MyEntity entity in MyEntities.GetEntities()) { if (!(entity is MyCubeGrid grid)) { continue; } foreach (var block in grid.GetBlocks()) { long buildBy = block.BuiltBy; if (!stats.TryGetValue(buildBy, out BuildStats statsForPlayer)) { statsForPlayer = new BuildStats(); stats.Add(buildBy, statsForPlayer); } statsForPlayer.BlockCount++; statsForPlayer.PcuCount += BlockUtils.GetPcu(block); } } return(stats); }
public void build <T>(List <T> primitives, uint leafSize = 3, bool printStats = false) where T : IModel { if (primitives.Count == 0) { init_empty(); return; } buildData dat; dat.maxPrims = (int)leafSize; dat.numPrims = (uint)primitives.Count; dat.indices = new uint[dat.numPrims]; dat.primBound = new AxisAlignedBox[dat.numPrims]; bounds = primitives[0].getBounds(); for (int i = 0; i < dat.numPrims; ++i) { dat.indices[i] = (uint)i; dat.primBound[i] = primitives[i].getBounds(); bounds.merge(dat.primBound[i]); } List <uint> tempTree = new List <uint>(); BuildStats stats = new BuildStats(); buildHierarchy(tempTree, dat, stats); objects = new uint[dat.numPrims]; for (int i = 0; i < dat.numPrims; ++i) { objects[i] = dat.indices[i]; } tree = tempTree.ToArray(); }
public async Task <ActionResult> Stats(bool pr = false) { var util = Factory.Create <BuildCounterEntity>(_storageAccount.CreateCloudTableClient(), TableNames.CounterBuilds); var map = new Dictionary <DateTimeKey, BuildStats>(); var endDate = DateTimeOffset.UtcNow; var startDate = endDate.AddDays(-7); foreach (var entity in await util.QueryAsync(startDate: startDate, endDate: endDate)) { BuildStats stats; var date = util.GetDateTimeKey(entity); if (!map.TryGetValue(date, out stats)) { stats = new BuildStats(date.DateTime); map[date] = stats; } stats.BuildSucceededCount += entity.CommitSucceededCount; stats.BuildFailedCount += entity.CommitFailedCount; if (pr) { stats.BuildSucceededCount += entity.PullRequestSucceededCount; stats.BuildFailedCount += entity.PullRequestFailedCount; } } var model = new BuildStatsModel(map.Values.OrderBy(x => x.Date).ToList(), pr); return(View(viewName: "Stats", model: model)); }
public void build <T>(List <T> primitives, GetBounds <T> getBounds, uint leafSize = 3, bool printStats = false) { if (primitives.Count == 0) { init_empty(); return; } buildData dat; dat.maxPrims = (int)leafSize; dat.numPrims = (uint)primitives.Count; dat.indices = new uint[dat.numPrims]; dat.primBound = new AxisAlignedBox[dat.numPrims]; getBounds(primitives[0], out bounds); for (int i = 0; i < dat.numPrims; ++i) { dat.primBound[i] = AxisAlignedBox.NaN; dat.indices[i] = (uint)i; getBounds(primitives[i], out dat.primBound[i]); bounds.merge(dat.primBound[i]); } List <uint> tempTree = new List <uint>(); BuildStats stats = new BuildStats(); buildHierarchy(tempTree, dat, stats); for (int i = 0; i < dat.numPrims; ++i) { objects.Add(dat.indices[i]); } tree = tempTree; }
public void build(PrimitiveList primitives) { this.primitives = primitives; int n = primitives.getNumPrimitives(); UI.printDetailed(UI.Module.ACCEL, "Getting bounding box ..."); bounds = primitives.getWorldBounds(null); objects = new int[n]; for (int i = 0; i < n; i++) { objects[i] = i; } UI.printDetailed(UI.Module.ACCEL, "Creating tree ..."); int initialSize = 3 * (2 * 6 * n + 1); List <int> tempTree = new List <int>((initialSize + 3) / 4); BuildStats stats = new BuildStats(); Timer t = new Timer(); t.start(); buildHierarchy(tempTree, objects, stats); t.end(); UI.printDetailed(UI.Module.ACCEL, "Trimming tree ..."); tree = tempTree.ToArray(); // display stats stats.printStats(); UI.printDetailed(UI.Module.ACCEL, " * Creation time: {0}", t); UI.printDetailed(UI.Module.ACCEL, " * Usage of init: {0,9:0.00}%", (double)(100 * tree.Length) / initialSize); UI.printDetailed(UI.Module.ACCEL, " * Tree memory: {0}", Memory.SizeOf(tree)); UI.printDetailed(UI.Module.ACCEL, " * Indices memory: {0}", Memory.SizeOf(objects)); }
public void build(PrimitiveList primitives) { this.primitives = primitives; int n = primitives.getNumPrimitives(); UI.printDetailed(UI.Module.ACCEL, "Getting bounding box ..."); bounds = primitives.getWorldBounds(null); objects = new int[n]; for (int i = 0; i < n; i++) objects[i] = i; UI.printDetailed(UI.Module.ACCEL, "Creating tree ..."); int initialSize = 3 * (2 * 6 * n + 1); List<int> tempTree = new List<int>((initialSize + 3) / 4); BuildStats stats = new BuildStats(); Timer t = new Timer(); t.start(); buildHierarchy(tempTree, objects, stats); t.end(); UI.printDetailed(UI.Module.ACCEL, "Trimming tree ..."); tree = tempTree.ToArray(); // display stats stats.printStats(); UI.printDetailed(UI.Module.ACCEL, " * Creation time: %s", t); UI.printDetailed(UI.Module.ACCEL, " * Usage of init: %3d%%", 100 * tree.Length / initialSize); UI.printDetailed(UI.Module.ACCEL, " * Tree memory: %s", Memory.SizeOf(tree)); UI.printDetailed(UI.Module.ACCEL, " * Indices memory: %s", Memory.SizeOf(objects)); }
void BuildHierarchy(List <uint> tempTree, buildData dat, BuildStats stats) { // create space for the first node tempTree.Add(3u << 30); // dummy leaf tempTree.Add(0); tempTree.Add(0); // seed bbox AABound gridBox = new(); gridBox.lo = bounds.Lo; gridBox.hi = bounds.Hi; AABound nodeBox = gridBox; // seed subdivide function Subdivide(0, (int)(dat.numPrims - 1), tempTree, dat, gridBox, nodeBox, 0, 1, stats); }
private void buildHierarchy(List <int> tempTree, int[] indices, BuildStats stats) { // create space for the first node tempTree.Add(3 << 30); // dummy leaf tempTree.Add(0); tempTree.Add(0); if (objects.Length == 0) { return; } // seed bbox float[] gridBox = { bounds.getMinimum().x, bounds.getMaximum().x, bounds.getMinimum().y, bounds.getMaximum().y, bounds.getMinimum().z, bounds.getMaximum().z }; float[] nodeBox = { bounds.getMinimum().x, bounds.getMaximum().x, bounds.getMinimum().y, bounds.getMaximum().y, bounds.getMinimum().z, bounds.getMaximum().z }; // seed subdivide function subdivide(0, objects.Length - 1, tempTree, indices, gridBox, nodeBox, 0, 1, stats); }
public BuildContext() { Stats = new BuildStats(); Env = new BuildEnvironment(this); ProbedPaths = new ConcurrentBag<string>(); }
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; }
public void build(PrimitiveList primitives) { UI.printDetailed(UI.Module.ACCEL, "KDTree settings"); UI.printDetailed(UI.Module.ACCEL, " * Max Leaf Size: {0}", maxPrims); UI.printDetailed(UI.Module.ACCEL, " * Max Depth: {0}", MAX_DEPTH); UI.printDetailed(UI.Module.ACCEL, " * Traversal cost: {0}", TRAVERSAL_COST); UI.printDetailed(UI.Module.ACCEL, " * Intersect cost: {0}", INTERSECT_COST); UI.printDetailed(UI.Module.ACCEL, " * Empty bonus: {0}", EMPTY_BONUS); UI.printDetailed(UI.Module.ACCEL, " * Dump leaves: {0}", dump ? "enabled" : "disabled"); Timer total = new Timer(); total.start(); this.primitiveList = primitives; // get the object space bounds bounds = primitives.getWorldBounds(null); int nPrim = primitiveList.getNumPrimitives(), nSplits = 0; BuildTask task = new BuildTask(nPrim); Timer prepare = new Timer(); prepare.start(); for (int i = 0; i < nPrim; i++) { for (int axis = 0; axis < 3; axis++) { float ls = primitiveList.getPrimitiveBound(i, 2 * axis + 0); float rs = primitiveList.getPrimitiveBound(i, 2 * axis + 1); if (ls == rs) { // flat in this dimension task.splits[nSplits] = pack(ls, PLANAR, axis, i); nSplits++; } else { task.splits[nSplits + 0] = pack(ls, OPENED, axis, i); task.splits[nSplits + 1] = pack(rs, CLOSED, axis, i); nSplits += 2; } } } task.n = nSplits; prepare.end(); Timer t = new Timer(); List <int> tempTree = new List <int>(); List <int> tempList = new List <int>(); tempTree.Add(0); tempTree.Add(1); t.start(); // sort it Timer sorting = new Timer(); sorting.start(); radix12(task.splits, task.n); sorting.end(); // build the actual tree BuildStats stats = new BuildStats(); buildTree(bounds.getMinimum().x, bounds.getMaximum().x, bounds.getMinimum().y, bounds.getMaximum().y, bounds.getMinimum().z, bounds.getMaximum().z, task, 1, tempTree, 0, tempList, stats); t.end(); // write out arrays // free some memory task = null; tree = tempTree.ToArray(); tempTree = null; this.primitives = tempList.ToArray(); tempList = null; total.end(); // display some extra info stats.printStats(); UI.printDetailed(UI.Module.ACCEL, " * Node memory: {0}", Memory.SizeOf(tree)); UI.printDetailed(UI.Module.ACCEL, " * Object memory: {0}", Memory.SizeOf(this.primitives)); UI.printDetailed(UI.Module.ACCEL, " * Prepare time: {0}", prepare); UI.printDetailed(UI.Module.ACCEL, " * Sorting time: {0}", sorting); UI.printDetailed(UI.Module.ACCEL, " * Tree creation: {0}", t); UI.printDetailed(UI.Module.ACCEL, " * Build time: {0}", total); if (dump) { try { UI.printInfo(UI.Module.ACCEL, "Dumping mtls to {0}.mtl ...", dumpPrefix); StreamWriter mtlFile = new StreamWriter(dumpPrefix + ".mtl"); int maxN = stats.maxObjects; for (int n = 0; n <= maxN; n++) { float blend = (float)n / (float)maxN; Color nc; if (blend < 0.25) { nc = Color.blend(Color.BLUE, Color.GREEN, blend / 0.25f); } else if (blend < 0.5) { nc = Color.blend(Color.GREEN, Color.YELLOW, (blend - 0.25f) / 0.25f); } else if (blend < 0.75) { nc = Color.blend(Color.YELLOW, Color.RED, (blend - 0.50f) / 0.25f); } else { nc = Color.MAGENTA; } mtlFile.WriteLine(string.Format("newmtl mtl{0}", n)); float[] rgb = nc.getRGB(); mtlFile.WriteLine("Ka 0.1 0.1 0.1"); mtlFile.WriteLine(string.Format("Kd {0}g {1}g {2}g", rgb[0], rgb[1], rgb[2])); mtlFile.WriteLine("illum 1\n"); } StreamWriter objFile = new StreamWriter(dumpPrefix + ".obj"); UI.printInfo(UI.Module.ACCEL, "Dumping tree to {0}.obj ...", dumpPrefix); dumpObj(0, 0, maxN, new BoundingBox(bounds), objFile, mtlFile); objFile.Close(); mtlFile.Close(); } catch (Exception e) { Console.WriteLine(e); } } }
public void build(PrimitiveList primitives) { UI.printDetailed(UI.Module.ACCEL, "KDTree settings"); UI.printDetailed(UI.Module.ACCEL, " * Max Leaf Size: {0}", maxPrims); UI.printDetailed(UI.Module.ACCEL, " * Max Depth: {0}", MAX_DEPTH); UI.printDetailed(UI.Module.ACCEL, " * Traversal cost: {0}", TRAVERSAL_COST); UI.printDetailed(UI.Module.ACCEL, " * Intersect cost: {0}", INTERSECT_COST); UI.printDetailed(UI.Module.ACCEL, " * Empty bonus: {0}", EMPTY_BONUS); UI.printDetailed(UI.Module.ACCEL, " * Dump leaves: {0}", dump ? "enabled" : "disabled"); Timer total = new Timer(); total.start(); primitiveList = primitives; // get the object space bounds bounds = primitives.getWorldBounds(null); int nPrim = primitiveList.getNumPrimitives(), nSplits = 0; BuildTask task = new BuildTask(nPrim); Timer prepare = new Timer(); prepare.start(); for (int i = 0; i < nPrim; i++) { for (int axis = 0; axis < 3; axis++) { float ls = primitiveList.getPrimitiveBound(i, 2 * axis + 0); float rs = primitiveList.getPrimitiveBound(i, 2 * axis + 1); if (ls == rs) { // flat in this dimension task.splits[nSplits] = pack(ls, PLANAR, axis, i); nSplits++; } else { task.splits[nSplits + 0] = pack(ls, OPENED, axis, i); task.splits[nSplits + 1] = pack(rs, CLOSED, axis, i); nSplits += 2; } } } task.n = nSplits; prepare.end(); Timer t = new Timer(); List<int> tempTree = new List<int>(); List<int> tempList = new List<int>(); tempTree.Add(0); tempTree.Add(1); t.start(); // sort it Timer sorting = new Timer(); sorting.start(); radix12(task.splits, task.n); sorting.end(); // build the actual tree BuildStats stats = new BuildStats(); buildTree(bounds.getMinimum().x, bounds.getMaximum().x, bounds.getMinimum().y, bounds.getMaximum().y, bounds.getMinimum().z, bounds.getMaximum().z, task, 1, tempTree, 0, tempList, stats); t.end(); // write out arrays // free some memory task = null; tree = tempTree.ToArray(); tempTree = null; this.primitives = tempList.ToArray(); tempList = null; total.end(); // display some extra info stats.printStats(); UI.printDetailed(UI.Module.ACCEL, " * Node memory: {0}", Memory.SizeOf(tree)); UI.printDetailed(UI.Module.ACCEL, " * Object memory: {0}", Memory.SizeOf(this.primitives)); UI.printDetailed(UI.Module.ACCEL, " * Prepare time: {0}", prepare); UI.printDetailed(UI.Module.ACCEL, " * Sorting time: {0}", sorting); UI.printDetailed(UI.Module.ACCEL, " * Tree creation: {0}", t); UI.printDetailed(UI.Module.ACCEL, " * Build time: {0}", total); if (dump) { try { UI.printInfo(UI.Module.ACCEL, "Dumping mtls to {0}.mtl ...", dumpPrefix); StreamWriter mtlFile = new StreamWriter(dumpPrefix + ".mtl"); int maxN = stats.maxObjects; for (int n = 0; n <= maxN; n++) { float blend = (float)n / (float)maxN; Color nc; if (blend < 0.25) nc = Color.blend(Color.BLUE, Color.GREEN, blend / 0.25f); else if (blend < 0.5) nc = Color.blend(Color.GREEN, Color.YELLOW, (blend - 0.25f) / 0.25f); else if (blend < 0.75) nc = Color.blend(Color.YELLOW, Color.RED, (blend - 0.50f) / 0.25f); else nc = Color.MAGENTA; mtlFile.WriteLine(string.Format("newmtl mtl{0}", n)); float[] rgb = nc.getRGB(); mtlFile.WriteLine("Ka 0.1 0.1 0.1"); mtlFile.WriteLine(string.Format("Kd {0}g {1}g {2}g", rgb[0], rgb[1], rgb[2])); mtlFile.WriteLine("illum 1\n"); } StreamWriter objFile = new StreamWriter(dumpPrefix + ".obj"); UI.printInfo(UI.Module.ACCEL, "Dumping tree to {0}.obj ...", dumpPrefix); dumpObj(0, 0, maxN, new BoundingBox(bounds), objFile, mtlFile); objFile.Close(); mtlFile.Close(); } catch (Exception e) { Console.WriteLine(e); } } }
private void buildHierarchy(List<int> tempTree, int[] indices, BuildStats stats) { // create space for the first node tempTree.Add(3 << 30); // dummy leaf tempTree.Add(0); tempTree.Add(0); if (objects.Length == 0) return; // seed bbox float[] gridBox = { bounds.getMinimum().x, bounds.getMaximum().x, bounds.getMinimum().y, bounds.getMaximum().y, bounds.getMinimum().z, bounds.getMaximum().z }; float[] nodeBox = { bounds.getMinimum().x, bounds.getMaximum().x, bounds.getMinimum().y, bounds.getMaximum().y, bounds.getMinimum().z, bounds.getMaximum().z }; // seed subdivide function subdivide(0, objects.Length - 1, tempTree, indices, gridBox, nodeBox, 0, 1, stats); }
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 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 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; }