Adding contact pairs through a user collision algorithm

dumbo2007
Posts: 66
Joined: Sun Jan 30, 2011 8:46 pm

Adding contact pairs through a user collision algorithm

Post by dumbo2007 »

Hi,

I am attempting to replace the contact manifold generation logic for box/box overlap with my own. So I am basically adding a user algorithm for the box-box collision case. I have a few questions regarding the contact pairs which are generated for a single manifold between 2 boxes.

1. The manifold can have 1,2,3 or 4 pairs. Are there any specific conditions when 2 pairs are generated or 3, 4 etc? For example I have noticed that for bodies resting on each other 2 pairs seem to be enough.

2. If I choose a bunch of contact points and add them using btDiscreteCollisionDetectorInterface:addContactPoint(), then will bullet take care of ordering the contact pairs correctly such that they are in a clockwise or anticlockwise order (for 3 or 4 pairs), or are there certain guidelines to be followed while adding contact pairs ?
dumbo2007
Posts: 66
Joined: Sun Jan 30, 2011 8:46 pm

Re: Adding contact pairs through a user collision algorithm

Post by dumbo2007 »

ok so I have modified the box-box collision algorithm to insert contact pairs. The contact pairs are generated by a custom algorithm and are exactly the same as generated by the box-box detector used by Bullet.

btBoxBoxCollisionAlgorithm.cpp was modified in just one function : processCollision(). The rest of the code is exactly the same (except for commenting out some of the Bullet contact pair generation logic). I call the following function in the added code :

Code: Select all

 resultOut->addContactPoint(ptB, normalWorldOnB,
					   -(current_manifold->rt_contacts[i].depth));
I have calculated the normal and penetration depth correctly. Yet Bullet does not apply a force to hold a box in place when its lying on the top of a bigger box (gravity has been added to the dynamic world). The minute I switch back to Bullet's own logic, the boxes are stable. What am I doing wrong while inserting my own manifolds ?

I am going to hardcode the manifold points that Bullet generates, and insert them using the insert them with addContactPoint() while removing the calls to

Code: Select all

 btBoxBoxDetector detector(box0, box1);
    detector.getClosestPoints(input, *resultOut, dispatchInfo.m_debugDraw);
...and see if that works.
Last edited by dumbo2007 on Wed Oct 19, 2011 5:34 pm, edited 1 time in total.
dumbo2007
Posts: 66
Joined: Sun Jan 30, 2011 8:46 pm

Re: Adding contact pairs through a user collision algorithm

Post by dumbo2007 »

Hmm still no luck, I am currently testing with a simple case of a box lying on a plane :

Image

The cube is a unit cube lying on a larger cuboid that is quite thin along the Z axis (I have kept Z axis as up and gravity acts towards -ve Z)

Image

The code I use after registering the custom collision algorithm is as follows in processCollision() :

Code: Select all

void
btRTCollisionAlgorithm::processCollision(btCollisionObject* body0,
					 btCollisionObject* body1,
					 const btDispatcherInfo& dispatchInfo,
					 btManifoldResult* resultOut)
{
    if (!m_manifoldPtr)
	return;

    btCollisionObject* col0 = body0;
    btCollisionObject* col1 = body1;
#ifndef DEBUG_MF
    btBoxShape* box0 = (btBoxShape*)col0->getCollisionShape();
    btBoxShape* box1 = (btBoxShape*)col1->getCollisionShape();
#endif //DEBUG_MF

    //quellage
    bu_log("%d", dispatchInfo.m_stepCount);

    /// report a contact. internally this will be kept persistent, and contact reduction is done
    resultOut->setPersistentManifold(m_manifoldPtr);
#ifndef USE_PERSISTENT_CONTACTS
    m_manifoldPtr->clearManifold();
#endif //USE_PERSISTENT_CONTACTS

    btDiscreteCollisionDetectorInterface::ClosestPointInput input;
    input.m_maximumDistanceSquared = BT_LARGE_FLOAT;
    input.m_transformA = body0->getWorldTransform();
    input.m_transformB = body1->getWorldTransform();

#ifndef DEBUG_MF
    //This part will get replaced with a call to rt
    btBoxBoxDetector detector(box0, box1);
    detector.getClosestPoints(input, *resultOut, dispatchInfo.m_debugDraw);
#endif //DEBUG_MF

    //------------------- DEBUG ---------------------------

    int i;

    //Get the user pointers to struct rigid_body, for printing the body name
    struct rigid_body *rbA = (struct rigid_body *)col0->getUserPointer();
    struct rigid_body *rbB = (struct rigid_body *)col1->getUserPointer();

    if (rbA != NULL && rbB != NULL) {

	   	//Find the manifold in rbB's list which connects rbB and rbA
		if( BU_STR_EQUAL(rt_mf->rbA->rb_namep, rbA->rb_namep) &&
				BU_STR_EQUAL(rt_mf->rbB->rb_namep, rbB->rb_namep) ){

		   // Now add the RT contact pairs
		   for (i=0; i<rt_mf->num_contacts; i++){

			   btVector3 ptA, ptB, normalWorldOnB;

			   VMOVE(ptA, rt_mf->contacts[i].ptA);
			   VMOVE(ptB, rt_mf->contacts[i].ptB);
			   VMOVE(normalWorldOnB, rt_mf->contacts[i].normalWorldOnB);

			   //Positive depth for penetration
			   resultOut->addContactPoint(normalWorldOnB, ptB, rt_mf->contacts[i].depth);

			   bu_log("processCollision: Added RT contact %d, %s(%f, %f, %f) , \
					   %s(%f, %f, %f), n(%f, %f, %f), %f\n",
						   i+1,
						rt_mf->rbA->rb_namep, V3ARGS(ptA),
						rt_mf->rbB->rb_namep, V3ARGS(ptB),
						V3ARGS(normalWorldOnB),
						rt_mf->contacts[i].depth);
		   }

	   }//end- if( bu_strcmp...


    } //end- if (rbA != NULL && rbB...





    //------------------------------------------------------

#ifdef USE_PERSISTENT_CONTACTS
    // refreshContactPoints is only necessary when using persistent contact points.
    // otherwise all points are newly added
    if (m_ownManifold) {
	resultOut->refreshContactPoints();
    }
#endif //USE_PERSISTENT_CONTACTS

}
So I currently create manifold points based on the overlap volume, which is marked in pink above. Its dimensions are of course much thinner than those exaggerated above. struct rigid_body is a custom data structure a pointer to which is stored in the rigid body user pointer member. Its gives access to the manifold contact pairs of the rigid body inside the collision pipeline. The contact pairs are generated in the nearphase callback which is invoked by Bullet before processCollision() is called.

Image

Thus if the Box is penetrating the ground plane (GP), which is also a thin box, then the manifold points for the particular case in the picture can be
  • (0,0,0)
    (1,0,-0.001)
    (1,0,-0.001)
    (0,0,-0.001)
Since addContactPoint(normalWorldOnB, ptB, depth) requires only the points for B and the normal & depth which allows it to calculate the points for A, I call it in the code above as follows :

resultOut->addContactPoint( (0,0,1), (0,0,-0.001), 0.001);
resultOut->addContactPoint( (0,0,1), (0,0,-0.001), 0.001);

where the points belonging to B are the bottom points in the overlap region marked in pink.

This allows it to calculate the points for body A (the box) using
pointA = pointB + normalBtoA * depth

Well, the box continues to pass through the ground plane after I do this even though I can see that the contact points I feed are being processed (using a contact processed callback).

Image

Image

Here is some output from the callback, when I am injecting my own points (i.e. not bullet's default boxbox contact pair generator ) :

Iteration 1

Code: Select all

Contact processed between sim_box.r(0.000000, 1.000000, 0.000000) & sim_gp.r(0.000000, 1.000000, 0.000000)!
Contact processed between sim_box.r(0.000000, 0.000000, 0.000000) & sim_gp.r(0.000000, 0.000000, 0.000000)!
Contact processed between sim_box.r(1.000000, 0.000000, 0.000000) & sim_gp.r(1.000000, 0.000000, 0.000000)!
Contact processed between sim_box.r(1.000000, 1.000000, 0.000000) & sim_gp.r(1.000000, 1.000000, 0.000000)!

Iteration 2

Code: Select all

Contact processed between sim_box.r(0.000000, 1.000000, 0.000000) & sim_gp.r(0.000000, 1.000000, -0.000001)!
Contact processed between sim_box.r(0.000000, 0.000000, 0.000000) & sim_gp.r(0.000000, 0.000000, -0.000001)!
Contact processed between sim_box.r(1.000000, -0.000000, 0.000000) & sim_gp.r(1.000000, 0.000000, -0.000001)!
Contact processed between sim_box.r(1.000000, 1.000000, 0.000000) & sim_gp.r(1.000000, 1.000000, -0.000001)!
and so on, the box A continues to pass through the ground plane B in successive iterations. So what else do I need to set apart from calling addContactPoints() ? If I see the points generated by Bullet then I see that A penetrates B initially but is gradually pushed out after iteration 12, and is then stable after about 25, not the same with my case however :(
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: Adding contact pairs through a user collision algorithm

Post by Erwin Coumans »

Contacts points require negative depth values for penetration.

Positive distance (depth) means they are separated.

Also, the contact normal needs to point from object B towards object A and the contact point needs to be located on the surface of object B.
Can you try that?

If that still doesn't work, can you compare the distance/normal with a simpler case (sphere/sphere)?
Thanks,
Erwin
dumbo2007
Posts: 66
Joined: Sun Jan 30, 2011 8:46 pm

Re: Adding contact pairs through a user collision algorithm

Post by dumbo2007 »

Hi Erwin,

Thanks for the reply. Well it works for the simple case of a sphere - sphere collision.

Image

Code: Select all

void
btRTCollisionAlgorithm::processCollision(btCollisionObject* col0,
					 btCollisionObject* col1,
					 const btDispatcherInfo& dispatchInfo,
					 btManifoldResult* resultOut)
{
    if (!m_manifoldPtr)
	return;

    //quellage
    bu_log("%d", dispatchInfo.m_stepCount);

    /// report a contact. internally this will be kept persistent, and contact reduction is done
    resultOut->setPersistentManifold(m_manifoldPtr);
#ifndef USE_PERSISTENT_CONTACTS
    m_manifoldPtr->clearManifold();
#endif //USE_PERSISTENT_CONTACTS



    //------------------- DEBUG ---------------------------

    int i;

    //Get the user pointers to struct rigid_body, for printing the body name
    struct rigid_body *rbA = (struct rigid_body *)col0->getUserPointer();
    struct rigid_body *rbB = (struct rigid_body *)col1->getUserPointer();

    if (rbA != NULL && rbB != NULL) {
	    //Scan all raytrace generated manifolds of rbB looking for a rbA-rbB manifold----------------------

		struct sim_manifold *rt_mf = &(rbB->rt_manifold);	

		//Find the manifold in rbB's list which connects rbB and rbA
		if( BU_STR_EQUAL(rt_mf->rbA->rb_namep, rbA->rb_namep) &&
				BU_STR_EQUAL(rt_mf->rbB->rb_namep, rbB->rb_namep) ){

		   // Now add the contact pairs
		   for (i=0; i<rt_mf->num_contacts; i++){

			   btVector3 ptA, ptB, normalWorldOnB;

			   VMOVE(ptA, rt_mf->contacts[i].ptA);
			   VMOVE(ptB, rt_mf->contacts[i].ptB);
			   VMOVE(normalWorldOnB, rt_mf->contacts[i].normalWorldOnB);

			   //Negative depth for penetration
			   resultOut->addContactPoint(normalWorldOnB, ptB, rt_mf->contacts[i].depth);

			   bu_log("processCollision: Added contact %d, A(ignore): %s(%f, %f, %f) , \
					   B: %s(%f, %f, %f), n(%f, %f, %f), depth=%f\n",
						   i+1,
						rt_mf->rbA->rb_namep, V3ARGS(ptA),
						rt_mf->rbB->rb_namep, V3ARGS(ptB),
						V3ARGS(normalWorldOnB),
						rt_mf->contacts[i].depth);
		   }

	   }//end- if( bu_strcmp...


    } //end- if (rbA != NULL && rbB...

}
I made sure the normal points from the object Bullet considers as object B, to the object Bullet takes as object A. The depth calculation is the same as in btSphereSphereCollisionAlgorithm.cpp

The box-box case then also should work, since the only difference in that case is that I am adding 4 contact points in 1 go(right at the 1st call, without any accumulation of points over multiple iterations) instead of the single contact point of sphere-sphere. I will try the box-box collision again.
dumbo2007
Posts: 66
Joined: Sun Jan 30, 2011 8:46 pm

Re: Adding contact pairs through a user collision algorithm

Post by dumbo2007 »

ok now the box-box collision also work. Seems the normals were probably wrong. I tried to use the sphere-sphere collision algorithm with 2 boxes to check if contact pairs decide object collisions, and it worked as well with one of the boxes rolling into and penetrating the other as if it were a sphere.

I have another question. I tested the box-box contact pair generation using the following setup of 2 boxes on top of each other :

Image


The bottom box B is static, A can move. During the simulation I found that A continues to penetrate B at least till iteration 20 before stabilizing at 0.00041:

Image

The default box-box collision algorithm does not show this behavior however. In fact the penetration is 0 and stays at 0 for it. In my case I need to have some penetration before I can generate contact pairs, but the penetration should not increase beyond the first few iterations as Bullet starts applying a force to stop it(I assume)