Esempio n. 1
0
 public TradeOffBvhConstructor(int maxRecursion               = int.MaxValue,
                               int recursionDepth             = 0,
                               SortingAccelerator accelerator = null)
 {
     this.maxRecursion   = maxRecursion;
     this.recursionDepth = recursionDepth;
     this.accelerator    = accelerator;
 }
        public static IPrimitive CreateNewHierachy(List <IPrimitive> traceableItems, int maxRecursion = int.MaxValue, int recursionDepth = 0, SortingAccelerator accelerator = null)
        {
            if (accelerator == null)
            {
                accelerator = new SortingAccelerator();
            }

            int numItems = traceableItems.Count;

            if (numItems == 0)
            {
                return(null);
            }

            if (numItems == 1)
            {
                return(traceableItems[0]);
            }

            int bestAxis           = -1;
            int bestIndexToSplitOn = -1;
            var axisSorter         = new CompareCentersOnAxis(0);

            if (recursionDepth < maxRecursion)
            {
                if (numItems > 50)
                {
                    bestAxis           = accelerator.NextAxis;
                    bestIndexToSplitOn = numItems / 2;
                }
                else
                {
                    double totalIntersectCost = 0;
                    int    skipInterval       = 1;
                    for (int i = 0; i < numItems; i += skipInterval)
                    {
                        IPrimitive item = traceableItems[i];
                        totalIntersectCost += item.GetIntersectCost();
                    }

                    // get the bounding box of all the items we are going to consider.
                    AxisAlignedBoundingBox OverallBox = traceableItems[0].GetAxisAlignedBoundingBox();
                    for (int i = skipInterval; i < numItems; i += skipInterval)
                    {
                        OverallBox += traceableItems[i].GetAxisAlignedBoundingBox();
                    }

                    double areaOfTotalBounds = OverallBox.GetSurfaceArea();

                    double bestCost = totalIntersectCost;

                    var      totalDeviationOnAxis = default(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;
                        traceableItems.Sort(axisSorter);

                        // Get all left bounds
                        AxisAlignedBoundingBox currentLeftBounds = traceableItems[0].GetAxisAlignedBoundingBox();
                        surfaceArreaOfItem[0] = currentLeftBounds.GetSurfaceArea();
                        for (int itemIndex = 1; itemIndex < numItems - 1; itemIndex += skipInterval)
                        {
                            currentLeftBounds            += traceableItems[itemIndex].GetAxisAlignedBoundingBox();
                            surfaceArreaOfItem[itemIndex] = currentLeftBounds.GetSurfaceArea();

                            totalDeviationOnAxis[axis] += Math.Abs(traceableItems[itemIndex].GetCenter()[axis] - traceableItems[itemIndex - 1].GetCenter()[axis]);
                        }

                        // Get all right bounds
                        if (numItems > 1)
                        {
                            AxisAlignedBoundingBox currentRightBounds = traceableItems[numItems - 1].GetAxisAlignedBoundingBox();
                            rightBoundsAtItem[numItems - 2] = currentRightBounds.GetSurfaceArea();
                            for (int itemIndex = numItems - 1; itemIndex > 1; itemIndex -= skipInterval)
                            {
                                currentRightBounds += traceableItems[itemIndex - 1].GetAxisAlignedBoundingBox();
                                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
                                intersectCostOnLeft += traceableItems[itemIndex].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 UnboundCollection(traceableItems));
            }
            else
            {
                var leftItems  = new List <IPrimitive>(bestIndexToSplitOn + 1);
                var rightItems = new List <IPrimitive>(numItems - bestIndexToSplitOn + 1);
                if (numItems > 100)
                {
                    // there are lots of items, lets find a sampled bounds and then choose a center
                    var totalBounds = AxisAlignedBoundingBox.Empty();
                    for (int i = 0; i < 50; i++)
                    {
                        totalBounds.ExpandToInclude(traceableItems[i * numItems / 50].GetCenter());
                    }

                    bestAxis = totalBounds.XSize > totalBounds.YSize ? 0 : 1;
                    bestAxis = totalBounds.Size[bestAxis] > totalBounds.ZSize ? bestAxis : 2;
                    var axisCenter = totalBounds.Center[bestAxis];
                    for (int i = 0; i < numItems; i++)
                    {
                        if (traceableItems[i].GetAxisCenter(bestAxis) <= axisCenter)
                        {
                            leftItems.Add(traceableItems[i]);
                        }
                        else
                        {
                            rightItems.Add(traceableItems[i]);
                        }
                    }
                }
                else                 // sort them and find the center
                {
                    axisSorter.WhichAxis = bestAxis;
                    traceableItems.Sort(axisSorter);
                    for (int i = 0; i <= bestIndexToSplitOn; i++)
                    {
                        leftItems.Add(traceableItems[i]);
                    }

                    for (int i = bestIndexToSplitOn + 1; i < numItems; i++)
                    {
                        rightItems.Add(traceableItems[i]);
                    }
                }

                IPrimitive leftGroup  = CreateNewHierachy(leftItems, maxRecursion, recursionDepth + 1, accelerator);
                IPrimitive rightGroup = CreateNewHierachy(rightItems, maxRecursion, recursionDepth + 1, accelerator);
                var        newBVHNode = new BoundingVolumeHierarchy(leftGroup, rightGroup, bestAxis);
                return(newBVHNode);
            }
        }
Esempio n. 3
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);
            }
        }
		public static IPrimitive CreateNewHierachy(List<IPrimitive> traceableItems, int maxRecursion = int.MaxValue, int recursionDepth = 0, SortingAccelerator accelerator = null)
		{
			if (accelerator == null)
			{
				accelerator = new SortingAccelerator();
			}

			int numItems = traceableItems.Count;

			if (numItems == 0)
			{
				return null;
			}

			if (numItems == 1)
			{
				return traceableItems[0];
			}

			int bestAxis = -1;
			int bestIndexToSplitOn = -1;
			CompareCentersOnAxis axisSorter = new CompareCentersOnAxis(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)
					{
						IPrimitive item = traceableItems[i];
						totalIntersectCost += item.GetIntersectCost();
					}

					// get the bounding box of all the items we are going to consider.
					AxisAlignedBoundingBox OverallBox = traceableItems[0].GetAxisAlignedBoundingBox();
					for (int i = skipInterval; i < numItems; i += skipInterval)
					{
						OverallBox += traceableItems[i].GetAxisAlignedBoundingBox();
					}
					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;
						traceableItems.Sort(axisSorter);

						// Get all left bounds
						AxisAlignedBoundingBox currentLeftBounds = traceableItems[0].GetAxisAlignedBoundingBox();
						surfaceArreaOfItem[0] = currentLeftBounds.GetSurfaceArea();
						for (int itemIndex = 1; itemIndex < numItems - 1; itemIndex += skipInterval)
						{
							currentLeftBounds += traceableItems[itemIndex].GetAxisAlignedBoundingBox();
							surfaceArreaOfItem[itemIndex] = currentLeftBounds.GetSurfaceArea();

							totalDeviationOnAxis[axis] += Math.Abs(traceableItems[itemIndex].GetCenter()[axis] - traceableItems[itemIndex - 1].GetCenter()[axis]);
						}

						// Get all right bounds
						if (numItems > 1)
						{
							AxisAlignedBoundingBox currentRightBounds = traceableItems[numItems - 1].GetAxisAlignedBoundingBox();
							rightBoundsAtItem[numItems - 2] = currentRightBounds.GetSurfaceArea();
							for (int itemIndex = numItems - 1; itemIndex > 1; itemIndex -= skipInterval)
							{
								currentRightBounds += traceableItems[itemIndex - 1].GetAxisAlignedBoundingBox();
								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
								intersectCostOnLeft += traceableItems[itemIndex].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 UnboundCollection(traceableItems);
			}
			else
			{
				axisSorter.WhichAxis = bestAxis;
				traceableItems.Sort(axisSorter);
				List<IPrimitive> leftItems = new List<IPrimitive>(bestIndexToSplitOn + 1);
				List<IPrimitive> rightItems = new List<IPrimitive>(numItems - bestIndexToSplitOn + 1);
				for (int i = 0; i <= bestIndexToSplitOn; i++)
				{
					leftItems.Add(traceableItems[i]);
				}
				for (int i = bestIndexToSplitOn + 1; i < numItems; i++)
				{
					rightItems.Add(traceableItems[i]);
				}
				IPrimitive leftGroup = CreateNewHierachy(leftItems, maxRecursion, recursionDepth + 1, accelerator);
				IPrimitive rightGroup = CreateNewHierachy(rightItems, maxRecursion, recursionDepth + 1, accelerator);
				BoundingVolumeHierarchy newBVHNode = new BoundingVolumeHierarchy(leftGroup, rightGroup, bestAxis);
				return newBVHNode;
			}
		}