Exemplo n.º 1
0
        private static Tuple <HullVoronoiExploder_Response, bool> SplitHull(ITriangleIndexed[] convexHull, Tuple <Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options, double aabbLen)
        {
            #region intersect with the hull

            var hits = shots.
                       Select(o => new HullVoronoiExploder_ShotHit()
            {
                Shot = o,
                Hit  = GetHit(convexHull, o.Item1, o.Item2, o.Item3),
            }).
                       Where(o => o.Hit != null).
                       ToArray();

            if (hits.Length == 0)
            {
                return(null);
            }

            #endregion

            #region voronoi

            Point3D[] controlPoints = GetVoronoiCtrlPoints(hits, convexHull, options.MinCount, options.MaxCount, aabbLen);

            VoronoiResult3D voronoi = Math3D.GetVoronoi(controlPoints, true);
            if (voronoi == null)
            {
                return(null);
            }

            #endregion

            // There is enough to start populating the response
            HullVoronoiExploder_Response retVal = new HullVoronoiExploder_Response()
            {
                Hits          = hits,
                ControlPoints = controlPoints,
                Voronoi       = voronoi,
            };

            #region intersect voronoi and hull

            // Intersect
            Tuple <int, ITriangleIndexed[]>[] shards = null;
            try
            {
                shards = Math3D.GetIntersection_Hull_Voronoi_full(convexHull, voronoi);
            }
            catch (Exception)
            {
                return(Tuple.Create(retVal, false));
            }

            if (shards == null)
            {
                return(Tuple.Create(retVal, false));
            }

            // Smooth
            if (options.ShouldSmoothShards)
            {
                shards = shards.
                         Select(o => Tuple.Create(o.Item1, Asteroid.SmoothTriangles(o.Item2))).
                         ToArray();
            }

            // Validate
            shards = shards.
                     Where(o => o.Item2 != null && o.Item2.Length >= 3).
                     Where(o =>
            {
                Vector3D firstNormal = o.Item2[0].NormalUnit;
                return(o.Item2.Skip(1).Any(p => !Math.Abs(Vector3D.DotProduct(firstNormal, p.NormalUnit)).IsNearValue(1)));
            }).
                     ToArray();

            if (shards.Length == 0)
            {
                return(Tuple.Create(retVal, false));
            }

            #endregion

            #region populate shards

            retVal.Shards = shards.
                            Select(o =>
            {
                var aabb = Math3D.GetAABB(o.Item2);

                double radius = Math.Sqrt((aabb.Item2 - aabb.Item1).Length / 2);

                Point3D center      = Math3D.GetCenter(TriangleIndexed.GetUsedPoints(o.Item2));
                Vector3D centerVect = center.ToVector();

                Point3D[] allPoints = o.Item2[0].AllPoints.
                                      Select(p => p - centerVect).
                                      ToArray();

                TriangleIndexed[] shiftedTriangles = o.Item2.
                                                     Select(p => new TriangleIndexed(p.Index0, p.Index1, p.Index2, allPoints)).
                                                     ToArray();

                return(new HullVoronoiExploder_Shard()
                {
                    VoronoiControlPointIndex = o.Item1,
                    Hull_ParentCoords = o.Item2,
                    Hull_Centered = shiftedTriangles,
                    Radius = radius,
                    Center_ParentCoords = center,
                });
            }).
                            Where(o => o != null).
                            ToArray();

            #endregion

            return(Tuple.Create(retVal, true));
        }
        private static Tuple<HullVoronoiExploder_Response, bool> SplitHull(ITriangleIndexed[] convexHull, Tuple<Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options, double aabbLen)
        {
            #region intersect with the hull

            var hits = shots.
                Select(o => new HullVoronoiExploder_ShotHit()
                {
                    Shot = o,
                    Hit = GetHit(convexHull, o.Item1, o.Item2, o.Item3),
                }).
                Where(o => o.Hit != null).
                ToArray();

            if (hits.Length == 0)
            {
                return null;
            }

            #endregion

            #region voronoi

            Point3D[] controlPoints = GetVoronoiCtrlPoints(hits, convexHull, options.MinCount, options.MaxCount, aabbLen);

            VoronoiResult3D voronoi = Math3D.GetVoronoi(controlPoints, true);
            if (voronoi == null)
            {
                return null;
            }

            #endregion

            // There is enough to start populating the response
            HullVoronoiExploder_Response retVal = new HullVoronoiExploder_Response()
            {
                Hits = hits,
                ControlPoints = controlPoints,
                Voronoi = voronoi,
            };

            #region intersect voronoi and hull

            // Intersect
            Tuple<int, ITriangleIndexed[]>[] shards = null;
            try
            {
                shards = Math3D.GetIntersection_Hull_Voronoi_full(convexHull, voronoi);
            }
            catch (Exception)
            {
                return Tuple.Create(retVal, false);
            }

            if (shards == null)
            {
                return Tuple.Create(retVal, false);
            }

            // Smooth
            if (options.ShouldSmoothShards)
            {
                shards = shards.
                    Select(o => Tuple.Create(o.Item1, Asteroid.SmoothTriangles(o.Item2))).
                    ToArray();
            }

            // Validate
            shards = shards.
                    Where(o => o.Item2 != null && o.Item2.Length >= 3).
                    Where(o =>
                    {
                        Vector3D firstNormal = o.Item2[0].NormalUnit;
                        return o.Item2.Skip(1).Any(p => !Math.Abs(Vector3D.DotProduct(firstNormal, p.NormalUnit)).IsNearValue(1));
                    }).
                    ToArray();

            if (shards.Length == 0)
            {
                return Tuple.Create(retVal, false);
            }

            #endregion

            #region populate shards

            retVal.Shards = shards.
                Select(o =>
                {
                    var aabb = Math3D.GetAABB(o.Item2);

                    double radius = Math.Sqrt((aabb.Item2 - aabb.Item1).Length / 2);

                    Point3D center = Math3D.GetCenter(TriangleIndexed.GetUsedPoints(o.Item2));
                    Vector3D centerVect = center.ToVector();

                    Point3D[] allPoints = o.Item2[0].AllPoints.
                        Select(p => p - centerVect).
                        ToArray();

                    TriangleIndexed[] shiftedTriangles = o.Item2.
                        Select(p => new TriangleIndexed(p.Index0, p.Index1, p.Index2, allPoints)).
                        ToArray();

                    return new HullVoronoiExploder_Shard()
                    {
                        VoronoiControlPointIndex = o.Item1,
                        Hull_ParentCoords = o.Item2,
                        Hull_Centered = shiftedTriangles,
                        Radius = radius,
                        Center_ParentCoords = center,
                    };
                }).
                Where(o => o != null).
                ToArray();

            #endregion

            return Tuple.Create(retVal, true);
        }
Exemplo n.º 3
0
        private void ClearShots()
        {
            _shots = null;
            _voronoi = null;
            _explosion = null;

            ClearVisuals();
        }
Exemplo n.º 4
0
        private void ProcessShots()
        {
            int parsedInt;
            int? minCount = null;
            if (int.TryParse(txtMinShards.Text, out parsedInt))
            {
                minCount = parsedInt;
                txtMinShards.Effect = null;
            }
            else
            {
                txtMinShards.Effect = _errorEffect;
            }

            int? maxCount = null;
            if (int.TryParse(txtMaxShards.Text, out parsedInt))
            {
                maxCount = parsedInt;
                txtMaxShards.Effect = null;
            }
            else
            {
                txtMaxShards.Effect = _errorEffect;
            }

            // Dist dot power
            double? distDotPow = null;
            txtDistDotPow.Effect = null;
            if (chkUseDistDot.IsChecked.Value)
            {
                double parsed;
                if (double.TryParse(txtDistDotPow.Text, out parsed))
                {
                    distDotPow = parsed;
                }
                else
                {
                    txtDistDotPow.Effect = _errorEffect;
                }
            }

            HullVoronoiExploder_Options options = new HullVoronoiExploder_Options()
            {
                DistanceDot_Power = distDotPow,
                InteriorVelocityCenterPercent = chkUseInteriorVelocityCenterPercent.IsChecked.Value ? trkInteriorVelocityCenterPercent.Value : (double?)null,
                RandomVelocityPercent = chkUseRandomVelocityPercent.IsChecked.Value ? trkRandomVelocityPercent.Value : (double?)null,
                ShouldSmoothShards = chkSmoothShards.IsChecked.Value,
            };

            if (minCount != null) options.MinCount = minCount.Value;
            if (maxCount != null) options.MaxCount = maxCount.Value;

            // The length of _shots direction is for the gui (it starts beyond the asteroid, and goes well past it).  The worker needs an actual length
            var adjustedShots = _shots.
                Select(o => Tuple.Create(o.Item1, o.Item2.ToUnit() * trkShotMultiplier.Value, o.Item3)).
                ToArray();

            _explosion = HullVoronoiExploder.ShootHull(_asteroid, adjustedShots, options);

            DrawShatteredAsteroid();
        }
Exemplo n.º 5
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;
            }