Skip to main content

pfx_physics/rigid/
collider.rs

1use rapier3d::prelude::SharedShape;
2use rapier3d::prelude::{CoefficientCombineRule, Group, InteractionGroups, InteractionTestMode};
3
4use super::Pose;
5use super::convert::vec;
6
7#[derive(Clone, Debug, PartialEq)]
8pub enum Shape {
9    Sphere { radius: f32 },
10    Cylinder { half_height: f32, radius: f32 },
11    Disc { radius: f32, thickness: f32 },
12    Capsule { half_height: f32, radius: f32 },
13    Box { half: [f32; 3] },
14    RoundBox { half: [f32; 3], radius: f32 },
15    Hull { points: Vec<[f32; 3]> },
16}
17
18impl Shape {
19    pub(crate) fn shared(&self) -> Option<SharedShape> {
20        match self {
21            Self::Sphere { radius } => Some(SharedShape::ball(*radius)),
22            Self::Cylinder {
23                half_height,
24                radius,
25            } => Some(SharedShape::cylinder(*half_height, *radius)),
26            Self::Disc { radius, thickness } => {
27                Some(SharedShape::cylinder(thickness * 0.5, *radius))
28            }
29            Self::Capsule {
30                half_height,
31                radius,
32            } => Some(SharedShape::capsule_y(*half_height, *radius)),
33            Self::Box { half } => Some(SharedShape::cuboid(half[0], half[1], half[2])),
34            Self::RoundBox { half, radius } => {
35                let radius = radius.min(half[0]).min(half[1]).min(half[2]).max(0.0);
36                Some(SharedShape::round_cuboid(
37                    half[0] - radius,
38                    half[1] - radius,
39                    half[2] - radius,
40                    radius,
41                ))
42            }
43            Self::Hull { points } => {
44                let points: Vec<_> = points.iter().map(|&p| vec(p)).collect();
45                SharedShape::convex_hull(&points)
46            }
47        }
48    }
49}
50
51#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
52pub enum Combine {
53    #[default]
54    Average,
55    Min,
56    Multiply,
57    Max,
58}
59
60impl Combine {
61    pub fn apply(a: f32, b: f32, rule_a: Self, rule_b: Self) -> f32 {
62        match rule_a.max(rule_b) {
63            Self::Average => (a + b) * 0.5,
64            Self::Min => a.min(b).abs(),
65            Self::Multiply => a * b,
66            Self::Max => a.max(b),
67        }
68    }
69
70    pub(crate) fn rule(self) -> CoefficientCombineRule {
71        match self {
72            Self::Average => CoefficientCombineRule::Average,
73            Self::Min => CoefficientCombineRule::Min,
74            Self::Multiply => CoefficientCombineRule::Multiply,
75            Self::Max => CoefficientCombineRule::Max,
76        }
77    }
78}
79
80#[derive(Clone, Copy, Debug, PartialEq)]
81pub struct Friction {
82    pub still: f32,
83    pub sliding: f32,
84    pub combine: Combine,
85}
86
87impl Friction {
88    pub const SLIP: f32 = 0.01;
89
90    pub fn new(still: f32, sliding: f32) -> Self {
91        Self {
92            still,
93            sliding,
94            combine: Combine::Average,
95        }
96    }
97
98    pub fn uniform(coefficient: f32) -> Self {
99        Self::new(coefficient, coefficient)
100    }
101
102    pub fn with_combine(mut self, combine: Combine) -> Self {
103        self.combine = combine;
104        self
105    }
106}
107
108impl Default for Friction {
109    fn default() -> Self {
110        Self::uniform(0.5)
111    }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115pub struct Layers {
116    pub member: u32,
117    pub mask: u32,
118}
119
120impl Layers {
121    pub const ALL: Self = Self {
122        member: u32::MAX,
123        mask: u32::MAX,
124    };
125
126    pub fn new(member: u32, mask: u32) -> Self {
127        Self { member, mask }
128    }
129
130    pub(crate) fn groups(self) -> InteractionGroups {
131        InteractionGroups::new(
132            Group::from_bits_retain(self.member),
133            Group::from_bits_retain(self.mask),
134            InteractionTestMode::And,
135        )
136    }
137}
138
139impl Default for Layers {
140    fn default() -> Self {
141        Self {
142            member: 1,
143            mask: u32::MAX,
144        }
145    }
146}
147
148#[derive(Clone, Debug, PartialEq)]
149pub struct ColliderDesc {
150    pub shape: Shape,
151    pub offset: Pose,
152    pub density: f32,
153    pub friction: Friction,
154    pub restitution: f32,
155    pub restitution_combine: Combine,
156    pub sensor: bool,
157    pub layers: Layers,
158    pub impulse_threshold: Option<f32>,
159}
160
161impl ColliderDesc {
162    pub fn new(shape: Shape) -> Self {
163        Self {
164            shape,
165            offset: Pose::IDENTITY,
166            density: 1.0,
167            friction: Friction::default(),
168            restitution: 0.0,
169            restitution_combine: Combine::Average,
170            sensor: false,
171            layers: Layers::default(),
172            impulse_threshold: None,
173        }
174    }
175
176    pub fn sphere(radius: f32) -> Self {
177        Self::new(Shape::Sphere { radius })
178    }
179
180    pub fn cuboid(half: [f32; 3]) -> Self {
181        Self::new(Shape::Box { half })
182    }
183
184    pub fn round_box(half: [f32; 3], radius: f32) -> Self {
185        Self::new(Shape::RoundBox { half, radius })
186    }
187
188    pub fn with_offset(mut self, offset: Pose) -> Self {
189        self.offset = offset;
190        self
191    }
192
193    pub fn with_density(mut self, density: f32) -> Self {
194        self.density = density;
195        self
196    }
197
198    pub fn with_friction(mut self, friction: Friction) -> Self {
199        self.friction = friction;
200        self
201    }
202
203    pub fn with_restitution(mut self, restitution: f32, combine: Combine) -> Self {
204        self.restitution = restitution;
205        self.restitution_combine = combine;
206        self
207    }
208
209    pub fn with_layers(mut self, layers: Layers) -> Self {
210        self.layers = layers;
211        self
212    }
213
214    pub fn with_impulse_threshold(mut self, impulse: f32) -> Self {
215        self.impulse_threshold = Some(impulse);
216        self
217    }
218
219    pub fn as_sensor(mut self) -> Self {
220        self.sensor = true;
221        self
222    }
223}