feat: reduce the amount of duplicate work the broad-phase is doing for user changes and CCD + release v0.28.0 (#872)

* feat: reduce the amount of duplicate work the broad-phase is doing for user changes and CCD

* Release v0.28.0

* chore: fix warnings

* chore: clippy fixes

* chore: more clippy fixes
This commit is contained in:
Sébastien Crozet
2025-08-08 18:15:34 +02:00
committed by GitHub
parent 038eb34aba
commit 317322b31b
43 changed files with 351 additions and 328 deletions

View File

@@ -1,11 +1,10 @@
use super::TOIEntry;
use crate::dynamics::{IslandManager, RigidBodyHandle, RigidBodySet};
use crate::geometry::{ColliderParent, ColliderSet, CollisionEvent, NarrowPhase};
use crate::dynamics::{IntegrationParameters, IslandManager, RigidBodyHandle, RigidBodySet};
use crate::geometry::{BroadPhaseBvh, ColliderParent, ColliderSet, CollisionEvent, NarrowPhase};
use crate::math::Real;
use crate::parry::utils::SortedPair;
use crate::pipeline::{EventHandler, QueryFilter, QueryPipeline};
use crate::pipeline::{EventHandler, QueryFilter};
use crate::prelude::{ActiveEvents, CollisionEventFlags};
use parry::partitioning::{Bvh, BvhBuildStrategy};
use parry::utils::hashmap::HashMap;
use std::collections::BinaryHeap;
@@ -16,26 +15,14 @@ pub enum PredictedImpacts {
}
/// Solver responsible for performing motion-clamping on fast-moving bodies.
#[derive(Clone)]
#[derive(Clone, Default)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct CCDSolver {
// TODO PERF: for now the CCD solver maintains its own bvh for CCD queries.
// At each frame it get rebuilt.
// We should consider an alternative to directly use the broad-phases.
#[cfg_attr(feature = "serde-serialize", serde(skip))]
bvh: Bvh,
}
impl Default for CCDSolver {
fn default() -> Self {
Self::new()
}
}
pub struct CCDSolver;
impl CCDSolver {
/// Initializes a new CCD solver
pub fn new() -> Self {
Self { bvh: Bvh::new() }
Self
}
/// Apply motion-clamping to the bodies affected by the given `impacts`.
@@ -101,42 +88,35 @@ impl CCDSolver {
#[profiling::function]
pub fn find_first_impact(
&mut self,
dt: Real,
dt: Real, // NOTE: this doesnt necessarily match the `params.dt`.
params: &IntegrationParameters,
islands: &IslandManager,
bodies: &RigidBodySet,
colliders: &ColliderSet,
broad_phase: &mut BroadPhaseBvh,
narrow_phase: &NarrowPhase,
) -> Option<Real> {
// Update the query pipeline with the colliders predicted positions.
self.bvh = Bvh::from_iter(
BvhBuildStrategy::Binned,
colliders.iter_enabled().map(|(co_handle, co)| {
let id = co_handle.into_raw_parts().0;
if let Some(co_parent) = co.parent {
let rb = &bodies[co_parent.handle];
for (handle, co) in colliders.iter_enabled() {
if let Some(co_parent) = co.parent {
let rb = &bodies[co_parent.handle];
if rb.is_ccd_active() {
let predicted_pos = rb
.pos
.integrate_forces_and_velocities(dt, &rb.forces, &rb.vels, &rb.mprops);
let next_position = predicted_pos * co_parent.pos_wrt_parent;
(
id as usize,
co.shape.compute_swept_aabb(&co.pos, &next_position),
)
} else {
(id as usize, co.shape.compute_aabb(&co.pos))
let swept_aabb = co.shape.compute_swept_aabb(&co.pos, &next_position);
broad_phase.set_aabb(params, handle, swept_aabb);
}
}),
);
}
}
let query_pipeline = QueryPipeline {
// NOTE: the upcast needs at least rust 1.86
dispatcher: narrow_phase.query_dispatcher(),
bvh: &self.bvh,
let query_pipeline = broad_phase.as_query_pipeline(
narrow_phase.query_dispatcher(),
bodies,
colliders,
filter: QueryFilter::default(),
};
QueryFilter::default(),
);
let mut pairs_seen = HashMap::default();
let mut min_toi = dt;
@@ -234,43 +214,39 @@ impl CCDSolver {
#[profiling::function]
pub fn predict_impacts_at_next_positions(
&mut self,
dt: Real,
params: &IntegrationParameters,
islands: &IslandManager,
bodies: &RigidBodySet,
colliders: &ColliderSet,
broad_phase: &mut BroadPhaseBvh,
narrow_phase: &NarrowPhase,
events: &dyn EventHandler,
) -> PredictedImpacts {
let dt = params.dt;
let mut frozen = HashMap::<_, Real>::default();
let mut all_toi = BinaryHeap::new();
let mut pairs_seen = HashMap::default();
let mut min_overstep = dt;
// Update the query pipeline with the colliders `next_position`.
self.bvh = Bvh::from_iter(
BvhBuildStrategy::Binned,
colliders.iter_enabled().map(|(co_handle, co)| {
let id = co_handle.into_raw_parts().0;
if let Some(co_parent) = co.parent {
for (handle, co) in colliders.iter_enabled() {
if let Some(co_parent) = co.parent {
let rb = &bodies[co_parent.handle];
if rb.is_ccd_active() {
let rb_next_pos = &bodies[co_parent.handle].pos.next_position;
let next_position = rb_next_pos * co_parent.pos_wrt_parent;
(
id as usize,
co.shape.compute_swept_aabb(&co.pos, &next_position),
)
} else {
(id as usize, co.shape.compute_aabb(&co.pos))
let swept_aabb = co.shape.compute_swept_aabb(&co.pos, &next_position);
broad_phase.set_aabb(params, handle, swept_aabb);
}
}),
);
}
}
let query_pipeline = QueryPipeline {
dispatcher: narrow_phase.query_dispatcher(),
bvh: &self.bvh,
let query_pipeline = broad_phase.as_query_pipeline(
narrow_phase.query_dispatcher(),
bodies,
colliders,
filter: QueryFilter::default(),
};
QueryFilter::default(),
);
/*
*