// == Create the smaller spheres first then add the larger spheres round them // The larger spheres are created from a number of smaller boundingBoxes // The larger spheres are centred on the middle of the grid of the smaller boxes. // Create all the smaller boxes to be an exact division of roughly the size of // the smaller box we want. // Fill those smaller boxes with triangles // Remove any that are empty // Calculate the positions of the larger spheres // Convert the smaller boxes to sphere // Work out which smaller spheres fit in to which larger spheres // Remove any empty larger spheres. // The result is likely to have overlapping smaller spheres that stick out // of the larger spheres. // This overlap sometimes causes undesirable bouncing when colliding. // There is a separate function that should be run after the smaller spheres // have been edited that better fits the larger spheres with no overlap. public static void CreateModelFittedBounds(DiabolicalModel aModel, float smallerBoundWidth, float largerBoundMultiple, MainForm form) { form.ShowStatus("Exposing Triangles..."); // The model and spheres need to be in object space not world space aModel.ExposeVertices(); form.ShowStatus("Overall Bounds..."); BoundingBox outsideBox = aModel.CalculateBoundBox(0.01f, false); // Get a lot of small boxes to fill the model bounds float boxWidth = BestFitSmallWidth(LongestEdgeXorZ(outsideBox), smallerBoundWidth, largerBoundMultiple); form.ShowStatus("Bounding grid..."); List<BoundingBox> boxes = FillWithBoxes(outsideBox, boxWidth); aModel.SmallerBounds = CreateSpheresFilledWithTriangles(boxes, aModel, form); // Work out the larger cubes based on the multiple of smaller ones we want to fit boxWidth *= largerBoundMultiple; boxes = FillWithBoxes(outsideBox, boxWidth); aModel.LargerBounds = FillWithSmallerBounds(boxes, aModel); }
/// <summary> /// This is run in conjunction with other optimisations. /// Call this after the smaller bounds have been edited just before saving the model. /// Optimisation Includes: /// - Make sure the larger bounds fully contain all the smaller spheres /// Any smaller bound overlapping can cause undesirable bouncing collisions. /// - Removes any empty larger bounds /// </summary> public static void OptimiseModelBounds(DiabolicalModel aModel, MainForm form) { if (aModel.LargerBounds.Count < 1) { return; } form.ShowStatus("Optimise Larger Bounds..."); // Work backwards just in case any bounds are removed for (int b = aModel.LargerBounds.Count - 1; b >= 0; b--) { if (aModel.LargerBounds[b].IDs.Count < 1) { // If empty remove aModel.LargerBounds.RemoveAt(b); } else { // Fit round the smaller spheres as efficiently as possible without overlap aModel.LargerBounds[b].Sphere = SphereToEncompassSmaller(b, aModel); aModel.LargerBounds[b].CentreInObjectSpace = aModel.LargerBounds[b].Sphere.Center; } } }
/// <summary> /// Used for filling and refilling spheres. /// The triangle ID is added to the list within each smaller sphere. /// </summary> private static void FillWithTriangles(List<StructureSphere> spheres, DiabolicalModel aModel, MainForm form) { int status = 0; Triangle tri = new Triangle(); bool result = false; BoundingSphere bound; int count = aModel.CountTriangles(); for (int t = 0; t < count; t++) { status++; if (status > 33) { status = 0; form.ShowStatus("Fill with triangle: " + t.ToString()); } aModel.GetTriangle(ref t, ref tri); for (int s = 0; s < spheres.Count; s++) { bound = spheres[s].Sphere; tri.Intersects(ref bound, out result); if (result) { spheres[s].IDs.Add(t); } } } }
/// <summary> /// Optimise the size of the spheres. /// </summary> private static void OptimiseFittedSphere(List<StructureSphere> spheres, DiabolicalModel aModel, MainForm form) { form.ShowStatus("Optimise Sphere Sizes..."); Triangle tri = new Triangle(); List<Vector3> points = new List<Vector3>(); for (int s = 0; s < spheres.Count; s++) { points.Clear(); for (int t = 0; t < spheres[s].IDs.Count; t++) { aModel.GetTriangle(ref t, ref tri); points.AddRange(tri.PointsInsideSphere(spheres[s].Sphere)); } if (points.Count > 0) { spheres[s].RePosition(SmallestToFit(points)); } } }
// Create the smallest bounding spheres private static List<StructureSphere> CreateSpheresFilledWithTriangles(List<BoundingBox> boxes, DiabolicalModel aModel, MainForm form) { List<StructureSphere> spheres = new List<StructureSphere>(boxes.Count); form.ShowStatus("Convert To Spheres..."); for (int b = 0; b < boxes.Count; b++) { BoundingSphere ball = BoundingSphere.CreateFromBoundingBox(boxes[b]); spheres.Add(new StructureSphere(ball.Center, ball.Radius)); } form.ShowStatus("Fill With Triangles..."); FillWithTriangles(spheres, aModel, form); form.ShowStatus("Remove Empty Spheres..."); // Easier to add those that have something in them rather than remove from a list List<StructureSphere> results = new List<StructureSphere>(); for (int r = 0; r < spheres.Count; r++) { // Add any that have triangles in them if (spheres[r].IDs.Count > 0) { results.Add(spheres[r]); } } OptimiseFittedSphere(results, aModel, form); return results; }