#include "physics/particle_contact.hpp" namespace physics { void particle_contact::resolve(float duration) { resolve_velocity(duration); resolve_interpenetration(duration); } float particle_contact::calculate_separating_velocity() const { vec3 relative_velocity = particle[0]->velocity; if (particle[1]) relative_velocity -= particle[1]->velocity; return dot(relative_velocity, contact_normal); } void particle_contact::resolve_velocity(float duration) { float separating_velocity = calculate_separating_velocity(); if (separating_velocity > 0) { // no impulse required return; } float new_separating_velocity = -separating_velocity * restitution; // velocity build-up due to acceleration only vec3 acceleration_caused_velocity = particle[0]->acceleration; if (particle[1]) acceleration_caused_velocity -= particle[1]->acceleration; float acceleration_caused_separating_velocity = dot(acceleration_caused_velocity, contact_normal) * duration; // remove acceleration velocity from separating velocity if (acceleration_caused_separating_velocity < 0) { new_separating_velocity += restitution * acceleration_caused_separating_velocity; if (new_separating_velocity < 0) new_separating_velocity = 0; } float delta_velocity = new_separating_velocity - separating_velocity; float total_inverse_mass = get_total_inverse_mass(); if (total_inverse_mass <= 0) return; float impulse = delta_velocity / total_inverse_mass; vec3 impulse_per_inverse_mass = contact_normal * impulse; particle[0]->velocity = particle[0]->velocity + impulse_per_inverse_mass * particle[0]->inverse_mass; if (particle[1]) { particle[1]->velocity = particle[1]->velocity + impulse_per_inverse_mass * -particle[1]->inverse_mass; } } void particle_contact::resolve_interpenetration(float duration) { if (penetration <= 0) return; float total_inverse_mass = get_total_inverse_mass(); if (total_inverse_mass <= 0) return; vec3 move_per_inverse_mass = contact_normal * (penetration / total_inverse_mass); particle[0]->position += move_per_inverse_mass * particle[0]->inverse_mass; if (particle[1]) { particle[1]->position += move_per_inverse_mass * -particle[1]->inverse_mass; } } void particle_contact_resolver::resolve_contacts(particle_contact * contacts, int contacts_length, float duration) { iterations = 0; while (iterations < max_iterations) { float max = 0; int max_index = -1; for (int i = 0; i < contacts_length; i++) { float separating_velocity = contacts[i].calculate_separating_velocity(); if (separating_velocity < max) { max = separating_velocity; max_index = i; } } if (max_index == -1) break; contacts[max_index].resolve(duration); iterations += 1; } } }