示例#1
0
        static GJKoutput GJK(Collider a, Collider b)
        {
            Vector2 GetVertNearestToOrigin(Line line)
            {
                return(GetFarthestPointInDifference(GetNormalToLineFacingOrigin(line), a, b));
            }

            bool SimplexContainsOrigin(List <Vector2> simplex)
            {
                for (int i = 0; i < 3; i++)
                {
                    int j = (i + 1) % 3;
                    int k = (i + 2) % 3;

                    //side jk third vert is i
                    //a_k * x + b_k * y + c_k = 0
                    float a_k = simplex[j].y - simplex[k].y;
                    float b_k = simplex[k].x - simplex[j].x;
                    float c_k = Vector2.CrossProduct(simplex[j], simplex[k]);

                    float half_space_simplex_i = sgn(a_k * simplex[i].x + b_k * simplex[i].y + c_k);
                    float half_space_origin    = sgn(c_k);


                    if (half_space_simplex_i == -half_space_origin)//if Simplex[i] and origin
                    //are in different half-spaces
                    {
                        return(false);
                    }
                }

                return(true);
            }

            Vector2 init_direction = new Vector2(1, 0);//may be any instread of Vector2.zero

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

            Simplex.Add(GetFarthestPointInDifference(init_direction, a, b));
            Simplex.Add(GetFarthestPointInDifference(-init_direction, a, b));
            Simplex.Add(GetVertNearestToOrigin(new Line(Simplex[0], Simplex[1])));

            while (true)
            {
                if (SimplexContainsOrigin(Simplex))
                {
                    return(new GJKoutput {
                        result = true, simplex = Simplex
                    });
                }

                //determine nearest side to origin
                //
                //if we have triangle the nearest side to origin is side such that another vertex of simplex
                //and origin lying in different half-spaces relatively this side
                //
                //ax + by + c = 0
                //if sgn(a * dot0.x + b * dot0.y + c) == - sgn(a * dot1.x + b * dot1.y + c) then dot 0 and dot 1
                //lying on different sides of line ax + by + c = 0

                for (int i = 0; i < 3; i++)
                {
                    int j = (i + 1) % 3;
                    int k = (i + 2) % 3;

                    //side ij third vert is k

                    float a_k = Simplex[i].y - Simplex[j].y;
                    float b_k = Simplex[j].x - Simplex[i].x;
                    float c_k = Vector2.CrossProduct(Simplex[i], Simplex[j]);

                    if (sgn(a_k * Simplex[k].x + b_k * Simplex[k].y + c_k) == -sgn(c_k))
                    {
                        //needed side
                        Simplex[k] = GetVertNearestToOrigin(new Line(Simplex[i], Simplex[j]));

                        if (Simplex[k] * GetNormalToLineFacingOrigin(new Line(Simplex[i], Simplex[j])) <= 0)
                        {
                            //no origin in minkovski difference
                            return(new GJKoutput()
                            {
                                result = false
                            });
                        }

                        if (((Simplex[k].x == Simplex[i].x) && (Simplex[k].y == Simplex[i].y)) ||
                            ((Simplex[k].x == Simplex[j].x) && (Simplex[k].y == Simplex[j].y)))
                        {
                            //no origin in minkovski difference
                            return(new GJKoutput()
                            {
                                result = false
                            });
                        }

                        break;
                    }
                }
            }
        }
示例#2
0
        internal static void Resolve(Collider a, Collider b, CollisionInfo info)
        {
            Body a_body = a.gameobject.body;
            Body b_body = b.gameobject.body;

            float bouncity         = Body.CalculateBouncity(a_body.Bouncity, b_body.Bouncity);
            float static_friction  = Body.CalculateStaticFriction(a_body.StaticFriction, b_body.StaticFriction);
            float dynamic_friction = Body.CalculateDynamicFriction(a_body.DynamicFriction, b_body.DynamicFriction);

            int ContactPointCount = info.ContactPoints.Count;

            for (int i = 0; i < ContactPointCount; i++)
            {
                var contact = info.ContactPoints[i];

                Vector2 ra = contact.point - (a_body.CenterOfMass_local + a.gameobject.Position);
                Vector2 rb = contact.point - (b_body.CenterOfMass_local + b.gameobject.Position);

                Vector2 RelativeVelocity = (b_body.Velocity + Vector2.CrossProduct(b_body.AngularVelocity, rb))
                                           - (a_body.Velocity + Vector2.CrossProduct(a_body.AngularVelocity, ra));

                float VelAlongNormal = (RelativeVelocity * contact.normal);

                if (VelAlongNormal > 0)
                {
                    continue;
                }

                float denominator = a_body.Inv_mass + b_body.Inv_mass
                                    + sqr(Vector2.CrossProduct(ra, contact.normal)) * a_body.Inv_MomentOfInertia
                                    + sqr(Vector2.CrossProduct(rb, contact.normal)) * b_body.Inv_MomentOfInertia;
                denominator *= ContactPointCount;

                //formula for impulse scalar
                float ImpulseScalar = (-(1 + bouncity) * VelAlongNormal) / denominator;

                Vector2 Impulse = contact.normal * ImpulseScalar;

                //applying impulse
                a_body.Velocity        -= a_body.Inv_mass * Impulse;
                a_body.AngularVelocity -= a_body.Inv_MomentOfInertia * Vector2.CrossProduct(ra, Impulse);

                b_body.Velocity        += b_body.Inv_mass * Impulse;
                b_body.AngularVelocity += b_body.Inv_MomentOfInertia * Vector2.CrossProduct(rb, Impulse);
                //**********************

                //friction
                //re-count relative velocity
                RelativeVelocity = (b_body.Velocity + Vector2.CrossProduct(b_body.AngularVelocity, rb))
                                   - (a_body.Velocity + Vector2.CrossProduct(a_body.AngularVelocity, ra));

                Vector2 tangent = (RelativeVelocity - (RelativeVelocity * contact.normal)
                                   * contact.normal).normalized;

                float jt = -(RelativeVelocity * tangent) / denominator;

                Vector2 FrictionImpulse;

                if (abs(jt) < ImpulseScalar * static_friction)
                {
                    FrictionImpulse = jt * tangent;
                }
                else
                {
                    FrictionImpulse = -ImpulseScalar * tangent * dynamic_friction;
                }

                //applying friction impulse
                a_body.Velocity        -= a_body.Inv_mass * FrictionImpulse;
                a_body.AngularVelocity -= a_body.Inv_MomentOfInertia * Vector2.CrossProduct(ra, FrictionImpulse);

                b_body.Velocity        += b_body.Inv_mass * FrictionImpulse;
                b_body.AngularVelocity += b_body.Inv_MomentOfInertia * Vector2.CrossProduct(rb, FrictionImpulse);
            }

            foreach (var contact in info.ContactPoints)
            {
                //positional correction

                float percent = 0.6f;
                float slop    = 0.01f;

                Vector2 correction = ((max(contact.depth - slop, 0)
                                       / (a_body.Inv_mass + b_body.Inv_mass)) * contact.normal * percent) / ContactPointCount;

                a.gameobject.Position -= a_body.Inv_mass * correction;
                b.gameobject.Position += b_body.Inv_mass * correction;

                //*******************
            }
        }
示例#3
0
        public static CollisionInfo PolygonvsPolygon(Polygon a, Polygon b)
        {
            int GetFarthestPointIndex(Vector2[] polygon, Vector2 direction)
            {
                int index = 0;

                if (direction.y == 0)//x coord check
                {
                    for (int vert = 0; vert < polygon.Length; vert++)
                    {
                        if (sgn(polygon[vert].x - polygon[index].x) == sgn(direction.x))//if direction == right searching max(x) else min
                        {
                            index = vert;
                        }
                    }

                    return(index);
                }

                float k = -direction.x / direction.y; //perpendicular direction angular coefficient

                //y = kx + b line equation. we find line equation for each point and b
                //is the intersection point of this line with Oy. Extremum of b means that there is the
                //ultimate point in the direction, perpendicular to this line i.e. "direction" vector

                for (int vert = 0; vert < polygon.Length; vert++)
                {
                    if (sgn((polygon[vert].y - polygon[vert].x * k) - (polygon[index].y - polygon[index].x * k)) ==
                        sgn(direction.y))//if top direction searching max(b) else min(b)
                    {
                        index = vert;
                    }
                }

                return(index);
            }

            float GetSqrDistanceFromPointToLine(Vector2 point, Line line)
            {
                //ax + by + c = 0
                //distance = |a * x1 + b * y1 + c| / sqrt(a ^ 2 + b ^ 2)
                float a_k = line.start.y - line.end.y;
                float b_k = line.end.x - line.start.x;
                float c_k = Vector2.CrossProduct(line.start, line.end);

                return(sqr(a_k * point.x + b_k * point.y + c_k) / (sqr(a_k) + sqr(b_k)));
            }

            bool PolygonContainsPoint(Vector2[] poly, Vector2 point)
            {
                for (int i = 0; i < poly.Length; i++)
                {
                    int j = (i + 1) % poly.Length;

                    float a_k = poly[i].y - poly[j].y;
                    float b_k = poly[j].x - poly[i].x;
                    float c_k = Vector2.CrossProduct(poly[i], poly[j]);

                    int k = (i + 2) % poly.Length;

                    float vert_halfspace  = sgn(a_k * poly[k].x + b_k * poly[k].y + c_k);
                    float point_halfspace = sgn(a_k * point.x + b_k * point.y + c_k);

                    if (point_halfspace == -vert_halfspace)
                    {
                        return(false);
                    }
                }

                return(true);
            }

            CollisionInfo info = new CollisionInfo();

            GJKoutput GJKoutput = GJK(a, b);

            if (!GJKoutput.result)
            {
                return new CollisionInfo()
                       {
                           ContactPoints = new List <CollisionInfo.ContactPoint>()
                       }
            }
            ;

            EPAoutput EPAoutput = EPA(GJKoutput.simplex, a, b);

            #region shitcode
            Vector2 normal   = EPAoutput.normal;
            float   distance = EPAoutput.distance;

            info.ContactPoints = new List <CollisionInfo.ContactPoint>();

            bool flip = false;

            int incident_edge_start = GetFarthestPointIndex(a.Verts, normal);
            int incident_edge_end   = (incident_edge_start + 1) % a.Verts.Length;

            if (!(abs((a.Verts[incident_edge_start] - a.Verts[incident_edge_end]) * normal) < tolerance))
            {
                incident_edge_end = (incident_edge_start - 1 + a.Verts.Length) % a.Verts.Length;
            }

            if (!(abs((a.Verts[incident_edge_start] - a.Verts[incident_edge_end]) * normal) < tolerance))
            {
                flip = true;

                incident_edge_start = GetFarthestPointIndex(b.Verts, -normal);
                incident_edge_end   = (incident_edge_start + 1) % b.Verts.Length;

                if (!(abs((b.Verts[incident_edge_start] - b.Verts[incident_edge_end]) * normal) < tolerance))
                {
                    incident_edge_end = (incident_edge_start - 1 + b.Verts.Length) % b.Verts.Length;
                }
            }

            Line incident_edge;

            if (!flip)
            {    //a
                incident_edge = new Line(a.Verts[incident_edge_start], a.Verts[incident_edge_end]);
            }
            else
            {    //b
                incident_edge = new Line(b.Verts[incident_edge_start], b.Verts[incident_edge_end]);
            }


            if (!flip)
            {    //a
                int b_farthest              = GetFarthestPointIndex(b.Verts, -normal);
                int b_farthest_left_neighb  = (b_farthest + 1) % b.Verts.Length;
                int b_farthest_right_neighb = (b_farthest - 1 + b.Verts.Length) % b.Verts.Length;

                info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                {
                    depth  = distance,
                    normal = normal,
                    point  = b.Verts[b_farthest] + normal * distance
                });

                bool a_contains_left  = PolygonContainsPoint(a.Verts, b.Verts[b_farthest_left_neighb]);
                bool a_contains_right = PolygonContainsPoint(a.Verts, b.Verts[b_farthest_right_neighb]);

                if (a_contains_left && a_contains_right)
                {
                    float left_sqr_dist              = GetSqrDistanceFromPointToLine(b.Verts[b_farthest_left_neighb], incident_edge);
                    float right_sqr_dist             = GetSqrDistanceFromPointToLine(b.Verts[b_farthest_right_neighb], incident_edge);
                    int   b_almostfarthest           = (right_sqr_dist > left_sqr_dist) ? b_farthest_right_neighb : b_farthest_left_neighb;
                    float b_almost_farthest_distance = sqrt(max(right_sqr_dist, left_sqr_dist));

                    info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                    {
                        depth  = b_almost_farthest_distance,
                        normal = normal,
                        point  = b.Verts[b_almostfarthest] + normal * b_almost_farthest_distance
                    });
                }
                else if (!a_contains_left && !a_contains_right)
                {
                    return(info);
                }
                else if (a_contains_left)
                {
                    float dist = sqrt(GetSqrDistanceFromPointToLine(b.Verts[b_farthest_left_neighb], incident_edge));

                    info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                    {
                        depth  = dist,
                        normal = normal,
                        point  = b.Verts[b_farthest_left_neighb] + normal * dist
                    });
                }
                else
                {
                    float dist = sqrt(GetSqrDistanceFromPointToLine(b.Verts[b_farthest_right_neighb], incident_edge));

                    info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                    {
                        depth  = dist,
                        normal = normal,
                        point  = b.Verts[b_farthest_right_neighb] + normal * dist
                    });
                }

                return(info);
            }
            else
            {    //b
                int a_farthest              = GetFarthestPointIndex(a.Verts, normal);
                int a_farthest_left_neighb  = (a_farthest + 1) % a.Verts.Length;
                int a_farthest_right_neighb = (a_farthest - 1 + a.Verts.Length) % a.Verts.Length;

                info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                {
                    depth  = distance,
                    normal = normal,
                    point  = a.Verts[a_farthest] + normal * distance
                });

                bool b_contains_left  = PolygonContainsPoint(b.Verts, a.Verts[a_farthest_left_neighb]);
                bool b_contains_right = PolygonContainsPoint(b.Verts, a.Verts[a_farthest_right_neighb]);

                if (b_contains_left && b_contains_right)
                {
                    float left_sqr_dist              = GetSqrDistanceFromPointToLine(a.Verts[a_farthest_left_neighb], incident_edge);
                    float right_sqr_dist             = GetSqrDistanceFromPointToLine(a.Verts[a_farthest_right_neighb], incident_edge);
                    int   a_almostfarthest           = (right_sqr_dist > left_sqr_dist) ? a_farthest_right_neighb : a_farthest_left_neighb;
                    float a_almost_farthest_distance = sqrt(max(right_sqr_dist, left_sqr_dist));

                    info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                    {
                        depth  = a_almost_farthest_distance,
                        normal = normal,
                        point  = a.Verts[a_almostfarthest] + normal * a_almost_farthest_distance
                    });
                }
                else if (!b_contains_left && !b_contains_right)
                {
                    return(info);
                }
                else if (b_contains_left)
                {
                    float dist = sqrt(GetSqrDistanceFromPointToLine(a.Verts[a_farthest_left_neighb], incident_edge));

                    info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                    {
                        depth  = dist,
                        normal = normal,
                        point  = a.Verts[a_farthest_left_neighb] + normal * dist
                    });
                }
                else
                {
                    float dist = sqrt(GetSqrDistanceFromPointToLine(a.Verts[a_farthest_right_neighb], incident_edge));

                    info.ContactPoints.Add(new CollisionInfo.ContactPoint()
                    {
                        depth  = dist,
                        normal = normal,
                        point  = a.Verts[a_farthest_right_neighb] + normal * dist
                    });
                }

                return(info);
            }
            #endregion
        }