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))); }
/// <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); }
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); }
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)); }
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); } }
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; } }