Tangent vector

Please don't post Bullet support questions here, use the above forums instead.
Post Reply
c0der
Posts: 74
Joined: Sun Jul 08, 2012 11:32 am

Tangent vector

Post by c0der »

Hi,

I have implemented contact caching for 3D OBB collisions as in Box2D. With the prestep in Box2D, the relative velocity is not used to calculate the tangent vector, instead a scalar is crossed with the contact normal.

How do I compute the tangent vector in 3D to put in the prestep function of Box2D in the arbiters class? Initially I start off with Cross(normal, vector(1,1,1)) but this doesnt work. In using the relative velocity, initially it is parallel to the contact normal as there is no rotation of the bodies, so this returns a zero tangent vector and zero massTangent.

It's definitely the friction impulse causing erratic behaviour as I have disabled it and the normal impulse behaves correctly.

Here is my preStep function:

Code: Select all

void AMG3DRigidBodyCollisionResponder::preStep(float dt)
{
	const float k_allowedPenetration = 0.01f;
	float k_biasFactor = (m_bPositionCorrection) ? 0.2f : 0.0f;

	if(dt<=0.0f)
		return;

	for(int i=0; i<m_cdContactData.iNumContacts; ++i) {		
		AMG3DVector4 rap = m_cdContactData.contacts[i].vContactPoint - m_pRigidBody1->position;
		AMG3DVector4 rbp = m_cdContactData.contacts[i].vContactPoint - m_pRigidBody2->position;

		// Precompute normal mass, tangent mass and bias
		AMG3DVector4 normal = m_cdContactData.vContactNormal;
		AMG3DVector4 rapcrossn = rap.cross(normal);
		AMG3DVector4 rbpcrossn = rbp.cross(normal);
		float Kn = m_pRigidBody1->invMass + m_pRigidBody2->invMass;
		Kn += ( (m_pRigidBody1->invIWorld*rapcrossn).cross(rap) + (m_pRigidBody2->invIWorld*rbpcrossn).cross(rbp) ).dot(normal);
		m_cdContactData.contacts[i].massNormal = 1.0f/Kn;
		
		AMG3DVector4 tangent;
		AMG3DVector4Cross(&tangent, normal, AMG3DVector4(1,1,1));

		AMG3DVector4 rapcrosst = rap.cross(tangent);
		AMG3DVector4 rbpcrosst = rbp.cross(tangent);
		float Kt = m_pRigidBody1->invMass + m_pRigidBody2->invMass;
		Kt += ( (m_pRigidBody1->invIWorld*rapcrosst).cross(rap) + (m_pRigidBody2->invIWorld*rbpcrosst).cross(rbp) ).dot(tangent);
		m_cdContactData.contacts[i].massTangent = 1.0f/Kt;

		if(m_bAccumulateImpulses) {
			// Apply normal + friction impulse
			AMG3DVector4 P = m_cdContactData.contacts[i].Pn * normal + m_cdContactData.contacts[i].Pt * tangent;

			m_pRigidBody1->velocity += P*m_pRigidBody1->invMass;
			m_pRigidBody1->angularVelocity += m_pRigidBody1->invIWorld*rap.cross(P);

			m_pRigidBody2->velocity -= P*m_pRigidBody2->invMass;
			m_pRigidBody2->angularVelocity -= m_pRigidBody2->invIWorld*rbp.cross(P);
		}

		m_cdContactData.contacts[i].bias = k_biasFactor / dt * Max(0.0f, m_cdContactData.contacts[i].fPenetrationDepth - k_allowedPenetration);
	}
}
Thanks !
Dirk Gregorius
Posts: 861
Joined: Sun Jul 03, 2005 4:06 pm
Location: Kirkland, WA

Re: Tangent vector

Post by Dirk Gregorius »

Look for btPlaneSpace() in the code.
c0der
Posts: 74
Joined: Sun Jul 08, 2012 11:32 am

Re: Tangent vector

Post by c0der »

Thanks. I have looked it up in Google code but can't find the function body of btPlaneSpace1. Friction should oppose motion, so technically if the relative velocity is pointing down the y axis and the plane normal is pointing up, then the tangent vector should be zero. However this causes a large angular velocity when accumulating impulses. How would you compute the tangent vector in the preStep and accumulateImpulses functions?

Erin's code in 2D doesn't use relative velocity to compute the tangent vector, he just takes the cross product of the plane normal and a scalar of 1.
Dirk Gregorius
Posts: 861
Joined: Sun Jul 03, 2005 4:06 pm
Location: Kirkland, WA

Re: Tangent vector

Post by Dirk Gregorius »

Say you have a vector u = ( x1, y1 ). You search for a vector v = ( x2, y2 ) such that dot( u, v ) = 0 since then they are orthogonal. Let's look into this a bit more detailed:

dot( u, v ) = x1 * x2 + y1 * y2

Let's assume we know u and look for v. Let's choose v = ( -y1, x1 ) and plug this in.

dot( u, v ) = x1 * (-y1) + y1 * x1 = -x1*y1 + x1*y1 = 0

Obviously with this choise v is orthogonal to u. Here is the rule. Given a vector and you need a vector orthogonal to it you switch the components and multiply one by -1. If you multiply the first one by -1 you essentially rotate CCW. If you multiply the second component by -1 (in our example above if we would have chosen v = ( y1, -x1 ) instead) this rotates CW.

In 3D you do the same and choose one component to zero. You have to choose the component you choose to zero wisely though since your vector might be in some plane already. This is what btPlaneSpace1() will do. It should be in the Bullet math library. Alternatively you can google for dPlaneSpace() in the ODE. I think the code comes from there originally.

HTH,
-Dirk
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Tangent vector

Post by Erwin Coumans »

You could use 2 arbitrary vectors orthogonal to the normal, using btPlaneSpace1, here it is:
https://code.google.com/p/bullet/source ... arch+Trunk

By default, Bullet uses the relative velocity as primary friction direction F0 , and computes a secondary F2 friction vector that is orthogonal F0 and N using the cross product. Only if the projection is zero, we use btPlaneSpace1.
See https://code.google.com/p/bullet/source ... er.cpp#833

Code: Select all

 cp.m_lateralFrictionDir1 = vel - cp.m_normalWorldOnB * rel_vel;
 btScalar lat_rel_vel = cp.m_lateralFrictionDir1.length2();
 if (!(infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION) && lat_rel_vel > SIMD_EPSILON)
 {
              cp.m_lateralFrictionDir1 *= 1.f/btSqrt(lat_rel_vel);
              if((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS))
              {
                     cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB);
I suggest to use the relative velocity as primary friction direction to avoid artifacts: If you ignore the relative velocity and always using btPlaneSpace1 you can get an artifact that a sliding object will start aligning with one of the principal axis. This will result in a curved path instead of a straight one.

Thanks,
Erwin
c0der
Posts: 74
Joined: Sun Jul 08, 2012 11:32 am

Re: Tangent vector

Post by c0der »

Thanks all. I have taken out the preStep as the normal and tangent masses are required to be calculated at every impulse iteration when using relative velocity, which changes as opposed to Erin's fixed tangent vector.

I am getting stable stacking at 10 impulse iterations, but since I have implemented contact caching, is it still required that the boxes be put to sleep at some stage? Contact caching updates an existing contact and keeps its impulse accumulation, but this contact will always be there since the objects will continue touching in the stacking case and eventually the stack will be unstable?

Thanks for all your help
Post Reply