private static FSATAxis[] GetRectAxis_sat(ref Vector2[] vertices)
        {
            FSATAxis[] res = new FSATAxis[2];

            res[0]             = new FSATAxis();
            res[0].Edge        = new FEdge(vertices[0], vertices[1], 0, 1);
            res[0].Axis        = MeshUtils.CalcNormal(res[0].Edge.AV, res[0].Edge.BV);
            res[0].Edge.Normal = res[0].Axis;

            res[1]             = new FSATAxis();
            res[1].Edge        = new FEdge(vertices[1], vertices[2], 1, 2);
            res[1].Axis        = MeshUtils.CalcNormal(res[1].Edge.AV, res[1].Edge.BV);
            res[1].Edge.Normal = res[1].Axis;

            return(res);
        }
        private static FSATAxis[] GetAxis_sat(ref Vector2[] vertices)
        {
            FSATAxis[] res = new FSATAxis[vertices.Length];

            for (int i = 0; i < res.Length; i++)
            {
                int nxtVertex = CollMeshUtils.RightIndex(i, vertices.Length);

                res[i]             = new FSATAxis();
                res[i].Edge        = new FEdge(vertices[i], vertices[nxtVertex], i, nxtVertex);
                res[i].Axis        = MeshUtils.CalcNormal(res[i].Edge.AV, res[i].Edge.BV);
                res[i].Edge.Normal = res[i].Axis;
            }

            return(res);
        }
        private static Vector2 ProjOntoAxis_sat(FSATAxis axis, ref Vector2[] vertices)
        {
            Vector2 minMax = Vector2.zero;

            minMax.x = Vector2.Dot(axis.Axis, vertices[0]);
            minMax.y = minMax.x;

            for (int i = 1; i < vertices.Length; i++)
            {
                float d = Vector2.Dot(axis.Axis, vertices[i]);

                if (d < minMax.x)
                {
                    minMax.x = d;
                }
                else if (d > minMax.y)
                {
                    minMax.y = d;
                }
            }

            return(minMax);
        }
        private static FSATAxis GetCircleAxis_sat(Vector2 circleCenter, ref Vector2[] polyVertices)
        {
            FSATAxis bestAxis = new FSATAxis()
            {
                Axis = circleCenter - polyVertices[0]
            };
            float shortestDist = bestAxis.Axis.sqrMagnitude;

            for (int i = 1; i < polyVertices.Length; i++)
            {
                Vector2 rel  = circleCenter - polyVertices[i];
                float   dist = rel.sqrMagnitude;

                if (dist < shortestDist)
                {
                    shortestDist  = dist;
                    bestAxis.Axis = rel;
                }
            }

            bestAxis.Axis.Normalize();

            return(bestAxis);
        }
        private static bool SphereSAT(FCollPoly p1, FCollSphere p2, out CollisionContact manifold)
        {
            FSATAxis[] axisPoly      = GetAxis_sat(ref p1.Vertices);
            FSATAxis   circleAxis    = GetCircleAxis_sat(p2.Center, ref p1.Vertices);
            FSATAxis   collisionAxis = axisPoly[0];
            float      penetration   = 100000f;

            manifold = null;

            for (int i = 0; i < axisPoly.Length; i++)
            {
                Vector2 proj1        = ProjOntoAxis_sat(axisPoly[i], ref p1.Vertices);
                float   circleCenter = Vector2.Dot(p2.Center, axisPoly[i].Axis);
                Vector2 proj2        = new Vector2(circleCenter - p2.Radius, circleCenter + p2.Radius);

                float overlap = 0f;

                if (!GetOverlapFromProjection_sat(proj1, proj2, out axisPoly[i].BodyA, out overlap))
                {
                    return(false);
                }
                else
                {
                    if (ContainsOtherProjection_sat(proj1, proj2) || ContainsOtherProjection_sat(proj2, proj1))
                    {
                        float mins = Mathf.Abs(proj1.x - proj2.x);
                        float maxs = Mathf.Abs(proj1.y - proj2.y);

                        if (mins < maxs)
                        {
                            overlap += mins;
                        }
                        else
                        {
                            overlap += maxs;
                        }
                    }

                    if (overlap < penetration)
                    {
                        collisionAxis = axisPoly[i];
                        penetration   = overlap;
                    }
                }
            }


            //Circle axis
            {
                Vector2 proj1        = ProjOntoAxis_sat(circleAxis, ref p1.Vertices);
                float   circleCenter = Vector2.Dot(p2.Center, circleAxis.Axis);
                Vector2 proj2        = new Vector2(circleCenter - p2.Radius, circleCenter + p2.Radius);

                float overlap = 0f;

                if (!GetOverlapFromProjection_sat(proj1, proj2, out circleAxis.BodyA, out overlap))
                {
                    return(false);
                }
                else
                {
                    if (ContainsOtherProjection_sat(proj1, proj2) || ContainsOtherProjection_sat(proj2, proj1))
                    {
                        float mins = Mathf.Abs(proj1.x - proj2.x);
                        float maxs = Mathf.Abs(proj1.y - proj2.y);

                        if (mins < maxs)
                        {
                            overlap += mins;
                        }
                        else
                        {
                            overlap += maxs;
                        }
                    }

                    if (overlap < penetration)
                    {
                        collisionAxis = circleAxis;
                        penetration   = overlap;
                    }
                }
            }

            //Debug.Log("Collision axis: " + collisionAxis.Axis);
            //Debug.Log("Penetration: " + penetration);

            manifold = new CollisionContact();

            FEdge eRef;

            if (!collisionAxis.BodyA)
            {
                collisionAxis.Axis *= -1.0f;
            }

            eRef = GetBestEdge_sat(ref p1.Vertices, collisionAxis.Axis);

            eRef.Normal = MeshUtils.CalcNormal(eRef.AV, eRef.BV);

            Vector2 vLeft     = p1.Vertices[CollMeshUtils.LeftIndex(eRef.AI, p1.Vertices.Length)];
            Vector2 vRight    = p1.Vertices[CollMeshUtils.RightIndex(eRef.BI, p1.Vertices.Length)];
            Vector2 normLeft  = MeshUtils.CalcNormalRaw(vLeft, eRef.AV);
            Vector2 normRight = MeshUtils.CalcNormalRaw(eRef.BV, vRight);
            Vector2 leftRel   = p2.Center - eRef.AV;
            Vector2 rightRel  = p2.Center - eRef.BV;

            if (Vector2.Dot(normLeft, leftRel) > 0f && Vector2.Dot(eRef.Normal, leftRel) > 0f)
            {
                manifold.Penetration   = penetration;
                manifold.Normal        = collisionAxis.Axis;
                manifold.ContactPoints = new Vector2[] { p2.Center - leftRel.normalized * p2.Radius };
            }
            else if (Vector2.Dot(normRight, rightRel) > 0f && Vector2.Dot(eRef.Normal, rightRel) > 0f)
            {
                manifold.Penetration   = penetration;
                manifold.Normal        = collisionAxis.Axis;
                manifold.ContactPoints = new Vector2[] { p2.Center - rightRel.normalized * p2.Radius };
            }
            else
            {
                manifold.Penetration   = penetration;
                manifold.Normal        = collisionAxis.Axis;
                manifold.ContactPoints = new Vector2[] { p2.Center - eRef.Normal * p2.Radius };
            }

            manifold.BodyAInc    = false;
            manifold.EdgeNormalA = eRef.Normal;
            manifold.EdgeNormalB = (p2.Center - manifold.ContactPoints[0]).normalized;

            return(true);
        }
        private static bool PolySAT(FCollPoly p1, FCollPoly p2, out CollisionContact manifold)
        {
            manifold = null;

            FSATAxis[] axis1 = GetAxis_sat(ref p1.Vertices);
            FSATAxis[] axis2 = GetAxis_sat(ref p2.Vertices);

            FSATAxis collisionAxis = axis1[0];
            float    penetration   = 100000f;

            for (int i = 0; i < axis1.Length; i++)
            {
                Vector2 proj1 = ProjOntoAxis_sat(axis1[i], ref p1.Vertices);
                Vector2 proj2 = ProjOntoAxis_sat(axis1[i], ref p2.Vertices);

                float overlap = 0f;

                if (!GetOverlapFromProjection_sat(proj1, proj2, out axis1[i].BodyA, out overlap))
                {
                    return(false);
                }
                else
                {
                    if (ContainsOtherProjection_sat(proj1, proj2) || ContainsOtherProjection_sat(proj2, proj1))
                    {
                        float mins = Mathf.Abs(proj1.x - proj2.x);
                        float maxs = Mathf.Abs(proj1.y - proj2.y);

                        if (mins < maxs)
                        {
                            overlap += mins;
                        }
                        else
                        {
                            overlap += maxs;
                        }
                    }

                    if (overlap < penetration)
                    {
                        collisionAxis = axis1[i];
                        penetration   = overlap;
                    }
                }
            }

            for (int i = 0; i < axis2.Length; i++)
            {
                Vector2 proj1 = ProjOntoAxis_sat(axis2[i], ref p1.Vertices);
                Vector2 proj2 = ProjOntoAxis_sat(axis2[i], ref p2.Vertices);

                float overlap = 0f;

                if (!GetOverlapFromProjection_sat(proj1, proj2, out axis2[i].BodyA, out overlap))
                {
                    return(false);
                }
                else
                {
                    if (ContainsOtherProjection_sat(proj1, proj2) || ContainsOtherProjection_sat(proj2, proj1))
                    {
                        float mins = Mathf.Abs(proj1.x - proj2.x);
                        float maxs = Mathf.Abs(proj1.y - proj2.y);

                        if (mins < maxs)
                        {
                            overlap += mins;
                        }
                        else
                        {
                            overlap += maxs;
                        }
                    }

                    if (overlap < penetration)
                    {
                        collisionAxis = axis2[i];
                        penetration   = overlap;
                    }
                }
            }

            manifold = new CollisionContact();

            FEdge eRef;
            FEdge eInc;

            if (collisionAxis.BodyA)
            {
                eRef = GetBestEdge_sat(ref p1.Vertices, collisionAxis.Axis);
                eInc = GetBestEdge_sat(ref p2.Vertices, -collisionAxis.Axis);
            }
            else
            {
                eInc = GetBestEdge_sat(ref p1.Vertices, -collisionAxis.Axis);
                eRef = GetBestEdge_sat(ref p2.Vertices, collisionAxis.Axis);
            }

            bool flipped = false;

            if (Mathf.Abs(Vector2.Dot(eInc.BV - eInc.AV, collisionAxis.Axis)) < Mathf.Abs(Vector2.Dot(eRef.BV - eRef.AV, collisionAxis.Axis)))
            {
                FEdge copy = eRef;
                eRef = eInc;
                eInc = copy;

                flipped = true;
            }

            Vector2 refDir = (eRef.BV - eRef.AV).normalized;

            float offset = Vector2.Dot(refDir, eRef.AV);

            List <Vector2> contactPoints = Clip_sat(eInc.AV, eInc.BV, refDir, offset);

            if (contactPoints.Count < 2)
            {
                return(true);
            }

            offset = Vector2.Dot(eRef.BV, refDir);

            contactPoints = Clip_sat(contactPoints[0], contactPoints[1], -refDir, -offset);

            if (contactPoints.Count < 2)
            {
                return(true);
            }

            eRef.Normal = MeshUtils.CalcNormal(eRef.AV, eRef.BV);
            eInc.Normal = MeshUtils.CalcNormal(eInc.AV, eInc.BV);

            //Debug.Log(eRef.Normal);

            if (Vector2.Dot(eRef.Normal, contactPoints[1] - eRef.AV) > 0f)
            {
                contactPoints.RemoveAt(1);
            }

            if (Vector2.Dot(eRef.Normal, contactPoints[0] - eRef.AV) > 0f)
            {
                contactPoints.RemoveAt(0);
            }

            manifold.ContactPoints = contactPoints.ToArray();
            manifold.Penetration   = penetration;

            if (flipped)
            {
                manifold.Normal      = -collisionAxis.Axis;
                manifold.BodyAInc    = collisionAxis.BodyA;
                manifold.EdgeNormalA = manifold.BodyAInc ? eInc.Normal : eRef.Normal;
                manifold.EdgeNormalB = manifold.BodyAInc ? eRef.Normal : eInc.Normal;
            }
            else
            {
                manifold.Normal      = collisionAxis.Axis;
                manifold.BodyAInc    = !collisionAxis.BodyA;
                manifold.EdgeNormalA = manifold.BodyAInc ? eInc.Normal : eRef.Normal;
                manifold.EdgeNormalB = manifold.BodyAInc ? eRef.Normal : eInc.Normal;
            }


            return(true);
        }