Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement sweep-based CCD #391

Merged
merged 7 commits into from
Jul 2, 2024
Merged

Implement sweep-based CCD #391

merged 7 commits into from
Jul 2, 2024

Conversation

Jondolf
Copy link
Owner

@Jondolf Jondolf commented Jul 1, 2024

Objective

#385 added speculative collision, a form of Continuous Collision Detection (CCD) with the goal of preventing tunneling. However, it isn't perfect; speculative collision can sometimes cause ghost collisions, and it can miss collisions against objects spinning at very high speeds.

Another form of CCD is swept CCD, which sweeps colliders from their previous positions to the current ones, and moves the bodies to the time of impact if a hit was found. This can be more reliable than speculative collision, and can act as a sort of safety net when you want to ensure that an object has no tunneling, at the cost of being more expensive and causing "time loss" where bodies appear to stop momentarily as they are brought back in time.

Both forms of CCD are valuable and should exist. They can also be used together to complement each other!

Solution

Add a CcdPlugin that performs sweep-based CCD for rigid bodies that have the SweptCcd component.

commands.spawn((
    RigidBody::Dynamic,
    Collider::circle(0.5),
    // Enable swept CCD for this rigid body.
    SweptCcd::default(),
));

Two sweep modes are supported:

  • If SweptCcd::mode is SweepMode::Linear, only translational motion is considered.
  • If SweptCcd::mode is SweepMode::NonLinear, both translational and rotational motion are considered. This is more expensive.

By default, the mode is SweepMode::NonLinear, but it can be specified using associated constants SweptCcd::Linear or SweptCcd::NonLinear, or by using the SweptCcd::new_with_mode constructor.

It may not be desirable to have CCD enabled for all collisions. To enable it based on the relative velocities of the bodies involved, velocity thresholds can be set:

SweptCcd::NonLinear.with_velocity_threshold(linear, angular)

Additionally, swept CCD can be disabled for collisions against dynamic bodies:

SweptCcd::NonLinear.include_dynamic(false)

Below you can see the new ccd example and the different forms of CCD in action!

2024-07-02.02-31-48.mp4

As you can see, all the different modes and combinations behave differently, and have their own unique characteristics and trade-offs. In this spinning example, sweep-based CCD clearly has time loss, especially with speculative collision disabled. Non-linear swept CCD is also the most reliable however, as it doesn't let any of the projectiles tunnel through. Linear swept CCD on the other hand struggles a lot in this case since it doesn't take rotational motion into account.

Of course, this is a very extreme and relatively niche example. For most objects in most games, speculative collision should be enough, and swept CCD can be used as a safety net when it isn't.

Implementation

The implementation works as follows:

  • Store broad phase intersections for entities with SweptCcd in an AabbIntersections component.
  • In solve_swept_ccd, iterate through the AABB intersections for each entity with SweptCcd, and perform shape casts to find the minimum time of impact.
    • If SweptCcd::mode is SweepMode::Linear, only translational motion is considered.
    • If SweptCcd::mode is SweepMode::NonLinear, both translational and rotational motion are considered. This is more expensive.
  • Integrate the positions of the bodies for the duration of the minimum time of impact to move them into a touching state.
  • Normal collision detection and collision response will handle the collision during the next frame.

A number of physics engines were used as inspiration for the implementation and terminology, primarily Box2D V3, Bepu, and Jolt.

Unlike Rapier, I did not implement substepping for the time of impact solver, because it is more expensive and complex, and likely much more difficult to parallelize in the future. I believe Box2D V3 is opting for a similar approach for similar reasons.

@Jondolf Jondolf added C-Enhancement New feature or request A-Collision Relates to the broad phase, narrow phase, colliders, or other collision functionality A-Dynamics Relates to rigid body dynamics: motion, mass, constraint solving, joints, CCD, and so on labels Jul 1, 2024
@Jondolf Jondolf merged commit 3bb6b42 into avian Jul 2, 2024
4 checks passed
@Jondolf Jondolf deleted the swept-ccd branch July 2, 2024 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Collision Relates to the broad phase, narrow phase, colliders, or other collision functionality A-Dynamics Relates to rigid body dynamics: motion, mass, constraint solving, joints, CCD, and so on C-Enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant