public static IPrimitive Convert(PolygonMesh.Mesh simpleMesh, MaterialAbstract partMaterial = null) { List<IPrimitive> renderCollection = new List<IPrimitive>(); if (partMaterial == null) { partMaterial = new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0); } int index = 0; Vector3[] triangle = new Vector3[3]; foreach (PolygonMesh.Face face in simpleMesh.Faces) { foreach (PolygonMesh.Vertex vertex in face.Vertices()) { triangle[index++] = vertex.Position; if (index == 3) { index = 0; renderCollection.Add(new TriangleShape(triangle[0], triangle[1], triangle[2], partMaterial)); } } } return BoundingVolumeHierarchy.CreateNewHierachy(renderCollection); }
public void PolygonHitTests() { SolidMaterial redStuff = new SolidMaterial(new ColorF(1, 0, 0), 0, 0, 2); { TriangleShape facingPositiveX = new TriangleShape(new Vector3(0, 1, -1), new Vector3(0, 0, 1), new Vector3(0, -1, -1), redStuff); IntersectInfo positiveXInfo = facingPositiveX.GetClosestIntersection(new Ray(new Vector3(1, 0, 0), new Vector3(-1, 0, 0))); Assert.IsTrue(positiveXInfo.HitPosition == new Vector3(0, 0, 0)); Assert.IsTrue(positiveXInfo.HitType == IntersectionType.FrontFace); Assert.IsTrue(positiveXInfo.ClosestHitObject == facingPositiveX); Assert.IsTrue(positiveXInfo.DistanceToHit == 1); IntersectInfo negativeXInfo = facingPositiveX.GetClosestIntersection(new Ray(new Vector3(-1, 0, 0), new Vector3(1, 0, 0))); Assert.IsTrue(negativeXInfo == null); } { TriangleShape facingNegativeX = new TriangleShape(new Vector3(0, -1, -1), new Vector3(0, 0, 1), new Vector3(0, 1, -1), redStuff); IntersectInfo positiveXInfo = facingNegativeX.GetClosestIntersection(new Ray(new Vector3(1, 0, 0), new Vector3(-1, 0, 0))); Assert.IsTrue(positiveXInfo == null); IntersectInfo negativeXInfo = facingNegativeX.GetClosestIntersection(new Ray(new Vector3(-1, 0, 0), new Vector3(1, 0, 0))); Assert.IsTrue(negativeXInfo.HitPosition == new Vector3(0, 0, 0)); Assert.IsTrue(negativeXInfo.HitType == IntersectionType.FrontFace); Assert.IsTrue(negativeXInfo.ClosestHitObject == facingNegativeX); Assert.IsTrue(negativeXInfo.DistanceToHit == 1); } }
public void PolygonHitTests() { SolidMaterial redStuff = new SolidMaterial(new RGBA_Floats(1, 0, 0), 0, 0, 2); { TriangleShape facingPositiveX = new TriangleShape(new Vector3(0, 1, -1), new Vector3(0, 0, 1), new Vector3(0, -1, -1), redStuff); IntersectInfo positiveXInfo = facingPositiveX.GetClosestIntersection(new Ray(new Vector3(1, 0, 0), new Vector3(-1, 0, 0))); Assert.IsTrue(positiveXInfo.hitPosition == new Vector3(0, 0, 0)); Assert.IsTrue(positiveXInfo.hitType == IntersectionType.FrontFace); Assert.IsTrue(positiveXInfo.closestHitObject == facingPositiveX); Assert.IsTrue(positiveXInfo.distanceToHit == 1); IntersectInfo negativeXInfo = facingPositiveX.GetClosestIntersection(new Ray(new Vector3(-1, 0, 0), new Vector3(1, 0, 0))); Assert.IsTrue(negativeXInfo == null); } { TriangleShape facingNegativeX = new TriangleShape(new Vector3(0, -1, -1), new Vector3(0, 0, 1), new Vector3(0, 1, -1), redStuff); IntersectInfo positiveXInfo = facingNegativeX.GetClosestIntersection(new Ray(new Vector3(1, 0, 0), new Vector3(-1, 0, 0))); Assert.IsTrue(positiveXInfo == null); IntersectInfo negativeXInfo = facingNegativeX.GetClosestIntersection(new Ray(new Vector3(-1, 0, 0), new Vector3(1, 0, 0))); Assert.IsTrue(negativeXInfo.hitPosition == new Vector3(0, 0, 0)); Assert.IsTrue(negativeXInfo.hitType == IntersectionType.FrontFace); Assert.IsTrue(negativeXInfo.closestHitObject == facingNegativeX); Assert.IsTrue(negativeXInfo.distanceToHit == 1); } }
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 AddPolygonTest() { double[][] points = new double[][] { new double[] { 0.0f, 1.0f, 0.0f }, new double[] { 0.943f, -0.333f, 0.0f }, new double[] { -0.471f, -0.333f, 0.8165f }, new double[] { -0.471f, -0.333f, -0.8165f } }; SolidMaterial redStuff = new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0); // setup a solid reflecting sphere renderCollection.Add(new TriangleShape(new Vector3(points[0]), new Vector3(points[1]), new Vector3(points[2]), redStuff)); renderCollection.Add(new TriangleShape(new Vector3(points[0]), new Vector3(points[3]), new Vector3(points[1]), redStuff)); renderCollection.Add(new TriangleShape(new Vector3(points[0]), new Vector3(points[2]), new Vector3(points[3]), redStuff)); renderCollection.Add(new TriangleShape(new Vector3(points[1]), new Vector3(points[3]), new Vector3(points[2]), redStuff)); }
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); }
public static IPrimitive Convert(PolygonMesh.MeshGroup meshGroup, MaterialAbstract partMaterial = null) { List<IPrimitive> renderCollection = new List<IPrimitive>(); SolidMaterial otherMaterial = new SolidMaterial(new RGBA_Floats(.1, .2, .9), .01, 0.0, 2.0); if (partMaterial == null) { partMaterial = new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0); } int index = 0; Vector3[] triangle = new Vector3[3]; foreach (PolygonMesh.Mesh mesh in meshGroup.Meshes) { int materialIntdex = MeshMaterialData.Get(mesh).MaterialIndex; foreach (PolygonMesh.Face face in mesh.Faces) { foreach (PolygonMesh.Vertex vertex in face.Vertices()) { triangle[index++] = vertex.Position; if (index == 3) { index = 0; if (materialIntdex == 1) { renderCollection.Add(new TriangleShape(triangle[0], triangle[1], triangle[2], partMaterial)); } else { renderCollection.Add(new TriangleShape(triangle[0], triangle[1], triangle[2], otherMaterial)); } } } } } return BoundingVolumeHierarchy.CreateNewHierachy(renderCollection); }
private void AddAxisMarker() { int count = 10; double size = .1; for (int i = 1; i < count + 1; i++) { RGBA_Floats xColor = new RGBA_Floats(1, i / (double)count, i / (double)count); SolidMaterial xMaterial = new SolidMaterial(xColor, 0, 0.0, 2.0); BoxShape xBox = new BoxShape(new Vector3(i * size, 0, 0), new Vector3(i * size + size, size, size), xMaterial); renderCollection.Add(xBox); RGBA_Floats yColor = new RGBA_Floats(i / (double)count, 1, i / (double)count); SolidMaterial yMaterial = new SolidMaterial(yColor, 0, 0.0, 2.0); BoxShape yBox = new BoxShape(new Vector3(0, i * size, 0), new Vector3(size, i * size + size, size), yMaterial); //yBox.Transform.Position += new Vector3D(1, 1, 1); renderCollection.Add(yBox); RGBA_Floats zColor = new RGBA_Floats(i / (double)count, i / (double)count, 1); SolidMaterial zMaterial = new SolidMaterial(zColor, 0, 0.0, 2.0); BoxShape zBox = new BoxShape(new Vector3(0, 0, i * size), new Vector3(size, size, i * size + size), zMaterial); renderCollection.Add(zBox); } }
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 void DifferenceTestsForBox() { SolidMaterial redMaterial = new SolidMaterial(RGBA_Floats.Red, 0, 0, 0); SolidMaterial blueMaterial = new SolidMaterial(RGBA_Floats.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 }
private void CalculateIntersectCostsAndSaveToFile() { int numInterations = 5000000; AxisAlignedBoundingBox referenceCostObject = new AxisAlignedBoundingBox(new Vector3(-.5, -.5, -.5), new Vector3(.5, .5, .5)); Stopwatch timer = new Stopwatch(); Vector3 accumulation = new Vector3(); timer.Start(); for (int i = 0; i < numInterations; i++) { accumulation += GetRandomIntersectingRay().directionNormal; } long notIntersectStuff = timer.ElapsedMilliseconds; timer.Restart(); for (int i = 0; i < numInterations; i++) { GetRandomIntersectingRay().Intersection(referenceCostObject); } long referenceMiliseconds = timer.ElapsedMilliseconds; SolidMaterial material = new SolidMaterial(RGBA_Floats.Black, 0, 0, 1); long sphereMiliseconds = CalculateIntersectCostsForItem(new SphereShape(new Vector3(), .5, material), numInterations); long cylinderMiliseconds = CalculateIntersectCostsForItem(new CylinderShape(.5, 1, material), numInterations); long boxMiliseconds = CalculateIntersectCostsForItem(new BoxShape(new Vector3(-.5, -.5, -.5), new Vector3(.5, .5, .5), material), numInterations); long planeMiliseconds = CalculateIntersectCostsForItem(new PlaneShape(new Vector3(0, 0, 1), 0, material), numInterations); BaseShape triangleTest = new TriangleShape(new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 0), material); long triangleMiliseconds = CalculateIntersectCostsForItem(triangleTest, numInterations); System.IO.File.WriteAllText("Cost Of Primitive.txt", "Cost of Primitives" + "\r\n" + numInterations.ToString("N0") + " intersections per primitive." + "\r\nTest Overhead: " + notIntersectStuff.ToString() + GetStringForFile("AABB", referenceMiliseconds, notIntersectStuff) + GetStringForFile("Sphere", sphereMiliseconds, notIntersectStuff) + GetStringForFile("Cylider", cylinderMiliseconds, notIntersectStuff) + GetStringForFile("Box", boxMiliseconds, notIntersectStuff) + GetStringForFile("Plane", planeMiliseconds, notIntersectStuff) + GetStringForFile("Triangle", triangleMiliseconds, notIntersectStuff) ); }
public static IPrimitive ConvertUnoptomized(PolygonMesh.Mesh simpleMesh) { List<IPrimitive> renderCollection = new List<IPrimitive>(); //SolidMaterial redStuff = new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0); SolidMaterial mhBlueStuff = new SolidMaterial(new RGBA_Floats(0, .32, .58), .01, 0.0, 2.0); int index = 0; Vector3[] triangle = new Vector3[3]; //PolygonMesh.Mesh simpleMesh = PolygonMesh.Processors.StlProcessing.Load("complex.stl"); //PolygonMesh.Mesh simpleMesh = PolygonMesh.Processors.StlProcessing.Load("Spider With Base.stl"); foreach (PolygonMesh.Face face in simpleMesh.Faces) { foreach (PolygonMesh.Vertex vertex in face.Vertices()) { triangle[index++] = vertex.Position; if (index == 3) { index = 0; renderCollection.Add(new TriangleShape(triangle[0], triangle[1], triangle[2], mhBlueStuff)); } } } return new UnboundCollection(renderCollection); }
private void AddPolygonTest() { double[][] points = new double[][] { new double[] {0.0f, 1.0f, 0.0f}, new double[] {0.943f, -0.333f, 0.0f}, new double[] {-0.471f, -0.333f, 0.8165f}, new double[] {-0.471f, -0.333f, -0.8165f} }; SolidMaterial redStuff = new SolidMaterial(new RGBA_Floats(.9, .2, .1), .01, 0.0, 2.0); // setup a solid reflecting sphere renderCollection.Add(new TriangleShape(new Vector3(points[0]), new Vector3(points[1]), new Vector3(points[2]), redStuff)); renderCollection.Add(new TriangleShape(new Vector3(points[0]), new Vector3(points[3]), new Vector3(points[1]), redStuff)); renderCollection.Add(new TriangleShape(new Vector3(points[0]), new Vector3(points[2]), new Vector3(points[3]), redStuff)); renderCollection.Add(new TriangleShape(new Vector3(points[1]), new Vector3(points[3]), new Vector3(points[2]), redStuff)); }
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 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<IPrimitive> subtractShapes = new List<IPrimitive>(); SolidMaterial material = new SolidMaterial(RGBA_Floats.Red, 0, 0, 0); #if false // 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 IPrimitive subtractGroup = BoundingVolumeHierarchy.CreateNewHierachy(subtractShapes); Difference merge = new Difference(box1, subtractGroup); renderCollection.Add(merge); }