Exemple #1
0
 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;
 }
Exemple #2
0
        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);
            }
        }