Esempio n. 1
0
        /// <param name="depth">Number of iterations.</param>
        /// <param name="clipModels">Adds the ClipModel instances to this list.</param>
        private static List<ClipModel> _getClipModels(IRenderable entity, Model model, IList<IPortal> portalList, Vector2 centerPoint, IPortal portalEnter, Matrix4 modelMatrix, int depth, int count)
        {
            List<ClipModel> clipModels = new List<ClipModel>();
            if (depth <= 0)
            {
                return clipModels;
            }

            List<IPortal> collisions = Portal.GetCollisions(
                centerPoint,
                Vector2Ext.Transform(model.GetWorldConvexHull(),
                entity.GetWorldTransform().GetMatrix() * modelMatrix),
                portalList,
                PORTAL_CLIP_MARGIN);

            List<LineF> clipLines = new List<LineF>();
            foreach (IPortal portal in collisions)
            {
                Vector2[] pv = Portal.GetWorldVerts(portal);
                LineF clipLine = new LineF(pv);

                LineF portalLine = new LineF(pv);
                Vector2 normal = portal.WorldTransform.GetRight();
                if (portal.WorldTransform.MirrorX)
                {
                    normal = -normal;
                }

                Vector2 portalNormal = portal.WorldTransform.Position + normal;
                if (portalLine.GetSideOf(centerPoint) != portalLine.GetSideOf(portalNormal))
                {
                    normal *= Portal.EnterMinDistance;
                }
                else
                {
                    clipLine = clipLine.Reverse();
                    normal *= -Portal.EnterMinDistance;
                }

                clipLines.Add(clipLine);
                if (portalEnter == null || portal != portalEnter.Linked)
                {
                    Vector2 centerPointNext = Vector2Ext.Transform(portal.WorldTransform.Position + normal, Portal.GetLinkedMatrix(portal));
                    clipModels.AddRange(_getClipModels(entity, model, portalList, centerPointNext, portal, modelMatrix * Portal.GetLinkedMatrix(portal), depth - 1, count + 1));
                }
            }
            clipModels.Add(new ClipModel(entity, model, clipLines.ToArray(), modelMatrix));
            return clipModels;
        }
Esempio n. 2
0
        public static Vector2 GetLocalOrigin(Body body)
        {
            BodyData data = GetData(body);
            Vector2 center;
            //If this isn't the root body then the center point will be just outside of the parent portal.
            if (data.IsChild)
            {
                LineF portalLine = new LineF(Portal.GetWorldVerts(data.BodyParent.Portal.Linked));
                Vector2 offset = portalLine.Delta.PerpendicularLeft.Normalized() * 0.01f;
                center = portalLine.Center + offset;
                if (portalLine.GetSideOf(center + offset) == portalLine.GetSideOf(body.Position))
                {
                    center = portalLine.Center - offset;
                }
            }
            else
            {
                center = (Vector2)body.Position;
            }

            return center;
        }
Esempio n. 3
0
        /// <summary>
        /// Determine a position that is sufficiently far away from all portals so that it isn't ambiguous which side of a 
        /// portal the position is at.
        /// </summary>
        /// <param name="portals"></param>
        /// <param name="portalPrevious">The last portal that was exited.</param>
        /// <param name="transform"></param>
        /// <param name="velocity"></param>
        private static Transform2 AddMargin(IEnumerable<IPortal> portals, IPortal portalPrevious, Transform2 transform, Transform2 velocity)
        {
            transform = transform.ShallowClone();
            foreach (IPortal p in portals)
            {
                if (!Portal.IsValid(p))
                {
                    continue;
                }
                LineF exitLine = new LineF(Portal.GetWorldVerts(p));
                Vector2 position = transform.Position;
                double distanceToPortal = MathExt.PointLineDistance(position, exitLine, true);
                if (distanceToPortal < Portal.EnterMinDistance)
                {
                    Vector2 exitNormal = p.WorldTransform.GetRight();
                    Side sideOf;
                    if (p == portalPrevious)
                    {
                        sideOf = exitLine.GetSideOf(position + velocity.Position);
                    }
                    else
                    {
                        sideOf = exitLine.GetSideOf(position - velocity.Position);
                    }
                    if (sideOf != exitLine.GetSideOf(exitNormal + p.WorldTransform.Position))
                    {
                        exitNormal = -exitNormal;
                    }

                    Vector2 pos = exitNormal * (Portal.EnterMinDistance - (float)distanceToPortal);
                    transform.Position += pos;
                    break;
                }
            }
            return transform;
        }
Esempio n. 4
0
        /// <summary>
        /// Returns true if a contact should not be disabled due to portal clipping.
        /// </summary>
        private bool IsContactValid(Contact contact)
        {
            FixtureData[] fixtureData = new FixtureData[2];
            fixtureData[0] = FixtureExt.GetData(contact.FixtureA);
            fixtureData[1] = FixtureExt.GetData(contact.FixtureB);

            BodyData[] bodyData = new BodyData[2];
            bodyData[0] = BodyExt.GetData(contact.FixtureA.Body);
            bodyData[1] = BodyExt.GetData(contact.FixtureB.Body);

            Xna.Vector2 normal;
            FixedArray2<Xna.Vector2> vList;
            contact.GetWorldManifold(out normal, out vList);

            if (bodyData[0].IsChild || bodyData[1].IsChild)
            {
                if (bodyData[0].IsChild && bodyData[1].IsChild)
                {
                    //return true;
                }

                int childIndex = bodyData[0].IsChild ? 0 : 1;
                int otherIndex = bodyData[0].IsChild ? 1 : 0;
                BodyData bodyDataChild = bodyData[childIndex];
                BodyData bodyDataOther = bodyData[otherIndex];
                FixtureData fixtureDataChild = fixtureData[childIndex];
                FixtureData fixtureDataOther = fixtureData[otherIndex];

            }

            //Contact is invalid if it is between two fixtures where one fixture is colliding with a portal on the other fixture.
            if (fixtureData[0].IsPortalParentless() && fixtureData[1].IsPortalParentless())
            {
                for (int i = 0; i < fixtureData.Length; i++)
                {
                    int iNext = (i + 1) % fixtureData.Length;
                    var intersection = fixtureData[iNext].GetPortalChildren().Intersect(fixtureData[i].PortalCollisions);
                    if (intersection.Count() > 0)
                    {
                        //Debug.Fail("Fixtures with portal collisions should be filtered.");
                        return false;
                    }
                }
            }

            //Contact is invalid if it is too close to a portal.
            foreach (IPortal p in Scene.GetPortalList())
            {
                if (!Portal.IsValid(p))
                {
                    continue;
                }
                FixturePortal portal = p as FixturePortal;
                if (portal != null)
                {
                    //Don't consider this portal if its fixtures are part of the contact.
                    if (fixtureData[0].PartOfPortal(portal) || fixtureData[1].PartOfPortal(portal))
                    {
                        continue;
                    }

                    LineF line = new LineF(Portal.GetWorldVerts(portal));
                    double[] vDist = new double[] {
                        MathExt.PointLineDistance(vList[0], line, true),
                        MathExt.PointLineDistance(vList[1], line, true)
                    };
                    //Only consider contacts that are between the fixture this portal is parented too and some other fixture.
                    if (contact.FixtureA == FixtureExt.GetFixtureAttached(portal) || contact.FixtureB == FixtureExt.GetFixtureAttached(portal))
                    {
                        if (contact.Manifold.PointCount == 1)
                        {
                            if (vDist[0] < FixturePortal.CollisionMargin)
                            {
                                return false;
                            }
                        }
                        else if (vDist[0] < FixturePortal.CollisionMargin && vDist[1] < FixturePortal.CollisionMargin)
                        {
                            return false;
                        }
                    }
                }
            }

            //Contact is invalid if it is on the opposite side of a colliding portal.
            for (int i = 0; i < fixtureData.Length; i++)
            {
                int iNext = (i + 1) % fixtureData.Length;
                foreach (IPortal portal in fixtureData[i].PortalCollisions)
                {
                    LineF line = new LineF(Portal.GetWorldVerts(portal));

                    FixturePortal cast = portal as FixturePortal;
                    if (cast != null)
                    {
                        if (fixtureData[i].PartOfPortal(cast) || fixtureData[iNext].PartOfPortal(cast))
                        {
                            continue;
                        }
                    }

                    //Contact is invalid if it is on the opposite side of the portal from its body origin.
                    //Xna.Vector2 pos = BodyExt.GetData(fixtureData[i].Fixture.Body).PreviousPosition;
                    Vector2 pos = BodyExt.GetLocalOrigin(fixtureData[i].Fixture.Body);
                    bool oppositeSides0 = line.GetSideOf(vList[0]) != line.GetSideOf(pos);
                    Debug.Assert(contact.Manifold.PointCount > 0);
                    if (contact.Manifold.PointCount == 1)
                    {
                        if (oppositeSides0)
                        {
                            return false;
                        }
                    }
                    else
                    //else if (line.GetSideOf((vList[0] + vList[1])/2) != line.GetSideOf(pos))
                    {
                        bool oppositeSides1 = line.GetSideOf(vList[1]) != line.GetSideOf(pos);
                        /*if (oppositeSides0 && oppositeSides1)
                        {
                            return false;
                        }
                        if (oppositeSides0)
                        {
                            contact.Manifold.Points[0] = contact.Manifold.Points[1];
                        }
                        contact.Manifold.PointCount = 1;

                        return true;*/
                        if (!oppositeSides0 && !oppositeSides1)
                        {
                            continue;
                        }
                        if (!fixtureData[iNext].PortalCollisions.Contains(portal) || !(oppositeSides0 || oppositeSides1))
                        {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
Esempio n. 5
0
        /// <summary>
        /// Get all valid portals that collide with a polygon.  Portals can occlude eachother.
        /// </summary>
        /// <param name="margin">Minimum distance inside of polygon for a portal collision to count.  
        /// Useful for avoiding round off errors.</param>
        public static List<IPortal> GetCollisions(Vector2 center, IList<Vector2> polygon, IList<IPortal> portals, double margin = 0)
        {
            List<IPortal> collisions = new List<IPortal>();
            foreach (IPortal p in portals.Where(item => IsValid(item)))
            {
                LineF portalLine = new LineF(GetWorldVerts(p));
                if (MathExt.LineInPolygon(portalLine, polygon) &&
                    (margin == 0 ||
                    MathExt.PointPolygonDistance(portalLine[0], polygon) > margin ||
                    MathExt.PointPolygonDistance(portalLine[1], polygon) > margin))
                {
                    collisions.Add(p);
                }
            }

            var ordered = collisions.OrderBy(item => (item.WorldTransform.Position - center).Length).ToList();
            for (int i = 0; i < ordered.Count; i++)
            {
                IPortal portal = ordered[i];
                for (int j = ordered.Count - 1; j > i; j--)
                {
                    LineF currentLine = new LineF(GetWorldVerts(ordered[i]));
                    LineF checkLine = new LineF(GetWorldVerts(ordered[j]));
                    Side checkSide = currentLine.GetSideOf(checkLine);
                    if (checkSide != currentLine.GetSideOf(center))
                    {
                        ordered.RemoveAt(j);
                    }
                }
            }
            return ordered.ToList();
        }
Esempio n. 6
0
 public void GetSideOfTest14()
 {
     float rot = 0;
     for (int i = 0; i < 10; i++)
     {
         Vector2 v0 = new Vector2((float)Math.Cos(rot), (float)Math.Sin(rot));
         Vector2 v1 = new Vector2((float)Math.Cos(rot + Math.PI), (float)Math.Sin(rot + Math.PI));
         Vector2 v2 = new Vector2((float)Math.Cos(rot + Math.PI/2), (float)Math.Sin(rot + Math.PI/2));
         LineF line = new LineF(v0, v1);
         Assert.IsTrue(line.GetSideOf(v2) == Side.Right);
     }
 }
Esempio n. 7
0
 public void GetSideOfTest13()
 {
     LineF line = new LineF(new Vector2(5, 20), new Vector2(10, 25));
     LineF lineCheck = new LineF(new Vector2(-25, 10), new Vector2(25, 10));
     Assert.IsTrue(line.GetSideOf(lineCheck) == Side.Neither);
 }
Esempio n. 8
0
 public void GetSideOfTest12()
 {
     LineF line = new LineF(new Vector2(-5, 20), new Vector2(20, 0));
     Assert.IsFalse(line.GetSideOf(new Vector2(0, 0)) == Side.Left);
 }
Esempio n. 9
0
 public void GetSideOfTest11()
 {
     LineF line = new LineF(new Vector2(-1, 1), new Vector2(0, 0));
     Assert.IsFalse(line.GetSideOf(new Vector2(0, -100)) == Side.Left);
 }
Esempio n. 10
0
 public void GetSideOfTest1()
 {
     LineF line = new LineF(new Vector2(0, 0), new Vector2(1, 0));
     Assert.IsTrue(line.GetSideOf(new Vector2(0, 100)) == Side.Left);
 }
Esempio n. 11
0
 private static bool _isPortalValid(IPortal previous, IPortal next, Vector2 viewPos)
 {
     //skip this portal if it isn't linked
     if (!Portal.IsValid(next))
     {
         return false;
     }
     //or it's the exit portal
     if (previous != null && next == previous.Linked)
     {
         return false;
     }
     //or if the portal is one sided and the view point is on the wrong side
     Vector2[] pv2 = Portal.GetWorldVerts(next);
     LineF portalLine = new LineF(pv2);
     if (next.OneSided)
     {
         if (portalLine.GetSideOf(pv2[0] + next.WorldTransform.GetRight()) != portalLine.GetSideOf(viewPos))
         {
             return false;
         }
     }
     //or if this portal isn't inside the fov of the exit portal
     if (previous != null)
     {
         LineF portalEnterLine = new LineF(Portal.GetWorldVerts(previous.Linked));
         if (!portalEnterLine.IsInsideFOV(viewPos, portalLine))
         {
             return false;
         }
     }
     return true;
 }
Esempio n. 12
0
        private static List<ClipPolygon> _getClipModels(IList<Vector2> polygon, IList<IPortal> portalList, Vector2 centerPoint, IPortal portalEnter, Matrix4 modelMatrix, int depth, int count)
        {
            List<ClipPolygon> clipModels = new List<ClipPolygon>();
            if (depth <= 0)
            {
                return clipModels;
            }
            List<IPortal> collisions = new List<IPortal>();
            foreach (IPortal portal in portalList)
            {
                if (!Portal.IsValid(portal))
                {
                    continue;
                }

                LineF portalLine = new LineF(Portal.GetWorldVerts(portal));

                List<Vector2> convexHull = new List<Vector2>(polygon);
                convexHull.Add(centerPoint);
                convexHull = (List<Vector2>)Vector2Ext.Transform(MathExt.GetConvexHull(convexHull, true), modelMatrix);

                if (MathExt.LinePolygonDistance(portalLine, convexHull) < PORTAL_CLIP_MARGIN)
                {
                    collisions.Add(portal);
                }
            }

            collisions = collisions.OrderBy(item => (item.WorldTransform.Position - centerPoint).Length).ToList();
            for (int i = 0; i < collisions.Count; i++)
            {
                IPortal portal = collisions[i];
                for (int j = collisions.Count - 1; j > i; j--)
                {
                    LineF currentLine = new LineF(Portal.GetWorldVerts(collisions[i]));
                    LineF checkLine = new LineF(Portal.GetWorldVerts(collisions[j]));
                    Side checkSide = currentLine.GetSideOf(checkLine);
                    if (checkSide != currentLine.GetSideOf(centerPoint))
                    {
                        collisions.RemoveAt(j);
                    }
                }
            }

            List<LineF> clipLines = new List<LineF>();
            foreach (IPortal portal in collisions)
            {
                Vector2[] pv = Portal.GetWorldVerts(portal);
                LineF clipLine = new LineF(pv);

                LineF portalLine = new LineF(pv);
                Vector2 normal = portal.WorldTransform.GetRight();
                if (portal.WorldTransform.MirrorX)
                {
                    normal = -normal;
                }

                Vector2 portalNormal = portal.WorldTransform.Position + normal;
                if (portalLine.GetSideOf(centerPoint) != portalLine.GetSideOf(portalNormal))
                {
                    normal *= Portal.EnterMinDistance;
                }
                else
                {
                    clipLine = clipLine.Reverse();
                    normal *= -Portal.EnterMinDistance;
                }

                clipLines.Add(clipLine);
                if (portalEnter == null || portal != portalEnter.Linked)
                {
                    Vector2 centerPointNext = Vector2Ext.Transform(portal.WorldTransform.Position + normal, Portal.GetLinkedMatrix(portal));
                    clipModels.AddRange(_getClipModels(polygon, portalList, centerPointNext, portal, modelMatrix * Portal.GetLinkedMatrix(portal), depth - 1, count + 1));
                }
            }
            clipModels.Add(new ClipPolygon(polygon, clipLines.ToArray(), modelMatrix));
            return clipModels;
        }