use na::RealField;
use slotmap::{Key, SlotMap};
use crate::pipeline::narrow_phase::{
ContactDispatcher, ContactEvent, ContactEvents, ContactManifoldGenerator, Interaction,
InteractionGraph, ProximityDetector, ProximityDispatcher, ProximityEvent, ProximityEvents,
};
use crate::pipeline::object::{
CollisionObjectHandle, CollisionObjectRef, CollisionObjectSet, GeometricQueryType,
};
use crate::query::{ContactId, ContactManifold, Proximity};
pub struct NarrowPhase<N: RealField, Handle: CollisionObjectHandle> {
contact_dispatcher: Box<dyn ContactDispatcher<N>>,
proximity_dispatcher: Box<dyn ProximityDispatcher<N>>,
contact_events: ContactEvents<Handle>,
proximity_events: ProximityEvents<Handle>,
id_allocator: SlotMap<ContactId, bool>,
}
impl<N: RealField, Handle: CollisionObjectHandle> NarrowPhase<N, Handle> {
pub fn new(
contact_dispatcher: Box<dyn ContactDispatcher<N>>,
proximity_dispatcher: Box<dyn ProximityDispatcher<N>>,
) -> NarrowPhase<N, Handle> {
NarrowPhase {
contact_dispatcher,
proximity_dispatcher,
contact_events: ContactEvents::new(),
proximity_events: ProximityEvents::new(),
id_allocator: SlotMap::with_key(),
}
}
fn garbage_collect_ids(&mut self, interactions: &mut InteractionGraph<N, Handle>) {
for interaction in interactions.0.edge_weights_mut() {
match interaction {
Interaction::Contact(_, manifold) => {
for contact in manifold.contacts() {
if !contact.id.is_null() {
self.id_allocator[contact.id] = true;
}
}
}
Interaction::Proximity(..) => {}
}
}
self.id_allocator
.retain(|_, is_valid| std::mem::replace(is_valid, false))
}
pub fn update_contact(
&mut self,
co1: &impl CollisionObjectRef<N>,
co2: &impl CollisionObjectRef<N>,
handle1: Handle,
handle2: Handle,
detector: &mut dyn ContactManifoldGenerator<N>,
manifold: &mut ContactManifold<N>,
) {
let had_contacts = manifold.len() != 0;
if let Some(prediction) = co1
.query_type()
.contact_queries_to_prediction(co2.query_type())
{
manifold.save_cache_and_clear();
let _ = detector.generate_contacts(
&*self.contact_dispatcher,
&co1.position(),
co1.shape(),
None,
&co2.position(),
co2.shape(),
None,
&prediction,
manifold,
);
for contact in manifold.contacts_mut() {
if contact.id.is_null() {
contact.id = self.id_allocator.insert(false)
}
}
} else {
panic!("Unable to compute contact between collision objects with query types different from `GeometricQueryType::Contacts(..)`.")
}
if manifold.len() == 0 {
if had_contacts {
self.contact_events
.push(ContactEvent::Stopped(handle1, handle2));
}
} else {
if !had_contacts {
self.contact_events
.push(ContactEvent::Started(handle1, handle2));
}
}
}
#[doc(hidden)]
pub fn emit_proximity_event(
&mut self,
handle1: Handle,
handle2: Handle,
prev_prox: Proximity,
new_prox: Proximity,
) {
if prev_prox != new_prox {
self.proximity_events
.push(ProximityEvent::new(handle1, handle2, prev_prox, new_prox));
}
}
pub fn update_proximity(
&mut self,
co1: &impl CollisionObjectRef<N>,
co2: &impl CollisionObjectRef<N>,
handle1: Handle,
handle2: Handle,
detector: &mut dyn ProximityDetector<N>,
curr_proximity: &mut Proximity,
) {
if let Some(new_proximity) = detector.update(
&*self.proximity_dispatcher,
&co1.position(),
co1.shape(),
&co2.position(),
co2.shape(),
co1.query_type().query_limit() + co2.query_type().query_limit(),
) {
self.emit_proximity_event(handle1, handle2, *curr_proximity, new_proximity);
*curr_proximity = new_proximity;
}
}
pub fn update_interaction(
&mut self,
co1: &impl CollisionObjectRef<N>,
co2: &impl CollisionObjectRef<N>,
handle1: Handle,
handle2: Handle,
interaction: &mut Interaction<N>,
) {
match interaction {
Interaction::Contact(detector, manifold) => {
self.update_contact(co1, co2, handle1, handle2, &mut **detector, manifold)
}
Interaction::Proximity(detector, prox) => {
self.update_proximity(co1, co2, handle1, handle2, &mut **detector, prox)
}
}
}
pub fn update<Objects>(
&mut self,
interactions: &mut InteractionGraph<N, Objects::CollisionObjectHandle>,
objects: &Objects,
) where
Objects: CollisionObjectSet<N, CollisionObjectHandle = Handle>,
{
for eid in interactions.0.edge_indices() {
let (id1, id2) = interactions.0.edge_endpoints(eid).unwrap();
let handle1 = interactions.0[id1];
let handle2 = interactions.0[id2];
let co1 = objects.collision_object(handle1).unwrap();
let co2 = objects.collision_object(handle2).unwrap();
let flags1 = co1.update_flags();
let flags2 = co2.update_flags();
if flags1.needs_narrow_phase_update() || flags2.needs_narrow_phase_update() {
self.update_interaction(
co1,
co2,
handle1,
handle2,
interactions.0.edge_weight_mut(eid).unwrap(),
)
}
}
self.garbage_collect_ids(interactions)
}
pub fn handle_interaction<Objects>(
&mut self,
interactions: &mut InteractionGraph<N, Objects::CollisionObjectHandle>,
objects: &Objects,
handle1: Objects::CollisionObjectHandle,
handle2: Objects::CollisionObjectHandle,
started: bool,
) where
Objects: CollisionObjectSet<N, CollisionObjectHandle = Handle>,
{
let co1 = objects.collision_object(handle1).unwrap();
let co2 = objects.collision_object(handle2).unwrap();
let id1 = co1.graph_index().expect(crate::NOT_REGISTERED_ERROR);
let id2 = co2.graph_index().expect(crate::NOT_REGISTERED_ERROR);
if started {
if !interactions.0.contains_edge(id1, id2) {
match (co1.query_type(), co2.query_type()) {
(GeometricQueryType::Contacts(..), GeometricQueryType::Contacts(..)) => {
let dispatcher = &self.contact_dispatcher;
if let Some(detector) =
dispatcher.get_contact_algorithm(co1.shape(), co2.shape())
{
let manifold = detector.init_manifold();
let _ = interactions.0.add_edge(
id1,
id2,
Interaction::Contact(detector, manifold),
);
}
}
(_, GeometricQueryType::Proximity(_))
| (GeometricQueryType::Proximity(_), _) => {
let dispatcher = &self.proximity_dispatcher;
if let Some(detector) =
dispatcher.get_proximity_algorithm(co1.shape(), co2.shape())
{
let _ = interactions.0.add_edge(
id1,
id2,
Interaction::Proximity(detector, Proximity::Disjoint),
);
}
}
}
}
} else {
if let Some(eid) = interactions.0.find_edge(id1, id2) {
let endpoints = interactions.0.edge_endpoints(eid).unwrap();
let handle1 = *interactions.0.node_weight(endpoints.0).unwrap();
let handle2 = *interactions.0.node_weight(endpoints.1).unwrap();
if let Some(detector) = interactions.0.remove_edge(eid) {
match detector {
Interaction::Contact(_, mut manifold) => {
if manifold.len() != 0 {
self.contact_events
.push(ContactEvent::Stopped(handle1, handle2));
}
manifold.clear();
}
Interaction::Proximity(_, prev_prox) => {
self.emit_proximity_event(
handle1,
handle2,
prev_prox,
Proximity::Disjoint,
);
}
}
}
}
}
}
pub fn contact_events(&self) -> &ContactEvents<Handle> {
&self.contact_events
}
pub fn proximity_events(&self) -> &ProximityEvents<Handle> {
&self.proximity_events
}
pub fn clear_events(&mut self) {
self.contact_events.clear();
self.proximity_events.clear();
}
}