mod contacts;
mod intersections;
mod pair_management;
mod pair_update;
mod queries;
mod solver_graph;
#[cfg(test)]
#[cfg(feature = "f32")]
#[cfg(feature = "dim3")]
mod test;
use crate::alloc_prelude::*;
use crate::data::Coarena;
use crate::dynamics::solver::solver_contact_graph::{
GENERIC_BUCKET, SolverContactGraph, bucket_id,
};
use crate::dynamics::{IslandManager, RigidBodySet};
use crate::geometry::{
ColliderGraphIndex, ColliderHandle, ColliderSet, ContactData, ContactManifoldData, ContactPair,
InteractionGraph, IntersectionPair, SolverFlags,
};
use alloc::sync::Arc;
use parry::query::{DefaultQueryDispatcher, PersistentQueryDispatcher};
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
struct ColliderGraphIndices {
contact_graph_index: ColliderGraphIndex,
intersection_graph_index: ColliderGraphIndex,
}
impl ColliderGraphIndices {
fn invalid() -> Self {
Self {
contact_graph_index: InteractionGraph::<(), ()>::invalid_graph_index(),
intersection_graph_index: InteractionGraph::<(), ()>::invalid_graph_index(),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum PairRemovalMode {
FromContactGraph,
FromIntersectionGraph,
Auto,
}
fn strong_wake_sleeping_side(
islands: &mut IslandManager,
bodies: &mut RigidBodySet,
h1: Option<crate::dynamics::RigidBodyHandle>,
h2: Option<crate::dynamics::RigidBodyHandle>,
) {
for h in [h1, h2].into_iter().flatten() {
let sleeping_dyn = bodies
.get(h)
.is_some_and(|rb| rb.is_dynamic() && rb.activation.sleeping);
if sleeping_dyn {
islands.wake_up(bodies, h, true);
}
}
}
fn pack_color_body_info(info: Option<(u32, bool)>) -> u32 {
match info {
None => u32::MAX,
Some((id, fixed)) => (id << 1) | fixed as u32,
}
}
fn unpack_color_body_info(packed: u32) -> Option<(u32, bool)> {
if packed == u32::MAX {
None
} else {
Some((packed >> 1, packed & 1 != 0))
}
}
fn assign_pair_solver_color(
masks: &mut Vec<u128>,
pair: &mut ContactPair,
body1: Option<(u32, bool)>, body2: Option<(u32, bool)>,
) {
use crate::geometry::contact_pair::{
SOLVER_COLOR_OVERFLOW, SOLVER_COLOR_UNCOLORED, SOLVER_DYNAMIC_COLOR_COUNT,
};
if pair.solver_color != SOLVER_COLOR_UNCOLORED {
return;
}
let conflicting1 = body1.filter(|(_, fixed)| !fixed).map(|(id, _)| id);
let conflicting2 = body2.filter(|(_, fixed)| !fixed).map(|(id, _)| id);
let max_id = conflicting1.max(conflicting2).map(|id| id as usize);
if let Some(max_id) = max_id {
if masks.len() <= max_id {
masks.resize(max_id + 1, 0);
}
}
let (color, bodies) = match (conflicting1, conflicting2) {
(Some(i1), Some(i2)) => {
let mask = masks[i1 as usize] | masks[i2 as usize];
let dynamic_free = !mask & ((1u128 << SOLVER_DYNAMIC_COLOR_COUNT) - 1);
(dynamic_free.trailing_zeros(), [i1, i2])
}
(Some(i1), None) => {
let mask = masks[i1 as usize];
(127u32.wrapping_sub((!mask).leading_zeros()), [i1, u32::MAX])
}
(None, Some(i2)) => {
let mask = masks[i2 as usize];
(127u32.wrapping_sub((!mask).leading_zeros()), [i2, u32::MAX])
}
(None, None) => {
pair.solver_color = SOLVER_COLOR_OVERFLOW;
pair.solver_color_bodies = [u32::MAX; 2];
return;
}
};
if color >= 128 {
pair.solver_color = SOLVER_COLOR_OVERFLOW;
pair.solver_color_bodies = [u32::MAX; 2];
return;
}
for id in bodies {
if id != u32::MAX {
masks[id as usize] |= 1 << color;
}
}
pair.solver_color = color as u8;
pair.solver_color_bodies = bodies;
}
fn clear_pair_solver_color(masks: &mut [u128], pair: &mut ContactPair) {
use crate::geometry::contact_pair::{SOLVER_COLOR_OVERFLOW, SOLVER_COLOR_UNCOLORED};
if pair.solver_color < SOLVER_COLOR_OVERFLOW {
for id in pair.solver_color_bodies {
if id != u32::MAX {
if let Some(mask) = masks.get_mut(id as usize) {
*mask &= !(1u128 << pair.solver_color);
}
}
}
}
pair.solver_color = SOLVER_COLOR_UNCOLORED;
pair.solver_color_bodies = [u32::MAX; 2];
}
fn clear_filtered_pair(pair: &mut ContactPair) -> bool {
let in_graph = pair
.solver_manifolds()
.iter()
.any(|m| m.data.graph_pos.is_some());
pair.clear();
in_graph
}
const PAIR_HINT_DYN_BIT: u16 = 1 << 15;
const PAIR_HINT_COUNT_MASK: u16 = PAIR_HINT_DYN_BIT - 1;
fn single_manifold_bucket_drift(pair: &ContactPair, selectable: bool) -> bool {
use crate::geometry::contact_pair::{SOLVER_COLOR_OVERFLOW, SOLVER_COLOR_UNCOLORED};
let manifold = &pair.solver_manifolds()[0];
let qualifies = selectable
&& manifold
.data
.solver_flags
.contains(SolverFlags::COMPUTE_IMPULSES)
&& manifold.data.num_active_contacts() != 0;
let pos = manifold.data.graph_pos;
if !qualifies {
return pos.is_some();
}
if !pos.is_some() {
return true;
}
if pos.bucket() == GENERIC_BUCKET {
return false;
}
let mut color = pair.solver_color;
if color == SOLVER_COLOR_UNCOLORED {
color = SOLVER_COLOR_OVERFLOW;
}
pos.bucket() != bucket_id(color)
}
fn pair_qualified_manifold_count(pair: &ContactPair) -> u16 {
let solver_manifolds = if pair.solver_clusters.is_empty() {
&pair.manifolds
} else {
&pair.solver_clusters
};
let mut count: u16 = 0;
for manifold in solver_manifolds {
if manifold
.data
.solver_flags
.contains(SolverFlags::COMPUTE_IMPULSES)
&& manifold.data.num_active_contacts() != 0
{
count = count.saturating_add(1);
}
}
count.min(PAIR_HINT_COUNT_MASK)
}
fn collect_pairs_to_update<E>(
candidates: &mut Vec<u32>,
graph_indices: &Coarena<ColliderGraphIndices>,
graph: &crate::data::graph::Graph<ColliderHandle, E>,
islands: &IslandManager,
bodies: &RigidBodySet,
colliders: &ColliderSet,
modified_colliders: &[ColliderHandle],
select_graph_id: impl Fn(&ColliderGraphIndices) -> ColliderGraphIndex,
) {
candidates.clear();
if graph.edges.is_empty() {
return;
}
let num_active = islands.active_bodies().count();
if num_active * 2 >= bodies.len() {
candidates.extend(0..graph.edges.len() as u32);
return;
}
let mut push_edges_of = |handle: ColliderHandle, require_change_flags: bool| {
let Some(co) = colliders.get(handle) else {
return;
};
if require_change_flags && !co.changes.needs_narrow_phase_update() {
return;
}
let Some(gid) = graph_indices.get(handle.0) else {
return;
};
for edge in graph.edges(select_graph_id(gid)) {
candidates.push(edge.id().index() as u32);
}
};
for handle in modified_colliders {
push_edges_of(*handle, true);
}
for body_handle in islands.active_bodies() {
if let Some(rb) = bodies.get(body_handle) {
for co_handle in rb.colliders() {
push_edges_of(*co_handle, false);
}
}
}
candidates.sort_unstable();
candidates.dedup();
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone)]
pub struct NarrowPhase {
#[cfg_attr(
feature = "serde-serialize",
serde(skip, default = "crate::geometry::default_persistent_query_dispatcher")
)]
query_dispatcher: Arc<dyn PersistentQueryDispatcher<ContactManifoldData, ContactData>>,
contact_graph: InteractionGraph<ColliderHandle, ContactPair>,
intersection_graph: InteractionGraph<ColliderHandle, IntersectionPair>,
graph_indices: Coarena<ColliderGraphIndices>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
update_candidates: Vec<u32>,
#[cfg_attr(feature = "serde-serialize", serde(default))]
body_solver_color_masks: Vec<u128>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
body_qualify_info: Vec<u64>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
awake_body_mask: Vec<bool>,
pair_solver_hints: Vec<u16>,
solver_contact_graph: SolverContactGraph,
solver_graph_valid: bool,
solver_graph_epoch: u32,
solver_graph_mb_epoch: u32,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
solver_graph_dirty: Vec<u32>,
force_event_pairs: Vec<u32>,
force_event_pos: Vec<u32>,
force_event_flagged: Vec<u32>,
force_list_valid: bool,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
solver_color_todo: Vec<(u32, u32, u32)>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
retired_pairs: Vec<ContactPair>,
}
pub(crate) type ContactManifoldIndex = usize;
impl Default for NarrowPhase {
fn default() -> Self {
Self::new()
}
}
impl NarrowPhase {
pub fn new() -> Self {
Self::with_query_dispatcher(DefaultQueryDispatcher)
}
pub fn with_query_dispatcher<D>(d: D) -> Self
where
D: 'static + PersistentQueryDispatcher<ContactManifoldData, ContactData>,
{
Self {
query_dispatcher: Arc::new(d),
contact_graph: InteractionGraph::new(),
intersection_graph: InteractionGraph::new(),
graph_indices: Coarena::new(),
update_candidates: Vec::new(),
retired_pairs: Vec::new(),
body_solver_color_masks: Vec::new(),
body_qualify_info: Vec::new(),
awake_body_mask: Vec::new(),
pair_solver_hints: Vec::new(),
solver_contact_graph: SolverContactGraph::new(),
solver_graph_valid: false,
solver_graph_epoch: 0,
solver_graph_mb_epoch: 0,
solver_graph_dirty: Vec::new(),
force_event_pairs: Vec::new(),
force_event_pos: Vec::new(),
force_event_flagged: Vec::new(),
force_list_valid: false,
solver_color_todo: Vec::new(),
}
}
fn refresh_awake_body_mask(&mut self, islands: &IslandManager) {
self.awake_body_mask.clear();
let len = islands
.active_bodies()
.map(|h| h.into_raw_parts().0 as usize)
.max()
.map(|m| m + 1)
.unwrap_or(0);
self.awake_body_mask.resize(len, false);
for handle in islands.active_bodies() {
self.awake_body_mask[handle.into_raw_parts().0 as usize] = true;
}
}
}