Ejemplo n.º 1
0
        private static void Main(string[] args)
        {
            var start1 = new SphereCoordinate(0.5 * Math.PI, -0.25 * Math.PI);
            Console.WriteLine((CartesianVector)start1);

            var end1 = new SphereCoordinate(0.5 * Math.PI, 0.25 * Math.PI);
            Console.WriteLine((CartesianVector)end1);

            var start2 = new SphereCoordinate(0.25 * Math.PI, 0);
            Console.WriteLine((CartesianVector)start2);

            var end2 = new SphereCoordinate(0.75 * Math.PI, 0);
            Console.WriteLine((CartesianVector)end2);

            var arc1 = new GreatCircleSegment(start1, end1);
            var arc2 = new GreatCircleSegment(start2, end2);

            Console.WriteLine();
            Console.WriteLine(arc1.Midpoint);
            Console.WriteLine(new CartesianVector(0, 0, 1));
            Console.WriteLine((SphereCoordinate)arc1.Midpoint);
            Console.WriteLine((SphereCoordinate)new CartesianVector(0, 0, 1));
            Console.WriteLine((CartesianVector)(SphereCoordinate)arc1.Midpoint);
            Console.WriteLine();

            SphereCoordinate intersection;
            Console.WriteLine(arc1.Intersects(arc2, out intersection) + "   " + intersection + "   " + (CartesianVector)intersection);

            Console.WriteLine();
            var quarterSpherePoly = new VoronoiCell(new SphereCoordinate(0, 0), new SphereCoordinate(0.5 * Math.PI, 0), new SphereCoordinate(0.5 * Math.PI, 0.5 * Math.PI), new SphereCoordinate(0.5 * Math.PI, Math.PI));
            Console.WriteLine("Area of quarter sphere Polygon: " + quarterSpherePoly.Area);
            Console.WriteLine("Area of whole sphere: " + 4 * Math.PI);
            Console.WriteLine("Area of quarter sphere: " + Math.PI);

            Console.WriteLine();
            Console.WriteLine(Math.Acos(new CartesianVector(0, 1, 0).DotProduct(new SphereCoordinate(Math.PI / 2, 0))));
            Console.WriteLine(Math.Acos(new CartesianVector(0, 1, 0).DotProduct(new SphereCoordinate(Math.PI / 2, -Math.PI / 2))));
            Console.WriteLine(Math.Acos(new CartesianVector(0, 1, 0).DotProduct(new SphereCoordinate(Math.PI / 2, 0))));
            Console.WriteLine(Math.Acos(new CartesianVector(0, 1, 0).DotProduct(new SphereCoordinate(Math.PI / 2, -Math.PI / 2))));

            Console.WriteLine();
            Console.WriteLine(Math.Acos(new CartesianVector(1, 1, 0).AsUnitVector.DotProduct(new SphereCoordinate(Math.PI / 2, 0))));
            Console.WriteLine(Math.Acos(new CartesianVector(1, 1, 0).AsUnitVector.DotProduct(new SphereCoordinate(Math.PI / 4, -Math.PI / 2))));

            Console.WriteLine();
            Console.WriteLine(Math.Acos(new CartesianVector(0, 1, 0).DotProduct(new CartesianVector(1, 1, 0).AsUnitVector)));
            Console.WriteLine(Math.PI / 4);

            Console.ReadLine();
        }
Ejemplo n.º 2
0
        static void Main(string[] args)
        {
            try
            {

                AlgoTradingClass td = new AlgoTradingClass();
                VoronoiCell vgc = new VoronoiCell();
                double[] X = { 0.5, -0.5 };
                double[] Y = { 0, 0.5, 0.1 };

                td.AlgoTradingDemo1CS();

                //MWArray[] Results2 = (MWArray[])td.AlgoTrading1CS(void);

                MWArray[] Results = (MWArray[])vgc.NDimVoronoiDiagram(3, (MWNumericArray)X, (MWNumericArray)Y);
                double[,] EdgeX = (double[,])Results[0].ToArray();
                double[,] EdgeY = (double[,])Results[1].ToArray();
            }
            catch (Exception exception)
            {
                Console.WriteLine("Error: {0}", exception);
            }
        }
Ejemplo n.º 3
0
        private void HandleLimbCollision(Impact collision, Limb limb)
        {
            if (limb?.body?.FarseerBody == null || limb.character == null)
            {
                return;
            }

            if (limb.Mass > MinImpactLimbMass)
            {
                Vector2 normal =
                    Vector2.DistanceSquared(Body.SimPosition, limb.SimPosition) < 0.0001f ?
                    Vector2.UnitY :
                    Vector2.Normalize(Body.SimPosition - limb.SimPosition);

                float impact = Math.Min(Vector2.Dot(collision.Velocity, -normal), 50.0f) * Math.Min(limb.Mass / 100.0f, 1);

                ApplyImpact(impact, -normal, collision.ImpactPos, applyDamage: false);
                foreach (Submarine dockedSub in submarine.DockedTo)
                {
                    dockedSub.SubBody.ApplyImpact(impact, -normal, collision.ImpactPos, applyDamage: false);
                }
            }

            //find all contacts between the limb and level walls
            List <Contact> levelContacts = new List <Contact>();
            ContactEdge    contactEdge   = limb.body.FarseerBody.ContactList;

            while (contactEdge?.Contact != null)
            {
                if (contactEdge.Contact.Enabled &&
                    contactEdge.Contact.IsTouching &&
                    contactEdge.Other?.UserData is VoronoiCell)
                {
                    levelContacts.Add(contactEdge.Contact);
                }
                contactEdge = contactEdge.Next;
            }

            if (levelContacts.Count == 0)
            {
                return;
            }

            //if the limb is in contact with the level, apply an artifical impact to prevent the sub from bouncing on top of it
            //not a very realistic way to handle the collisions (makes it seem as if the characters were made of reinforced concrete),
            //but more realistic than bouncing and prevents using characters as "bumpers" that prevent all collision damage
            Vector2 avgContactNormal = Vector2.Zero;

            foreach (Contact levelContact in levelContacts)
            {
                levelContact.GetWorldManifold(out Vector2 contactNormal, out FixedArray2 <Vector2> temp);

                //if the contact normal is pointing from the limb towards the level cell it's touching, flip the normal
                VoronoiCell cell = levelContact.FixtureB.UserData is VoronoiCell ?
                                   ((VoronoiCell)levelContact.FixtureB.UserData) : ((VoronoiCell)levelContact.FixtureA.UserData);

                var cellDiff = ConvertUnits.ToDisplayUnits(limb.body.SimPosition) - cell.Center;
                if (Vector2.Dot(contactNormal, cellDiff) < 0)
                {
                    contactNormal = -contactNormal;
                }

                avgContactNormal += contactNormal;

                //apply impacts at the positions where this sub is touching the limb
                ApplyImpact((Vector2.Dot(-collision.Velocity, contactNormal) / 2.0f) / levelContacts.Count, contactNormal, collision.ImpactPos, applyDamage: false);
            }
            avgContactNormal /= levelContacts.Count;

            float contactDot = Vector2.Dot(Body.LinearVelocity, -avgContactNormal);

            if (contactDot > 0.001f)
            {
                Vector2 velChange = Vector2.Normalize(Body.LinearVelocity) * contactDot;
                if (!MathUtils.IsValid(velChange))
                {
                    GameAnalyticsManager.AddErrorEventOnce(
                        "SubmarineBody.HandleLimbCollision:" + submarine.ID,
                        GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                        "Invalid velocity change in SubmarineBody.HandleLimbCollision (submarine velocity: " + Body.LinearVelocity
                        + ", avgContactNormal: " + avgContactNormal
                        + ", contactDot: " + contactDot
                        + ", velChange: " + velChange + ")");
                    return;
                }

                Body.LinearVelocity -= velChange;

                float damageAmount = contactDot * Body.Mass / limb.character.Mass;
                limb.character.LastDamageSource = submarine;
                limb.character.DamageLimb(ConvertUnits.ToDisplayUnits(collision.ImpactPos), limb,
                                          AfflictionPrefab.ImpactDamage.Instantiate(damageAmount).ToEnumerable(), 0.0f, true, 0.0f);

                if (limb.character.IsDead)
                {
                    foreach (LimbJoint limbJoint in limb.character.AnimController.LimbJoints)
                    {
                        if (limbJoint.IsSevered || (limbJoint.LimbA != limb && limbJoint.LimbB != limb))
                        {
                            continue;
                        }
                        limb.character.AnimController.SeverLimbJoint(limbJoint);
                    }
                }
            }
        }
Ejemplo n.º 4
0
        public static Body GeneratePolygons(List <VoronoiCell> cells, Level level, out List <Vector2[]> renderTriangles)
        {
            renderTriangles = new List <Vector2[]>();

            List <Vector2> tempVertices = new List <Vector2>();
            List <Vector2> bodyPoints   = new List <Vector2>();

            Body cellBody = new Body()
            {
                SleepingAllowed     = false,
                BodyType            = BodyType.Static,
                CollisionCategories = Physics.CollisionLevel
            };

            GameMain.World.Add(cellBody);

            for (int n = cells.Count - 1; n >= 0; n--)
            {
                VoronoiCell cell = cells[n];

                bodyPoints.Clear();
                tempVertices.Clear();
                foreach (GraphEdge ge in cell.Edges)
                {
                    if (Vector2.DistanceSquared(ge.Point1, ge.Point2) < 0.01f)
                    {
                        continue;
                    }
                    if (!tempVertices.Any(v => Vector2.DistanceSquared(ge.Point1, v) < 1.0f))
                    {
                        tempVertices.Add(ge.Point1);
                        bodyPoints.Add(ge.Point1);
                    }
                    if (!tempVertices.Any(v => Vector2.DistanceSquared(ge.Point2, v) < 1.0f))
                    {
                        tempVertices.Add(ge.Point2);
                        bodyPoints.Add(ge.Point2);
                    }
                }

                if (tempVertices.Count < 3 || bodyPoints.Count < 2)
                {
                    cells.RemoveAt(n);
                    continue;
                }

                Vector2 minVert = tempVertices[0];
                Vector2 maxVert = tempVertices[0];
                foreach (var vert in tempVertices)
                {
                    minVert = new Vector2(
                        Math.Min(minVert.X, vert.X),
                        Math.Min(minVert.Y, vert.Y));
                    maxVert = new Vector2(
                        Math.Max(maxVert.X, vert.X),
                        Math.Max(maxVert.Y, vert.Y));
                }
                Vector2 center = (minVert + maxVert) / 2;
                renderTriangles.AddRange(MathUtils.TriangulateConvexHull(tempVertices, center));

                if (bodyPoints.Count < 2)
                {
                    continue;
                }

                if (bodyPoints.Count < 3)
                {
                    foreach (Vector2 vertex in tempVertices)
                    {
                        if (bodyPoints.Contains(vertex))
                        {
                            continue;
                        }
                        bodyPoints.Add(vertex);
                        break;
                    }
                }

                for (int i = 0; i < bodyPoints.Count; i++)
                {
                    cell.BodyVertices.Add(bodyPoints[i]);
                    bodyPoints[i] = ConvertUnits.ToSimUnits(bodyPoints[i]);
                }

                if (cell.CellType == CellType.Empty)
                {
                    continue;
                }

                cellBody.UserData = cell;
                var triangles = MathUtils.TriangulateConvexHull(bodyPoints, ConvertUnits.ToSimUnits(center));

                for (int i = 0; i < triangles.Count; i++)
                {
                    //don't create a triangle if the area of the triangle is too small
                    //(apparently Farseer doesn't like polygons with a very small area, see Shape.ComputeProperties)
                    Vector2 a    = triangles[i][0];
                    Vector2 b    = triangles[i][1];
                    Vector2 c    = triangles[i][2];
                    float   area = Math.Abs(a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y)) / 2.0f;
                    if (area < 1.0f)
                    {
                        continue;
                    }

                    Vertices     bodyVertices = new Vertices(triangles[i]);
                    PolygonShape polygon      = new PolygonShape(bodyVertices, 5.0f);
                    Fixture      fixture      = new Fixture(polygon)
                    {
                        UserData = cell
                    };
                    cellBody.Add(fixture, resetMassData: false);

                    if (fixture.Shape.MassData.Area < FarseerPhysics.Settings.Epsilon)
                    {
                        DebugConsole.ThrowError("Invalid triangle created by CaveGenerator (" + triangles[i][0] + ", " + triangles[i][1] + ", " + triangles[i][2] + ")");
                        GameAnalyticsManager.AddErrorEventOnce(
                            "CaveGenerator.GeneratePolygons:InvalidTriangle",
                            GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                            "Invalid triangle created by CaveGenerator (" + triangles[i][0] + ", " + triangles[i][1] + ", " + triangles[i][2] + "). Seed: " + level.Seed);
                    }
                }
                cell.Body = cellBody;
            }

            cellBody.CollisionCategories = Physics.CollisionLevel;
            cellBody.ResetMassData();

            return(cellBody);
        }
Ejemplo n.º 5
0
        void VoronoiTesselation()
        {
            bool usesUserDefinedSites = false;

            if (voronoiSites != null && voronoiSites.Count > 0)
            {
                numProvinces         = voronoiSites.Count;
                usesUserDefinedSites = true;
            }
            if (centers == null || centers.Length != numProvinces)
            {
                centers = new Point[numProvinces];
            }
            for (int k = 0; k < centers.Length; k++)
            {
                if (usesUserDefinedSites)
                {
                    Vector2 p = voronoiSites [k];
                    centers [k] = new Point(p.x, p.y);
                }
                else
                {
                    centers [k] = new Point(UnityEngine.Random.Range(-0.49f, 0.49f), UnityEngine.Random.Range(-0.49f, 0.49f));
                }
            }

            if (voronoi == null)
            {
                voronoi = new VoronoiFortune();
            }
            for (int k = 0; k < goodGridRelaxation; k++)
            {
                voronoi.AssignData(centers);
                voronoi.DoVoronoi();
                if (k < goodGridRelaxation - 1)
                {
                    for (int j = 0; j < numProvinces; j++)
                    {
                        Point centroid = voronoi.cells [j].centroid;
                        centers [j] = (centers [j] + centroid) / 2;
                    }
                }
            }

            // Make cell regions: we assume cells have only 1 region but that can change in the future
            for (int k = 0; k < voronoi.cells.Length; k++)
            {
                VoronoiCell voronoiCell = voronoi.cells [k];
                Vector2     center      = voronoiCell.center.vector3;
                MapProvince cell        = new MapProvince(center);
                MapRegion   cr          = new MapRegion(cell);
                if (edgeNoise > 0)
                {
                    cr.polygon = voronoiCell.GetPolygon(voronoiCell.center, edgeMaxLength, edgeNoise);
                }
                else
                {
                    cr.polygon = voronoiCell.GetPolygon();
                }
                if (cr.polygon != null)
                {
                    // Add segments
                    int segmentsCount = voronoiCell.segments.Count;
                    for (int i = 0; i < segmentsCount; i++)
                    {
                        Segment s = voronoiCell.segments [i];
                        if (!s.deleted)
                        {
                            if (edgeNoise > 0)
                            {
                                cr.segments.AddRange(s.subdivisions);
                            }
                            else
                            {
                                cr.segments.Add(s);
                            }
                        }
                    }
                    cell.region = cr;
                    mapProvinces.Add(cell);
                }
            }
        }
Ejemplo n.º 6
0
        public bool OnCollision(Fixture f1, Fixture f2, Contact contact)
        {
            Limb limb = f2.Body.UserData as Limb;

            if (limb != null)
            {
                bool collision = HandleLimbCollision(contact, limb);

                if (collision && limb.Mass > 100.0f)
                {
                    Vector2 normal = Vector2.Normalize(Body.SimPosition - limb.SimPosition);

                    float impact = Math.Min(Vector2.Dot(Velocity - limb.LinearVelocity, -normal), 50.0f) / 5.0f * Math.Min(limb.Mass / 200.0f, 1);

                    ApplyImpact(impact, -normal, contact);
                    foreach (Submarine dockedSub in submarine.DockedTo)
                    {
                        dockedSub.SubBody.ApplyImpact(impact, -normal, contact);
                    }
                }

                return(collision);
            }

            VoronoiCell cell = f2.Body.UserData as VoronoiCell;

            if (cell != null)
            {
                var collisionNormal = Vector2.Normalize(ConvertUnits.ToDisplayUnits(Body.SimPosition) - cell.Center);

                float wallImpact = Vector2.Dot(Velocity, -collisionNormal);

                ApplyImpact(wallImpact, -collisionNormal, contact);
                foreach (Submarine dockedSub in submarine.DockedTo)
                {
                    dockedSub.SubBody.ApplyImpact(wallImpact, -collisionNormal, contact);
                }

                Vector2 n;
                FixedArray2 <Vector2> particlePos;
                contact.GetWorldManifold(out n, out particlePos);

#if CLIENT
                int particleAmount = (int)(wallImpact * 10.0f);
                for (int i = 0; i < particleAmount; i++)
                {
                    GameMain.ParticleManager.CreateParticle("iceshards",
                                                            ConvertUnits.ToDisplayUnits(particlePos[0]) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
                                                            Rand.Vector(Rand.Range(50.0f, 500.0f)) + Velocity);
                }
#endif

                return(true);
            }

            Submarine otherSub = f2.Body.UserData as Submarine;
            if (otherSub != null)
            {
                Debug.Assert(otherSub != submarine);

                Vector2 normal;
                FixedArray2 <Vector2> points;
                contact.GetWorldManifold(out normal, out points);
                if (contact.FixtureA.Body == otherSub.SubBody.Body.FarseerBody)
                {
                    normal = -normal;
                }

                float thisMass  = Body.Mass + submarine.DockedTo.Sum(s => s.PhysicsBody.Mass);
                float otherMass = otherSub.PhysicsBody.Mass + otherSub.DockedTo.Sum(s => s.PhysicsBody.Mass);

                float massRatio = otherMass / (thisMass + otherMass);

                float impact = (Vector2.Dot(Velocity - otherSub.Velocity, normal) / 2.0f) * massRatio;

                ApplyImpact(impact, normal, contact);
                foreach (Submarine dockedSub in submarine.DockedTo)
                {
                    dockedSub.SubBody.ApplyImpact(impact, normal, contact);
                }

                return(true);
            }

            return(true);
        }
Ejemplo n.º 7
0
 public static Vector3 GetFirstCorner(VoronoiCell cell, VoronoiDirection direction)
 {
     return(cell.Corners[direction]);
 }
Ejemplo n.º 8
0
 public static float OuterToInner(VoronoiCell cell, VoronoiDirection direction)
 {
     return(InnerRadius(cell, direction) / OuterRadius(cell, direction));
 }
Ejemplo n.º 9
0
 public static float OuterRadius(VoronoiCell cell, VoronoiDirection direction)
 {
     return((cell.Corners[direction].magnitude + cell.Corners[direction + 1].magnitude) * 0.5f);
 }
Ejemplo n.º 10
0
        private void HandleLimbCollision(Contact contact, Limb limb)
        {
            if (limb.Mass > 100.0f)
            {
                Vector2 normal = Vector2.DistanceSquared(Body.SimPosition, limb.SimPosition) < 0.0001f ?
                                 Vector2.UnitY :
                                 Vector2.Normalize(Body.SimPosition - limb.SimPosition);

                float impact = Math.Min(Vector2.Dot(Velocity - limb.LinearVelocity, -normal), 50.0f) / 5.0f * Math.Min(limb.Mass / 200.0f, 1);

                ApplyImpact(impact, -normal, contact);
                foreach (Submarine dockedSub in submarine.DockedTo)
                {
                    dockedSub.SubBody.ApplyImpact(impact, -normal, contact);
                }
            }

            //find all contacts between the limb and level walls
            List <Contact> levelContacts = new List <Contact>();
            ContactEdge    contactEdge   = limb.body.FarseerBody.ContactList;

            while (contactEdge.Next != null)
            {
                if (contactEdge.Contact.Enabled &&
                    contactEdge.Other.UserData is VoronoiCell &&
                    contactEdge.Contact.IsTouching)
                {
                    levelContacts.Add(contactEdge.Contact);
                }
                contactEdge = contactEdge.Next;
            }

            if (levelContacts.Count == 0)
            {
                return;
            }

            //if the limb is in contact with the level, apply an artifical impact to prevent the sub from bouncing on top of it
            //not a very realistic way to handle the collisions (makes it seem as if the characters were made of reinforced concrete),
            //but more realistic than bouncing and prevents using characters as "bumpers" that prevent all collision damage

            //TODO: apply impact damage and/or gib the character that got crushed between the sub and the level?
            Vector2 avgContactNormal = Vector2.Zero;

            foreach (Contact levelContact in levelContacts)
            {
                Vector2 contactNormal;
                FixedArray2 <Vector2> temp;
                levelContact.GetWorldManifold(out contactNormal, out temp);

                //if the contact normal is pointing from the limb towards the level cell it's touching, flip the normal
                VoronoiCell cell = levelContact.FixtureB.UserData is VoronoiCell ?
                                   ((VoronoiCell)levelContact.FixtureB.UserData) : ((VoronoiCell)levelContact.FixtureA.UserData);

                var cellDiff = ConvertUnits.ToDisplayUnits(limb.body.SimPosition) - cell.Center;
                if (Vector2.Dot(contactNormal, cellDiff) < 0)
                {
                    contactNormal = -contactNormal;
                }

                avgContactNormal += contactNormal;

                //apply impacts at the positions where this sub is touching the limb
                ApplyImpact((Vector2.Dot(-Velocity, contactNormal) / 2.0f) / levelContacts.Count, contactNormal, levelContact);
            }
            avgContactNormal /= levelContacts.Count;

            float contactDot = Vector2.Dot(Body.LinearVelocity, -avgContactNormal);

            if (contactDot > 0.001f)
            {
                Body.LinearVelocity -= Vector2.Normalize(Body.LinearVelocity) * contactDot;

                float damageAmount = contactDot * Body.Mass / limb.character.Mass;

                Vector2 n;
                FixedArray2 <Vector2> contactPos;
                contact.GetWorldManifold(out n, out contactPos);
                limb.character.DamageLimb(ConvertUnits.ToDisplayUnits(contactPos[0]), limb, DamageType.Blunt, damageAmount, 0.0f, 0.0f, true, 0.0f);

                if (limb.character.IsDead)
                {
                    foreach (LimbJoint limbJoint in limb.character.AnimController.LimbJoints)
                    {
                        if (limbJoint.IsSevered || (limbJoint.LimbA != limb && limbJoint.LimbB != limb))
                        {
                            continue;
                        }
                        limb.character.AnimController.SeverLimbJoint(limbJoint);
                    }
                }
            }
        }
Ejemplo n.º 11
0
        private void GenerateRuin(List <VoronoiCell> mainPath)
        {
            Vector2 ruinSize   = new Vector2(Rand.Range(5000.0f, 8000.0f, Rand.RandSync.Server), Rand.Range(5000.0f, 8000.0f, Rand.RandSync.Server));
            float   ruinRadius = Math.Max(ruinSize.X, ruinSize.Y) * 0.5f;

            Vector2 ruinPos = cells[Rand.Int(cells.Count, Rand.RandSync.Server)].Center;

            int iter = 0;

            ruinPos.Y = Math.Min(borders.Y + borders.Height - ruinSize.Y / 2, ruinPos.Y);

            while (mainPath.Any(p => Vector2.Distance(ruinPos, p.Center) < ruinRadius * 2.0f))
            {
                Vector2 weighedPathPos = ruinPos;
                iter++;

                foreach (VoronoiCell pathCell in mainPath)
                {
                    float dist = Vector2.Distance(pathCell.Center, ruinPos);
                    if (dist > 10000.0f)
                    {
                        continue;
                    }

                    Vector2 moveAmount = Vector2.Normalize(ruinPos - pathCell.Center) * 100000.0f / dist;

                    //if (weighedPathPos.Y + moveAmount.Y > borders.Bottom - ruinSize.X)
                    //{
                    //    moveAmount.X = (Math.Abs(moveAmount.Y) + Math.Abs(moveAmount.X))*Math.Sign(moveAmount.X);
                    //    moveAmount.Y = 0.0f;
                    //}

                    weighedPathPos  += moveAmount;
                    weighedPathPos.Y = Math.Min(borders.Y + borders.Height - ruinSize.Y / 2, weighedPathPos.Y);
                }

                ruinPos = weighedPathPos;

                if (iter > 10000)
                {
                    break;
                }
            }

            VoronoiCell closestPathCell = null;
            float       closestDist     = 0.0f;

            foreach (VoronoiCell pathCell in mainPath)
            {
                float dist = Vector2.Distance(pathCell.Center, ruinPos);
                if (closestPathCell == null || dist < closestDist)
                {
                    closestPathCell = pathCell;
                    closestDist     = dist;
                }
            }

            var ruin = new Ruin(closestPathCell, cells, new Rectangle(MathUtils.ToPoint(ruinPos - ruinSize * 0.5f), MathUtils.ToPoint(ruinSize)));

            ruins.Add(ruin);

            ruin.RuinShapes.Sort((shape1, shape2) => shape2.DistanceFromEntrance.CompareTo(shape1.DistanceFromEntrance));
            for (int i = 0; i < 4; i++)
            {
                positionsOfInterest.Add(new InterestingPosition(ruin.RuinShapes[i].Rect.Center.ToVector2(), PositionType.Ruin));
            }

            foreach (RuinShape ruinShape in ruin.RuinShapes)
            {
                var tooClose = GetTooCloseCells(ruinShape.Rect.Center.ToVector2(), Math.Max(ruinShape.Rect.Width, ruinShape.Rect.Height));

                foreach (VoronoiCell cell in tooClose)
                {
                    if (cell.CellType == CellType.Empty)
                    {
                        continue;
                    }
                    foreach (GraphEdge e in cell.edges)
                    {
                        Rectangle rect = ruinShape.Rect;
                        rect.Y += rect.Height;
                        if (MathUtils.GetLineRectangleIntersection(e.point1, e.point2, rect) != null)
                        {
                            cell.CellType = CellType.Removed;

                            int x = (int)Math.Floor(cell.Center.X / GridCellSize);
                            int y = (int)Math.Floor(cell.Center.Y / GridCellSize);

                            cellGrid[x, y].Remove(cell);
                            cells.Remove(cell);
                            break;
                        }
                    }
                }
            }
        }
Ejemplo n.º 12
0
        public void Generate(bool mirror = false)
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            if (loaded != null)
            {
                loaded.Unload();
            }
            loaded = this;

            positionsOfInterest = new List <InterestingPosition>();

            Voronoi voronoi = new Voronoi(1.0);

            List <Vector2> sites = new List <Vector2>();

            bodies = new List <Body>();

            Rand.SetSyncedSeed(ToolBox.StringToInt(seed));

#if CLIENT
            renderer = new LevelRenderer(this);

            backgroundColor = generationParams.BackgroundColor;
            float avgValue = (backgroundColor.R + backgroundColor.G + backgroundColor.G) / 3;
            GameMain.LightManager.AmbientLight = new Color(backgroundColor * (10.0f / avgValue), 1.0f);
#endif

            float minWidth = 6500.0f;
            if (Submarine.MainSub != null)
            {
                Rectangle dockedSubBorders = Submarine.MainSub.GetDockedBorders();
                minWidth = Math.Max(minWidth, Math.Max(dockedSubBorders.Width, dockedSubBorders.Height));
            }

            startPosition = new Vector2(
                Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server),
                Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, Rand.RandSync.Server));

            endPosition = new Vector2(
                borders.Width - Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server),
                Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, Rand.RandSync.Server));

            List <Vector2> pathNodes   = new List <Vector2>();
            Rectangle      pathBorders = borders;// new Rectangle((int)minWidth, (int)minWidth, borders.Width - (int)minWidth * 2, borders.Height - (int)minWidth);
            pathBorders.Inflate(-minWidth * 2, -minWidth * 2);

            pathNodes.Add(new Vector2(startPosition.X, borders.Height));

            Vector2 nodeInterval = generationParams.MainPathNodeIntervalRange;

            for (float x = startPosition.X + Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server);
                 x < endPosition.X - Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server);
                 x += Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server))
            {
                pathNodes.Add(new Vector2(x, Rand.Range(pathBorders.Y, pathBorders.Bottom, Rand.RandSync.Server)));
            }

            pathNodes.Add(new Vector2(endPosition.X, borders.Height));

            if (pathNodes.Count <= 2)
            {
                pathNodes.Add((startPosition + endPosition) / 2);
            }

            List <List <Vector2> > smallTunnels = new List <List <Vector2> >();
            for (int i = 0; i < generationParams.SmallTunnelCount; i++)
            {
                var tunnelStartPos = pathNodes[Rand.Range(2, pathNodes.Count - 2, Rand.RandSync.Server)];
                tunnelStartPos.X = MathHelper.Clamp(tunnelStartPos.X, pathBorders.X, pathBorders.Right);

                float tunnelLength = Rand.Range(
                    generationParams.SmallTunnelLengthRange.X,
                    generationParams.SmallTunnelLengthRange.Y,
                    Rand.RandSync.Server);

                var tunnelNodes = MathUtils.GenerateJaggedLine(
                    tunnelStartPos,
                    new Vector2(tunnelStartPos.X, pathBorders.Bottom) + Rand.Vector(tunnelLength, Rand.RandSync.Server),
                    4, 1000.0f);

                List <Vector2> tunnel = new List <Vector2>();
                foreach (Vector2[] tunnelNode in tunnelNodes)
                {
                    if (!pathBorders.Contains(tunnelNode[0]))
                    {
                        continue;
                    }
                    tunnel.Add(tunnelNode[0]);
                }

                if (tunnel.Any())
                {
                    smallTunnels.Add(tunnel);
                }
            }

            Vector2 siteInterval = generationParams.VoronoiSiteInterval;
            Vector2 siteVariance = generationParams.VoronoiSiteVariance;
            for (float x = siteInterval.X / 2; x < borders.Width; x += siteInterval.X)
            {
                for (float y = siteInterval.Y / 2; y < borders.Height; y += siteInterval.Y)
                {
                    Vector2 site = new Vector2(
                        x + Rand.Range(-siteVariance.X, siteVariance.X, Rand.RandSync.Server),
                        y + Rand.Range(-siteVariance.Y, siteVariance.Y, Rand.RandSync.Server));

                    if (smallTunnels.Any(t => t.Any(node => Vector2.Distance(node, site) < siteInterval.Length())))
                    {
                        //add some more sites around the small tunnels to generate more small voronoi cells
                        if (x < borders.Width - siteInterval.X)
                        {
                            sites.Add(new Vector2(x, y) + Vector2.UnitX * siteInterval * 0.5f);
                        }
                        if (y < borders.Height - siteInterval.Y)
                        {
                            sites.Add(new Vector2(x, y) + Vector2.UnitY * siteInterval * 0.5f);
                        }
                        if (x < borders.Width - siteInterval.X && y < borders.Height - siteInterval.Y)
                        {
                            sites.Add(new Vector2(x, y) + Vector2.One * siteInterval * 0.5f);
                        }
                    }

                    if (mirror)
                    {
                        site.X = borders.Width - site.X;
                    }

                    sites.Add(site);
                }
            }

            Stopwatch sw2 = new Stopwatch();
            sw2.Start();

            List <GraphEdge> graphEdges = voronoi.MakeVoronoiGraph(sites, borders.Width, borders.Height);

            Debug.WriteLine("MakeVoronoiGraph: " + sw2.ElapsedMilliseconds + " ms");
            sw2.Restart();

            //construct voronoi cells based on the graph edges
            cells = CaveGenerator.GraphEdgesToCells(graphEdges, borders, GridCellSize, out cellGrid);

            Debug.WriteLine("find cells: " + sw2.ElapsedMilliseconds + " ms");
            sw2.Restart();

            List <VoronoiCell> mainPath = CaveGenerator.GeneratePath(pathNodes, cells, cellGrid, GridCellSize,
                                                                     new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), 0.5f, mirror);

            for (int i = 2; i < mainPath.Count; i += 3)
            {
                positionsOfInterest.Add(new InterestingPosition(mainPath[i].Center, PositionType.MainPath));
            }

            List <VoronoiCell> pathCells = new List <VoronoiCell>(mainPath);

            EnlargeMainPath(pathCells, minWidth);

            foreach (InterestingPosition positionOfInterest in positionsOfInterest)
            {
                WayPoint wayPoint = new WayPoint(positionOfInterest.Position, SpawnType.Enemy, null);
                wayPoint.MoveWithLevel = true;
            }

            startPosition.X = pathCells[0].Center.X;

            foreach (List <Vector2> tunnel in smallTunnels)
            {
                if (tunnel.Count < 2)
                {
                    continue;
                }

                //find the cell which the path starts from
                int startCellIndex = CaveGenerator.FindCellIndex(tunnel[0], cells, cellGrid, GridCellSize, 1);
                if (startCellIndex < 0)
                {
                    continue;
                }

                //if it wasn't one of the cells in the main path, don't create a tunnel
                if (cells[startCellIndex].CellType != CellType.Path)
                {
                    continue;
                }

                var newPathCells = CaveGenerator.GeneratePath(tunnel, cells, cellGrid, GridCellSize, pathBorders);

                positionsOfInterest.Add(new InterestingPosition(tunnel.Last(), PositionType.Cave));

                if (tunnel.Count > 4)
                {
                    positionsOfInterest.Add(new InterestingPosition(tunnel[tunnel.Count / 2], PositionType.Cave));
                }

                pathCells.AddRange(newPathCells);
            }

            Debug.WriteLine("path: " + sw2.ElapsedMilliseconds + " ms");
            sw2.Restart();

            cells = CleanCells(pathCells);

            pathCells.AddRange(CreateBottomHoles(generationParams.BottomHoleProbability, new Rectangle(
                                                     (int)(borders.Width * 0.2f), 0,
                                                     (int)(borders.Width * 0.6f), (int)(borders.Height * 0.8f))));

            foreach (VoronoiCell cell in cells)
            {
                if (cell.Center.Y < borders.Height / 2)
                {
                    continue;
                }
                cell.edges.ForEach(e => e.OutsideLevel = true);
            }

            foreach (VoronoiCell cell in pathCells)
            {
                cell.edges.ForEach(e => e.OutsideLevel = false);

                cell.CellType = CellType.Path;
                cells.Remove(cell);
            }

            //generate some narrow caves
            int caveAmount = 0;// Rand.Int(3, false);
            List <VoronoiCell> usedCaveCells = new List <VoronoiCell>();
            for (int i = 0; i < caveAmount; i++)
            {
                Vector2     startPoint = Vector2.Zero;
                VoronoiCell startCell  = null;

                var caveCells = new List <VoronoiCell>();

                int maxTries = 5, tries = 0;
                while (tries < maxTries)
                {
                    startCell = cells[Rand.Int(cells.Count, Rand.RandSync.Server)];

                    //find an edge between the cell and the already carved path
                    GraphEdge startEdge =
                        startCell.edges.Find(e => pathCells.Contains(e.AdjacentCell(startCell)));

                    if (startEdge != null)
                    {
                        startPoint  = (startEdge.point1 + startEdge.point2) / 2.0f;
                        startPoint += startPoint - startCell.Center;

                        //get the cells in which the cave will be carved
                        caveCells = GetCells(startCell.Center, 2);
                        //remove cells that have already been "carved" out
                        caveCells.RemoveAll(c => c.CellType == CellType.Path);

                        //if any of the cells have already been used as a cave, continue and find some other cells
                        if (usedCaveCells.Any(c => caveCells.Contains(c)))
                        {
                            continue;
                        }
                        break;
                    }

                    tries++;
                }

                //couldn't find a place for a cave -> abort
                if (tries >= maxTries)
                {
                    break;
                }

                if (!caveCells.Any())
                {
                    continue;
                }

                usedCaveCells.AddRange(caveCells);

                List <VoronoiCell> caveSolidCells;
                var cavePathCells = CaveGenerator.CarveCave(caveCells, startPoint, out caveSolidCells);

                //remove the large cells used as a "base" for the cave (they've now been replaced with smaller ones)
                caveCells.ForEach(c => cells.Remove(c));

                cells.AddRange(caveSolidCells);

                foreach (VoronoiCell cell in cavePathCells)
                {
                    cells.Remove(cell);
                }

                pathCells.AddRange(cavePathCells);

                for (int j = cavePathCells.Count / 2; j < cavePathCells.Count; j += 10)
                {
                    positionsOfInterest.Add(new InterestingPosition(cavePathCells[j].Center, PositionType.Cave));
                }
            }

            for (int x = 0; x < cellGrid.GetLength(0); x++)
            {
                for (int y = 0; y < cellGrid.GetLength(1); y++)
                {
                    cellGrid[x, y].Clear();
                }
            }

            foreach (VoronoiCell cell in cells)
            {
                int x = (int)Math.Floor(cell.Center.X / GridCellSize);
                int y = (int)Math.Floor(cell.Center.Y / GridCellSize);

                if (x < 0 || y < 0 || x >= cellGrid.GetLength(0) || y >= cellGrid.GetLength(1))
                {
                    continue;
                }

                cellGrid[x, y].Add(cell);
            }

            ruins = new List <Ruin>();
            for (int i = 0; i < generationParams.RuinCount; i++)
            {
                GenerateRuin(mainPath);
            }

            startPosition.Y = borders.Height;
            endPosition.Y   = borders.Height;

            List <VoronoiCell> cellsWithBody = new List <VoronoiCell>(cells);

            List <Vector2[]> triangles;
            bodies = CaveGenerator.GeneratePolygons(cellsWithBody, out triangles);

#if CLIENT
            List <VertexPositionTexture> bodyVertices = CaveGenerator.GenerateRenderVerticeList(triangles);

            renderer.SetBodyVertices(bodyVertices.ToArray());
            renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells));

            renderer.PlaceSprites(generationParams.BackgroundSpriteAmount);
#endif

            ShaftBody = BodyFactory.CreateEdge(GameMain.World,
                                               ConvertUnits.ToSimUnits(new Vector2(borders.X, 0)),
                                               ConvertUnits.ToSimUnits(new Vector2(borders.Right, 0)));

            ShaftBody.SetTransform(ConvertUnits.ToSimUnits(new Vector2(0.0f, borders.Height)), 0.0f);

            ShaftBody.BodyType            = BodyType.Static;
            ShaftBody.CollisionCategories = Physics.CollisionLevel;

            bodies.Add(ShaftBody);

            foreach (VoronoiCell cell in cells)
            {
                foreach (GraphEdge edge in cell.edges)
                {
                    edge.cell1 = null;
                    edge.cell2 = null;
                    edge.site1 = null;
                    edge.site2 = null;
                }
            }

            //initialize MapEntities that aren't in any sub (e.g. items inside ruins)
            MapEntity.MapLoaded(null);

            Debug.WriteLine("Generatelevel: " + sw2.ElapsedMilliseconds + " ms");
            sw2.Restart();

            if (mirror)
            {
                Vector2 temp = startPosition;
                startPosition = endPosition;
                endPosition   = temp;
            }

            Debug.WriteLine("**********************************************************************************");
            Debug.WriteLine("Generated a map with " + sites.Count + " sites in " + sw.ElapsedMilliseconds + " ms");
            Debug.WriteLine("Seed: " + seed);
            Debug.WriteLine("**********************************************************************************");
        }
Ejemplo n.º 13
0
 public VoronoiLine(Line L, VoronoiCell neigbor, int indexNeigbor)
 {
     IndexNeigbor = indexNeigbor;
     Neigbor      = neigbor;
     _line        = ParseLine(L);
 }
        public static List <Body> GeneratePolygons(List <VoronoiCell> cells, out List <Vector2[]> renderTriangles, bool setSolid = true)
        {
            renderTriangles = new List <Vector2[]>();
            var bodies = new List <Body>();

            List <Vector2> tempVertices = new List <Vector2>();
            List <Vector2> bodyPoints   = new List <Vector2>();

            for (int n = cells.Count - 1; n >= 0; n--)
            {
                VoronoiCell cell = cells[n];

                bodyPoints.Clear();
                tempVertices.Clear();
                foreach (GraphEdge ge in cell.edges)
                {
                    if (Math.Abs(Vector2.Distance(ge.point1, ge.point2)) < 0.1f)
                    {
                        continue;
                    }
                    if (!tempVertices.Contains(ge.point1))
                    {
                        tempVertices.Add(ge.point1);
                    }
                    if (!tempVertices.Contains(ge.point2))
                    {
                        tempVertices.Add(ge.point2);
                    }

                    VoronoiCell adjacentCell = ge.AdjacentCell(cell);
                    //if (adjacentCell!=null && cells.Contains(adjacentCell)) continue;

                    if (setSolid)
                    {
                        ge.isSolid = (adjacentCell == null || !cells.Contains(adjacentCell));
                    }

                    if (!bodyPoints.Contains(ge.point1))
                    {
                        bodyPoints.Add(ge.point1);
                    }
                    if (!bodyPoints.Contains(ge.point2))
                    {
                        bodyPoints.Add(ge.point2);
                    }
                }

                if (tempVertices.Count < 3 || bodyPoints.Count < 2)
                {
                    cells.RemoveAt(n);
                    continue;
                }

                renderTriangles.AddRange(MathUtils.TriangulateConvexHull(tempVertices, cell.Center));

                if (bodyPoints.Count < 2)
                {
                    continue;
                }

                if (bodyPoints.Count < 3)
                {
                    foreach (Vector2 vertex in tempVertices)
                    {
                        if (bodyPoints.Contains(vertex))
                        {
                            continue;
                        }
                        bodyPoints.Add(vertex);
                        break;
                    }
                }

                for (int i = 0; i < bodyPoints.Count; i++)
                {
                    cell.bodyVertices.Add(bodyPoints[i]);
                    bodyPoints[i] = ConvertUnits.ToSimUnits(bodyPoints[i]);
                }


                if (cell.CellType == CellType.Empty)
                {
                    continue;
                }

                var triangles = MathUtils.TriangulateConvexHull(bodyPoints, ConvertUnits.ToSimUnits(cell.Center));

                Body cellBody = new Body(GameMain.World);

                for (int i = 0; i < triangles.Count; i++)
                {
                    //don't create a triangle if any of the vertices are too close to each other
                    //(apparently Farseer doesn't like polygons with a very small area, see Shape.ComputeProperties)
                    if (Vector2.Distance(triangles[i][0], triangles[i][1]) < 0.05f ||
                        Vector2.Distance(triangles[i][0], triangles[i][2]) < 0.05f ||
                        Vector2.Distance(triangles[i][1], triangles[i][2]) < 0.05f)
                    {
                        continue;
                    }

                    Vertices bodyVertices = new Vertices(triangles[i]);
                    FixtureFactory.AttachPolygon(bodyVertices, 5.0f, cellBody);
                }

                cellBody.UserData            = cell;
                cellBody.SleepingAllowed     = false;
                cellBody.BodyType            = BodyType.Kinematic;
                cellBody.CollisionCategories = Physics.CollisionLevel;

                cell.body = cellBody;
                bodies.Add(cellBody);
            }

            return(bodies);
        }
        public static List <VoronoiCell> CarveCave(List <VoronoiCell> cells, Vector2 startPoint, out List <VoronoiCell> newCells)
        {
            Voronoi voronoi = new Voronoi(1.0);

            List <Vector2> sites = new List <Vector2>();

            float siteInterval = 400.0f;
            float siteVariance = siteInterval * 0.4f;

            Vector4 edges = new Vector4(
                cells.Min(x => x.edges.Min(e => e.point1.X)),
                cells.Min(x => x.edges.Min(e => e.point1.Y)),
                cells.Max(x => x.edges.Max(e => e.point1.X)),
                cells.Max(x => x.edges.Max(e => e.point1.Y)));

            edges.X -= siteInterval * 2;
            edges.Y -= siteInterval * 2;
            edges.Z += siteInterval * 2;
            edges.W += siteInterval * 2;

            Rectangle borders = new Rectangle((int)edges.X, (int)edges.Y, (int)(edges.Z - edges.X), (int)(edges.W - edges.Y));

            for (float x = edges.X + siteInterval; x < edges.Z - siteInterval; x += siteInterval)
            {
                for (float y = edges.Y + siteInterval; y < edges.W - siteInterval; y += siteInterval)
                {
                    if (Rand.Int(5, Rand.RandSync.Server) == 0)
                    {
                        continue;                                         //skip some positions to make the cells more irregular
                    }
                    sites.Add(new Vector2(x, y) + Rand.Vector(siteVariance, Rand.RandSync.Server));
                }
            }

            List <GraphEdge> graphEdges = voronoi.MakeVoronoiGraph(sites, edges.X, edges.Y, edges.Z, edges.W);

            List <VoronoiCell>[,] cellGrid;
            newCells = GraphEdgesToCells(graphEdges, borders, 1000, out cellGrid);

            foreach (VoronoiCell cell in newCells)
            {
                //if the cell is at the edge of the graph, remove it
                if (cell.edges.Any(e =>
                                   e.point1.X == edges.X || e.point1.X == edges.Z ||
                                   e.point1.Y == edges.Z || e.point1.Y == edges.W))
                {
                    cell.CellType = CellType.Removed;
                    continue;
                }

                //remove cells that aren't inside any of the original "base cells"
                if (cells.Any(c => c.IsPointInside(cell.Center)))
                {
                    continue;
                }
                foreach (GraphEdge edge in cell.edges)
                {
                    //mark all the cells adjacent to the removed cell as edges of the cave
                    var adjacent = edge.AdjacentCell(cell);
                    if (adjacent != null && adjacent.CellType != CellType.Removed)
                    {
                        adjacent.CellType = CellType.Edge;
                    }
                }

                cell.CellType = CellType.Removed;
            }

            newCells.RemoveAll(newCell => newCell.CellType == CellType.Removed);

            //start carving from the edge cell closest to the startPoint
            VoronoiCell startCell   = null;
            float       closestDist = 0.0f;

            foreach (VoronoiCell cell in newCells)
            {
                if (cell.CellType != CellType.Edge)
                {
                    continue;
                }

                float dist = Vector2.Distance(startPoint, cell.Center);
                if (dist < closestDist || startCell == null)
                {
                    startCell   = cell;
                    closestDist = dist;
                }
            }

            startCell.CellType = CellType.Path;

            List <VoronoiCell> path = new List <VoronoiCell>()
            {
                startCell
            };
            VoronoiCell pathCell = startCell;

            for (int i = 0; i < newCells.Count / 2; i++)
            {
                var allowedNextCells = new List <VoronoiCell>();
                foreach (GraphEdge edge in pathCell.edges)
                {
                    var adjacent = edge.AdjacentCell(pathCell);
                    if (adjacent == null ||
                        adjacent.CellType == CellType.Removed ||
                        adjacent.CellType == CellType.Edge)
                    {
                        continue;
                    }

                    allowedNextCells.Add(adjacent);
                }

                if (allowedNextCells.Count == 0)
                {
                    if (i > 5)
                    {
                        break;
                    }

                    foreach (GraphEdge edge in pathCell.edges)
                    {
                        var adjacent = edge.AdjacentCell(pathCell);
                        if (adjacent == null ||
                            adjacent.CellType == CellType.Removed)
                        {
                            continue;
                        }

                        allowedNextCells.Add(adjacent);
                    }

                    if (allowedNextCells.Count == 0)
                    {
                        break;
                    }
                }

                //randomly pick one of the adjacent cells as the next cell
                pathCell = allowedNextCells[Rand.Int(allowedNextCells.Count, Rand.RandSync.Server)];

                //randomly take steps further away from the startpoint to make the cave expand further
                if (Rand.Int(4, Rand.RandSync.Server) == 0)
                {
                    float furthestDist = 0.0f;
                    foreach (VoronoiCell nextCell in allowedNextCells)
                    {
                        float dist = Vector2.Distance(startCell.Center, nextCell.Center);
                        if (dist > furthestDist || furthestDist == 0.0f)
                        {
                            furthestDist = dist;
                            pathCell     = nextCell;
                        }
                    }
                }

                pathCell.CellType = CellType.Path;
                path.Add(pathCell);
            }

            //make sure the tunnel is always wider than minPathWidth
            float minPathWidth = 100.0f;

            for (int i = 0; i < path.Count; i++)
            {
                var cell = path[i];
                foreach (GraphEdge edge in cell.edges)
                {
                    if (edge.point1 == edge.point2)
                    {
                        continue;
                    }
                    if (Vector2.Distance(edge.point1, edge.point2) > minPathWidth)
                    {
                        continue;
                    }

                    GraphEdge adjacentEdge = cell.edges.Find(e => e != edge && (e.point1 == edge.point1 || e.point2 == edge.point1));

                    var adjacentCell = adjacentEdge.AdjacentCell(cell);
                    if (i > 0 && (adjacentCell.CellType == CellType.Path || adjacentCell.CellType == CellType.Edge))
                    {
                        continue;
                    }

                    adjacentCell.CellType = CellType.Path;
                    path.Add(adjacentCell);
                }
            }

            return(path);
        }
Ejemplo n.º 16
0
        private void HandleSubCollision(Impact impact, Submarine otherSub)
        {
            Debug.Assert(otherSub != submarine);

            Vector2 normal = impact.Normal;

            if (impact.Target.Body == otherSub.SubBody.Body.FarseerBody)
            {
                normal = -normal;
            }

            float thisMass  = Body.Mass + submarine.DockedTo.Sum(s => s.PhysicsBody.Mass);
            float otherMass = otherSub.PhysicsBody.Mass + otherSub.DockedTo.Sum(s => s.PhysicsBody.Mass);
            float massRatio = otherMass / (thisMass + otherMass);

            float impulse = (Vector2.Dot(impact.Velocity, normal) / 2.0f) * massRatio;

            //apply impact to this sub (the other sub takes care of this in its own collision callback)
            ApplyImpact(impulse, normal, impact.ImpactPos);
            foreach (Submarine dockedSub in submarine.DockedTo)
            {
                dockedSub.SubBody.ApplyImpact(impulse, normal, impact.ImpactPos);
            }

            //find all contacts between this sub and level walls
            List <Contact> levelContacts = new List <Contact>();
            ContactEdge    contactEdge   = Body?.FarseerBody?.ContactList;

            while (contactEdge?.Next != null)
            {
                if (contactEdge.Contact.Enabled &&
                    contactEdge.Other.UserData is VoronoiCell &&
                    contactEdge.Contact.IsTouching)
                {
                    levelContacts.Add(contactEdge.Contact);
                }

                contactEdge = contactEdge.Next;
            }

            if (levelContacts.Count == 0)
            {
                return;
            }

            //if this sub is in contact with the level, apply artifical impacts
            //to both subs to prevent the other sub from bouncing on top of this one
            //and to fake the other sub "crushing" this one against a wall
            Vector2 avgContactNormal = Vector2.Zero;

            foreach (Contact levelContact in levelContacts)
            {
                levelContact.GetWorldManifold(out Vector2 contactNormal, out FixedArray2 <Vector2> temp);

                //if the contact normal is pointing from the sub towards the level cell we collided with, flip the normal
                VoronoiCell cell = levelContact.FixtureB.UserData is VoronoiCell ?
                                   ((VoronoiCell)levelContact.FixtureB.UserData) : ((VoronoiCell)levelContact.FixtureA.UserData);

                var cellDiff = ConvertUnits.ToDisplayUnits(Body.SimPosition) - cell.Center;
                if (Vector2.Dot(contactNormal, cellDiff) < 0)
                {
                    contactNormal = -contactNormal;
                }

                avgContactNormal += contactNormal;

                //apply impacts at the positions where this sub is touching the level
                ApplyImpact((Vector2.Dot(impact.Velocity, contactNormal) / 2.0f) * massRatio / levelContacts.Count, contactNormal, impact.ImpactPos);
            }
            avgContactNormal /= levelContacts.Count;

            //apply an impact to the other sub
            float contactDot = Vector2.Dot(otherSub.PhysicsBody.LinearVelocity, -avgContactNormal);

            if (contactDot > 0.0f)
            {
                if (otherSub.PhysicsBody.LinearVelocity.LengthSquared() > 0.0001f)
                {
                    otherSub.PhysicsBody.LinearVelocity -= Vector2.Normalize(otherSub.PhysicsBody.LinearVelocity) * contactDot;
                }

                impulse = Vector2.Dot(otherSub.Velocity, normal);
                otherSub.SubBody.ApplyImpact(impulse, normal, impact.ImpactPos);
                foreach (Submarine dockedSub in otherSub.DockedTo)
                {
                    dockedSub.SubBody.ApplyImpact(impulse, normal, impact.ImpactPos);
                }
            }
        }
Ejemplo n.º 17
0
 public static Vector3 GetSolidEdgeMiddle(VoronoiCell cell, VoronoiDirection direction)
 {
     return((cell.Corners[direction] + cell.Corners[direction + 1]) * (0.5f * SolidFactor));
 }
Ejemplo n.º 18
0
        public void Generate(VoronoiCell closestPathCell, List <VoronoiCell> caveCells, Rectangle area)
        {
            corridors.Clear();
            rooms.Clear();

            //area = new Rectangle(area.X, area.Y - area.Height, area.Width, area.Height);

            int iterations = Rand.Range(3, 4, false);

            float verticalProbability = Rand.Range(0.4f, 0.6f, false);

            BTRoom baseRoom = new BTRoom(area);

            rooms = new List <BTRoom> {
                baseRoom
            };

            for (int i = 0; i < iterations; i++)
            {
                rooms.ForEach(l => l.Split(0.3f, verticalProbability, 300));

                rooms = baseRoom.GetLeaves();
            }

            foreach (BTRoom leaf in rooms)
            {
                leaf.Scale
                (
                    new Vector2(Rand.Range(0.5f, 0.9f, false), Rand.Range(0.5f, 0.9f, false))
                );
            }

            baseRoom.GenerateCorridors(200, 256, corridors);

            walls = new List <Line>();

            rooms.ForEach(leaf =>
            {
                leaf.CreateWalls();
                //walls.AddRange(leaf.Walls);
            });

            //---------------------------

            BTRoom entranceRoom     = null;
            float  shortestDistance = 0.0f;

            foreach (BTRoom leaf in rooms)
            {
                float distance = Vector2.Distance(leaf.Rect.Center.ToVector2(), closestPathCell.Center);
                if (entranceRoom == null || distance < shortestDistance)
                {
                    entranceRoom     = leaf;
                    shortestDistance = distance;
                }
            }

            rooms.Remove(entranceRoom);

            //---------------------------

            foreach (BTRoom leaf in rooms)
            {
                foreach (Corridor corridor in corridors)
                {
                    leaf.SplitLines(corridor.Rect);
                }

                walls.AddRange(leaf.Walls);
            }


            foreach (Corridor corridor in corridors)
            {
                corridor.CreateWalls();

                foreach (BTRoom leaf in rooms)
                {
                    corridor.SplitLines(leaf.Rect);
                }

                foreach (Corridor corridor2 in corridors)
                {
                    if (corridor == corridor2)
                    {
                        continue;
                    }
                    corridor.SplitLines(corridor2.Rect);
                }


                walls.AddRange(corridor.Walls);
            }

            //leaves.Remove(entranceRoom);

            BTRoom.CalculateDistancesFromEntrance(entranceRoom, corridors);

            allShapes = GenerateStructures(caveCells);
        }
Ejemplo n.º 19
0
 public static float InnerRadius(VoronoiCell cell, VoronoiDirection direction)
 {
     return(Vector3.Lerp(cell.Corners[direction], cell.Corners[direction + 1], 0.5f).magnitude);
 }
Ejemplo n.º 20
0
        public static List <VoronoiCell> GeneratePath(
            List <VoronoiCell> targetCells, List <VoronoiCell> cells, List <VoronoiCell>[,] cellGrid,
            int gridCellSize, Rectangle limits, float wanderAmount = 0.3f, bool mirror = false)
        {
            Stopwatch sw2 = new Stopwatch();

            sw2.Start();

            //how heavily the path "steers" towards the endpoint
            //lower values will cause the path to "wander" more, higher will make it head straight to the end
            wanderAmount = MathHelper.Clamp(wanderAmount, 0.0f, 1.0f);

            List <GraphEdge>   allowedEdges = new List <GraphEdge>();
            List <VoronoiCell> pathCells    = new List <VoronoiCell>();

            VoronoiCell currentCell = targetCells[0];

            currentCell.CellType = CellType.Path;
            pathCells.Add(currentCell);

            int currentTargetIndex = 1;

            int iterationsLeft = cells.Count;

            do
            {
                int edgeIndex = 0;

                allowedEdges.Clear();
                foreach (GraphEdge edge in currentCell.Edges)
                {
                    var adjacentCell = edge.AdjacentCell(currentCell);
                    if (limits.Contains(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y))
                    {
                        allowedEdges.Add(edge);
                    }
                }

                //steer towards target
                if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Server) > wanderAmount || allowedEdges.Count == 0)
                {
                    double smallestDist = double.PositiveInfinity;
                    for (int i = 0; i < currentCell.Edges.Count; i++)
                    {
                        var    adjacentCell = currentCell.Edges[i].AdjacentCell(currentCell);
                        double dist         = MathUtils.Distance(
                            adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y,
                            targetCells[currentTargetIndex].Site.Coord.X, targetCells[currentTargetIndex].Site.Coord.Y);
                        if (dist < smallestDist)
                        {
                            edgeIndex    = i;
                            smallestDist = dist;
                        }
                    }
                }
                //choose random edge (ignoring ones where the adjacent cell is outside limits)
                else
                {
                    edgeIndex = Rand.Int(allowedEdges.Count, Rand.RandSync.Server);
                    if (mirror && edgeIndex > 0)
                    {
                        edgeIndex = allowedEdges.Count - edgeIndex;
                    }
                    edgeIndex = currentCell.Edges.IndexOf(allowedEdges[edgeIndex]);
                }

                currentCell          = currentCell.Edges[edgeIndex].AdjacentCell(currentCell);
                currentCell.CellType = CellType.Path;
                pathCells.Add(currentCell);

                iterationsLeft--;

                if (currentCell == targetCells[currentTargetIndex])
                {
                    currentTargetIndex += 1;
                    if (currentTargetIndex >= targetCells.Count)
                    {
                        break;
                    }
                }
            } while (currentCell != targetCells[targetCells.Count - 1] && iterationsLeft > 0);


            Debug.WriteLine("gettooclose: " + sw2.ElapsedMilliseconds + " ms");
            sw2.Restart();

            return(pathCells);
        }
Ejemplo n.º 21
0
 public static float InnerToOuter(VoronoiCell cell, VoronoiDirection direction)
 {
     return(1f / OuterToInner(cell, direction));
 }
Ejemplo n.º 22
0
        public static List <VoronoiCell> GraphEdgesToCells(List <GraphEdge> graphEdges, Rectangle borders, float gridCellSize, out List <VoronoiCell>[,] cellGrid)
        {
            List <VoronoiCell> cells = new List <VoronoiCell>();

            cellGrid = new List <VoronoiCell> [(int)Math.Ceiling(borders.Width / gridCellSize), (int)Math.Ceiling(borders.Height / gridCellSize)];
            for (int x = 0; x < borders.Width / gridCellSize; x++)
            {
                for (int y = 0; y < borders.Height / gridCellSize; y++)
                {
                    cellGrid[x, y] = new List <VoronoiCell>();
                }
            }

            foreach (GraphEdge ge in graphEdges)
            {
                if (Vector2.DistanceSquared(ge.Point1, ge.Point2) < 0.001f)
                {
                    continue;
                }

                for (int i = 0; i < 2; i++)
                {
                    Site site = (i == 0) ? ge.Site1 : ge.Site2;

                    int x = (int)(Math.Floor((site.Coord.X - borders.X) / gridCellSize));
                    int y = (int)(Math.Floor((site.Coord.Y - borders.Y) / gridCellSize));

                    x = MathHelper.Clamp(x, 0, cellGrid.GetLength(0) - 1);
                    y = MathHelper.Clamp(y, 0, cellGrid.GetLength(1) - 1);

                    VoronoiCell cell = cellGrid[x, y].Find(c => c.Site == site);

                    if (cell == null)
                    {
                        cell = new VoronoiCell(site);
                        cellGrid[x, y].Add(cell);
                        cells.Add(cell);
                    }

                    if (ge.Cell1 == null)
                    {
                        ge.Cell1 = cell;
                    }
                    else
                    {
                        ge.Cell2 = cell;
                    }
                    cell.Edges.Add(ge);
                }
            }

            //add edges to the borders of the graph
            foreach (var cell in cells)
            {
                Vector2?point1 = null, point2 = null;
                foreach (GraphEdge ge in cell.Edges)
                {
                    if (MathUtils.NearlyEqual(ge.Point1.X, borders.X) || MathUtils.NearlyEqual(ge.Point1.X, borders.Right) ||
                        MathUtils.NearlyEqual(ge.Point1.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point1.Y, borders.Bottom))
                    {
                        if (point1 == null)
                        {
                            point1 = ge.Point1;
                        }
                        else if (point2 == null)
                        {
                            if (MathUtils.NearlyEqual(point1.Value, ge.Point1))
                            {
                                continue;
                            }
                            point2 = ge.Point1;
                        }
                    }
                    if (MathUtils.NearlyEqual(ge.Point2.X, borders.X) || MathUtils.NearlyEqual(ge.Point2.X, borders.Right) ||
                        MathUtils.NearlyEqual(ge.Point2.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point2.Y, borders.Bottom))
                    {
                        if (point1 == null)
                        {
                            point1 = ge.Point2;
                        }
                        else
                        {
                            if (MathUtils.NearlyEqual(point1.Value, ge.Point2))
                            {
                                continue;
                            }
                            point2 = ge.Point2;
                        }
                    }
                    if (point1.HasValue && point2.HasValue)
                    {
                        Debug.Assert(point1 != point2);
                        bool point1OnSide = MathUtils.NearlyEqual(point1.Value.X, borders.X) || MathUtils.NearlyEqual(point1.Value.X, borders.Right);
                        bool point2OnSide = MathUtils.NearlyEqual(point2.Value.X, borders.X) || MathUtils.NearlyEqual(point2.Value.X, borders.Right);
                        //one point is one the side, another on top/bottom
                        // -> the cell is in the corner of the level, we need 2 edges
                        if (point1OnSide != point2OnSide)
                        {
                            Vector2 cornerPos = new Vector2(
                                point1.Value.X < borders.Center.X ? borders.X : borders.Right,
                                point1.Value.Y < borders.Center.Y ? borders.Y : borders.Bottom);
                            cell.Edges.Add(
                                new GraphEdge(point1.Value, cornerPos)
                            {
                                Cell1        = cell,
                                IsSolid      = true,
                                Site1        = cell.Site,
                                OutsideLevel = true
                            });
                            cell.Edges.Add(
                                new GraphEdge(point2.Value, cornerPos)
                            {
                                Cell1        = cell,
                                IsSolid      = true,
                                Site1        = cell.Site,
                                OutsideLevel = true
                            });
                        }
                        else
                        {
                            cell.Edges.Add(
                                new GraphEdge(point1.Value, point2.Value)
                            {
                                Cell1        = cell,
                                IsSolid      = true,
                                Site1        = cell.Site,
                                OutsideLevel = true
                            });
                        }
                        break;
                    }
                }
            }

            return(cells);
        }
Ejemplo n.º 23
0
 public static Vector3 GetSecondSolidCorner(VoronoiCell cell, VoronoiDirection direction)
 {
     return(cell.Corners[direction + 1] * SolidFactor);
 }
Ejemplo n.º 24
0
        public static List <VoronoiCell> GeneratePath(List <VoronoiCell> targetCells, List <VoronoiCell> cells)
        {
            Stopwatch sw2 = new Stopwatch();

            sw2.Start();

            List <VoronoiCell> pathCells = new List <VoronoiCell>();

            if (targetCells.Count == 0)
            {
                return(pathCells);
            }

            VoronoiCell currentCell = targetCells[0];

            currentCell.CellType = CellType.Path;
            pathCells.Add(currentCell);

            int currentTargetIndex = 0;

            int iterationsLeft = cells.Count / 2;

            do
            {
                int edgeIndex = 0;

                double smallestDist = double.PositiveInfinity;
                for (int i = 0; i < currentCell.Edges.Count; i++)
                {
                    var adjacentCell = currentCell.Edges[i].AdjacentCell(currentCell);
                    if (adjacentCell == null)
                    {
                        continue;
                    }
                    double dist = MathUtils.Distance(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y, targetCells[currentTargetIndex].Site.Coord.X, targetCells[currentTargetIndex].Site.Coord.Y);
                    dist += MathUtils.Distance(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y, currentCell.Site.Coord.X, currentCell.Site.Coord.Y) * 0.5f;

                    //disfavor short edges to prevent generating a very small passage
                    if (Vector2.DistanceSquared(currentCell.Edges[i].Point1, currentCell.Edges[i].Point2) < 150.0f * 150.0f)
                    {
                        //divide by the number of times the current cell has been used
                        //  prevents the path from getting "stuck" (jumping back and forth between adjacent cells)
                        //  if there's no other way to the destination than going through a short edge
                        dist *= 10.0f / Math.Max(pathCells.Count(c => c == currentCell), 1.0f);
                    }
                    if (dist < smallestDist)
                    {
                        edgeIndex    = i;
                        smallestDist = dist;
                    }
                }

                currentCell          = currentCell.Edges[edgeIndex].AdjacentCell(currentCell);
                currentCell.CellType = CellType.Path;
                pathCells.Add(currentCell);

                iterationsLeft--;

                if (currentCell == targetCells[currentTargetIndex])
                {
                    currentTargetIndex++;
                    if (currentTargetIndex >= targetCells.Count)
                    {
                        break;
                    }
                }
            } while (currentCell != targetCells[targetCells.Count - 1] && iterationsLeft > 0);


            Debug.WriteLine("gettooclose: " + sw2.ElapsedMilliseconds + " ms");
            sw2.Restart();

            return(pathCells);
        }
Ejemplo n.º 25
0
        /// <summary>
        /// Makes the cell rounder by subdividing the edges and offsetting them at the middle
        /// </summary>
        /// <param name="minEdgeLength">How small the individual subdivided edges can be (smaller values produce rounder shapes, but require more geometry)</param>
        public static void RoundCell(VoronoiCell cell, float minEdgeLength = 500.0f, float roundingAmount = 0.5f, float irregularity = 0.1f)
        {
            List <GraphEdge> tempEdges = new List <GraphEdge>();

            foreach (GraphEdge edge in cell.Edges)
            {
                if (!edge.IsSolid || edge.OutsideLevel)
                {
                    tempEdges.Add(edge);
                    continue;
                }

                //If the edge is next to an empty cell and there's another solid cell at the other side of the empty one,
                //don't touch this edge. Otherwise we may end up closing off small passages between cells.
                var adjacentEmptyCell = edge.AdjacentCell(cell);
                if (adjacentEmptyCell?.CellType == CellType.Solid)
                {
                    adjacentEmptyCell = null;
                }
                if (adjacentEmptyCell != null)
                {
                    GraphEdge adjacentEdge = null;
                    //find the edge at the opposite side of the adjacent cell
                    foreach (GraphEdge otherEdge in adjacentEmptyCell.Edges)
                    {
                        if (Vector2.Dot(adjacentEmptyCell.Center - edge.Center, adjacentEmptyCell.Center - otherEdge.Center) < 0 &&
                            otherEdge.AdjacentCell(adjacentEmptyCell)?.CellType == CellType.Solid)
                        {
                            adjacentEdge = otherEdge;
                            break;
                        }
                    }
                    if (adjacentEdge != null)
                    {
                        tempEdges.Add(edge);
                        continue;
                    }
                }

                List <Vector2> edgePoints = new List <Vector2>();
                Vector2        edgeNormal = edge.GetNormal(cell);

                float   edgeLength = Vector2.Distance(edge.Point1, edge.Point2);
                int     pointCount = (int)Math.Max(Math.Ceiling(edgeLength / minEdgeLength), 1);
                Vector2 edgeDir    = edge.Point2 - edge.Point1;
                for (int i = 0; i <= pointCount; i++)
                {
                    if (i == 0)
                    {
                        edgePoints.Add(edge.Point1);
                    }
                    else if (i == pointCount)
                    {
                        edgePoints.Add(edge.Point2);
                    }
                    else
                    {
                        float   centerF        = 0.5f - Math.Abs(0.5f - (i / (float)pointCount));
                        float   randomVariance = Rand.Range(0, irregularity, Rand.RandSync.Server);
                        Vector2 extrudedPoint  =
                            edge.Point1 +
                            edgeDir * (i / (float)pointCount) +
                            edgeNormal * edgeLength * (roundingAmount + randomVariance) * centerF;

                        var  nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 2);
                        bool isInside    = false;
                        foreach (var nearbyCell in nearbyCells)
                        {
                            if (nearbyCell == cell || nearbyCell.CellType != CellType.Solid)
                            {
                                continue;
                            }
                            //check if extruding the edge causes it to go inside another one
                            if (nearbyCell.IsPointInside(extrudedPoint))
                            {
                                isInside = true;
                                break;
                            }
                            //check if another edge will be inside this cell after the extrusion
                            Vector2 triangleCenter = (edge.Point1 + edge.Point2 + extrudedPoint) / 3;
                            foreach (GraphEdge nearbyEdge in nearbyCell.Edges)
                            {
                                if (!MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point1, extrudedPoint) &&
                                    !MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point2, extrudedPoint) &&
                                    !MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point1, edge.Point2))
                                {
                                    isInside = true;
                                    break;
                                }
                            }
                            if (isInside)
                            {
                                break;
                            }
                        }

                        if (!isInside)
                        {
                            edgePoints.Add(extrudedPoint);
                        }
                    }
                }

                for (int i = 0; i < edgePoints.Count - 1; i++)
                {
                    tempEdges.Add(new GraphEdge(edgePoints[i], edgePoints[i + 1])
                    {
                        Cell1          = edge.Cell1,
                        Cell2          = edge.Cell2,
                        IsSolid        = edge.IsSolid,
                        Site1          = edge.Site1,
                        Site2          = edge.Site2,
                        OutsideLevel   = edge.OutsideLevel,
                        NextToCave     = edge.NextToCave,
                        NextToMainPath = edge.NextToMainPath,
                        NextToSidePath = edge.NextToSidePath
                    });
                }
            }

            cell.Edges = tempEdges;
        }
Ejemplo n.º 26
0
        public static List <Body> GeneratePolygons(List <VoronoiCell> cells, Level level, out List <Vector2[]> renderTriangles, bool setSolid = true)
        {
            renderTriangles = new List <Vector2[]>();
            var bodies = new List <Body>();

            List <Vector2> tempVertices = new List <Vector2>();
            List <Vector2> bodyPoints   = new List <Vector2>();

            Body cellBody = new Body(GameMain.World)
            {
                SleepingAllowed     = false,
                BodyType            = BodyType.Static,
                CollisionCategories = Physics.CollisionLevel
            };

            bodies.Add(cellBody);

            for (int n = cells.Count - 1; n >= 0; n--)
            {
                VoronoiCell cell = cells[n];

                bodyPoints.Clear();
                tempVertices.Clear();
                foreach (GraphEdge ge in cell.edges)
                {
                    if (Math.Abs(Vector2.Distance(ge.point1, ge.point2)) < 0.1f)
                    {
                        continue;
                    }
                    if (!tempVertices.Contains(ge.point1))
                    {
                        tempVertices.Add(ge.point1);
                    }
                    if (!tempVertices.Contains(ge.point2))
                    {
                        tempVertices.Add(ge.point2);
                    }

                    VoronoiCell adjacentCell = ge.AdjacentCell(cell);
                    //if (adjacentCell!=null && cells.Contains(adjacentCell)) continue;

                    if (setSolid)
                    {
                        ge.isSolid = (adjacentCell == null || !cells.Contains(adjacentCell));
                    }

                    if (!bodyPoints.Contains(ge.point1))
                    {
                        bodyPoints.Add(ge.point1);
                    }
                    if (!bodyPoints.Contains(ge.point2))
                    {
                        bodyPoints.Add(ge.point2);
                    }
                }

                if (tempVertices.Count < 3 || bodyPoints.Count < 2)
                {
                    cells.RemoveAt(n);
                    continue;
                }

                renderTriangles.AddRange(MathUtils.TriangulateConvexHull(tempVertices, cell.Center));

                if (bodyPoints.Count < 2)
                {
                    continue;
                }

                if (bodyPoints.Count < 3)
                {
                    foreach (Vector2 vertex in tempVertices)
                    {
                        if (bodyPoints.Contains(vertex))
                        {
                            continue;
                        }
                        bodyPoints.Add(vertex);
                        break;
                    }
                }

                for (int i = 0; i < bodyPoints.Count; i++)
                {
                    cell.bodyVertices.Add(bodyPoints[i]);
                    bodyPoints[i] = ConvertUnits.ToSimUnits(bodyPoints[i]);
                }

                if (cell.CellType == CellType.Empty)
                {
                    continue;
                }

                cellBody.UserData = cell;
                var triangles = MathUtils.TriangulateConvexHull(bodyPoints, ConvertUnits.ToSimUnits(cell.Center));

                for (int i = 0; i < triangles.Count; i++)
                {
                    //don't create a triangle if any of the vertices are too close to each other
                    //(apparently Farseer doesn't like polygons with a very small area, see Shape.ComputeProperties)
                    if (Vector2.DistanceSquared(triangles[i][0], triangles[i][1]) < 0.006f ||
                        Vector2.DistanceSquared(triangles[i][0], triangles[i][2]) < 0.006f ||
                        Vector2.DistanceSquared(triangles[i][1], triangles[i][2]) < 0.006f)
                    {
                        continue;
                    }

                    Vertices bodyVertices = new Vertices(triangles[i]);
                    var      newFixture   = FixtureFactory.AttachPolygon(bodyVertices, 5.0f, cellBody);
                    newFixture.UserData = cell;

                    if (newFixture.Shape.MassData.Area < FarseerPhysics.Settings.Epsilon)
                    {
                        DebugConsole.ThrowError("Invalid triangle created by CaveGenerator (" + triangles[i][0] + ", " + triangles[i][1] + ", " + triangles[i][2] + ")");
                        GameAnalyticsManager.AddErrorEventOnce(
                            "CaveGenerator.GeneratePolygons:InvalidTriangle",
                            GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                            "Invalid triangle created by CaveGenerator (" + triangles[i][0] + ", " + triangles[i][1] + ", " + triangles[i][2] + "). Seed: " + level.Seed);
                    }
                }

                cell.body = cellBody;
            }

            return(bodies);
        }