public BvhTree(BvhTree <T> nodeA, BvhTree <T> nodeB, int splittingPlane) { this.splittingPlane = splittingPlane; this.nodeA = nodeA; this.nodeB = nodeB; this.Aabb = nodeA.Aabb + nodeB.Aabb; // we can cache this because it is not allowed to change. this.Center = Aabb.Center; }
public static BvhTree <T> CreateNewHierachy(List <BvhTreeItemData <T> > itemsToAdd, int maxRecursion = int.MaxValue, int recursionDepth = 0, SortingAccelerator accelerator = null) { if (accelerator == null) { accelerator = new SortingAccelerator(); } int numItems = itemsToAdd.Count; if (numItems == 0) { return(null); } if (numItems == 1) { return(new BvhTree <T>(itemsToAdd)); } int bestAxis = -1; int bestIndexToSplitOn = -1; var axisSorter = new AxisSorter(0); if (recursionDepth < maxRecursion) { if (numItems > 5000) { bestAxis = accelerator.NextAxis; bestIndexToSplitOn = numItems / 2; } else { double totalIntersectCost = 0; int skipInterval = 1; for (int i = 0; i < numItems; i += skipInterval) { var item = itemsToAdd[i]; if (item.Item is IIntersectable intersectable) { totalIntersectCost += intersectable.GetIntersectCost(); } else { totalIntersectCost += AxisAlignedBoundingBox.GetIntersectCost(); } } // get the bounding box of all the items we are going to consider. AxisAlignedBoundingBox OverallBox = itemsToAdd[0].Aabb; for (int i = skipInterval; i < numItems; i += skipInterval) { OverallBox += itemsToAdd[i].Aabb; } double areaOfTotalBounds = OverallBox.GetSurfaceArea(); double bestCost = totalIntersectCost; Vector3 totalDeviationOnAxis = new Vector3(); double[] surfaceArreaOfItem = new double[numItems - 1]; double[] rightBoundsAtItem = new double[numItems - 1]; for (int axis = 0; axis < 3; axis++) { double intersectCostOnLeft = 0; axisSorter.WhichAxis = axis; itemsToAdd.Sort(axisSorter); // Get all left bounds AxisAlignedBoundingBox currentLeftBounds = itemsToAdd[0].Aabb; surfaceArreaOfItem[0] = currentLeftBounds.GetSurfaceArea(); for (int itemIndex = 1; itemIndex < numItems - 1; itemIndex += skipInterval) { currentLeftBounds += itemsToAdd[itemIndex].Aabb; surfaceArreaOfItem[itemIndex] = currentLeftBounds.GetSurfaceArea(); totalDeviationOnAxis[axis] += Math.Abs(itemsToAdd[itemIndex].Center[axis] - itemsToAdd[itemIndex - 1].Center[axis]); } // Get all right bounds if (numItems > 1) { AxisAlignedBoundingBox currentRightBounds = itemsToAdd[numItems - 1].Aabb; rightBoundsAtItem[numItems - 2] = currentRightBounds.GetSurfaceArea(); for (int itemIndex = numItems - 1; itemIndex > 1; itemIndex -= skipInterval) { currentRightBounds += itemsToAdd[itemIndex - 1].Aabb; rightBoundsAtItem[itemIndex - 2] = currentRightBounds.GetSurfaceArea(); } } // Sweep from left for (int itemIndex = 0; itemIndex < numItems - 1; itemIndex += skipInterval) { double thisCost = 0; { // Evaluate Surface Cost Equation double costOfTwoAABB = 2 * AxisAlignedBoundingBox.GetIntersectCost(); // the cost of the two children AABB tests // do the left cost if (itemsToAdd[itemIndex].Item is IIntersectable intersectable) { intersectCostOnLeft += intersectable.GetIntersectCost(); } else { intersectCostOnLeft += AxisAlignedBoundingBox.GetIntersectCost(); } double leftCost = (surfaceArreaOfItem[itemIndex] / areaOfTotalBounds) * intersectCostOnLeft; // do the right cost double intersectCostOnRight = totalIntersectCost - intersectCostOnLeft; double rightCost = (rightBoundsAtItem[itemIndex] / areaOfTotalBounds) * intersectCostOnRight; thisCost = costOfTwoAABB + leftCost + rightCost; } if (thisCost < bestCost + .000000001) // if it is less within some tiny error { if (thisCost > bestCost - .000000001) { // they are the same within the error if (axis > 0 && bestAxis != axis) // we have changed axis since last best and we need to decide if this is better than the last axis best { if (totalDeviationOnAxis[axis] > totalDeviationOnAxis[axis - 1]) { // this new axis is better and we'll switch to it. Otherwise don't switch. bestCost = thisCost; bestIndexToSplitOn = itemIndex; bestAxis = axis; } } } else // this is just better { bestCost = thisCost; bestIndexToSplitOn = itemIndex; bestAxis = axis; } } } } } } if (bestAxis == -1) { // No better partition found return(new BvhTree <T>(itemsToAdd)); } else { axisSorter.WhichAxis = bestAxis; itemsToAdd.Sort(axisSorter); var leftItems = new List <BvhTreeItemData <T> >(bestIndexToSplitOn + 1); var rightItems = new List <BvhTreeItemData <T> >(numItems - bestIndexToSplitOn + 1); for (int i = 0; i <= bestIndexToSplitOn; i++) { leftItems.Add(itemsToAdd[i]); } for (int i = bestIndexToSplitOn + 1; i < numItems; i++) { rightItems.Add(itemsToAdd[i]); } var leftGroup = CreateNewHierachy(leftItems, maxRecursion, recursionDepth + 1, accelerator); var rightGroup = CreateNewHierachy(rightItems, maxRecursion, recursionDepth + 1, accelerator); var newBVHNode = new BvhTree <T>(leftGroup, rightGroup, bestAxis); return(newBVHNode); } }