Get geom surface normal with RayHelper

May 19, 2010 at 8:26 PM

Hi everyone,

Is there an easy way to compute the deflection of a ray off a geometry surface using RayHelper? From what I see, it gives you just the intersection points, but not the polygon segment which was intersected, so I can see no way of getting at the normal to use for computing deflection.

It would seem a common scenario for deflecting beams and whatnot.

Any ideas?

Looking forward to Farseer 3.0 :)

Keep up the good work!

May 19, 2010 at 9:15 PM

With Farseer 3.0 you get the normal when casting a ray. In Farseer 2.1 I think it's not possible.

May 19, 2010 at 11:03 PM

Thanks for the observation, Pnikosis.

Based on that I set out to compute my own normal. It turns out that LineSegmentGeomIntersect isn't awfully smart in computing intersections, it just iterates through all the line segments in the geometry and calls LineIntersect on the possible pairs, like this:

 

for (int i = 0; i < vertices.Count; i++)
{
    Vector2 point;
    if (LineIntersect(vertices[i], vertices[vertices.NextIndex(i)],
                            point1, point2, true, true, _epsilon, out point))
    {
        intersectionPoints.Add(point);
    }
}

 

So what we have to do is just perform this iteration ourselves and then just use Vector2.Reflect() to compute the deflection based on vertices[i] and vertices[vertices.NextIndex(i)].

In the meantime, I was getting extremely jittery behavior in the deflection, and I traced it down to the LineIntersect() method itself. I found out that although it receives a floatTolerance parameter as input, it doesn't use it anywhere in the body, falling back instead to a constant _epsilon static variable declared inside RayHelper.

Worse still, it didn't use these floating point tolerance checks in all numeric tests inside the code, so LineIntersect() currently suffers from all sorts of rounding issues. I guess that it won't be updated since Farseer 3.0 is the next step, so here is a quick patch for LineIntersect() if anyone else is experiencing rounding issues or overall strange intersection misses in Farseer 2.1.3:

public static bool LineIntersect(ref Vector2 point1, ref Vector2 point2, ref Vector2 point3, ref Vector2 point4,
                         bool firstIsSegment, bool secondIsSegment, float floatTolerance,
                         out Vector2 point)
{
    point = new Vector2();

    // these are reused later.
    // each lettered sub-calculation is used twice, except
    // for b and d, which are used 3 times
    float a = point4.Y - point3.Y;
    float b = point2.X - point1.X;
    float c = point4.X - point3.X;
    float d = point2.Y - point1.Y;

    // denominator to solution of linear system
    float denom = (a * b) - (c * d);

    // if denominator is 0, then lines are parallel
    if (!(denom >= -floatTolerance && denom <= floatTolerance))
    {
        float e = point1.Y - point3.Y;
        float f = point1.X - point3.X;
        float oneOverDenom = 1.0f / denom;

        // numerator of first equation
        float ua = (c * e) - (a * f);
        ua *= oneOverDenom;

        // check if intersection point of the two lines is on line segment 1
        if (!firstIsSegment || ua >= -floatTolerance && ua <= 1.0f + floatTolerance)
        {
            // numerator of second equation
            float ub = (b * e) - (d * f);
            ub *= oneOverDenom;

            // check if intersection point of the two lines is on line segment 2
            // means the line segments intersect, since we know it is on
            // segment 1 as well.
            if (!secondIsSegment || ub >= -floatTolerance && ub <= 1.0f + floatTolerance)
            {
                // check if they are coincident (no collision in this case)
                if ((ua < -floatTolerance || ua > floatTolerance) &&
                    (ub < -floatTolerance || ub > floatTolerance))
                {
                    //There is an intersection
                    point.X = point1.X + ua * b;
                    point.Y = point1.Y + ua * d;
                    return true;
                }
            }
        }
    }

    return false;
}

Hope this helps anybody. Deflection code is working like a charm now :)

May 20, 2010 at 12:36 PM
Another approach that should work, is by casting two rays, one at the side of the other with very little distance between them. This way, by the distance on the collision points you can get the angle of the collisioned vertice.