How can I implement better collision detection for rolling?

Discuss any questions about BEPUphysics or problems encountered.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

How can I implement better collision detection for rolling?

Post by Spankenstein »

I would like to implement collision detection for an object that ignores rolling and requires a certain impact force in order to register.

I'm currently overriding the 'Events_InitialCollisionDetected' method, is this the best one to use?

Code: Select all

protected override void Events_InitialCollisionDetected(EntityCollidable info, BroadPhaseEntry entry, NarrowPhasePair pair)
Ignoring rolling
- I could ignore contacts that belong to the same object each frame but what about a box where the object rolls along one side and collides with another wall belonging to the same box?
- What if the object is rolling on an uneven surface so I can't use normals to determine if the contact should count?

Impact force or collision sensitivity
- I thought about using the value of NormalImpulse but I'm uncertain if that is the correct method?

Code: Select all

            // Determine if a valid collidable pair has been created
            var collidablePair = pair as CollidablePairHandler;

            if (collidablePair == null)
            {
                return;
            }

            float collisionSensitivity = 1f;
            float impulse = collidablePair.Contacts[0].NormalImpulse;  

            if (impulse > collisionSensitivity)
            {
                Console.WriteLine("Collision Detected");
            }
I believe I should iterate through all the contacts created to see if any one of them has a NormalImpulse value that can register as a collision.

Here's a video of a spherical object moving inside a hollow cube. The object will flash and play a sound each time a new collision is detected without the above methods in place:

http://youtu.be/0GlBv7OLjxg

Some thoughts and help on this issue would be great. I would like to know if I have the correct idea before going down this route.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: How can I implement better collision detection for rolli

Post by Norbo »

I'm currently overriding the 'Events_InitialCollisionDetected' method, is this the best one to use?
If the container is one collidable and triggers are desired on every 'significant' impact even if it's with the same collidable, then using InitialCollisionDetected is not helpful. It only fires when contact manifold between two collidables goes from 0 to 1 or more contacts. Just using some contact list analysis will likely be more natural and convenient.

If the goal is to trigger whenever a fairly strong impact is detected, regardless of which object is associated with that impact, then the NormalImpulse measurement is indeed the way to go.

The NormalImpulse method will not help with insignificant collisions. If the ball slowly rolls up to a wall and touches, the NormalImpulse will probably be less than the NormalImpulse the floor is applying due to gravity. If such insignificant collisions need to be detected, another approach is required.

Analyzing the normals of contacts to determine when a sufficiently different collision has occurred would work too. Uneven surfaces capable of triggering the event could be avoided a bit by a threshold (although, really, a sufficiently uneven surface should intuitively trigger 'new collisions' unless you have a very particular behavior/meaning of 'new collision' in mind). Combining the normal analysis with NormalImpulse could be used to filter out weak collisions (or add in strong collisions that would otherwise be normal-filtered).

A 'new collision' is not a well-defined concept by itself, so to refine the filtering process more, the specific behaviors desired in every circumstance would be needed.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: How can I implement better collision detection for rolli

Post by Spankenstein »

Thanks Norbo, I'll code a bit more knowing I'm on the right track.
Just using some contact list analysis will likely be more natural and convenient.
Which event should I hook up for this purpose?
If the goal is to trigger whenever a fairly strong impact is detected, regardless of which object is associated with that impact, then the NormalImpulse measurement is indeed the way to go.
What would you consider the range of values for an insignificant impact to be?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: How can I implement better collision detection for rolli

Post by Norbo »

Which event should I hook up for this purpose?
Personally, I would probably not use any event. All Collidables expose a Pairs list. An arbitrarily placed processing stage can check an entity.CollisionInformation.Pairs list and the individual pairs' contacts easily.

If I had to pick an event, it would probably be PairTouched event. That triggers every time a pair is updated and ends up with more than 0 contacts.
What would you consider the range of values for an insignificant impact to be?
That depends entirely on the simulation and on what you want to consider insignificant. NormalImpulses will scale with the involved objects' masses and relative velocity. Unless there's some rigorous underpinning to the desired behavior, the best way to determine the threshold would likely be to, speaking technically, fiddle around and/or use the advanced technique of trial and error. :)
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: How can I implement better collision detection for rolli

Post by Spankenstein »

NormalImpulses will scale with the involved objects' masses and relative velocity
Would it work to scale based on ranges for unit mass and unit velocity?

So in other words if I knew the ignorable impulse range for two objects with a mass of 1 and velocity whose length is 1 then I can multiply that range by the new masses/velocities involved?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: How can I implement better collision detection for rolli

Post by Norbo »

Approximately, yes. It won't be perfect, though.

You may find that using the contact RelativeVelocity dotted with the contact normal produces more intuitive results. That way, you only have to deal with one parameter: relative velocity along the normal. This has the added benefit of being 0 (or fairly close to it) for contacts associated with rolling, while being significantly nonzero for many intuitively 'new' collisions. Extra normal-based filtering could still be useful depending on your goals, though.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: How can I implement better collision detection for rolli

Post by Spankenstein »

You may find that using the contact RelativeVelocity dotted with the contact normal produces more intuitive results
The '0.1f' value will be tweaked but is this looking correct? I presumed the absolute value would be required.

Code: Select all

            // Gather all contacts for this frame
            int numPairs = entity.CollisionInformation.Pairs.Count;

            for (int i = 0; i < numPairs; ++i)
            {
                contacts.AddRange(entity.CollisionInformation.Pairs[i].Contacts);
            }

            // Check each contact for projected velocity along the contact normal
            float dot;
            int numContacts = contacts.Count;
            
            for (int i = 0; i < numContacts; ++i)
            {
                dot = Vector3.Dot(contacts[i].RelativeVelocity, contacts[i].Contact.Normal);

                if (Math.Abs(dot) > 0.1f)
                {
                    Console.WriteLine("Collision Detected");
                    break;
                }
            }
The above method will be called once per frame per object instead of using the collision event.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: How can I implement better collision detection for rolli

Post by Norbo »

Using absolute value will consider separating speeds to be collisions. Using the signed dot and checking only for colliding speed (positive, if I remember correctly) rather than separating speed would help.

Also, the extra contacts list could be eliminated by combining the loops unless you had some other need for it.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: How can I implement better collision detection for rolli

Post by Spankenstein »

Also, the extra contacts list could be eliminated by combining the loops unless you had some other need for it.
Not at this point. It might be useful for filtering contacts in the future. By combining the loops I presume you were suggesting using a double loop?

Code: Select all

            float dot;
            int numContacts;
            int numPairs = entity.CollisionInformation.Pairs.Count;

            for (int i = 0; i < numPairs; ++i)
            {
                numContacts = entity.CollisionInformation.Pairs[i].Contacts.Count;

                for (int ii = 0; ii < numContacts; ++ii)
                {
                    dot = Vector3.Dot(entity.CollisionInformation.Pairs[i].Contacts[ii].RelativeVelocity, entity.CollisionInformation.Pairs[i].Contacts[ii].Contact.Normal);

                    // Using absolute value will consider separating speeds to be collisions
                    // Positive values are considered to be colliding and not separating (To Do: Check)
                    if (Math.Abs(dot) > 0.1f)
                    {
                        Console.WriteLine("Collision Detected");
                        break;
                    }
                }
            }
Using absolute value will consider separating speeds to be collisions. Using the signed dot and checking only for colliding speed (positive, if I remember correctly) rather than separating speed would help.
Thank you that didn't occur to me until you mentioned it. I will check if it is positive or negative values.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: How can I implement better collision detection for rolli

Post by Norbo »

By combining the loops I presume you were suggesting using a double loop?
Yup!
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: How can I implement better collision detection for rolli

Post by Spankenstein »

Excellent stuff. Thank you for the help once again :)
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: How can I implement better collision detection for rolli

Post by Spankenstein »

The dot product method doesn't appear to help filter out events.

I've tried normalizing the values involved but that doesn't help either.

Code: Select all

            for (int i = 0; i < numPairs; ++i)
            {
                numContacts = entity.CollisionInformation.Pairs[i].Contacts.Count;

                for (int ii = 0; ii < numContacts; ++ii)
                {
                    Vector3 d0 = entity.CollisionInformation.Pairs[i].Contacts[ii].RelativeVelocity;
                    Vector3 d1 = entity.CollisionInformation.Pairs[i].Contacts[ii].Contact.Normal;

                    d1.Normalize();

                    // Using absolute value will consider separating speeds to be collisions
                    dot = Vector3.Dot(d0, d1);

                    impulse = entity.CollisionInformation.Pairs[i].Contacts[ii].NormalImpulse;

                    if (dot < -0.99f)
                    {
                        collidedThisFrame = true;
                        break;
                    }
                }
            }
        }
There appears to be no discernible difference between a collision and a roll with a dot product. Any ideas?

EDIT:

I've also considered the entity.LinearVelocity instead of RelativeVelocity that seems closer but still not right.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: How can I implement better collision detection for rolli

Post by Norbo »

A threshold of 0.99 is quite low, assuming your objects are of fairly regular scale (say, a sphere of radius 1). Note that because the dot product result represents velocity along the normal, it can be any value- not just from 0 to 1. If both rolling contacts and impacts manage to pass that threshold, try increasing the threshold. On the other hand, if neither rolling contacts nor impacts manage to pass the threshold, try decreasing it.
I've also considered the entity.LinearVelocity instead of RelativeVelocity that seems closer but still not right.
The RelativeVelocity is more correct; the angular velocity must be included for a proper result unless there are certain guarantees (like the test object is always spherical and the other object is not rotating, in which case linear velocity is equivalent).
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: How can I implement better collision detection for rolli

Post by Spankenstein »

Something could be wrong with the impulse or maybe I'm expecting incorrect values?

I drop a box from a great height under the influence of gravity:

I log the impact each time it makes contact with the ground. I would have thought it would have a large value for the initial contact but it returns 0?

This is what I am using to log the impact value:

game.Debug.StreamWriter.WriteLine("Impulse {0}", entity.CollisionInformation.Pairs.Contacts[ii].NormalImpulse);

Code: Select all

            game.Debug.StreamWriter.WriteLine("=====================================================");

            for (int i = 0; i < numPairs; ++i)
            {
                numContacts = entity.CollisionInformation.Pairs[i].Contacts.Count;

                for (int ii = 0; ii < numContacts; ++ii)
                {
                    //Vector3 d0 = entity.CollisionInformation.Pairs[i].Contacts[ii].RelativeVelocity;
                    Vector3 d0 = entity.LinearVelocity;
                    Vector3 d1 = entity.CollisionInformation.Pairs[i].Contacts[ii].Contact.Normal;

                    //d0.Normalize();
                    d1.Normalize();

                    // Using absolute value will consider separating speeds to be collisions
                    dot = Vector3.Dot(d0, d1);

                    game.Debug.StreamWriter.WriteLine("Impulse {0}", entity.CollisionInformation.Pairs[i].Contacts[ii].NormalImpulse);

                    if (dot < -0.1f & Vector3.Dot(normal, d1) < 0.9f && entity.CollisionInformation.Pairs[i].Contacts[ii].NormalImpulse > 0.2f)
                    {
                        normal = d1;
                        collidedThisFrame = true;
                        break;
                    }
                }
            }
Which produces:

=====================================================
Impulse 0
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
=====================================================
Impulse 0.08125269
=====================================================
Impulse 0.08125269
=====================================================
Impulse 0.08125269

Sometimes the initial impact gives an high (expected) value of 17 but this is not always the case. Is this a bug?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: How can I implement better collision detection for rolli

Post by Norbo »

I log the impact each time it makes contact with the ground. I would have thought it would have a large value for the initial contact but it returns 0?
Oops! The relative velocity is computed at query-time, so when it's looked at after solving takes place, the normal velocity will be zero. Sorry, that slipped my mind; moving the analysis between the NarrowPhase and Solver will allow you to see the most recent contact state combined with the pre-solving velocity state. (Note that the NormalImpulses will be much less useful here since they haven't been recomputed yet.)

You can jump between those processing stages by either hooking an event on the relevant space stage's Starting/Finishing events or by using a IBeforeSolverUpdateable. Of course, doing certain things while the engine is running can be unsafe- avoid adding objects to or removing objects from the space in this context.
Sometimes the initial impact gives an high (expected) value of 17 but this is not always the case. Is this a bug?
The impulse is a direct readout of the accumulated impulse applied by the constraint. For it to be wrong, the simulation would have to be noticeably broken as well. Since the query only captures the most recent values, a Space.Update(dt) call could perform multiple time steps internally and obscure the big impact value with a subsequent low impulse frame.

Code: Select all

if (dot < -0.1f & Vector3.Dot(normal, d1) < 0.9f && entity.CollisionInformation.Pairs[i].Contacts[ii].NormalImpulse > 0.2f)
With a properly-captured relative velocity, the normal impulse is redundant unless you wish to filter based on the effective masses of objects in an indirect way. Also, watch out for that & operator- it won't allow short-circuit evaluation.
Post Reply