ferravex_physics/
api.rs

1pub use crate::{ColliderHandle, RigidBodyHandle};
2/// Rigid body state. Waking requests activation of its contact island on the next step.
3#[derive(Clone, Debug)]
4pub struct RigidBody {
5    core: body::Core,
6}
7impl body::Access for RigidBody {
8    fn core(&self) -> &body::Core {
9        &self.core
10    }
11    fn core_mut(&mut self) -> &mut body::Core {
12        &mut self.core
13    }
14}
15impl RigidBody {
16    pub fn translation(&self) -> Vector {
17        from3(self.core.position)
18    }
19    pub fn rotation(&self) -> &Rotation {
20        rotation(&self.core)
21    }
22    pub fn linvel(&self) -> Vector {
23        from3(self.core.velocity)
24    }
25    pub fn angvel(&self) -> Angular {
26        angular_from3(self.core.angular_velocity)
27    }
28    pub fn mass(&self) -> f32 {
29        self.core.mass
30    }
31    pub fn colliders(&self) -> &[ColliderHandle] {
32        &self.core.colliders
33    }
34    pub fn is_ccd_enabled(&self) -> bool {
35        self.core.ccd
36    }
37    pub fn enable_ccd(&mut self, enabled: bool) {
38        self.core.ccd = enabled;
39    }
40    pub fn is_sleeping(&self) -> bool {
41        self.core.sleeping
42    }
43    pub fn wake_up(&mut self, _strong: bool) {
44        self.core.wake();
45    }
46    pub fn set_translation(&mut self, p: Vector, wake: bool) {
47        assert!(p.is_finite());
48        if self.core.position!=to3(p) {self.core.invalidate_contacts();}
49        self.core.position = to3(p);
50        if wake {
51            self.core.wake();
52        }
53    }
54    pub fn set_rotation(&mut self, q: Rotation, wake: bool) {
55        self.core.invalidate_contacts();
56        self.core.set_rotation(rotation_to3(q));
57        if wake {
58            self.core.wake();
59        }
60    }
61    pub fn set_linvel(&mut self, v: Vector, wake: bool) {
62        assert!(v.is_finite());
63        self.core.velocity = to3(v);
64        if wake {
65            self.core.wake();
66        }
67    }
68    pub fn set_angvel(&mut self, v: Angular, wake: bool) {
69        let v = angular_to3(v);
70        assert!(v.is_finite());
71        self.core.angular_velocity = v;
72        if wake {
73            self.core.wake();
74        }
75    }
76    pub fn set_gravity_scale(&mut self, scale: f32, wake: bool) {
77        assert!(scale.is_finite());
78        if self.core.gravity_scale!=scale {self.core.invalidate_contacts();}
79        self.core.gravity_scale = scale;
80        if wake {
81            self.core.wake();
82        }
83    }
84    pub fn apply_impulse(&mut self, impulse: Vector, wake: bool) {
85        let v = to3(impulse);
86        assert!(v.is_finite());
87        self.core.velocity += v * self.core.inv_mass;
88        if wake {
89            self.core.wake();
90        }
91    }
92    pub fn apply_impulse_at_point(&mut self, impulse: Vector, point: Vector, wake: bool) {
93        let j = to3(impulse);
94        let p = to3(point);
95        assert!(j.is_finite() && p.is_finite());
96        let torque = (p - self.core.position).cross(j);
97        self.core.velocity += j * self.core.inv_mass;
98        self.core.angular_velocity += self.core.inverse_inertia(torque);
99        if wake {
100            self.core.wake();
101        }
102    }
103    pub fn apply_torque_impulse(&mut self, impulse: Angular, wake: bool) {
104        let v = angular_to3(impulse);
105        assert!(v.is_finite());
106        self.core.angular_velocity += self.core.inverse_inertia(v);
107        if wake {
108            self.core.wake();
109        }
110    }
111    pub fn add_force(&mut self, force: Vector, wake: bool) {
112        let f = to3(force);
113        assert!(f.is_finite());
114        self.core.force += f;
115        if wake {
116            self.core.wake();
117        }
118    }
119    pub fn add_torque(&mut self, torque: Angular, wake: bool) {
120        let t = angular_to3(torque);
121        assert!(t.is_finite());
122        self.core.torque += t;
123        if wake {
124            self.core.wake();
125        }
126    }
127    pub fn set_next_kinematic_translation(&mut self, p: Vector) {
128        assert!(p.is_finite());
129        self.core.next_position = Some(to3(p));
130    }
131    pub fn set_next_kinematic_rotation(&mut self, q: Rotation) {
132        self.core.next_rotation = Some(rotation_to3(q));
133    }
134}
135#[derive(Clone, Debug)]
136pub struct RigidBodyBuilder {
137    core: body::Core,
138}
139impl RigidBodyBuilder {
140    pub fn dynamic() -> Self {
141        Self {
142            core: body::Core::new(body::Kind::Dynamic, PLANAR),
143        }
144    }
145    pub fn fixed() -> Self {
146        Self {
147            core: body::Core::new(body::Kind::Fixed, PLANAR),
148        }
149    }
150    pub fn kinematic_position_based() -> Self {
151        Self {
152            core: body::Core::new(body::Kind::Kinematic, PLANAR),
153        }
154    }
155    pub fn translation(mut self, p: Vector) -> Self {
156        assert!(p.is_finite());
157        self.core.position = to3(p);
158        self
159    }
160    pub fn rotation(mut self, r: Angular) -> Self {
161        self.core
162            .set_rotation(Quat::from_scaled_axis(angular_to3(r)));
163        self
164    }
165    pub fn linvel(mut self, v: Vector) -> Self {
166        assert!(v.is_finite());
167        self.core.velocity = to3(v);
168        self
169    }
170    pub fn angvel(mut self, v: Angular) -> Self {
171        let v = angular_to3(v);
172        assert!(v.is_finite());
173        self.core.angular_velocity = v;
174        self
175    }
176    pub fn linear_damping(mut self, v: f32) -> Self {
177        assert!(v.is_finite() && v >= 0.);
178        self.core.linear_damping = v;
179        self
180    }
181    pub fn angular_damping(mut self, v: f32) -> Self {
182        assert!(v.is_finite() && v >= 0.);
183        self.core.angular_damping = v;
184        self
185    }
186    pub fn gravity_scale(mut self, v: f32) -> Self {
187        assert!(v.is_finite());
188        self.core.gravity_scale = v;
189        self
190    }
191    pub fn ccd_enabled(mut self, v: bool) -> Self {
192        self.core.ccd = v;
193        self
194    }
195    pub fn build(self) -> RigidBody {
196        self.into()
197    }
198}
199impl From<RigidBodyBuilder> for RigidBody {
200    fn from(b: RigidBodyBuilder) -> Self {
201        Self { core: b.core }
202    }
203}
204#[derive(Clone, Debug)]
205pub struct ColliderBuilder {
206    collider: Collider,
207}
208impl ColliderBuilder {
209    fn shape(shape: body::Shape) -> Self {
210        shape.validate();
211        Self {
212            collider: Collider::new(shape),
213        }
214    }
215    pub fn ball(radius: f32) -> Self {
216        Self::shape(body::Shape::Ball(radius))
217    }
218    pub fn density(mut self, v: f32) -> Self {
219        assert!(v.is_finite() && v > 0.);
220        self.collider.density = v;
221        self
222    }
223    pub fn friction(mut self, v: f32) -> Self {
224        assert!(v.is_finite() && v >= 0.);
225        self.collider.friction = v;
226        self
227    }
228    pub fn restitution(mut self, v: f32) -> Self {
229        self.collider.set_restitution(v);
230        self
231    }
232    pub fn sensor(mut self, v: bool) -> Self {
233        self.collider.sensor = v;
234        self
235    }
236    /// Collision layers match when each collider's membership intersects the other's filter.
237    pub fn collision_layers(mut self, membership: u32, filter: u32) -> Self {
238        self.collider.membership = membership;
239        self.collider.filter = filter;
240        self
241    }
242    pub fn build(self) -> Collider {
243        self.into()
244    }
245}
246impl From<ColliderBuilder> for Collider {
247    fn from(b: ColliderBuilder) -> Self {
248        b.collider
249    }
250}
251pub type RawPhysicsWorld = world::World<RigidBody>;
252pub struct PhysicsWorld {
253    pub raw: RawPhysicsWorld,
254}
255impl Default for PhysicsWorld {
256    fn default() -> Self {
257        Self::new([0.; DIM])
258    }
259}
260impl std::ops::Deref for PhysicsWorld {
261    type Target = RawPhysicsWorld;
262    fn deref(&self) -> &Self::Target {
263        &self.raw
264    }
265}
266impl std::ops::DerefMut for PhysicsWorld {
267    fn deref_mut(&mut self) -> &mut Self::Target {
268        &mut self.raw
269    }
270}
271const DIM: usize = if PLANAR { 2 } else { 3 };
272impl PhysicsWorld {
273    pub fn new(gravity: [f32; DIM]) -> Self {
274        Self {
275            raw: world::World::new(to3(Vector::from_array(gravity)), PLANAR),
276        }
277    }
278    pub fn add_ball(&mut self, p: [f32; DIM], radius: f32, dynamic: bool) -> RigidBodyHandle {
279        let b = if dynamic {
280            RigidBodyBuilder::dynamic()
281        } else {
282            RigidBodyBuilder::fixed()
283        };
284        self.raw
285            .insert(
286                b.translation(Vector::from_array(p)),
287                ColliderBuilder::ball(radius),
288            )
289            .0
290    }
291    pub fn add_box(&mut self, p: [f32; DIM], half: [f32; DIM], dynamic: bool) -> RigidBodyHandle {
292        let b = if dynamic {
293            RigidBodyBuilder::dynamic()
294        } else {
295            RigidBodyBuilder::fixed()
296        };
297        let h = if PLANAR {
298            Vec3::new(half[0], half[1], 0.5)
299        } else {
300            Vec3::new(half[0], half[1], half.get(2).copied().unwrap_or(0.5))
301        };
302        self.raw
303            .insert(
304                b.translation(Vector::from_array(p)),
305                ColliderBuilder::shape(body::Shape::Box(h)),
306            )
307            .0
308    }
309    /// Queries use current body poses directly; no separate broadphase synchronization is needed.
310    pub fn refresh_queries(&mut self) {}
311    pub fn raycast(
312        &self,
313        origin: [f32; DIM],
314        direction: [f32; DIM],
315        max_distance: f32,
316    ) -> Option<RayHit> {
317        self.raw
318            .cast_ray(
319                to3(Vector::from_array(origin)),
320                to3(Vector::from_array(direction)),
321                max_distance,
322            )
323            .map(|h| RayHit {
324                body: Some(h.0),
325                collider: h.1,
326                distance: h.2,
327                point: from3(h.3).to_array(),
328                normal: from3(h.4).to_array(),
329            })
330    }
331}
332#[derive(Clone, Copy, Debug)]
333pub struct RayHit {
334    pub body: Option<RigidBodyHandle>,
335    pub collider: ColliderHandle,
336    pub distance: f32,
337    pub point: [f32; DIM],
338    pub normal: [f32; DIM],
339}
340
341impl crate::character::Terrain for PhysicsWorld {
342    fn visit(&self,bounds:crate::character::QueryBounds,visitor:&mut dyn FnMut(crate::character::Obstacle)) {
343        crate::character::Terrain::visit(&self.raw,bounds,visitor);
344    }
345    fn obstacle(&self,id:u64)->Option<crate::character::Obstacle> {crate::character::Terrain::obstacle(&self.raw,id)}
346}