Exemple #1
0
        /// <summary>
        /// Generates collision data for a circle shaped entity and a polygon
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static CollisionData CirclePolygon(Circle a, Polygon b)
        {
            Matrix  rotation        = Matrix.CreateRotationZ(b.Entity.Orientation);
            Matrix  inverseRotation = Matrix.CreateTranslation(new Vector3(-b.Entity.Position, 0)) * Matrix.CreateRotationZ(-b.Entity.Orientation) * Matrix.CreateTranslation(new Vector3(b.Entity.Position, 0));
            Vector2 circlePosition  = Vector2.Transform(a.Entity.Position, inverseRotation);

            Vector2 delta = b.Entity.Position - circlePosition;

            Polygon.Projection proj;
            CollisionData      result = new CollisionData(float.MaxValue);

            float inter1 = 0, inter2 = 0;

            for (int i = 0; i < b.normals.Length; i++)
            {
                proj   = a.Project(b.normals[i], -delta);
                inter1 = b.projections[i].Max - proj.Min;
                inter2 = -(b.projections[i].Min - proj.Max);

#if DEBUG && SATDEBUG
                Vector2 p = b.Entity.Position;
                Integrator.line(p + Vector2.TransformNormal(b.normals[i], rotation) * proj.Min, p + Vector2.TransformNormal(b.normals[i], rotation) * proj.Max, Color.LimeGreen);
#endif

                if (inter1 < 0 || inter2 < 0)
                {
                    return(CollisionData.Empty);
                }

                if (inter1 < inter2 && inter1 < result.Interpenetration)
                {
                    result.Interpenetration = inter1;
                    result.Normal           = -Vector2.TransformNormal(b.normals[i], rotation);
                }
                else if (inter2 < result.Interpenetration)
                {
                    result.Interpenetration = inter2;
                    result.Normal           = Vector2.TransformNormal(b.normals[i], rotation);
                }
            }

            Vector2 closest = Vector2.UnitX;
            float   maxDist = float.MaxValue;
            for (int i = 0; i < b.Vertices.Length; i++)
            {
                Vector2 v = b.Vertices[i] + b.Entity.Position;
                Vector2 d = v - circlePosition;

                if (d.LengthSquared() < maxDist)
                {
                    closest = d;
                    maxDist = d.LengthSquared();
                }
            }
            Vector2 normal = closest.Normalized();

            proj = b.Project(normal, delta);
            Polygon.Projection self = a.Project(normal, Vector2.Zero);
            inter1 = self.Max - proj.Min;
            inter2 = -(self.Min - proj.Max);

#if DEBUG && SATDEBUG
            Vector2 p2 = a.Entity.Position;
            Integrator.line(p2 + Vector2.TransformNormal(normal, rotation) * proj.Min, p2 + Vector2.TransformNormal(normal, rotation) * proj.Max, Color.LimeGreen);
#endif

            if (inter1 < 0 || inter2 < 0)
            {
                return(CollisionData.Empty);
            }

            if (inter1 < inter2 && inter1 < result.Interpenetration)
            {
                result.Interpenetration = inter1;
                result.Normal           = Vector2.TransformNormal(normal, rotation);
            }
            else if (inter2 < result.Interpenetration)
            {
                result.Interpenetration = inter2;
                result.Normal           = -Vector2.TransformNormal(normal, rotation);
            }

#if DEBUG && SATDEBUG
            Vector2 pos = a.Entity.Position;
            Integrator.line(pos, pos + result.Normal * result.Interpenetration, Color.White);
#endif

            return(result);
        }
Exemple #2
0
        /// <summary>
        /// Gneerates collision data for two polygon shaped entities
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static CollisionData PolygonPolygon(Polygon a, Polygon b)
        {
            Vector2 delta = b.Entity.Position - a.Entity.Position;
            float   inter1 = 0, inter2 = 0;

            CollisionData result = new CollisionData(float.MaxValue);

            Polygon lookingAt = a;

            delta = Vector2.Transform(delta, Matrix.CreateRotationZ(-b.Entity.Orientation));
            Matrix rot = Matrix.CreateRotationZ(a.Entity.Orientation - b.Entity.Orientation);

            for (int i = 0; i < a.normals.Length; i++)
            {
                Polygon.Projection proj = b.Project(Vector2.TransformNormal(a.normals[i], rot), delta);
                inter1 = a.projections[i].Max - proj.Min;
                inter2 = -(a.projections[i].Min - proj.Max);

#if DEBUG && SATDEBUG
                Vector2 p = a.Entity.Position;
                Matrix  r = Matrix.CreateRotationZ(a.Entity.Orientation);
                Integrator.line(p + Vector2.TransformNormal(a.normals[i], r) * proj.Min, p + Vector2.TransformNormal(a.normals[i], r) * proj.Max, Color.HotPink);
#endif

                if (inter1 < 0 || inter2 < 0)
                {
                    return(CollisionData.Empty);
                }

                if (inter1 < inter2 && inter1 < result.Interpenetration)
                {
                    result.Interpenetration = inter1;
                    result.Normal           = Vector2.TransformNormal(a.normals[i], Matrix.CreateRotationZ(a.Entity.Orientation));
                    lookingAt = b;
                }
                else if (inter2 < result.Interpenetration)
                {
                    result.Interpenetration = inter2;
                    result.Normal           = -Vector2.TransformNormal(a.normals[i], Matrix.CreateRotationZ(a.Entity.Orientation));
                    lookingAt = b;
                }
            }

            delta = b.Entity.Position - a.Entity.Position;
            delta = Vector2.Transform(delta, Matrix.CreateRotationZ(-a.Entity.Orientation));
            rot   = Matrix.CreateRotationZ(b.Entity.Orientation - a.Entity.Orientation);

            for (int i = 0; i < b.normals.Length; i++)
            {
                Polygon.Projection proj = a.Project(Vector2.TransformNormal(b.normals[i], rot), -delta);
                inter1 = b.projections[i].Max - proj.Min;
                inter2 = -(b.projections[i].Min - proj.Max);

#if DEBUG && SATDEBUG
                Vector2 p = b.Entity.Position;
                Matrix  r = Matrix.CreateRotationZ(b.Entity.Orientation);
                Integrator.line(p + Vector2.TransformNormal(b.normals[i], r) * proj.Min, p + Vector2.TransformNormal(b.normals[i], r) * proj.Max, Color.LimeGreen);
#endif

                if (inter1 < 0 || inter2 < 0)
                {
                    return(CollisionData.Empty);
                }

                if (inter1 < inter2 && inter1 < result.Interpenetration)
                {
                    result.Interpenetration = inter1;
                    result.Normal           = -Vector2.TransformNormal(b.normals[i], Matrix.CreateRotationZ(b.Entity.Orientation));
                    lookingAt = a;
                }
                else if (inter2 < result.Interpenetration)
                {
                    result.Interpenetration = inter2;
                    result.Normal           = Vector2.TransformNormal(b.normals[i], Matrix.CreateRotationZ(b.Entity.Orientation));
                    lookingAt = a;
                }
            }

            int       dir     = lookingAt == a ? -1 : 1;
            float     bestDot = 0;
            Vector2   guess   = Vector2.Zero;
            Vector2[] verts   = lookingAt.RotatedVertices(lookingAt.Entity.Orientation);
            for (int i = 0; i < verts.Length; i++)
            {
                Vector2 v = verts[i];
                float   d = Vector2.Dot(v, result.Normal) * dir;
                if (d < bestDot - SATPositioningAngle)
                {
                    bestDot = d;
                    guess   = v;
                }
                else if (d < bestDot + SATPositioningAngle)
                {
                    bestDot = d;
                    guess  += v;
                    guess  *= .5f;
                }
            }
            result.Position = lookingAt.Entity.Position + guess;

#if DEBUG && SATDEBUG
            Vector2 pos = result.Position;
            Integrator.line(pos, pos + result.Normal * result.Interpenetration * 10, Color.White);
#endif

            return(result);
        }