I'm currently writing a physics simulation in 3D, that uses sequential impulses. I've managed to implement normal impulses, but I can't get friction impulses to work. As I understand I first get two tangent vectors, that are perpendicular to each other and the normal. Then I calculate the friction impulse and clamp it by total friction accumulator in the range of -FrictionCoefficient * NormalImpulse to FrictionCoefficient * NormalImpulse. I tried applying friction at the center of manifold, it seemed to work, but even with high friction coefficient values it slides a lot, so I don't consider it a solution.
Here's my code for tangents calculation:
glm::vec3 first_tangent{data.contactVelocity - collision_data.normal * glm::dot(data.contactVelocity, collision_data.normal)};
float lat_rel_vel{ glm::length2(first_tangent) };
glm::vec3 second_tangent{};
if(lat_rel_vel > 0.0f) {
first_tangent *= 1.0f / glm::sqrt(lat_rel_vel);
second_tangent = glm::cross(first_tangent, collision_data.normal);
second_tangent = glm::normalize(second_tangent);
}
else {
GetTwoTangential(collision_data.normal, first_tangent, second_tangent);
}
(GetTwoTangential function is the exact copy of btPlaneSpace1)
And here's my impulse calculation:
float frictional_mass1{ totalInverseMass +
glm::dot(first_tangent, glm::cross(first->GetInverseWorldTensor() * glm::cross(data.relativeFirst, first_tangent), data.relativeFirst) +
glm::cross(second->GetInverseWorldTensor() * glm::cross(data.relativeSecond, first_tangent), data.relativeSecond)) };
float j1{ -glm::dot(data.contactVelocity, first_tangent) };
j1 /= frictional_mass1;
const float tempFriction1{ accumulatedFrictions[currentManifold].first_tangent };
accumulatedFrictions[currentManifold].first_tangent = glm::clamp(accumulatedFrictions[currentManifold].first_tangent + j1,
-0.8f * accumulatedImpulses[currentManifold],
0.8f * accumulatedImpulses[currentManifold]);
const float deltaFriction1{ accumulatedFrictions[currentManifold].first_tangent - tempFriction1 };
glm::vec3 fullFriction_1{deltaFriction1 * first_tangent};
first->AppyLinearImpulse(-fullFriction_1);
second->AppyLinearImpulse(fullFriction_1);
first->AppyAngularImpulse(glm::cross(data.relativeFirst, -fullFriction_1));
second->AppyAngularImpulse(glm::cross(data.relativeSecond, fullFriction_1));
float frictional_mass2{ totalInverseMass +
glm::dot(second_tangent, glm::cross(first->GetInverseWorldTensor() * glm::cross(data.relativeFirst, second_tangent), data.relativeFirst) +
glm::cross(second->GetInverseWorldTensor() * glm::cross(data.relativeSecond, second_tangent), data.relativeSecond)) };
float j2{ -glm::dot(data.contactVelocity, second_tangent) };
j2 /= frictional_mass1;
const float tempFriction2{ accumulatedFrictions[currentManifold].second_tangent };
accumulatedFrictions[currentManifold].second_tangent = glm::clamp(accumulatedFrictions[currentManifold].second_tangent + j2,
-0.8f * accumulatedImpulses[currentManifold],
0.8f * accumulatedImpulses[currentManifold]);
const float deltaFriction2{ accumulatedFrictions[currentManifold].second_tangent - tempFriction2 };
glm::vec3 fullFriction_2{deltaFriction2 * second_tangent};
first->AppyLinearImpulse(-fullFriction_2);
second->AppyLinearImpulse(fullFriction_2);
first->AppyAngularImpulse(glm::cross(data.relativeFirst, -fullFriction_2));
second->AppyAngularImpulse(glm::cross(data.relativeSecond, fullFriction_2));
(0.8f is my friction coefficient, I use it as a placeholder for now)
So here's how my collision response works:
- Get true normal impulse( using sequential impulse solver to get normal impulses at each contact point)
- Get friction impulse( also using sequential impulses at each contact point)
- (Normal and friction impulses are calculated separately, first normal then friction)
- Integrate velocities