// == 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> /// Make sure any of the previous model settings are removed. /// </summary> public void ResetForNewModel() { modelAsset = new DiabolicalModel(debugShapes); lastLargerBound = -1; lastSmallerBound = -1; }
public DiabolicalManager(MainForm parent, Shapes sharedShapes) { form = parent; debugShapes = sharedShapes; modelAsset = new DiabolicalModel(debugShapes); }
private void ProcessData(string[] source, string fileName) { DiabolicalSourceData input = new DiabolicalSourceData(fileName, source); string directory = Path.GetDirectoryName(input.Identity); // == The model string filepath = Path.Combine(directory, input.ModelFilename); lastLoaded3DModelFile = filepath; if (input.EffectType == GlobalSettings.effectTypeAnimated) { form.LoadModel(true, filepath, input.RotateX, input.RotateY, input.RotateZ); } else { form.LoadModel(false, filepath, input.RotateX, input.RotateY, input.RotateZ); } if (form.CurrentModel != null) { // Create the class modelAsset = new DiabolicalModel(debugShapes); modelAsset.BuildModelAsset( input.ModelType, ParseData.StandardiseFolderCharacters(input.ModelFilename), input.EffectType, form.CurrentModel, CalculateBoundsFromModel(form.CurrentModel), input.RotateX, input.RotateY, input.RotateZ, input.Options); } // Change which menu items are enabled based on the loaded model type form.UpdateMenuItemVisibility(); }
/// <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; } } }
private static BoundingSphere SphereToEncompassSmaller(int larger, DiabolicalModel aModel) { BoundingSphere result = new BoundingSphere(); Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); // Get the min and max corners of the spheres as if a cube for (int s = 0; s < aModel.LargerBounds[larger].IDs.Count; s++) { Vector3 lowCorner = aModel.SmallerBounds[aModel.LargerBounds[larger].IDs[s]].CentreInObjectSpace; Vector3 highCorner = aModel.SmallerBounds[aModel.LargerBounds[larger].IDs[s]].CentreInObjectSpace; float radius = aModel.SmallerBounds[aModel.LargerBounds[larger].IDs[s]].Sphere.Radius; lowCorner.X -= radius; lowCorner.Y -= radius; lowCorner.Z -= radius; highCorner.X += radius; highCorner.Y += radius; highCorner.Z += radius; min = Vector3.Min(min, lowCorner); max = Vector3.Max(max, highCorner); } // Centre result.Center = (min + max) / 2f; // Radius = half the diagonal result.Radius = Vector3.Distance(min, max) * 0.5f; return result; }
/// <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)); } } }
/// <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); } } } }
// Create the larger bounding spheres private static List<StructureSphere> FillWithSmallerBounds(List<BoundingBox> boxes, DiabolicalModel aModel) { List<StructureSphere> spheres = new List<StructureSphere>(boxes.Count); // 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)); } // Loop through the smaller spheres and fit them in to the larger ones // The result of this will have overlapping smaller spheres for (int m = 0; m < aModel.SmallerBounds.Count; m++) { for (int s = 0; s < spheres.Count; s++) { // Is the smaller one in or touching the larger one if (spheres[s].Sphere.Contains(aModel.SmallerBounds[m].Sphere) != ContainmentType.Disjoint) { // Store the index to the smaller sphere spheres[s].IDs.Add(m); } } } // 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]); } } return results; }
// 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; }