#![allow(missing_docs)]
#![allow(dead_code)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct PairKey(u32, u32);
impl PairKey {
#[inline]
fn new(a: u32, b: u32) -> Self {
if a <= b { PairKey(a, b) } else { PairKey(b, a) }
}
#[inline]
fn matches(&self, entry: &CachedContact) -> bool {
*self == PairKey::new(entry.body_a, entry.body_b)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContactPoint {
pub pos_a: [f64; 3],
pub pos_b: [f64; 3],
pub normal: [f64; 3],
pub depth: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachedContact {
pub body_a: u32,
pub body_b: u32,
pub points: Vec<ContactPoint>,
pub normal_impulse: Vec<f64>,
pub tangent_impulse: Vec<[f64; 2]>,
pub lifetime: u32,
}
impl CachedContact {
fn new(a: u32, b: u32, points: Vec<ContactPoint>) -> Self {
let n = points.len();
Self {
body_a: a,
body_b: b,
points,
normal_impulse: vec![0.0; n],
tangent_impulse: vec![[0.0; 2]; n],
lifetime: 0,
}
}
pub fn warm_start_normal(&self) -> &[f64] {
&self.normal_impulse
}
pub fn warm_start_tangent(&self) -> &[[f64; 2]] {
&self.tangent_impulse
}
pub fn update_impulses(&mut self, normal: &[f64], tangent: &[[f64; 2]]) {
let n = self.points.len();
self.normal_impulse.resize(n, 0.0);
self.tangent_impulse.resize(n, [0.0; 2]);
let nlen = normal.len().min(n);
self.normal_impulse[..nlen].copy_from_slice(&normal[..nlen]);
let tlen = tangent.len().min(n);
self.tangent_impulse[..tlen].copy_from_slice(&tangent[..tlen]);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContactCache {
entries: Vec<CachedContact>,
pub max_lifetime: u32,
}
impl ContactCache {
pub fn new(max_lifetime: u32) -> Self {
Self {
entries: Vec::new(),
max_lifetime,
}
}
pub fn begin_step(&mut self) {
for e in &mut self.entries {
e.lifetime += 1;
}
}
pub fn update_pair(&mut self, a: u32, b: u32, points: Vec<ContactPoint>) {
let key = PairKey::new(a, b);
let n = points.len();
if let Some(e) = self.entries.iter_mut().find(|e| key.matches(e)) {
if e.points.len() != n {
e.normal_impulse = vec![0.0; n];
e.tangent_impulse = vec![[0.0; 2]; n];
}
e.points = points;
e.lifetime = 0;
} else {
self.entries.push(CachedContact::new(key.0, key.1, points));
}
}
pub fn lookup(&self, a: u32, b: u32) -> Option<&CachedContact> {
let key = PairKey::new(a, b);
self.entries.iter().find(|e| key.matches(e))
}
pub fn lookup_mut(&mut self, a: u32, b: u32) -> Option<&mut CachedContact> {
let key = PairKey::new(a, b);
self.entries.iter_mut().find(|e| key.matches(e))
}
pub fn store_impulses(&mut self, a: u32, b: u32, normal: &[f64], tangent: &[[f64; 2]]) {
let key = PairKey::new(a, b);
if let Some(e) = self.entries.iter_mut().find(|e| key.matches(e)) {
e.update_impulses(normal, tangent);
}
}
pub fn evict_stale(&mut self) -> usize {
let max = self.max_lifetime;
let before = self.entries.len();
self.entries.retain(|e| e.lifetime <= max);
before - self.entries.len()
}
pub fn remove(&mut self, a: u32, b: u32) {
let key = PairKey::new(a, b);
self.entries.retain(|e| !key.matches(e));
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn entry_count(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &CachedContact> {
self.entries.iter()
}
pub fn pairs_above_impulse_threshold(&self, threshold: f64) -> Vec<(u32, u32, f64)> {
self.entries
.iter()
.filter_map(|e| {
let sum: f64 = e.normal_impulse.iter().sum();
if sum > threshold {
Some((e.body_a, e.body_b, sum))
} else {
None
}
})
.collect()
}
pub fn highest_impact_pair(&self) -> Option<(u32, u32, f64)> {
self.entries
.iter()
.map(|e| {
let sum: f64 = e.normal_impulse.iter().sum();
(e.body_a, e.body_b, sum)
})
.max_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal))
}
}