Advertisement

3D Manifold Collision Response

Started by April 28, 2018 05:39 PM
26 comments, last by Dirk Gregorius 6 years, 8 months ago

Would it be enough? Well maybe. You won't have any nice stacking behavior, and you can have some jitter problems. But it is possible to hack around these problems.

@Randy Gaul,

I will give it a try, the collision response is an entire subject on it's own, it would take me months of work to get up to speed on it,

cheers

Reject the basic asumption of civialisation especially the importance of material possessions
Advertisement

It might not take you months. If you look carefully in the first link, here is the collision response code. It looks nearly identical in 3D.


void Arbiter::PreStep(float inv_dt)
{
	const float k_allowedPenetration = 0.01f;
	float k_biasFactor = World::positionCorrection ? 0.2f : 0.0f;

	for (int i = 0; i < numContacts; ++i)
	{
		Contact* c = contacts + i;

		Vec2 r1 = c->position - body1->position;
		Vec2 r2 = c->position - body2->position;

		// Precompute normal mass, tangent mass, and bias.
		float rn1 = Dot(r1, c->normal);
		float rn2 = Dot(r2, c->normal);
		float kNormal = body1->invMass + body2->invMass;
		kNormal += body1->invI * (Dot(r1, r1) - rn1 * rn1) + body2->invI * (Dot(r2, r2) - rn2 * rn2);
		c->massNormal = 1.0f / kNormal;

		Vec2 tangent = Cross(c->normal, 1.0f);
		float rt1 = Dot(r1, tangent);
		float rt2 = Dot(r2, tangent);
		float kTangent = body1->invMass + body2->invMass;
		kTangent += body1->invI * (Dot(r1, r1) - rt1 * rt1) + body2->invI * (Dot(r2, r2) - rt2 * rt2);
		c->massTangent = 1.0f /  kTangent;

		c->bias = -k_biasFactor * inv_dt * Min(0.0f, c->separation + k_allowedPenetration);

		if (World::accumulateImpulses)
		{
			// Apply normal + friction impulse
			Vec2 P = c->Pn * c->normal + c->Pt * tangent;

			body1->velocity -= body1->invMass * P;
			body1->angularVelocity -= body1->invI * Cross(r1, P);

			body2->velocity += body2->invMass * P;
			body2->angularVelocity += body2->invI * Cross(r2, P);
		}
	}
}

void Arbiter::ApplyImpulse()
{
	Body* b1 = body1;
	Body* b2 = body2;

	for (int i = 0; i < numContacts; ++i)
	{
		Contact* c = contacts + i;
		c->r1 = c->position - b1->position;
		c->r2 = c->position - b2->position;

		// Relative velocity at contact
		Vec2 dv = b2->velocity + Cross(b2->angularVelocity, c->r2) - b1->velocity - Cross(b1->angularVelocity, c->r1);

		// Compute normal impulse
		float vn = Dot(dv, c->normal);

		float dPn = c->massNormal * (-vn + c->bias);

		if (World::accumulateImpulses)
		{
			// Clamp the accumulated impulse
			float Pn0 = c->Pn;
			c->Pn = Max(Pn0 + dPn, 0.0f);
			dPn = c->Pn - Pn0;
		}
		else
		{
			dPn = Max(dPn, 0.0f);
		}

		// Apply contact impulse
		Vec2 Pn = dPn * c->normal;

		b1->velocity -= b1->invMass * Pn;
		b1->angularVelocity -= b1->invI * Cross(c->r1, Pn);

		b2->velocity += b2->invMass * Pn;
		b2->angularVelocity += b2->invI * Cross(c->r2, Pn);

		// Relative velocity at contact
		dv = b2->velocity + Cross(b2->angularVelocity, c->r2) - b1->velocity - Cross(b1->angularVelocity, c->r1);

		Vec2 tangent = Cross(c->normal, 1.0f);
		float vt = Dot(dv, tangent);
		float dPt = c->massTangent * (-vt);

		if (World::accumulateImpulses)
		{
			// Compute friction impulse
			float maxPt = friction * c->Pn;

			// Clamp friction
			float oldTangentImpulse = c->Pt;
			c->Pt = Clamp(oldTangentImpulse + dPt, -maxPt, maxPt);
			dPt = c->Pt - oldTangentImpulse;
		}
		else
		{
			float maxPt = friction * dPn;
			dPt = Clamp(dPt, -maxPt, maxPt);
		}

		// Apply contact impulse
		Vec2 Pt = dPt * tangent;

		b1->velocity -= b1->invMass * Pt;
		b1->angularVelocity -= b1->invI * Cross(c->r1, Pt);

		b2->velocity += b2->invMass * Pt;
		b2->angularVelocity += b2->invI * Cross(c->r2, Pt);
	}
}

 

Thanks @Randy Gaul,

I will study this algorithm. What is the purpose of the PreStep function?

Also, my collision detection algorithm does broad phase then narrow phase using sweeps followed by multi-sample SAT, it produces manifolds + collision times

For each collision during a tick should I move the collided objects to first point of collision then perform response or should I not move the collided objects & perform response straight away?

Reject the basic asumption of civialisation especially the importance of material possessions

PreStep calculates the "inverse mass" term - basically by how much the relative velocity at contact changes if you apply a unit impulse there. \((JWJ^T)^{-1}\) term (huh, what happened to tex math?). It is done there so you don't have to calculate it in the ApplyImpulse step, which allows you to call it multiple times at less cost.

It also applies accumulated impulses from the previous step, which makes the system more stable. Without those, you won't be able to make things like a stack of boxes without it falling apart.

Advertisement

@d07RiV,

if I apply impulses to each manifold point, would that not apply the same Linear Impulse several times incorrectly?

Reject the basic asumption of civialisation especially the importance of material possessions

You don't apply the same impulse every time. For each iteration at every manifold point you apply equal and opposite impulses such that the relative post velocity at the manifold point will be zero. Here relative post velocity means the relative velocity at the manifold point in the direction of the manifold normal *after* you applied the impulse. 

This topic is closed to new replies.

Advertisement