Collision detection method is correct but object shakes when resolution takes place?

Started by
9 comments, last by Spa8nky 14 years, 6 months ago
Hello folks, I have the following algorithm that works out how far an object is from a line segment whilst keeping the object inside a certain radius at all times. The problem is that when I am resolving the position of the object it will visibly shake. This occurs when it is at the outmost limit of the allowed distance from the closest point on the segement (i.e. > radius). What have I done wrong here?

            // Set projected position to current object position
            Vector3 position_Projected = debug_Polygon[0].Position;

            // Get key presses this frame
            if (InputState.Global.IsKeyDown(Keys.Left))
            {
                position_Projected = debug_Polygon[0].Position + new Vector3(-0.005f, 0, 0);
            }
            if (InputState.Global.IsKeyDown(Keys.Right))
            {
                position_Projected = debug_Polygon[0].Position + new Vector3(0.005f, 0, 0);
            }
            if (InputState.Global.IsKeyDown(Keys.Down))
            {
                position_Projected = debug_Polygon[0].Position + new Vector3(0, -0.005f, 0);
            }
            if (InputState.Global.IsKeyDown(Keys.Up))
            {
                position_Projected = debug_Polygon[0].Position + new Vector3(0, 0.005f, 0);
            }

            // Find the distance between the object and the nearest point on the segment
            float distance = debug_Segment.SquareDistanceToPoint(position_Projected);

            // If the object is outside the radius
            if (distance > 2.0f)
            {
                // Calculate penetration depth
                distance -= 2.0f;
                
                // Compute the closest point on the segement to the projected position
                Vector3 center_Segment = debug_Segment.ClosestPtPointSegment(position_Projected);

                // Calculate the normal based on the closest point on segment to object's position
                Vector3 normal = center_Segment - position_Projected;
                normal.Normalize();

                // Distance can also be found with normal (when it isn't normalized)
                //Console.WriteLine("Distance " + normal.LengthSquared());
                
                position_Projected += normal * (distance /* - radius_BoundingSphere */ - GameConstants.EPSILON);
            }

            // Update object's position with final projected position (doesn't change if key isn't pressed)
            debug_Polygon[0].Position = position_Projected;


Thank you.
Advertisement
It happens because of the Epsilon. In this case you should be able to remove it, if you only want to stay within a sphere. It's only necessary when you want the player to be able to be on both sides of a surface, to make sure he doesn't accidentally go through it. Staying within a sphere should be achievable by just setting distance to the radius whenever it's larger, and moving that distance in the direction of the endpoint.
Quote:Original post by Erik Rufelt
It happens because of the Epsilon.


I have tried removing the epsilon before and it still does exactly the same thing. Any other thoughts?
What kind of numbers for the distance and position vector do you get when the shaking occurs?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Distance (penetration depth): 0.009919167
Object position: {X:-1.412716 Y:0 Z:0}
Projected position: {X:-1.407797 Y:0 Z:0}

Distance (penetration depth): 0.01014829
Object position: {X:-1.412797 Y:0 Z:0}
Projected position: {X:-1.407649 Y:0 Z:0}

Distance (penetration depth): 0.006502628
Object position: {X:-1.411511 Y:0 Z:0}
Projected position: {X:-1.410008 Y:0 Z:0}

EDIT:

Theses are some distances from the segment to the new projected point, shouldn't they be dead on 2?

1.997116
1.979426
1.986003
1.999779
Quote:shouldn't they be dead on 2?

I don't see why they should. The line
if( distance > 2 ) distance -= 2;

could result in any value of distance, depending on the initial value.

If you want to keep the object inside the radius "at all times," why not:
position_Projected = normal * (radius-epsilon);

Also, you use "SquareDistanceToPoint." Is "distance" the actual penetration depth or the square of the penetration depth?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

"distance" is actually the square distance from object position to segment, does that mess up my calculations?

If I have the following:

position_Projected += normal * (float)Math.Sqrt(distance);


then the shaking is worse.

Quote:
position_Projected = normal * (radius-epsilon);


This makes the object wrap around the circle (warp from one side to opposite side)?!

Oops! How about:
position_Projected = center_Segment - normal*(radius-epsilon);

EDIT: which, actually, is what Erik suggested. Since your test is distance > 2 rather than distance => 2, you can leave the epsilon out (as Erik said).
EDIT2: By the way, are you using a radius of sqrt(2)?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Using your method and hopefully answering your edit questions I get the following working code:

            // Find the distance between the object and the nearest point on the segment            float distance = debug_Segment.SquareDistanceToPoint(position_Projected);            float radius = 2.0f;            float radius_Squared = radius * radius;            // If the object is outside the radius (> instead of >= means I don't have to use an Epsilon)            if (distance > radius_Squared)            {                                // Compute the closest point on the segement to the projected position                Vector3 center_Segment = debug_Segment.ClosestPtPointSegment(position_Projected);                // Calculate the normal based on the closest point on segment to object's position                Vector3 normal = center_Segment - position_Projected;                normal.Normalize();                // Distance can also be found with normal (when it isn't normalized)                //Console.WriteLine("Distance " + normal.LengthSquared());                position_Projected = center_Segment - normal * radius;            }


If this is what Erik was getting at then apologies to Erik for me being a bit slow on the uptake.

The line:

position_Projected = center_Segment - normal * radius;

is much simpler than my:

projectedPosition -= normal * (distance - radius - GameConstants.EPSILON);

option.

However, if I were to make the point a bounding sphere, how would I take into account the extra radius of the sphere?

Thank you.

EDIT: Misspelled Erik's name!

[Edited by - Spa8nky on October 15, 2009 6:17:47 PM]
Quote:if I were to make the point a bounding sphere..

If you mean to keep a bounding sphere inside the radius as you did a point, and assuming position_Projected is the center of that sphere (similar to your existing code):

float distance = debug_Segment.SquareDistanceToPoint(position_Projected);
// account for distance from pos_projected to outer edge of sphere
float temp = sqrt(distance)+sphere_radius;
distance = temp*temp;

and..

// if needs to be repositioned, keep the position + sphere_rad inside radius
position_Projected = center_Segment - normal * (radius-sphere_radius);

However, if you were avoiding the sqrt() for efficiency (which was a good idea), you would have to use a sqrt() anyway now. So you could change your code:

distance = sqrt(SquareDistance()) + sphere_radius;
if(distance > radius) ..

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

This topic is closed to new replies.

Advertisement