Следующий код для разрешения коллизий между двумя объектами предполагает, что resitution равен нулю. inverse_inertia
тела представлено в виде матрицы (glm::mat4
).
void apply_impulse(Body& body, glm::vec3 impulse, glm::vec3 offset)
{
body.velocity += impulse * body.inverse_mass;
body.angular_velocity += body.inverse_inertia * glm::cross(offset, impulse);
}
void resolve_collision(Body& a, Body& b, glm::vec3 contact_point, glm::vec3 normal)
{
glm::vec3 ra = contact_point - a.position;
glm::vec3 rb = contact_point - b.position;
glm::vec3 relative_velocity =
b.velocity + glm::cross(b.angular_velocity, rb) -
a.velocity - glm::cross(a.angular_velocity, ra);
// Not moving towards each other, ignore the collision as it will be resolved anyway
if (glm::dot(relative_velocity, normal) > 0) {
return;
}
normal = glm::normalize(normal);
float inverse_mass_sum = a.inverse_mass + b.inverse_mass +
glm::length2(a.inverse_inertia * glm::cross(ra, normal)) +
glm::length2(b.inverse_inertia * glm::cross(rb, normal));
float normal_impulse = -glm::dot(relative_velocity, normal) / inverse_mass_sum;
apply_impulse(a, -normal_impulse * normal, ra);
apply_impulse(b, normal_impulse * normal, rb);
// Recalculate after normal impulse
relative_velocity =
b.velocity + glm::cross(b.angular_velocity, rb) -
a.velocity - glm::cross(a.angular_velocity, ra);
glm::vec3 relative_momentum = relative_velocity / inverse_mass_sum;
// Apply friction
glm::vec3 friction_impulse;
if (glm::length2(relative_momentum) < glm::length2(normal_impulse * static_friction)) {
friction_impulse = -relative_momentum;
}
else {
friction_impulse = -normal_impulse * glm::normalize(relative_momentum) * dynamic_friction;
}
apply_impulse(a, -friction_impulse, ra);
apply_impulse(b, friction_impulse, rb);
}
Когда я запускаю этот код, он отлично работает с более низкими значениями, такими как 1, 2 или 3 из static_friction
и dynamic_friction
, хотя кажется, что он немного скользит. Но когда я увеличиваю их оба, скажем, до 9999, он мгновенно реагирует чрезмерно, отбрасывая объекты на экстремальных скоростях. Этого делать не следует, так как последний if
оператор resolve_collision
должен служить для "зажима" импульса трения до значения on, которое делает относительную скорость ровно равной нулю. Но, похоже, это не так.
При установке коэффициента трения около 10-20 кажется, что линейная и угловая скорости мяча колеблются взад-вперед.
Что я здесь делаю неправильно? Часть кода, которая, как мне кажется, скорее всего неверна:
float inverse_mass_sum = a.inverse_mass + b.inverse_mass +
glm::length2(a.inverse_inertia * glm::cross(ra, normal)) +
glm::length2(b.inverse_inertia * glm::cross(rb, normal));
Но я понятия не имею, как это неправильно. Я хочу добавить, что я мог бы сделать это только с одним движущимся объектом:
for (auto& platform : platforms) {
glm::vec3 contact_point;
if (intersects(platform, ball, contact_point)) {
// Only react if moving towards the platform
if (glm::dot(ball.position - contact_point, ball.velocity) > 0) {
continue;
}
// Collision with the platform applies a normal impulse
glm::vec3 normal = glm::normalize(ball.position - contact_point);
float normal_impulse = -ball.mass * glm::dot(normal, ball.velocity);
ball.velocity += normal_impulse * normal;
// Apply friction
glm::vec3 relative_momentum = ball.mass * ball.velocity +
inertia(ball) * glm::cross(ball.position - contact_point, ball.angular_velocity);
apply_impulse(
ball,
friction_impulse(relative_momentum, normal_impulse),
contact_point - ball.position);
}
}
Когда friction_impulse
определяется как:
glm::vec3 friction_impulse(glm::vec3 relative_momentum, float normal_impulse)
{
if (glm::length2(relative_momentum) < glm::length2(normal_impulse * static_friction)) {
return -relative_momentum;
}
else {
return -normal_impulse * glm::normalize(relative_momentum) * dynamic_friction;
}
}
Не стесняйтесь переместить это в gamedev или что-то другое, я не уверен, куда это поместить.