44 narrow_phase_table[plane_i][plane_i] = std::bind_front(&physics_system::narrow_phase_plane_plane,
this);
45 narrow_phase_table[plane_i][sphere_i] = std::bind_front(&physics_system::narrow_phase_plane_sphere,
this);
46 narrow_phase_table[plane_i][box_i] = std::bind_front(&physics_system::narrow_phase_plane_box,
this);
47 narrow_phase_table[plane_i][capsule_i] = std::bind_front(&physics_system::narrow_phase_plane_capsule,
this);
49 narrow_phase_table[sphere_i][plane_i] = std::bind_front(&physics_system::narrow_phase_sphere_plane,
this);
50 narrow_phase_table[sphere_i][sphere_i] = std::bind_front(&physics_system::narrow_phase_sphere_sphere,
this);
51 narrow_phase_table[sphere_i][box_i] = std::bind_front(&physics_system::narrow_phase_sphere_box,
this);
52 narrow_phase_table[sphere_i][capsule_i] = std::bind_front(&physics_system::narrow_phase_sphere_capsule,
this);
54 narrow_phase_table[box_i][plane_i] = std::bind_front(&physics_system::narrow_phase_box_plane,
this);
55 narrow_phase_table[box_i][sphere_i] = std::bind_front(&physics_system::narrow_phase_box_sphere,
this);
56 narrow_phase_table[box_i][box_i] = std::bind_front(&physics_system::narrow_phase_box_box,
this);
57 narrow_phase_table[box_i][capsule_i] = std::bind_front(&physics_system::narrow_phase_box_capsule,
this);
59 narrow_phase_table[capsule_i][plane_i] = std::bind_front(&physics_system::narrow_phase_capsule_plane,
this);
60 narrow_phase_table[capsule_i][sphere_i] = std::bind_front(&physics_system::narrow_phase_capsule_sphere,
this);
61 narrow_phase_table[capsule_i][box_i] = std::bind_front(&physics_system::narrow_phase_capsule_box,
this);
62 narrow_phase_table[capsule_i][capsule_i] = std::bind_front(&physics_system::narrow_phase_capsule_capsule,
this);
67 detect_collisions_broad();
68 detect_collisions_narrow();
69 solve_constraints(dt);
76 for (
const auto entity_id: transform_view)
84 [&, dt](
auto& transform)
87 transform.local = body.get_transform();
99 std::execution::par_unseq,
102 [&, alpha](
auto entity_id)
104 const auto& rigid_body = *(view.get<rigid_body_component>(entity_id).body);
105 auto& scene_object = *(view.get<scene_component>(entity_id).object);
107 scene_object.set_transform(rigid_body.interpolate(alpha));
115 float nearest_hit_sqr_distance = std::numeric_limits<float>::infinity();
116 std::uint32_t nearest_face_index = 0;
121 for (
const auto entity_id: rigid_body_view)
123 if (entity_id == ignore_eid)
131 const auto& collider = rigid_body.get_collider();
139 if (!(collider->get_layer_mask() & layer_mask))
148 const auto& transform = rigid_body.get_transform();
150 bs_ray.
origin = ((ray.
origin - transform.translation) * transform.rotation) / transform.scale;
162 nearest_entity_id = entity_id;
170 if (nearest_entity_id == entt::null)
175 return std::make_tuple(nearest_entity_id,
std::sqrt(nearest_hit_sqr_distance), nearest_face_index, nearest_hit_normal);
178 void physics_system::integrate(
float dt)
183 std::execution::par_unseq,
188 auto& body = *(view.get<rigid_body_component>(entity_id).body);
198 void physics_system::solve_constraints(
float dt)
202 [dt](
auto& component)
204 component.constraint->solve(dt);
209 void physics_system::detect_collisions_broad()
211 broad_phase_pairs.clear();
214 for (
auto i = view.begin();
i != view.end(); ++
i)
218 const auto& collider_a = body_a.get_collider();
224 for (
auto j = i + 1;
j != view.end(); ++
j)
228 const auto& collider_b = body_b.get_collider();
235 if (!(collider_a->get_layer_mask() & collider_b->get_layer_mask()))
241 if (body_a.is_static() && body_b.is_static())
246 broad_phase_pairs.emplace_back(&body_a, &body_b);
251 void physics_system::detect_collisions_narrow()
253 narrow_phase_manifolds.clear();
255 for (
const auto& pair: broad_phase_pairs)
257 auto& body_a = *pair.first;
258 auto& body_b = *pair.second;
260 narrow_phase_table[std::to_underlying(body_a.get_collider()->type())][std::to_underlying(body_b.get_collider()->type())](body_a, body_b);
264 void physics_system::resolve_collisions()
266 for (
const auto& manifold: narrow_phase_manifolds)
268 auto& body_a = *manifold.body_a;
269 auto& body_b = *manifold.body_b;
271 const auto& material_a = *body_a.get_collider()->get_material();
272 const auto& material_b = *body_b.get_collider()->get_material();
283 const float sum_inverse_mass = body_a.get_inverse_mass() + body_b.get_inverse_mass();
284 const float impulse_scale = 1.0f /
static_cast<float>(manifold.contact_count);
286 for (std::uint8_t i = 0;
i < manifold.contact_count; ++
i)
293 math::fvec3 relative_velocity = body_b.get_point_velocity(radius_b) - body_a.get_point_velocity(radius_a);
295 const float contact_velocity =
math::dot(relative_velocity, contact.
normal);
296 if (contact_velocity > 0.0f)
301 const float reaction_impulse_num = -(1.0f + restitution_coef) * contact_velocity;
304 const float reaction_impulse_den = sum_inverse_mass +
307 math::cross(body_a.get_inverse_inertia() * ra_cross_n, radius_a) +
308 math::cross(body_b.get_inverse_inertia() * rb_cross_n, radius_b),
311 const float reaction_impulse_mag = (reaction_impulse_num / reaction_impulse_den) * impulse_scale;
315 body_a.apply_impulse(-reaction_impulse, radius_a);
316 body_b.apply_impulse(reaction_impulse, radius_b);
320 math::fvec3 contact_tangent = relative_velocity - contact.
normal * contact_velocity;
322 if (sqr_tangent_length > 0.0f)
324 contact_tangent /=
std::sqrt(sqr_tangent_length);
327 const float friction_impulse_num =
math::dot(relative_velocity, -contact_tangent);
330 const float friction_impulse_den = sum_inverse_mass +
333 math::cross(body_a.get_inverse_inertia() * ra_cross_t, radius_a) +
334 math::cross(body_b.get_inverse_inertia() * rb_cross_t, radius_b),
337 float friction_impulse_mag = (friction_impulse_num / friction_impulse_den) * impulse_scale;
339 if (
std::abs(friction_impulse_mag) >= reaction_impulse_mag * static_friction_coef)
341 friction_impulse_mag = -reaction_impulse_mag * dynamic_friction_coef;
344 const math::fvec3 friction_impulse = contact_tangent * friction_impulse_mag;
346 body_a.apply_impulse(-friction_impulse, radius_a);
347 body_b.apply_impulse(friction_impulse, radius_b);
352 void physics_system::correct_positions()
354 const float depth_threshold = 0.01f;
355 const float correction_factor = 0.4f;
357 for (
const auto& manifold: narrow_phase_manifolds)
359 auto& body_a = *manifold.body_a;
360 auto& body_b = *manifold.body_b;
361 const float sum_inverse_mass = body_a.get_inverse_mass() + body_b.get_inverse_mass();
363 for (std::uint8_t i = 0;
i < manifold.contact_count; ++
i)
367 math::fvec3 correction = contact.
normal * (std::max<float>(0.0f, contact.
depth - depth_threshold) / sum_inverse_mass) * correction_factor;
369 body_a.set_position(body_a.get_position() - correction * body_a.get_inverse_mass());
370 body_b.set_position(body_b.get_position() + correction * body_b.get_inverse_mass());
387 const float plane_constant = plane_a.get_constant() -
math::dot(plane_normal, body_a.
get_position());
390 if (signed_distance > sphere_b.get_radius())
396 collision_manifold_type manifold;
397 manifold.body_a = &body_a;
398 manifold.body_b = &body_b;
399 manifold.contact_count = 1;
402 auto& contact = manifold.contacts[0];
404 contact.
normal = plane_normal;
405 contact.
depth =
std::abs(signed_distance - sphere_b.get_radius());
407 narrow_phase_manifolds.emplace_back(std::move(manifold));
417 const float plane_constant = plane_a.get_constant() -
math::dot(plane_normal, body_a.
get_position());
419 const auto& box_min = box_b.get_min();
420 const auto& box_max = box_b.get_max();
423 {box_min.
x(), box_min.y(), box_min.z()},
424 {box_min.x(), box_min.y(), box_max.z()},
425 {box_min.x(), box_max.y(), box_min.z()},
426 {box_min.x(), box_max.y(), box_max.z()},
427 {box_max.x(), box_min.y(), box_min.z()},
428 {box_max.x(), box_min.y(), box_max.z()},
429 {box_max.x(), box_max.y(), box_min.z()},
430 {box_max.x(), box_max.y(), box_max.z()}
434 collision_manifold_type manifold;
435 manifold.contact_count = 0;
438 for (std::size_t i = 0;
i < 8; ++
i)
443 const float signed_distance =
math::dot(plane_normal,
point) + plane_constant;
445 if (signed_distance <= 0.0f)
447 auto& contact = manifold.contacts[manifold.contact_count];
449 contact.
normal = plane_normal;
452 ++manifold.contact_count;
454 if (manifold.contact_count >= 4)
461 if (manifold.contact_count)
463 manifold.body_a = &body_a;
464 manifold.body_b = &body_b;
465 narrow_phase_manifolds.emplace_back(std::move(manifold));
486 capsule_b.get_radius()
490 const float distance_a =
plane.distance(
capsule.segment.a);
491 const float distance_b =
plane.distance(
capsule.segment.b);
493 collision_manifold_type manifold;
494 manifold.contact_count = 0;
496 if (distance_a <=
capsule.radius)
498 auto& contact = manifold.contacts[manifold.contact_count];
504 ++manifold.contact_count;
507 if (distance_b <=
capsule.radius)
509 auto& contact = manifold.contacts[manifold.contact_count];
515 ++manifold.contact_count;
518 if (manifold.contact_count)
520 manifold.body_a = &body_a;
521 manifold.body_b = &body_b;
522 narrow_phase_manifolds.emplace_back(std::move(manifold));
528 narrow_phase_plane_sphere(body_b, body_a);
539 const float radius_a = collider_a.get_radius();
540 const float radius_b = collider_b.get_radius();
543 const float sum_radii = radius_a + radius_b;
561 collision_manifold_type manifold;
562 manifold.body_a = &body_a;
563 manifold.body_b = &body_b;
564 manifold.contact_count = 1;
567 auto& contact = manifold.contacts[0];
573 contact.
point = center_a + contact.
normal * (radius_a - contact.
depth * 0.5f);
575 narrow_phase_manifolds.emplace_back(std::move(manifold));
590 const float radius_a = collider_a.get_radius();
598 const float radius_b = collider_b.get_radius();
601 const float sum_radii = radius_a + radius_b;
621 collision_manifold_type manifold;
622 manifold.contact_count = 1;
623 manifold.body_a = &body_a;
624 manifold.body_b = &body_b;
626 auto& contact = manifold.contacts[0];
632 contact.
point = center_a + contact.
normal * (radius_a - contact.
depth * 0.5f);
634 narrow_phase_manifolds.emplace_back(std::move(manifold));
639 narrow_phase_plane_box(body_b, body_a);
659 narrow_phase_plane_capsule(body_b, body_a);
664 narrow_phase_sphere_capsule(body_b, body_a);
684 collider_a.get_radius()
692 collider_b.get_radius()
696 const auto [closest_a, closest_b] =
geom::closest_point(capsule_a.segment, capsule_b.segment);
699 const float sum_radii = capsule_a.radius + capsule_b.radius;
717 collision_manifold_type manifold;
718 manifold.body_a = &body_a;
719 manifold.body_b = &body_b;
720 manifold.contact_count = 1;
723 auto& contact = manifold.contacts[0];
729 contact.
point = closest_a + contact.
normal * (capsule_a.radius - contact.
depth * 0.5f);
731 narrow_phase_manifolds.emplace_back(std::move(manifold));
Capsule collision object.
constexpr const std::shared_ptr< collider > & get_collider() const noexcept
Returns the collider of the rigid body.
constexpr const math::fquat & get_orientation() const noexcept
Returns the current orientation of the rigid body.
constexpr const math::fvec3 & get_position() const noexcept
Returns the current position of the rigid body.
constexpr const math::transform< float > & get_transform() const noexcept
Returns the transformation representing the current state of the rigid body.
void interpolate(float alpha)
void update(float t, float dt) override
Perform's a system's update() function.
physics_system(entity::registry ®istry)
std::optional< std::tuple< entity::id, float, std::uint32_t, math::fvec3 > > trace(const geom::ray< float, 3 > &ray, entity::id ignore_eid=entt::null, std::uint32_t layer_mask=~std::uint32_t{0}) const
Traces a ray to the nearest point of intersection.
Abstract base class for updatable systems.
entity::registry & registry
Registry on which the system operate.
constexpr int difference(T x, T y) noexcept
Returns the number of differing bits between two values, known as Hamming distance.
entt::registry registry
Component registry type.
entt::entity id
Entity ID type.
hypercapsule< T, 3 > capsule
3-dimensional capsule.
hyperplane< T, 3 > plane
3-dimensional hyperplane.
math::vector< T, N > point
n-dimensional point.
constexpr std::optional< T > intersection(const ray< T, N > &ray, const hyperplane< T, N > &hyperplane) noexcept
Ray-hyperplane intersection test.
constexpr point< T, N > closest_point(const ray< T, N > &a, const point< T, N > &b) noexcept
Calculates the closest point on a ray to a point.
constexpr T sqr_distance(const vector< T, N > &p0, const vector< T, N > &p1) noexcept
Calculates the square distance between two points.
constexpr vector< T, N > max(const vector< T, N > &x, const vector< T, N > &y)
Returns a vector containing the maximum elements of two vectors.
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
constexpr vector< T, N > abs(const vector< T, N > &x)
Returns the absolute values of each element.
constexpr T sqr_length(const quaternion< T > &q) noexcept
Calculates the square length of a quaternion.
T distance(const vector< T, N > &p0, const vector< T, N > &p1)
Calculates the distance between two points.
constexpr vector< T, 3 > cross(const vector< T, 3 > &x, const vector< T, 3 > &y) noexcept
Calculates the cross product of two vectors.
vector< T, N > sqrt(const vector< T, N > &x)
Takes the square root of each element.
constexpr T dot(const quaternion< T > &a, const quaternion< T > &b) noexcept
Calculates the dot product of two quaternions.
restitution_combine_mode
Specifies how coefficients of restitution should be calculated.
float combine_restitution(float a, float b, restitution_combine_mode mode) noexcept
Combines two restitution values into a coefficient of restitution.
@ capsule
Capsule collider.
friction_combine_mode
Specifies how coefficients of friction should be calculated.
float combine_friction(float a, float b, friction_combine_mode mode) noexcept
Combines two friction values into a coefficient of friction.
Half of a line proceeding from an initial point.
constexpr vector_type extrapolate(T distance) const noexcept
Extrapolates from the ray origin along the ray direction vector.
vector_type direction
Ray direction vector.
vector_type origin
Ray origin position.
constexpr element_type & x() noexcept
Returns a reference to the first element.
std::unique_ptr< physics::rigid_body > body