public Branch AddSubtree(string name) { var newBranch = new Branch(name); Subtrees.Add(newBranch); return(newBranch); }
unsafe int CreateStagingNode(int parentIndex, int indexInParent, ref BoundingBox boundingBox, ref Subtrees subtrees, int start, int count, Node *stagingNodes, ref int stagingNodeCount, out float childTreeletsCost) { var stagingNodeIndex = stagingNodeCount++; var stagingNode = stagingNodes + stagingNodeIndex; if (count <= ChildrenCapacity) { //No need to do any sorting. This node can fit every remaining subtree. var localIndexMap = subtrees.IndexMap + start; stagingNode->ChildCount = count; var stagingNodeBounds = &stagingNode->A; var stagingNodeChildren = &stagingNode->ChildA; var leafCounts = &stagingNode->LeafCountA; for (int i = 0; i < count; ++i) { var subtreeIndex = localIndexMap[i]; stagingNodeBounds[i] = subtrees.BoundingBoxes[subtreeIndex]; leafCounts[i] = subtrees.LeafCounts[subtreeIndex]; stagingNodeChildren[i] = Encode(subtreeIndex); } //Because subtrees do not change in size, they cannot change the cost. childTreeletsCost = 0; return(stagingNodeIndex); } const int recursionDepth = ChildrenCapacity == 32 ? 4 : ChildrenCapacity == 16 ? 3 : ChildrenCapacity == 8 ? 2 : ChildrenCapacity == 4 ? 1 : 0; SplitSubtreesIntoChildren(recursionDepth, ref subtrees, start, count, ref boundingBox, stagingNodes, stagingNodeIndex, ref stagingNodeCount, out childTreeletsCost); return(stagingNodeIndex); }
public void DeleteSubtree(Branch branch) { Subtrees.Remove(branch); }
unsafe void SplitSubtreesIntoChildren(int depthRemaining, ref Subtrees subtrees, int start, int count, ref BoundingBox boundingBox, Node *stagingNodes, int stagingNodeIndex, ref int stagingNodesCount, out float childrenTreeletsCost) { if (count > 1) { BoundingBox a, b; int leafCountA, leafCountB; int splitIndex; FindPartition(ref subtrees, start, count, out splitIndex, out a, out b, out leafCountA, out leafCountB); float costA, costB; if (depthRemaining > 0) { --depthRemaining; SplitSubtreesIntoChildren(depthRemaining, ref subtrees, start, splitIndex - start, ref a, stagingNodes, stagingNodeIndex, ref stagingNodesCount, out costA); SplitSubtreesIntoChildren(depthRemaining, ref subtrees, splitIndex, start + count - splitIndex, ref b, stagingNodes, stagingNodeIndex, ref stagingNodesCount, out costB); } else { //Recursion bottomed out. var stagingNode = stagingNodes + stagingNodeIndex; var childIndexA = stagingNode->ChildCount++; var childIndexB = stagingNode->ChildCount++; Debug.Assert(stagingNode->ChildCount <= ChildrenCapacity); var stagingBounds = &stagingNode->A; var stagingChildren = &stagingNode->ChildA; var stagingLeafCounts = &stagingNode->LeafCountA; stagingBounds[childIndexA] = a; stagingBounds[childIndexB] = b; stagingLeafCounts[childIndexA] = leafCountA; stagingLeafCounts[childIndexB] = leafCountB; int subtreeCountA = splitIndex - start; int subtreeCountB = start + count - splitIndex; if (subtreeCountA > 1) { stagingChildren[childIndexA] = CreateStagingNode(stagingNodeIndex, childIndexA, ref a, ref subtrees, start, subtreeCountA, stagingNodes, ref stagingNodesCount, out costA); costA += ComputeBoundsMetric(ref a); //An internal node was created; measure its cost. } else { Debug.Assert(subtreeCountA == 1); //Only one subtree. Don't create another node. stagingChildren[childIndexA] = Encode(subtrees.IndexMap[start]); costA = 0; } if (subtreeCountB > 1) { stagingChildren[childIndexB] = CreateStagingNode(stagingNodeIndex, childIndexB, ref b, ref subtrees, splitIndex, subtreeCountB, stagingNodes, ref stagingNodesCount, out costB); costB += ComputeBoundsMetric(ref b); //An internal node was created; measure its cost. } else { Debug.Assert(subtreeCountB == 1); //Only one subtree. Don't create another node. stagingChildren[childIndexB] = Encode(subtrees.IndexMap[splitIndex]); costB = 0; } } childrenTreeletsCost = costA + costB; } else { Debug.Assert(count == 1); //Only one subtree. Just stick it directly into the node. var childIndex = stagingNodes[stagingNodeIndex].ChildCount++; var subtreeIndex = subtrees.IndexMap[start]; Debug.Assert(stagingNodes[stagingNodeIndex].ChildCount <= ChildrenCapacity); (&stagingNodes[stagingNodeIndex].A)[childIndex] = subtrees.BoundingBoxes[subtreeIndex]; (&stagingNodes[stagingNodeIndex].ChildA)[childIndex] = Encode(subtreeIndex); (&stagingNodes[stagingNodeIndex].LeafCountA)[childIndex] = subtrees.LeafCounts[subtreeIndex]; //Subtrees cannot contribute to change in cost. childrenTreeletsCost = 0; } }
unsafe void FindPartition(ref Subtrees subtrees, int start, int count, out int splitIndex, out BoundingBox a, out BoundingBox b, out int leafCountA, out int leafCountB) { //A variety of potential microoptimizations exist here. //Don't reallocate centroids (because JIT is forced to zero by roslyn), do swaps better, etc. var indexMapX = stackalloc int[count]; var indexMapY = stackalloc int[count]; var indexMapZ = stackalloc int[count]; //Initialize the per-axis candidate maps. var localIndexMap = subtrees.IndexMap + start; for (int i = 0; i < count; ++i) { var originalValue = localIndexMap[i]; indexMapX[i] = originalValue; indexMapY[i] = originalValue; indexMapZ[i] = originalValue; } int xSplitIndex, xLeafCountA, xLeafCountB, ySplitIndex, yLeafCountA, yLeafCountB, zSplitIndex, zLeafCountA, zLeafCountB; BoundingBox xA, xB, yA, yB, zA, zB; float xCost, yCost, zCost; FindPartitionForAxis(subtrees.BoundingBoxes, subtrees.LeafCounts, subtrees.CentroidsX, indexMapX, count, out xSplitIndex, out xCost, out xA, out xB, out xLeafCountA, out xLeafCountB); FindPartitionForAxis(subtrees.BoundingBoxes, subtrees.LeafCounts, subtrees.CentroidsY, indexMapY, count, out ySplitIndex, out yCost, out yA, out yB, out yLeafCountA, out yLeafCountB); FindPartitionForAxis(subtrees.BoundingBoxes, subtrees.LeafCounts, subtrees.CentroidsZ, indexMapZ, count, out zSplitIndex, out zCost, out zA, out zB, out zLeafCountA, out zLeafCountB); int *bestIndexMap; if (xCost <= yCost && xCost <= zCost) { splitIndex = xSplitIndex; a = xA; b = xB; leafCountA = xLeafCountA; leafCountB = xLeafCountB; bestIndexMap = indexMapX; } else if (yCost <= zCost) { splitIndex = ySplitIndex; a = yA; b = yB; leafCountA = yLeafCountA; leafCountB = yLeafCountB; bestIndexMap = indexMapY; } else { splitIndex = zSplitIndex; a = zA; b = zB; leafCountA = zLeafCountA; leafCountB = zLeafCountB; bestIndexMap = indexMapZ; } for (int i = 0; i < count; ++i) { localIndexMap[i] = bestIndexMap[i]; } splitIndex += start; }