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