O código abaixo é de um exemplo do Microsoft XNA aqui . Essa é uma simulação bastante simples do corpo rígido que ignora muitos efeitos físicos (como o momento angular), mas tenta separar objetos (esferas) para que eles não se penetrem.
No entanto, a simulação permite que as esferas não apenas penetrem, mas quando muitas esferas são empilhadas umas sobre as outras, pequenas esferas podem estar quase completamente dentro de esferas maiores. Se eu fizer todas as esferas com o mesmo raio e massa, a simulação terá um desempenho razoavelmente bom (com interpenetração mínima).
Alguém pode explicar por que existe alguma interpenetração? Uma vez que move as posições das esferas, parece que a interpenetração deve ser impossível.
Para cada esfera na simulação, esse método é chamado em todas as outras esferas.
/// <summary>
// Given 2 spheres with velocity, mass and size, evaluate whether
// a collision occured, and if so, excatly where, and move sphere 2
// at the contact point with sphere 1, and generate new velocities.
/// </summary>
private void SphereCollisionImplicit(Sphere sphere1, Sphere sphere2)
{
const float K_ELASTIC = 0.75f;
Vector3 relativepos = sphere2.Position - sphere1.Position;
float distance = relativepos.Length();
float radii = sphere1.Radius + sphere2.Radius;
if (distance >= radii)
{
return; // No collision
}
// Add epsilon to avoid NaN.
distance += 0.000001f;
Vector3 relativeUnit = relativepos * (1.0f / distance);
Vector3 penetration = relativeUnit * (radii - distance);
// Adjust the spheres' relative positions
float mass1 = sphere1.Mass;
float mass2 = sphere2.Mass;
float m_inv = 1.0f / (mass1 + mass2);
float weight1 = mass1 * m_inv; // relative weight of sphere 1
float weight2 = mass2 * m_inv; // relative weight of sphere 2. w1+w2==1.0
sphere1.Position -= weight2 * penetration;
sphere2.Position += weight1 * penetration;
// Adjust the objects’ relative velocities, if they are
// moving toward each other.
//
// Note that we're assuming no friction, or equivalently, no angular momentum.
//
// velocityTotal = velocity of v2 in v1 stationary ref. frame
// get reference frame of common center of mass
Vector3 velocity1 = sphere1.Velocity;
Vector3 velocity2 = sphere2.Velocity;
Vector3 velocityTotal = velocity1 * weight1 + velocity2 * weight2;
Vector3 i2 = (velocity2 - velocityTotal) * mass2;
if (Vector3.Dot(i2, relativeUnit) < 0)
{
// i1+i2 == 0, approx
Vector3 di = Vector3.Dot(i2, relativeUnit) * relativeUnit;
i2 -= di * (K_ELASTIC + 1);
sphere1.Velocity = (-i2) / mass1 + velocityTotal;
sphere2.Velocity = i2 / mass2 + velocityTotal;
}
}
Em particular, eu acho que isso:
sphere1.Position -= weight2 * penetration;
sphere2.Position += weight1 * penetration;
Deve proibir completamente qualquer interpenetração, por que não?