public static HullVoronoiExploder_Response ShootHull(ITriangleIndexed[] convexHull, Tuple<Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options = null)
        {
            options = options ?? new HullVoronoiExploder_Options();

            var aabb = Math3D.GetAABB(convexHull);
            double aabbLen = (aabb.Item2 - aabb.Item1).Length;

            // Create a voronoi, and intersect with the hull
            Tuple<HullVoronoiExploder_Response, bool> retVal = null;
            for (int cntr = 0; cntr < 15; cntr++)
            {
                try
                {
                    retVal = SplitHull(convexHull, shots, options, aabbLen);
                    break;
                }
                catch (Exception)
                {
                    // Every once in a while, there is an error with the voronoi, or voronoi intersecting the hull, etc.  Just try
                    // again with new random points
                }
            }

            if (retVal == null) return null;
            else if (!retVal.Item2) return retVal.Item1;

            // Figure out velocities
            retVal.Item1.Velocities = GetVelocities(retVal.Item1.Shards, retVal.Item1.Hits, Math.Sqrt(aabbLen / 2), options);

            return retVal.Item1;
        }
예제 #2
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));
        }
예제 #3
0
        /// <summary>
        /// This will figure how much velocity to apply to each bodyCenter
        /// TODO: Also calculate angular velocities
        /// </summary>
        /// <param name="bodyCenters">The bodies to apply velocity to</param>
        /// <param name="hitStart">The point of impact</param>
        /// <param name="hitDirection">The direction and force of the impact</param>
        /// <param name="mainBodyRadius">The avg radius of the body that is getting blown apart</param>
        private static Vector3D[] DistributeForces(Point3D[] bodyCenters, Point3D hitStart, Vector3D hitDirection, double mainBodyRadius, HullVoronoiExploder_Options options)
        {
            Vector3D hitDirectionUnit = hitDirection.ToUnit();
            double   hitForceBase     = hitDirection.Length / mainBodyRadius;

            var vectors = bodyCenters.
                          Select(o =>
            {
                Vector3D direction     = o - hitStart;
                Vector3D directionUnit = direction.ToUnit();

                double distance       = direction.Length;
                double scaledDistance = distance;
                if (options.DistanceDot_Power != null)
                {
                    double linearDot = Math3D.GetLinearDotProduct(hitDirectionUnit, directionUnit);         // making it linear so the power function is more predictable

                    // Exaggerate the distance based on dot product.  That way, points in line with the hit will get more of the impact
                    double scale   = (1 - Math.Abs(linearDot));
                    scale          = Math.Pow(scale, options.DistanceDot_Power.Value);
                    scaledDistance = scale * distance;
                }

                return(new
                {
                    ForcePoint = o,
                    Distance = distance,
                    ScaledDistance = scaledDistance,
                    DirectionUnit = directionUnit,
                });
            }).
                          ToArray();

            double[] percents = GetPercentOfProjForce(vectors.Select(o => o.ScaledDistance).ToArray());

            Vector3D[] retVal = new Vector3D[vectors.Length];

            for (int cntr = 0; cntr < vectors.Length; cntr++)
            {
                retVal[cntr] = vectors[cntr].DirectionUnit * (hitForceBase * percents[cntr]);
            }

            return(retVal);
        }
예제 #4
0
        private static Vector3D[] CalculateVelocites(Point3D[] centers, HullVoronoiExploder_ShotHit[] hits, double hullRadius, HullVoronoiExploder_Options options)
        {
            Vector3D[] retVal = new Vector3D[centers.Length];

            foreach (var hit in hits)
            {
                Point3D shotStart = hit.Hit.Item1;
                if (options.InteriorVelocityCenterPercent != null)
                {
                    shotStart += ((hit.Hit.Item2 - hit.Hit.Item1) * options.InteriorVelocityCenterPercent.Value);
                }

                //Vector3D[] velocities = DistributeForces(centers, shotStart, hit.Shot.Item2 * (hit.Shot.Item3 * options.ShotMultiplier), hullRadius, options);
                Vector3D[] velocities = DistributeForces(centers, shotStart, hit.Shot.Item2 * hit.Shot.Item3, hullRadius, options);

                for (int cntr = 0; cntr < retVal.Length; cntr++)
                {
                    retVal[cntr] += velocities[cntr];
                }
            }

            return(retVal);
        }
예제 #5
0
        private static Vector3D[] GetVelocities(HullVoronoiExploder_Shard[] hullShards, HullVoronoiExploder_ShotHit[] hits, double hullRadius, HullVoronoiExploder_Options options)
        {
            //TODO: When the asteroid shards aren't resmoothed, the velocities are very small
            //
            //Maybe add more orth velocity
            //
            //Maybe pull the hit source slightly into the asteroid --- done
            //
            //Maybe add some random velocity (size proportional to the velocity) --- done

            Vector3D[] retVal = CalculateVelocites(hullShards.Select(o => o.Center_ParentCoords).ToArray(), hits, hullRadius, options);

            if (options.RandomVelocityPercent != null)
            {
                retVal = retVal.
                         Select(o => o + Math3D.GetRandomVector_Spherical(o.Length * options.RandomVelocityPercent.Value)).
                         ToArray();
            }

            return(retVal);
        }
예제 #6
0
        public static HullVoronoiExploder_Response ShootHull(ITriangleIndexed[] convexHull, Tuple <Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options = null)
        {
            options = options ?? new HullVoronoiExploder_Options();

            var    aabb    = Math3D.GetAABB(convexHull);
            double aabbLen = (aabb.Item2 - aabb.Item1).Length;

            // Create a voronoi, and intersect with the hull
            Tuple <HullVoronoiExploder_Response, bool> retVal = null;

            for (int cntr = 0; cntr < 15; cntr++)
            {
                try
                {
                    retVal = SplitHull(convexHull, shots, options, aabbLen);
                    break;
                }
                catch (Exception)
                {
                    // Every once in a while, there is an error with the voronoi, or voronoi intersecting the hull, etc.  Just try
                    // again with new random points
                }
            }

            if (retVal == null)
            {
                return(null);
            }
            else if (!retVal.Item2)
            {
                return(retVal.Item1);
            }

            // Figure out velocities
            retVal.Item1.Velocities = GetVelocities(retVal.Item1.Shards, retVal.Item1.Hits, Math.Sqrt(aabbLen / 2), options);

            return(retVal.Item1);
        }
        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);
        }
        /// <summary>
        /// This will figure how much velocity to apply to each bodyCenter
        /// TODO: Also calculate angular velocities
        /// </summary>
        /// <param name="bodyCenters">The bodies to apply velocity to</param>
        /// <param name="hitStart">The point of impact</param>
        /// <param name="hitDirection">The direction and force of the impact</param>
        /// <param name="mainBodyRadius">The avg radius of the body that is getting blown apart</param>
        private static Vector3D[] DistributeForces(Point3D[] bodyCenters, Point3D hitStart, Vector3D hitDirection, double mainBodyRadius, HullVoronoiExploder_Options options)
        {
            Vector3D hitDirectionUnit = hitDirection.ToUnit();
            double hitForceBase = hitDirection.Length / mainBodyRadius;

            var vectors = bodyCenters.
                Select(o =>
                {
                    Vector3D direction = o - hitStart;
                    Vector3D directionUnit = direction.ToUnit();

                    double distance = direction.Length;
                    double scaledDistance = distance;
                    if (options.DistanceDot_Power != null)
                    {
                        double linearDot = Math3D.GetLinearDotProduct(hitDirectionUnit, directionUnit);     // making it linear so the power function is more predictable

                        // Exaggerate the distance based on dot product.  That way, points in line with the hit will get more of the impact
                        double scale = (1 - Math.Abs(linearDot));
                        scale = Math.Pow(scale, options.DistanceDot_Power.Value);
                        scaledDistance = scale * distance;
                    }

                    return new
                    {
                        ForcePoint = o,
                        Distance = distance,
                        ScaledDistance = scaledDistance,
                        DirectionUnit = directionUnit,
                    };
                }).
                ToArray();

            double[] percents = GetPercentOfProjForce(vectors.Select(o => o.ScaledDistance).ToArray());

            Vector3D[] retVal = new Vector3D[vectors.Length];

            for (int cntr = 0; cntr < vectors.Length; cntr++)
            {
                retVal[cntr] = vectors[cntr].DirectionUnit * (hitForceBase * percents[cntr]);
            }

            return retVal;
        }
        private static Vector3D[] CalculateVelocites(Point3D[] centers, HullVoronoiExploder_ShotHit[] hits, double hullRadius, HullVoronoiExploder_Options options)
        {
            Vector3D[] retVal = new Vector3D[centers.Length];

            foreach (var hit in hits)
            {
                Point3D shotStart = hit.Hit.Item1;
                if (options.InteriorVelocityCenterPercent != null)
                {
                    shotStart += ((hit.Hit.Item2 - hit.Hit.Item1) * options.InteriorVelocityCenterPercent.Value);
                }

                //Vector3D[] velocities = DistributeForces(centers, shotStart, hit.Shot.Item2 * (hit.Shot.Item3 * options.ShotMultiplier), hullRadius, options);
                Vector3D[] velocities = DistributeForces(centers, shotStart, hit.Shot.Item2 * hit.Shot.Item3, hullRadius, options);

                for (int cntr = 0; cntr < retVal.Length; cntr++)
                {
                    retVal[cntr] += velocities[cntr];
                }
            }

            return retVal;
        }
        private static Vector3D[] GetVelocities(HullVoronoiExploder_Shard[] hullShards, HullVoronoiExploder_ShotHit[] hits, double hullRadius, HullVoronoiExploder_Options options)
        {
            //TODO: When the asteroid shards aren't resmoothed, the velocities are very small
            //
            //Maybe add more orth velocity
            //
            //Maybe pull the hit source slightly into the asteroid --- done
            //
            //Maybe add some random velocity (size proportional to the velocity) --- done

            Vector3D[] retVal = CalculateVelocites(hullShards.Select(o => o.Center_ParentCoords).ToArray(), hits, hullRadius, options);

            if (options.RandomVelocityPercent != null)
            {
                retVal = retVal.
                    Select(o => o + Math3D.GetRandomVector_Spherical(o.Length * options.RandomVelocityPercent.Value)).
                    ToArray();
            }

            return retVal;
        }
예제 #11
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();
        }
예제 #12
0
            // This also creates minerals
            private AsteroidOrMineralDefinition[] GetChildAsteroidsOrMinerals(Tuple<Point3D, Vector3D, double>[] hits, double overDamage)
            {
                int childCount = GetChildCounts(overDamage);

                // Reduce the length of the hits so the shard velocities aren't so high
                hits = hits.
                    Select(o => Tuple.Create(o.Item1, o.Item2 * .33, o.Item3)).
                    ToArray();

                HullVoronoiExploder_Options options = new HullVoronoiExploder_Options()
                {
                    MinCount = childCount,
                    MaxCount = childCount,
                };

                HullVoronoiExploder_Response shards = HullVoronoiExploder.ShootHull(_parent._triangles, hits, options);

                #region cap velocities

                //NOTE: This caps the velocities by running them through an s curve, but if they are all too high to begin with, they
                //will all become very near MAXVELOCITY

                if (shards != null && shards.Velocities != null)
                {
                    shards.Velocities = shards.Velocities.
                        Select(o =>
                        {
                            double length = o.Length;
                            double adjusted = Math1D.PositiveSCurve(length, MAXVELOCITY, .75);
                            double ratio = adjusted / length;
                            return o * ratio;
                        }).
                        ToArray();
                }

                #endregion

                return DetermineDestroyedChildrenMinerals(shards, overDamage, _parent.RadiusVect, _minChildRadius, _getMassByRadius, _getMineralsByDestroyedMass);
            }