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}