Example #1
0
        private void CreateScene()
        {
            scene            = new Scene();
            scene.camera     = new TrackBallCamera(trackballTumbleWidget);
            scene.background = new Background(new RGBA_Floats(0.5, .5, .5), 0.4);

            AddBoxAndSheresBooleanTest();
            //AddBoxAndBoxBooleanTest();
#if false
            renderCollection.Add(new BoxShape(new Vector3(), new Vector3(1, 1, 1),
                                              new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0)));
            renderCollection.Add(new BoxShape(new Vector3(.5, .5, .5), new Vector3(1.5, 1.5, 1.5),
                                              new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0)));
#endif
            //renderCollection.Add(new CylinderShape(.25, 1, new SolidMaterial(RGBA_Floats.Cyan, 0, 0, 0)));

            //AddTestStl();
            //AddPolygonTest();
            //AddSphereAndBox();
            //AddAxisMarker();
            //AddCubeOfShperes();

            //renderCollection.Add(MakerGearXCariage());

            allObjects       = BoundingVolumeHierarchy.CreateNewHierachy(renderCollection);
            allObjectsHolder = new Transform(allObjects);
            //allObjects = root;
            scene.shapes.Add(allObjectsHolder);

            //AddAFloor();

            //add two lights for better lighting effects
            scene.lights.Add(new PointLight(new Vector3(5000, 5000, 5000), new RGBA_Floats(0.8, 0.8, 0.8)));
            scene.lights.Add(new PointLight(new Vector3(-5000, -5000, 3000), new RGBA_Floats(0.5, 0.5, 0.5)));
        }
Example #2
0
        /// <summary>
        /// This will remove the shapes from 'Shapes' and add them to a Bounding Volume Hierarchy.  Then add that at a single element
        /// to 'Shapes'.  You could also create a list of 'List<IPrimitive>' and put that directly into a BVH and then add that
        /// to the Shapes list (there could be more than 1 BVH in the 'Shapes' list.
        /// </summary>
        public ITraceable MoveShapesIntoBoundingVolumeHierachy()
        {
            var rootObject = BoundingVolumeHierarchy.CreateNewHierachy(shapes);

            if (rootObject != null)
            {
                shapes.Clear();
                shapes.Add(rootObject);
            }

            return(rootObject);
        }
Example #3
0
        private void AddBoxAndBoxBooleanTest()
        {
            BoxShape box1 = new BoxShape(new Vector3(.5, .5, .5), new Vector3(1.5, 1.5, 1.5),
                                         new SolidMaterial(RGBA_Floats.Green, .01, 0.0, 2.0));

            List <IPrimitive> subtractShapes = new List <IPrimitive>();
            SolidMaterial     material       = new SolidMaterial(RGBA_Floats.Red, 0, 0, 0);

            subtractShapes.Add(new BoxShape(new Vector3(), new Vector3(1, 1, 1), material));

            IPrimitive subtractGroup = BoundingVolumeHierarchy.CreateNewHierachy(subtractShapes);
            Difference merge         = new Difference(box1, subtractGroup);

            renderCollection.Add(merge);
        }
Example #4
0
        private void AddCubeOfShperes()
        {
            List <IPrimitive> scanData1 = new List <IPrimitive>();
            Random            rand      = new Random(0);
            double            dist      = 2;

            for (int i = 0; i < 4000; i++)
            {
                BaseShape littleShpere = new SphereShape(new Vector3(rand.NextDouble() * dist - dist / 2, rand.NextDouble() * dist - dist / 2, rand.NextDouble() * dist - dist / 2),
                                                         rand.NextDouble() * .1 + .01,
                                                         new SolidMaterial(new RGBA_Floats(rand.NextDouble() * .5 + .5, rand.NextDouble() * .5 + .5, rand.NextDouble() * .5 + .5), 0, 0.0, 2.0));
                scanData1.Add(littleShpere);
            }

            renderCollection.Add(BoundingVolumeHierarchy.CreateNewHierachy(scanData1));
        }
Example #5
0
        private void CreateScene()
        {
            scene        = new Scene();
            scene.camera = new TrackBallCamera(trackballTumbleWidget);
            //scene.background = new Background(new RGBA_Floats(0.5, .5, .5), 0.4);
            scene.background = new Background(new RGBA_Floats(1, 1, 1, 0), 0.6);

            AddTestMesh(loadedMeshGroups);

            allObjects       = BoundingVolumeHierarchy.CreateNewHierachy(renderCollection);
            allObjectsHolder = new Transform(allObjects);
            //allObjects = root;
            scene.shapes.Add(allObjectsHolder);

            //AddAFloor();

            //add two lights for better lighting effects
            //scene.lights.Add(new Light(new Vector3(5000, 5000, 5000), new RGBA_Floats(0.8, 0.8, 0.8)));
            scene.lights.Add(new PointLight(new Vector3(-5000, -5000, 3000), new RGBA_Floats(0.5, 0.5, 0.5)));
        }
        private void AddBoxAndSheresBooleanTest()
        {
            BoxShape box1 = new BoxShape(new Vector3(.5, .5, .5), new Vector3(1.5, 1.5, 1.5),
                                         new SolidMaterial(RGBA_Floats.Green, 0, 0, 0));//.01, 0.0, 2.0));

            List <IRayTraceable> subtractShapes = new List <IRayTraceable>();
            SolidMaterial        material       = new SolidMaterial(RGBA_Floats.Red, 0, 0, 0);

#if true
            // two big spheres.  Looks good.
            subtractShapes.Add(new SphereShape(new Vector3(.5, .5, 1), .6, material));
            subtractShapes.Add(new SphereShape(new Vector3(1.5, .5, 1), .6, material));

            Transform cylinder = new Transform(new CylinderShape(.1, 3, material));
            cylinder.MoveToAbsolute(1, 1, 1);
            cylinder.RotateRelative(.1, .6, .6);
            //subtractShapes.Add(cylinder);
            //renderCollection.Add(cylinder);
#else
            for (int z = 0; z < 6; z++)
            {
                for (int y = 0; y < 6; y++)
                {
                    for (int x = 0; x < 6; x++)
                    {
                        subtractShapes.Add(new SphereShape(new Vector3(x * .2 + .5, y * .2 + .5, z * .2 + .5), .1, material));
                        //subtractShapes.Add(new SphereShape(new Vector3(x * .2 + .5, y * .2 + .5, z * .2 + .5), .13, material));
                    }
                }
            }
#endif


            IRayTraceable subtractGroup = BoundingVolumeHierarchy.CreateNewHierachy(subtractShapes);
            Difference    merge         = new Difference(box1, subtractGroup);

            renderCollection.Add(merge);
        }
        private void CreateScene()
        {
            scene            = new Scene();
            scene.camera     = new Camera((int)Width, (int)Height, MathHelper.DegreesToRadians(40));
            scene.background = new Background(new RGBA_Floats(0.5, .5, .5), 0.4);

            //AddBoxAndSheresBooleanTest();
            //AddBoxAndBoxBooleanTest();
#if false
            renderCollection.Add(new BoxShape(new Vector3(), new Vector3(1, 1, 1),
                                              new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0)));
            renderCollection.Add(new BoxShape(new Vector3(.5, .5, .5), new Vector3(1.5, 1.5, 1.5),
                                              new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0)));
#endif
            //renderCollection.Add(new CylinderShape(.25, 1, new SolidMaterial(RGBA_Floats.Cyan, 0, 0, 0)));

            AddTestStl();
            //AddPolygonTest();
            //AddSphereAndBox();
            //AddAxisMarker();
            //AddCubeOfShperes();

            //renderCollection.Add(MakerGearXCariage());

            allObjects         = BoundingVolumeHierarchy.CreateNewHierachy(renderCollection);
            trackBallTransform = new Transform(allObjects);
            //allObjects = root;
            scene.shapes.Add(trackBallTransform);

            //AddAFloor();

            //add two lights for better lighting effects
            scene.lights.Add(new Light(new Vector3(50, 10, 100), new RGBA_Floats(0.8, 0.8, 0.8)));
            scene.lights.Add(new Light(new Vector3(-30, 150, 50), new RGBA_Floats(0.8, 0.8, 0.8)));

            OrientCamera();
        }
        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);
            }
        }
Example #9
0
        public void DifferenceTestsForBox()
        {
            SolidMaterial redMaterial  = new SolidMaterial(ColorF.Red, 0, 0, 0);
            SolidMaterial blueMaterial = new SolidMaterial(ColorF.Blue, 0, 0, 0);
            Ray           castRay      = new Ray(new Vector3(0, -1, 0), Vector3.UnitY);

            BoxShape box1X1 = new BoxShape(new Vector3(-.5, -.5, -.5), new Vector3(.5, .5, .5), blueMaterial);

            // just a box all by itself
            {
                IntersectInfo testInfo = box1X1.GetClosestIntersection(castRay);

                Assert.IsTrue(testInfo.hitType == IntersectionType.FrontFace, "Found Hit : Box No CSG");
                Assert.IsTrue(testInfo.closestHitObject == box1X1, "Found Hit : Box No CSG");
                Assert.IsTrue(testInfo.HitPosition == new Vector3(0, -.5, 0), "Hit position y = -.5 : Box No CSG");
                Assert.IsTrue(testInfo.distanceToHit == .5, "Hit length = .5 : Box No CSG");
                Assert.IsTrue(testInfo.normalAtHit == -Vector3.UnitY, "Normal Correct : Box No CSG");
            }

            // one subtract from the front of a box, the front faces are aligned
            {
                BoxShape      subtractBox = new BoxShape(new Vector3(-.5, -.5, -.5), new Vector3(.5, 0, .5), redMaterial);
                Difference    merge       = new Difference(box1X1, subtractBox);
                IntersectInfo testInfo    = merge.GetClosestIntersection(castRay);

                Assert.IsTrue(testInfo.hitType == IntersectionType.FrontFace, "Found Hit : One Subtract");
                Assert.IsTrue(testInfo.closestHitObject == subtractBox, "Found Hit : One Subtract");
                Assert.IsTrue(testInfo.HitPosition == new Vector3(0, 0, 0), "Hit position y = 0 : One Subtract");
                Assert.IsTrue(testInfo.distanceToHit == 1, "Hit length = 1 : One Subtract");
                Assert.IsTrue(testInfo.normalAtHit == -Vector3.UnitY, "Normal Correct : One Subtract");
            }

#if false
            // An internal primary object that needs to be skipped over
            {
                List <IPrimitive> primaryShapes = new List <IPrimitive>();
                BoxShape          insideBox     = new BoxShape(new Vector3(-.1, -.1, -.1), new Vector3(.1, .1, .1), blueMaterial);
                primaryShapes.Add(box1X1);
                primaryShapes.Add(insideBox);
                IPrimitive primamryGroup = BoundingVolumeHierarchy.CreateNewHierachy(primaryShapes);

                List <IPrimitive> subtractShapes = new List <IPrimitive>();
                subtractShapes.Add(new BoxShape(new Vector3(-.5, -.5, -.5), new Vector3(.5, .4, .5), redMaterial));

                IPrimitive subtractGroup = BoundingVolumeHierarchy.CreateNewHierachy(subtractShapes);
                Difference merge         = new Difference(primamryGroup, subtractGroup);

                IntersectInfo testInfo = merge.GetClosestIntersection(castRay);

                Assert.IsTrue(testInfo.isHit == true, "Found Hit : 5 Subtracts");
                //Assert.IsTrue(testInfo.closestHitObject == subtractBox, "Found Hit : 5 Subtracts");
                Assert.IsTrue(testInfo.hitPosition == new Vector3(0, 0, 0), "Hit position y = 0 : 5 Subtracts");
                Assert.IsTrue(testInfo.distanceToHit == 1, "Hit length = 1 : 5 Subtracts");
                Assert.IsTrue(testInfo.normalAtHit == -Vector3.UnitY, "Normal Correct : 5 Subtracts");
            }

            // Go through 5 subtract boxes to get to 1/2 way through the main box.
            {
                List <IPrimitive> subtractShapes = new List <IPrimitive>();

                for (int i = 0; i < 5; i++)
                {
                    subtractShapes.Add(new BoxShape(new Vector3(-.5, -.5 + i * .1, -.5), new Vector3(.5, -.4 + i * .1, .5), redMaterial));
                }

                IPrimitive subtractGroup = BoundingVolumeHierarchy.CreateNewHierachy(subtractShapes);
                Difference merge         = new Difference(box1X1, subtractGroup);

                IntersectInfo testInfo = merge.GetClosestIntersection(castRay);

                Assert.IsTrue(testInfo.isHit == true, "Found Hit : 5 Subtracts");
                //Assert.IsTrue(testInfo.closestHitObject == subtractBox, "Found Hit : 5 Subtracts");
                Assert.IsTrue(testInfo.hitPosition == new Vector3(0, 0, 0), "Hit position y = 0 : 5 Subtracts");
                Assert.IsTrue(testInfo.distanceToHit == 1, "Hit length = 1 : 5 Subtracts");
                Assert.IsTrue(testInfo.normalAtHit == -Vector3.UnitY, "Normal Correct : 5 Subtracts");
            }
#endif
        }
		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;
			}
		}