private const double MOVEPERSTEPPERCENT = 1d;		// this seems to be stable with 100%, if nessassary, drop it down a bit so that parts don't move as far each step

        #endregion

        //TODO: Make a better version, probably a combination of pulling in and separating
        public static void PullInCrude(out bool changed, PartSeparator_Part[] parts)
        {
            // Figure out the max radius
            double[] sizes = parts.Select(o => (o.Size.X + o.Size.Y + o.Size.Z) / 3d).ToArray();
            double largestPart = sizes.Max();
            double maxRadius = largestPart * 8d;
            double maxRadiusSquare = maxRadius * maxRadius;

            Point3D center = Math3D.GetCenter(parts.Select(o => Tuple.Create(o.Position, o.Mass)).ToArray());

            changed = false;

            for (int cntr = 0; cntr < parts.Length; cntr++)
            {
                Vector3D offset = parts[cntr].Position - center;		//NOTE: This is just going to the center of the part, it's not considering the extents of the part (this method IS called crude)
                if (offset.LengthSquared < maxRadiusSquare)
                {
                    continue;
                }

                // Pull it straight in
                double difference = offset.Length - maxRadius;
                offset.Normalize();
                offset *= difference * -1d;

                parts[cntr].Position += offset;		//NOTE: I'm not going to change the center of mass

                changed = true;
            }
        }
Example #2
0
                // Mineral
                public AsteroidOrMineralDefinition(PartSeparator_Part part, MineralDNA mineralDefinition)
                {
                    this.IsAsteroid = false;
                    this.Part = part;

                    this.MineralDefinition = mineralDefinition;

                    this.AsteroidTriangles = null;
                    this.AsteroidRadius = 0;
                }
Example #3
0
                // Asteroid
                public AsteroidOrMineralDefinition(PartSeparator_Part part, ITriangleIndexed[] asteroidTriangles, double asteroidRadius)
                {
                    this.IsAsteroid = true;
                    this.Part = part;

                    this.AsteroidTriangles = asteroidTriangles;
                    this.AsteroidRadius = asteroidRadius;

                    this.MineralDefinition = null;
                }
        public static CollisionHull[] Separate(out bool changed, PartSeparator_Part[] parts, World world)
        {
            changed = false;

            bool[] hasMoved = new bool[parts.Length];		// defaults to false

            CollisionHull[] hulls = parts.
                Select(o => o.CreateCollisionHull(world)).
                ToArray();

            // Move the parts
            for (int cntr = 0; cntr < MAXSTEPS; cntr++)		// execution will break out of this loop early if parts are no longer intersecting
            {
                Intersection[] intersections = GetIntersections(parts, hulls, hasMoved, world);
                if (intersections.Length == 0)
                {
                    break;
                }

                DoStep(intersections, parts, hasMoved);

                changed = true;
            }

            // Ensure hulls are synced
            for (int cntr = 0; cntr < parts.Length; cntr++)
            {
                if (hasMoved[cntr])
                {
                    hulls[cntr].Dispose();
                    hulls[cntr] = parts[cntr].CreateCollisionHull(world);
                }
            }

            // Exit Function
            return hulls;
        }
        private static void DoStep_Move(PartSeparator_Part[] parts, SortedList<int, List<Tuple<Vector3D?, Quaternion?>>> moves)
        {
            foreach (int partIndex in moves.Keys)
            {
                foreach (var move in moves[partIndex])
                {
                    if (move.Item1 != null)
                    {
                        parts[partIndex].Position += move.Item1.Value;
                    }

                    if (move.Item2 != null)
                    {
                        parts[partIndex].Orientation = parts[partIndex].Orientation.RotateBy(move.Item2.Value);
                    }
                }
            }
        }
        private static void DoStep(Intersection[] intersections, PartSeparator_Part[] parts, bool[] hasMoved)
        {
            SortedList<int, List<Tuple<Vector3D?, Quaternion?>>> moves = new SortedList<int, List<Tuple<Vector3D?, Quaternion?>>>();

            double crazyScale = 1d;
            if (intersections.Length > parts.Length)
            {
                // If there are lots of parts intersecting each other at once, this will move parts too far (because it does a scan of all intersections,
                // then moves the parts using the sum of all intersections).  This is a crude attempt to soften that effect
                crazyScale = Convert.ToDouble(parts.Length) / Convert.ToDouble(intersections.Length);
            }

            // Shoot through all the part pairs
            foreach (var intersection in intersections)
            {
                double mass1 = parts[intersection.Index1].Mass;
                double mass2 = parts[intersection.Index2].Mass;
                double totalMass = mass1 + mass2;

                double sumPenetration = intersection.Intersections.Sum(o => o.PenetrationDistance);		// there really needs to be a joke here.  Something about seven inches at a time
                double avgPenetration = sumPenetration / Convert.ToDouble(intersection.Intersections.Length);

                Vector3D direction = (parts[intersection.Index2].Position - parts[intersection.Index1].Position).ToUnit();

                double sizeScale = MOVEPERSTEPPERCENT * (1d / Convert.ToDouble(intersection.Intersections.Length));

                // Shoot through the intersecting points between these two parts
                foreach (var intersectPoint in intersection.Intersections)
                {
                    // The sum of scaledDistance needs to add up to avgPenetration
                    double percentDistance = intersectPoint.PenetrationDistance / sumPenetration;
                    double scaledDistance = avgPenetration * percentDistance;

                    // May not want to move the full distance in one step
                    scaledDistance *= MOVEPERSTEPPERCENT * crazyScale;

                    double distance1 = scaledDistance * ((totalMass - mass1) / totalMass);
                    double distance2 = scaledDistance * ((totalMass - mass2) / totalMass);

                    // Part1
                    Vector3D translation, torque;
                    Vector3D offset1 = intersectPoint.ContactPoint - parts[intersection.Index1].Position;
                    Math3D.SplitForceIntoTranslationAndTorque(out translation, out torque, offset1, direction * (-1d * distance1));
                    DoStep_AddForce(moves, intersection.Index1, translation, DoStep_Rotate(torque, intersection.AvgSize1, sizeScale));		// don't use the full size, or the rotation won't even be noticable

                    // Part2
                    Vector3D offset2 = intersectPoint.ContactPoint - parts[intersection.Index2].Position;
                    Math3D.SplitForceIntoTranslationAndTorque(out translation, out torque, offset2, direction * distance2);
                    DoStep_AddForce(moves, intersection.Index2, translation, DoStep_Rotate(torque, intersection.AvgSize2, sizeScale));
                }
            }

            // Apply the movements
            DoStep_Move(parts, moves);

            // Remember which parts were modified
            foreach (int index in moves.Keys)
            {
                hasMoved[index] = true;
            }
        }
        /// <summary>
        /// This finds intersections between all the hulls
        /// </summary>
        private static Intersection[] GetIntersections(PartSeparator_Part[] parts, CollisionHull[] hulls, bool[] hasMoved, World world)
        {
            List<Intersection> retVal = new List<Intersection>();

            // Compare each hull to the others
            for (int outer = 0; outer < hulls.Length - 1; outer++)
            {
                double? sizeOuter = null;

                for (int inner = outer + 1; inner < hulls.Length; inner++)
                {
                    // Rebuild hulls if nessessary
                    if (hasMoved[outer])
                    {
                        hulls[outer].Dispose();
                        hulls[outer] = parts[outer].CreateCollisionHull(world);
                        hasMoved[outer] = false;
                    }

                    if (hasMoved[inner])
                    {
                        hulls[inner].Dispose();
                        hulls[inner] = parts[inner].CreateCollisionHull(world);
                        hasMoved[inner] = false;
                    }

                    // Get intersecting points
                    CollisionHull.IntersectionPoint[] points = hulls[outer].GetIntersectingPoints_HullToHull(100, hulls[inner], 0);

                    if (points != null && points.Length > 0)
                    {
                        sizeOuter = sizeOuter ?? (parts[outer].Size.X + parts[outer].Size.X + parts[outer].Size.X) / 3d;
                        double sizeInner = (parts[inner].Size.X + parts[inner].Size.X + parts[inner].Size.X) / 3d;

                        double sumSize = sizeOuter.Value + sizeInner;
                        double minSize = sumSize * IGNOREDEPTHPERCENT;

                        // Filter out the shallow penetrations
                        //TODO: May need to add the lost distance to the remaining intersections
                        points = points.Where(o => o.PenetrationDistance > minSize).ToArray();

                        if (points != null && points.Length > 0)
                        {
                            retVal.Add(new Intersection(outer, inner, sizeOuter.Value, sizeInner, points));
                        }
                    }
                }
            }

            // Exit Function
            return retVal.ToArray();
        }
Example #8
0
            private AsteroidOrMineralDefinition[] DetermineDestroyedChildrenMinerals(HullVoronoiExploder_Response shards, double overDamage, Vector3D parentRadius, double minChildRadius, Func<double, ITriangleIndexed[], double> getMassByRadius, Func<double, MineralDNA[]> getMineralsByDestroyedMass)
            {
                const double MAXOVERDMG = 13;       // overdamage of 1 is the smallest value (the asteroid was barely destroyed).  Larger values are overkill, and the asteroid becomes more fully destroyed

                if (shards == null || shards.Shards == null || shards.Shards.Length == 0)
                {
                    // There was problem, so just pop out some minerals
                    return DetermineDestroyedChildrenMinerals_Minerals(parentRadius, getMassByRadius, getMineralsByDestroyedMass);
                }

                Random rand = StaticRandom.GetRandomForThread();

                #region calculate volumes

                var shardVolumes = shards.Shards.
                    Select(o =>
                        {
                            Vector3D radius = GetEllipsoidRadius(o.Hull_Centered);
                            return new { Radius = radius, Volume = GetEllipsoidVolume(radius) };
                        }).
                    ToArray();

                // Figure out how much volume should permanently be destroyed
                double parentVolume = GetEllipsoidVolume(parentRadius);
                double volumeToDestroy = GetVolumeToDestroy(parentVolume, overDamage, MAXOVERDMG);

                #endregion

                double destroyedVolume = 0;
                bool[] shouldSelfDestruct = new bool[shards.Shards.Length];

                #region detect too small

                // Get rid of any that are too small
                //TODO: Also get rid of any that are too thin
                for (int cntr = 0; cntr < shards.Shards.Length; cntr++)
                {
                    if (shards.Shards[cntr].Radius < minChildRadius)
                    {
                        shouldSelfDestruct[cntr] = true;
                        destroyedVolume += shardVolumes[cntr].Volume;
                    }
                }

                #endregion

                if (destroyedVolume < volumeToDestroy)
                {
                    #region remove more

                    // Find the shards that could be removed
                    var candidates = Enumerable.Range(0, shards.Shards.Length).
                        Select(o => new
                        {
                            Index = o,
                            Shard = shards.Shards[o],
                            Volume = shardVolumes[o]
                        }).
                        Where(o => !shouldSelfDestruct[o.Index] && o.Volume.Volume < volumeToDestroy - destroyedVolume).
                        OrderBy(o => o.Volume.Volume).
                        ToList();

                    while (candidates.Count > 0 && destroyedVolume < volumeToDestroy)
                    {
                        // Figure out which to self destruct (the rand power will favor inicies closer to zero)
                        int index = UtilityCore.GetIndexIntoList(rand.NextPow(2), candidates.Count);

                        // Remove it
                        shouldSelfDestruct[candidates[index].Index] = true;
                        destroyedVolume += candidates[index].Volume.Volume;
                        candidates.RemoveAt(index);

                        // Remove the items at the end that are now too large
                        index = candidates.Count - 1;
                        while (index >= 0)
                        {
                            if (candidates[index].Volume.Volume < volumeToDestroy - destroyedVolume)
                            {
                                break;      // it's sorted, so the rest will also be under
                            }
                            else
                            {
                                candidates.RemoveAt(index);
                                index--;
                            }
                        }
                    }

                    #endregion
                }

                #region distribute minerals

                // Figure out the mineral value of the destroyed volume
                MineralDNA[] mineralDefinitions = null;
                if (destroyedVolume > 0 && getMineralsByDestroyedMass != null)
                {
                    double inferredRadius = GetEllipsoidRadius(destroyedVolume);
                    double destroyedMass = getMassByRadius(inferredRadius, null);

                    if (destroyedMass > 0)
                    {
                        mineralDefinitions = getMineralsByDestroyedMass(destroyedMass);
                    }
                }

                // Figure out which of the temp asteroids should contain minerals
                var packedMinerals = new Tuple<int, MineralDNA[]>[0];

                if (mineralDefinitions != null && mineralDefinitions.Length > 0)
                {
                    int[] destroyedIndicies = Enumerable.Range(0, shouldSelfDestruct.Length).
                        Where(o => shouldSelfDestruct[o]).
                        ToArray();

                    packedMinerals = DistributeMinerals(mineralDefinitions, destroyedIndicies);
                }

                #endregion

                #region final array

                AsteroidOrMineralDefinition[] retVal = new AsteroidOrMineralDefinition[shards.Shards.Length];

                for (int cntr = 0; cntr < retVal.Length; cntr++)
                {
                    Vector3D velocity = new Vector3D(0, 0, 0);
                    if (shards.Velocities != null && shards.Velocities.Length == shards.Shards.Length)
                    {
                        velocity = shards.Velocities[cntr];
                    }

                    //NOTE: Only position is needed (The first attempt created random asteroids and pulled them apart.  This second attempt
                    //doesn't need to pull them apart)
                    PartSeparator_Part part = new PartSeparator_Part(new[] { new Point3D() }, 0, shards.Shards[cntr].Center_ParentCoords, Quaternion.Identity);

                    //MineralDNA[] mineralsAfter = packedMinerals?.FirstOrDefault(o => o.Item1 == cntr)?.Item2;
                    MineralDNA[] mineralsAfter = null;
                    if (packedMinerals != null)
                    {
                        var found = packedMinerals.FirstOrDefault(o => o.Item1 == cntr);
                        if (found != null)
                        {
                            mineralsAfter = found.Item2;
                        }
                    }

                    retVal[cntr] = new AsteroidOrMineralDefinition(part, shards.Shards[cntr].Hull_Centered, shards.Shards[cntr].Radius, velocity, shouldSelfDestruct[cntr], mineralsAfter);
                }

                #endregion

                return retVal;
            }
Example #9
0
                // Mineral
                public AsteroidOrMineralDefinition(PartSeparator_Part part, MineralDNA mineralDefinition, Vector3D velocity)
                {
                    this.IsAsteroid = false;
                    this.Part = part;

                    this.MineralDefinition = mineralDefinition;

                    this.Velocity = velocity;

                    this.AsteroidTriangles = null;
                    this.AsteroidRadius = 0;
                    this.ShouldAsteroidSelfDestruct = false;
                }
Example #10
0
                // Asteroid
                public AsteroidOrMineralDefinition(PartSeparator_Part part, ITriangleIndexed[] asteroidTriangles, double asteroidRadius, Vector3D velocity, bool shouldSelfDestruct, MineralDNA[] mineralsAfterSelfDestruct)
                {
                    this.IsAsteroid = true;
                    this.Part = part;

                    this.AsteroidTriangles = asteroidTriangles;
                    this.AsteroidRadius = asteroidRadius;
                    this.ShouldAsteroidSelfDestruct = shouldSelfDestruct;
                    this.MineralsAfterSelfDestruct = mineralsAfterSelfDestruct;

                    this.Velocity = velocity;

                    this.MineralDefinition = null;
                }
Example #11
0
            /// <summary>
            /// Defines the child asteroid shapes, then finds positions/orientations for all child asteroids and minerals (makes
            /// sure nothing overlaps)
            /// </summary>
            private AsteroidOrMineralDefinition[] PositionChildAsteroidsAndMinerals(double[] asteroidVolumes, MineralDNA[] mineralDefinitions)
            {
                const double FOURTHIRDSPI = 4d / 3d * Math.PI;
                const double ONETHRID = 1d / 3d;

                double positionRange = _radiusMin * .05;

                #region Asteroids

                PartSeparator_Part[] asteroidParts = new PartSeparator_Part[asteroidVolumes.Length];
                ITriangleIndexed[][] asteroidTriangles = new ITriangleIndexed[asteroidVolumes.Length][];
                double[] asteroidRadii = new double[asteroidVolumes.Length];

                for (int cntr = 0; cntr < asteroidVolumes.Length; cntr++)
                {
                    // r^3=v/(4/3pi)
                    asteroidRadii[cntr] = Math.Pow(asteroidVolumes[cntr] / FOURTHIRDSPI, ONETHRID);

                    asteroidTriangles[cntr] = GetHullTriangles_Initial(asteroidRadii[cntr]);

                    double currentMass = _getMassByRadius(asteroidRadii[cntr], asteroidTriangles[cntr]);

                    asteroidParts[cntr] = new PartSeparator_Part(asteroidTriangles[cntr][0].AllPoints, currentMass, Math3D.GetRandomVector_Spherical(positionRange).ToPoint(), Math3D.GetRandomRotation());
                }

                #endregion
                #region Minerals

                PartSeparator_Part[] mineralParts = new PartSeparator_Part[mineralDefinitions.Length];

                for (int cntr = 0; cntr < mineralDefinitions.Length; cntr++)
                {
                    //NOTE: Copied logic from Mineral that gets collision points and mass

                    // Points
                    Point3D[] mineralPoints = UtilityWPF.GetPointsFromMesh((MeshGeometry3D)_sharedVisuals.Value.GetMineralMesh(mineralDefinitions[cntr].MineralType));

                    // Mass
                    double mass = mineralDefinitions[cntr].Density * mineralDefinitions[cntr].Volume;

                    // Store it
                    mineralParts[cntr] = new PartSeparator_Part(mineralPoints, mass, Math3D.GetRandomVector_Spherical(positionRange).ToPoint(), Math3D.GetRandomRotation());
                }

                #endregion

                #region Pull Apart

                if (asteroidParts.Length + mineralParts.Length > 1)
                {
                    PartSeparator_Part[] allParts = UtilityCore.ArrayAdd(asteroidParts, mineralParts);

                    bool dummy;
                    CollisionHull[] hulls = PartSeparator.Separate(out dummy, allParts, _world);

                    foreach (CollisionHull hull in hulls)
                    {
                        hull.Dispose();
                    }
                }

                #endregion

                #region Build Return

                List<AsteroidOrMineralDefinition> retVal = new List<AsteroidOrMineralDefinition>();

                for (int cntr = 0; cntr < asteroidParts.Length; cntr++)
                {
                    retVal.Add(new AsteroidOrMineralDefinition(asteroidParts[cntr], asteroidTriangles[cntr], asteroidRadii[cntr]));
                }

                for (int cntr = 0; cntr < mineralParts.Length; cntr++)
                {
                    retVal.Add(new AsteroidOrMineralDefinition(mineralParts[cntr], mineralDefinitions[cntr]));
                }

                #endregion

                return retVal.ToArray();
            }
Example #12
0
            private AsteroidOrMineralDefinition[] PositionMinerals(MineralDNA[] minerals, double? maxRandomVelocity)
            {
                double positionRange = _radiusMin * .05;

                #region build parts

                PartSeparator_Part[] parts = new PartSeparator_Part[minerals.Length];

                for (int cntr = 0; cntr < minerals.Length; cntr++)
                {
                    //NOTE: Copied logic from Mineral that gets collision points and mass

                    // Points
                    Point3D[] mineralPoints = UtilityWPF.GetPointsFromMesh((MeshGeometry3D)_sharedVisuals.Value.GetMineralMesh(minerals[cntr].MineralType));

                    // Mass
                    double mass = minerals[cntr].Density * minerals[cntr].Volume;

                    // Store it
                    parts[cntr] = new PartSeparator_Part(mineralPoints, mass, Math3D.GetRandomVector_Spherical(positionRange).ToPoint(), Math3D.GetRandomRotation());
                }

                #endregion

                #region pull apart

                if (parts.Length > 1)
                {
                    bool dummy;
                    CollisionHull[] hulls = PartSeparator.Separate(out dummy, parts, _world);

                    foreach (CollisionHull hull in hulls)
                    {
                        hull.Dispose();
                    }
                }

                #endregion

                #region build return

                List<AsteroidOrMineralDefinition> retVal = new List<AsteroidOrMineralDefinition>();

                for (int cntr = 0; cntr < parts.Length; cntr++)
                {
                    Vector3D velocity = new Vector3D(0, 0, 0);
                    if (maxRandomVelocity != null)
                    {
                        velocity = Math3D.GetRandomVector_Spherical(maxRandomVelocity.Value);
                    }

                    retVal.Add(new AsteroidOrMineralDefinition(parts[cntr], minerals[cntr], velocity));
                }

                #endregion

                return retVal.ToArray();
            }