1use rapier3d::parry::mass_properties::MassProperties;
2use rapier3d::prelude::{
3 ActiveCollisionTypes, ActiveEvents, ActiveHooks, BroadPhaseBvh, ColliderBuilder,
4 ColliderHandle, CollisionEvent, CollisionEventFlags, ContactModificationContext, LockedAxes,
5 PhysicsHooks, PhysicsWorld, RigidBodyBuilder, RigidBodyHandle,
6};
7
8use super::convert::{
9 Iso, Quat, Vec3, body_handle, body_id, collider_handle, collider_id, iso, lerp_iso, pose, vec,
10};
11use super::event::Collector;
12use super::gravity::{SourceId, acceleration};
13use super::{
14 BodyDesc, BodyId, BodyState, ColliderDesc, ColliderId, Combine, Drive, Event, Friction, Kind,
15 Locks, Pose, Source,
16};
17
18#[derive(Clone, Copy, Debug, PartialEq)]
19pub struct WorldDesc {
20 pub rate: f32,
21 pub substeps: u32,
22 pub iterations: u32,
23 pub gravity: [f32; 3],
24 pub ccd: bool,
25}
26
27impl Default for WorldDesc {
28 fn default() -> Self {
29 Self {
30 rate: 60.0,
31 substeps: 1,
32 iterations: 4,
33 gravity: [0.0, -9.81, 0.0],
34 ccd: true,
35 }
36 }
37}
38
39struct Extra {
40 generation: u32,
41 kind: Kind,
42 locks: Locks,
43 anchor: Iso,
44 force: Vec3,
45 torque: Vec3,
46 drive: Option<Drive>,
47 target: Option<Iso>,
48 mass_given: bool,
49}
50
51#[derive(Clone, Copy)]
52struct Surface {
53 generation: u32,
54 friction: Friction,
55}
56
57pub struct World {
58 pub(super) physics: PhysicsWorld,
59 pub(super) queries: BroadPhaseBvh,
60 rate: f32,
61 substeps: u32,
62 extras: Vec<Option<Extra>>,
63 surfaces: Vec<Option<Surface>>,
64 sources: Vec<(SourceId, Source)>,
65 next_source: u32,
66 forced: Vec<RigidBodyHandle>,
67 collector: Collector,
68 events: Vec<Event>,
69 steps: u64,
70}
71
72impl Default for World {
73 fn default() -> Self {
74 Self::new(WorldDesc::default())
75 }
76}
77
78impl World {
79 pub fn new(desc: WorldDesc) -> Self {
80 let rate = if desc.rate.is_finite() && desc.rate > 0.0 {
81 desc.rate
82 } else {
83 60.0
84 };
85 let substeps = desc.substeps.max(1);
86 let mut physics = PhysicsWorld::new();
87 physics.gravity = vec(desc.gravity);
88 physics.integration_parameters.dt = 1.0 / rate / substeps as f32;
89 physics.integration_parameters.num_solver_iterations = desc.iterations.max(1) as usize;
90 physics.integration_parameters.max_ccd_substeps = usize::from(desc.ccd);
91 Self {
92 physics,
93 queries: BroadPhaseBvh::new(),
94 rate,
95 substeps,
96 extras: Vec::new(),
97 surfaces: Vec::new(),
98 sources: Vec::new(),
99 next_source: 0,
100 forced: Vec::new(),
101 collector: Collector::default(),
102 events: Vec::new(),
103 steps: 0,
104 }
105 }
106
107 pub fn rate(&self) -> f32 {
108 self.rate
109 }
110
111 pub fn substeps(&self) -> u32 {
112 self.substeps
113 }
114
115 pub fn dt(&self) -> f32 {
116 1.0 / self.rate
117 }
118
119 pub fn steps(&self) -> u64 {
120 self.steps
121 }
122
123 pub fn time(&self) -> f64 {
124 self.steps as f64 / f64::from(self.rate)
125 }
126
127 pub fn gravity(&self) -> [f32; 3] {
128 self.physics.gravity.to_array()
129 }
130
131 pub fn set_gravity(&mut self, gravity: [f32; 3]) {
132 self.physics.gravity = vec(gravity);
133 self.wake_all();
134 }
135
136 pub fn gravity_at(&self, point: [f32; 3]) -> [f32; 3] {
137 let world = self.physics.gravity;
138 if self.sources.is_empty() {
139 return world.to_array();
140 }
141 (world + acceleration(&self.sources, world, vec(point))).to_array()
142 }
143
144 pub(super) fn planned(&self, body: BodyId) -> Option<Iso> {
145 let extra = self.extra(body)?;
146 if extra.kind != Kind::Kinematic {
147 return None;
148 }
149 let from = *self.physics.bodies.get(body_handle(body))?.position();
150 let to = match (extra.drive, extra.target) {
151 (_, Some(target)) => target,
152 (Some(mut drive), None) => drive.advance(1.0 / self.rate, &from),
153 (None, None) => return Some(from),
154 };
155 Some(locked(extra.locks, &extra.anchor, &to))
156 }
157
158 pub fn events(&self) -> &[Event] {
159 &self.events
160 }
161
162 pub fn add_source(&mut self, source: Source) -> SourceId {
163 let id = SourceId(self.next_source);
164 self.next_source += 1;
165 self.sources.push((id, source));
166 self.wake_all();
167 id
168 }
169
170 pub fn remove_source(&mut self, id: SourceId) -> bool {
171 let before = self.sources.len();
172 self.sources.retain(|(s, _)| *s != id);
173 self.sources.len() != before
174 }
175
176 pub fn source_mut(&mut self, id: SourceId) -> Option<&mut Source> {
177 self.sources
178 .iter_mut()
179 .find(|(s, _)| *s == id)
180 .map(|(_, source)| source)
181 }
182
183 pub fn add_body(&mut self, desc: BodyDesc) -> BodyId {
184 let start = iso(desc.pose);
185 let builder = match desc.kind {
186 Kind::Dynamic => RigidBodyBuilder::dynamic(),
187 Kind::Kinematic => RigidBodyBuilder::kinematic_position_based(),
188 Kind::Fixed => RigidBodyBuilder::fixed(),
189 };
190 let mut builder = builder
191 .pose(start)
192 .linvel(masked(vec(desc.linear_velocity), desc.locks.translation))
193 .angvel(masked(vec(desc.angular_velocity), desc.locks.rotation))
194 .gravity_scale(desc.gravity_scale)
195 .linear_damping(desc.linear_damping)
196 .angular_damping(desc.angular_damping)
197 .locked_axes(locked_axes(desc.locks))
198 .ccd_enabled(desc.ccd)
199 .can_sleep(desc.can_sleep)
200 .sleeping(desc.asleep);
201 if let Some(mass) = desc.mass {
202 builder = builder.additional_mass_properties(MassProperties::new(
203 vec(mass.center),
204 mass.mass,
205 vec(mass.inertia),
206 ));
207 }
208 let handle = self.physics.bodies.insert(builder);
209 let (index, generation) = handle.into_raw_parts();
210 let slot = index as usize;
211 if self.extras.len() <= slot {
212 self.extras.resize_with(slot + 1, || None);
213 }
214 self.extras[slot] = Some(Extra {
215 generation,
216 kind: desc.kind,
217 locks: desc.locks,
218 anchor: start,
219 force: Vec3::ZERO,
220 torque: Vec3::ZERO,
221 drive: None,
222 target: None,
223 mass_given: desc.mass.is_some(),
224 });
225 body_id(handle)
226 }
227
228 pub fn add_collider(&mut self, body: BodyId, desc: ColliderDesc) -> Option<ColliderId> {
229 let mass_given = self.extra(body)?.mass_given;
230 let shape = desc.shape.shared()?;
231 let handle = body_handle(body);
232 let mut events = ActiveEvents::COLLISION_EVENTS;
233 let mut builder = ColliderBuilder::new(shape)
234 .position(iso(desc.offset))
235 .density(if mass_given { 0.0 } else { desc.density })
236 .friction(desc.friction.sliding)
237 .friction_combine_rule(desc.friction.combine.rule())
238 .restitution(desc.restitution)
239 .restitution_combine_rule(desc.restitution_combine.rule())
240 .sensor(desc.sensor)
241 .collision_groups(desc.layers.groups());
242 if desc.sensor {
243 builder = builder.active_collision_types(ActiveCollisionTypes::all());
244 }
245 if desc.friction.still != desc.friction.sliding {
246 builder = builder.active_hooks(ActiveHooks::MODIFY_SOLVER_CONTACTS);
247 }
248 if let Some(impulse) = desc.impulse_threshold {
249 events |= ActiveEvents::CONTACT_FORCE_EVENTS;
250 builder = builder
251 .contact_force_event_threshold(impulse / self.physics.integration_parameters.dt);
252 }
253 let builder = builder.active_events(events);
254 let physics = &mut self.physics;
255 let collider = physics
256 .colliders
257 .insert_with_parent(builder, handle, &mut physics.bodies);
258 if let Some(rb) = physics.bodies.get_mut(handle) {
259 rb.recompute_mass_properties_from_colliders(&physics.colliders);
260 }
261 let (index, generation) = collider.into_raw_parts();
262 let slot = index as usize;
263 if self.surfaces.len() <= slot {
264 self.surfaces.resize_with(slot + 1, || None);
265 }
266 self.surfaces[slot] = Some(Surface {
267 generation,
268 friction: desc.friction,
269 });
270 Some(collider_id(collider))
271 }
272
273 pub fn remove_body(&mut self, body: BodyId) -> bool {
274 let handle = body_handle(body);
275 let Some(rb) = self.physics.bodies.get(handle) else {
276 return false;
277 };
278 for collider in rb.colliders().to_vec() {
279 self.forget_surface(collider);
280 }
281 self.physics.remove_body(handle);
282 self.extras[body.index as usize] = None;
283 self.forced.retain(|h| *h != handle);
284 true
285 }
286
287 pub fn remove_collider(&mut self, collider: ColliderId) -> bool {
288 let handle = collider_handle(collider);
289 let removed = self.physics.remove_collider(handle).is_some();
290 if removed {
291 self.forget_surface(handle);
292 }
293 removed
294 }
295
296 pub fn bodies(&self) -> Vec<BodyId> {
297 self.extras
298 .iter()
299 .enumerate()
300 .filter_map(|(index, extra)| {
301 extra.as_ref().map(|e| BodyId {
302 index: index as u32,
303 generation: e.generation,
304 })
305 })
306 .collect()
307 }
308
309 pub fn body(&self, body: BodyId) -> Option<BodyState> {
310 let rb = self.physics.bodies.get(body_handle(body))?;
311 Some(BodyState {
312 pose: pose(rb.position()),
313 linear_velocity: rb.linvel().to_array(),
314 angular_velocity: rb.angvel().to_array(),
315 asleep: rb.is_sleeping(),
316 })
317 }
318
319 pub fn pose(&self, body: BodyId) -> Option<Pose> {
320 self.physics
321 .bodies
322 .get(body_handle(body))
323 .map(|rb| pose(rb.position()))
324 }
325
326 pub fn mass(&self, body: BodyId) -> Option<f32> {
327 self.physics
328 .bodies
329 .get(body_handle(body))
330 .map(|rb| rb.mass())
331 }
332
333 pub fn body_of(&self, collider: ColliderId) -> Option<BodyId> {
334 self.physics
335 .colliders
336 .get(collider_handle(collider))?
337 .parent()
338 .map(body_id)
339 }
340
341 pub fn is_asleep(&self, body: BodyId) -> bool {
342 self.physics
343 .bodies
344 .get(body_handle(body))
345 .is_some_and(|rb| rb.is_sleeping())
346 }
347
348 pub fn set_pose(&mut self, body: BodyId, to: Pose) -> bool {
349 let to = iso(to);
350 let Some(extra) = self.extra_mut(body) else {
351 return false;
352 };
353 extra.anchor = to;
354 extra.target = None;
355 let Some(rb) = self.physics.bodies.get_mut(body_handle(body)) else {
356 return false;
357 };
358 rb.set_position(to, true);
359 true
360 }
361
362 pub fn set_velocity(&mut self, body: BodyId, linear: [f32; 3], angular: [f32; 3]) -> bool {
363 let Some(locks) = self.extra(body).map(|e| e.locks) else {
364 return false;
365 };
366 let Some(rb) = self.physics.bodies.get_mut(body_handle(body)) else {
367 return false;
368 };
369 rb.set_linvel(masked(vec(linear), locks.translation), true);
370 rb.set_angvel(masked(vec(angular), locks.rotation), true);
371 true
372 }
373
374 pub fn apply_impulse(&mut self, body: BodyId, impulse: [f32; 3]) -> bool {
375 self.with_body(body, |rb| rb.apply_impulse(vec(impulse), true))
376 }
377
378 pub fn apply_impulse_at(&mut self, body: BodyId, impulse: [f32; 3], point: [f32; 3]) -> bool {
379 self.with_body(body, |rb| {
380 rb.apply_impulse_at_point(vec(impulse), vec(point), true)
381 })
382 }
383
384 pub fn apply_torque_impulse(&mut self, body: BodyId, impulse: [f32; 3]) -> bool {
385 self.with_body(body, |rb| rb.apply_torque_impulse(vec(impulse), true))
386 }
387
388 pub fn add_force(&mut self, body: BodyId, force: [f32; 3]) -> bool {
389 let Some(extra) = self.extra_mut(body) else {
390 return false;
391 };
392 extra.force += vec(force);
393 true
394 }
395
396 pub fn add_force_at(&mut self, body: BodyId, force: [f32; 3], point: [f32; 3]) -> bool {
397 let Some(center) = self
398 .physics
399 .bodies
400 .get(body_handle(body))
401 .map(|rb| rb.center_of_mass())
402 else {
403 return false;
404 };
405 let Some(extra) = self.extra_mut(body) else {
406 return false;
407 };
408 let force = vec(force);
409 extra.force += force;
410 extra.torque += (vec(point) - center).cross(force);
411 true
412 }
413
414 pub fn add_torque(&mut self, body: BodyId, torque: [f32; 3]) -> bool {
415 let Some(extra) = self.extra_mut(body) else {
416 return false;
417 };
418 extra.torque += vec(torque);
419 true
420 }
421
422 pub fn set_gravity_scale(&mut self, body: BodyId, scale: f32) -> bool {
423 self.with_body(body, |rb| rb.set_gravity_scale(scale, true))
424 }
425
426 pub fn set_damping(&mut self, body: BodyId, linear: f32, angular: f32) -> bool {
427 self.with_body(body, |rb| {
428 rb.set_linear_damping(linear);
429 rb.set_angular_damping(angular);
430 })
431 }
432
433 pub fn set_ccd(&mut self, body: BodyId, ccd: bool) -> bool {
434 self.with_body(body, |rb| rb.enable_ccd(ccd))
435 }
436
437 pub fn set_locks(&mut self, body: BodyId, locks: Locks) -> bool {
438 let Some(current) = self
439 .physics
440 .bodies
441 .get(body_handle(body))
442 .map(|rb| *rb.position())
443 else {
444 return false;
445 };
446 let Some(extra) = self.extra_mut(body) else {
447 return false;
448 };
449 extra.locks = locks;
450 extra.anchor = current;
451 self.with_body(body, |rb| rb.set_locked_axes(locked_axes(locks), true))
452 }
453
454 pub fn locks(&self, body: BodyId) -> Option<Locks> {
455 self.extra(body).map(|e| e.locks)
456 }
457
458 pub fn sleep(&mut self, body: BodyId) -> bool {
459 self.with_body(body, |rb| rb.sleep())
460 }
461
462 pub fn wake(&mut self, body: BodyId) -> bool {
463 let handle = body_handle(body);
464 if self.physics.bodies.get(handle).is_none() {
465 return false;
466 }
467 self.physics.wake_up(handle, true);
468 true
469 }
470
471 pub fn set_drive(&mut self, body: BodyId, drive: Option<Drive>) -> bool {
472 let Some(extra) = self.extra_mut(body) else {
473 return false;
474 };
475 extra.drive = drive;
476 true
477 }
478
479 pub fn drive(&self, body: BodyId) -> Option<&Drive> {
480 self.extra(body)?.drive.as_ref()
481 }
482
483 pub fn drive_mut(&mut self, body: BodyId) -> Option<&mut Drive> {
484 self.extra_mut(body)?.drive.as_mut()
485 }
486
487 pub fn move_to(&mut self, body: BodyId, to: Pose) -> bool {
488 let Some(extra) = self.extra_mut(body) else {
489 return false;
490 };
491 extra.target = Some(iso(to));
492 true
493 }
494
495 pub fn step(&mut self) {
496 self.events.clear();
497 let dt = 1.0 / self.rate;
498 let n = self.substeps;
499 let motions = self.motions(dt);
500 let mut started: Vec<(ColliderHandle, ColliderHandle, usize)> = Vec::new();
501 for i in 0..n {
502 self.apply_forces(dt / n as f32);
503 for (handle, from, to) in &motions {
504 let at = if i + 1 == n {
505 *to
506 } else {
507 lerp_iso(from, to, (i + 1) as f32 / n as f32)
508 };
509 if let Some(rb) = self.physics.bodies.get_mut(*handle) {
510 rb.set_next_kinematic_position(at);
511 }
512 }
513 let hooks = Hooks {
514 surfaces: &self.surfaces,
515 };
516 self.physics.step_with_events(&hooks, &self.collector);
517 self.collect(&mut started);
518 self.enforce_locks();
519 }
520 for extra in self.extras.iter_mut().flatten() {
521 extra.force = Vec3::ZERO;
522 extra.torque = Vec3::ZERO;
523 }
524 self.steps += 1;
525 self.refresh_queries();
526 }
527
528 pub fn refresh_queries(&mut self) {
529 let physics = &mut self.physics;
530 let moved: Vec<(ColliderHandle, Iso)> = physics
531 .colliders
532 .iter()
533 .filter_map(|(handle, collider)| {
534 let parent = physics.bodies.get(collider.parent()?)?;
535 let at = *parent.position() * *collider.position_wrt_parent()?;
536 (at != *collider.position()).then_some((handle, at))
537 })
538 .collect();
539 for (handle, at) in moved {
540 if let Some(collider) = physics.colliders.get_mut(handle) {
541 collider.set_position(at);
542 }
543 }
544 let mut queries = BroadPhaseBvh::new();
545 for (handle, collider) in physics.colliders.iter() {
546 queries.set_aabb(
547 &physics.integration_parameters,
548 handle,
549 collider.compute_aabb(),
550 );
551 }
552 self.queries = queries;
553 }
554
555 pub fn run(&mut self, steps: u32) {
556 for _ in 0..steps {
557 self.step();
558 }
559 }
560
561 fn wake_all(&mut self) {
562 for (index, slot) in self.extras.iter().enumerate() {
563 if let Some(extra) = slot
564 && extra.kind == Kind::Dynamic
565 {
566 let handle = RigidBodyHandle::from_raw_parts(index as u32, extra.generation);
567 self.physics
568 .islands
569 .wake_up(&mut self.physics.bodies, handle, true);
570 }
571 }
572 }
573
574 fn extra(&self, body: BodyId) -> Option<&Extra> {
575 self.extras
576 .get(body.index as usize)?
577 .as_ref()
578 .filter(|e| e.generation == body.generation)
579 }
580
581 fn extra_mut(&mut self, body: BodyId) -> Option<&mut Extra> {
582 self.extras
583 .get_mut(body.index as usize)?
584 .as_mut()
585 .filter(|e| e.generation == body.generation)
586 }
587
588 fn with_body(
589 &mut self,
590 body: BodyId,
591 f: impl FnOnce(&mut rapier3d::prelude::RigidBody),
592 ) -> bool {
593 match self.physics.bodies.get_mut(body_handle(body)) {
594 Some(rb) => {
595 f(rb);
596 true
597 }
598 None => false,
599 }
600 }
601
602 fn forget_surface(&mut self, handle: ColliderHandle) {
603 let (index, generation) = handle.into_raw_parts();
604 if let Some(slot) = self.surfaces.get_mut(index as usize)
605 && slot.is_some_and(|s| s.generation == generation)
606 {
607 *slot = None;
608 }
609 }
610
611 fn motions(&mut self, dt: f32) -> Vec<(RigidBodyHandle, Iso, Iso)> {
612 let mut motions = Vec::new();
613 for (index, slot) in self.extras.iter_mut().enumerate() {
614 let Some(extra) = slot else {
615 continue;
616 };
617 if extra.kind != Kind::Kinematic {
618 continue;
619 }
620 let handle = RigidBodyHandle::from_raw_parts(index as u32, extra.generation);
621 let Some(rb) = self.physics.bodies.get(handle) else {
622 continue;
623 };
624 let from = *rb.position();
625 let to = match (&mut extra.drive, extra.target.take()) {
626 (_, Some(target)) => target,
627 (Some(drive), None) => drive.advance(dt, &from),
628 (None, None) => continue,
629 };
630 motions.push((handle, from, locked(extra.locks, &extra.anchor, &to)));
631 }
632 motions
633 }
634
635 fn apply_forces(&mut self, h: f32) {
636 for handle in self.forced.drain(..) {
637 if let Some(rb) = self.physics.bodies.get_mut(handle) {
638 rb.reset_forces(false);
639 rb.reset_torques(false);
640 }
641 }
642 let world = self.physics.gravity;
643 for (index, slot) in self.extras.iter().enumerate() {
644 let Some(extra) = slot else {
645 continue;
646 };
647 if extra.kind != Kind::Dynamic {
648 continue;
649 }
650 let handle = RigidBodyHandle::from_raw_parts(index as u32, extra.generation);
651 let Some(rb) = self.physics.bodies.get(handle) else {
652 continue;
653 };
654 let kick = if !self.sources.is_empty() && !rb.is_sleeping() {
655 acceleration(&self.sources, world, rb.center_of_mass())
656 * (rb.mass() * rb.gravity_scale() * h)
657 } else {
658 Vec3::ZERO
659 };
660 let user = extra.force != Vec3::ZERO || extra.torque != Vec3::ZERO;
661 if kick == Vec3::ZERO && !user {
662 continue;
663 }
664 if let Some(rb) = self.physics.bodies.get_mut(handle) {
665 if kick != Vec3::ZERO {
666 rb.apply_impulse(kick, false);
667 }
668 if user {
669 rb.add_force(extra.force, true);
670 rb.add_torque(extra.torque, true);
671 self.forced.push(handle);
672 }
673 }
674 }
675 }
676
677 fn collect(&mut self, started: &mut Vec<(ColliderHandle, ColliderHandle, usize)>) {
678 let collisions = self
679 .collector
680 .collisions
681 .lock()
682 .map(|mut events| std::mem::take(&mut *events))
683 .unwrap_or_default();
684 for event in collisions {
685 let (c1, c2, flags, entered) = match event {
686 CollisionEvent::Started(c1, c2, flags) => (c1, c2, flags, true),
687 CollisionEvent::Stopped(c1, c2, flags) => (c1, c2, flags, false),
688 };
689 let (a, b) = (collider_id(c1), collider_id(c2));
690 if flags.contains(CollisionEventFlags::SENSOR) {
691 let second = self
692 .physics
693 .colliders
694 .get(c2)
695 .is_some_and(|c| c.is_sensor());
696 let first = self
697 .physics
698 .colliders
699 .get(c1)
700 .is_some_and(|c| c.is_sensor());
701 let (sensor, other) = if second && !first { (b, a) } else { (a, b) };
702 self.events.push(if entered {
703 Event::SensorEntered { sensor, other }
704 } else {
705 Event::SensorExited { sensor, other }
706 });
707 } else if entered {
708 started.push((c1, c2, self.events.len()));
709 self.events
710 .push(Event::ContactStarted { a, b, impulse: 0.0 });
711 } else {
712 self.events.push(Event::ContactStopped { a, b });
713 }
714 }
715 for (c1, c2, at) in started.iter() {
716 let Some(pair) = self.physics.narrow_phase.contact_pair(*c1, *c2) else {
717 continue;
718 };
719 let add = pair.total_impulse_magnitude();
720 if let Some(Event::ContactStarted { impulse, .. }) = self.events.get_mut(*at) {
721 *impulse += add;
722 }
723 }
724 let impulses = self
725 .collector
726 .impulses
727 .lock()
728 .map(|mut impulses| std::mem::take(&mut *impulses))
729 .unwrap_or_default();
730 for (c1, c2, add) in impulses {
731 let (a, b) = (collider_id(c1), collider_id(c2));
732 let existing = self.events.iter_mut().find_map(|event| match event {
733 Event::ContactImpulse {
734 a: ea,
735 b: eb,
736 impulse,
737 } if *ea == a && *eb == b => Some(impulse),
738 _ => None,
739 });
740 match existing {
741 Some(impulse) => *impulse += add,
742 None => self
743 .events
744 .push(Event::ContactImpulse { a, b, impulse: add }),
745 }
746 }
747 }
748
749 fn enforce_locks(&mut self) {
750 for (index, slot) in self.extras.iter().enumerate() {
751 let Some(extra) = slot else {
752 continue;
753 };
754 if extra.kind != Kind::Dynamic || !extra.locks.any() {
755 continue;
756 }
757 let handle = RigidBodyHandle::from_raw_parts(index as u32, extra.generation);
758 let Some(rb) = self.physics.bodies.get(handle) else {
759 continue;
760 };
761 let at = *rb.position();
762 let fixed = locked(extra.locks, &extra.anchor, &at);
763 let linear = rb.linvel();
764 let angular = rb.angvel();
765 let held_linear = masked(linear, extra.locks.translation);
766 let held_angular = masked(angular, extra.locks.rotation);
767 let moved = !same_iso(&fixed, &at);
768 let spun = held_linear != linear || held_angular != angular;
769 if !moved && !spun {
770 continue;
771 }
772 if let Some(rb) = self.physics.bodies.get_mut(handle) {
773 if moved {
774 rb.set_position(fixed, false);
775 }
776 if spun {
777 rb.set_linvel(held_linear, false);
778 rb.set_angvel(held_angular, false);
779 }
780 }
781 }
782 }
783}
784
785struct Hooks<'a> {
786 surfaces: &'a [Option<Surface>],
787}
788
789impl Hooks<'_> {
790 fn friction(&self, handle: ColliderHandle) -> Option<Friction> {
791 let (index, generation) = handle.into_raw_parts();
792 self.surfaces
793 .get(index as usize)?
794 .filter(|s| s.generation == generation)
795 .map(|s| s.friction)
796 }
797}
798
799impl PhysicsHooks for Hooks<'_> {
800 fn modify_solver_contacts(&self, context: &mut ContactModificationContext) {
801 let (Some(f1), Some(f2)) = (
802 self.friction(context.collider1),
803 self.friction(context.collider2),
804 ) else {
805 return;
806 };
807 let bodies = context.bodies;
808 let colliders = context.colliders;
809 let (b1, b2, c1) = (context.rigid_body1, context.rigid_body2, context.collider1);
810 let Some(rigid) = context.rigid_mut() else {
811 return;
812 };
813 let Some(point) = rigid.manifold.points.first() else {
814 return;
815 };
816 let Some(position) = colliders.get(c1).map(|c| *c.position()) else {
817 return;
818 };
819 let p = position.transform_point(point.local_p1);
820 let velocity = |handle: Option<RigidBodyHandle>| {
821 handle
822 .and_then(|h| bodies.get(h))
823 .map(|rb| rb.velocity_at_point(p))
824 .unwrap_or(Vec3::ZERO)
825 };
826 let relative = velocity(b1) - velocity(b2);
827 let normal = *rigid.normal;
828 let tangent = relative - normal * relative.dot(normal);
829 let (a, b) = if tangent.length() > Friction::SLIP {
830 (f1.sliding, f2.sliding)
831 } else {
832 (f1.still, f2.still)
833 };
834 *rigid.friction = Combine::apply(a, b, f1.combine, f2.combine);
835 }
836}
837
838fn masked(v: Vec3, locks: [bool; 3]) -> Vec3 {
839 Vec3::new(
840 if locks[0] { 0.0 } else { v.x },
841 if locks[1] { 0.0 } else { v.y },
842 if locks[2] { 0.0 } else { v.z },
843 )
844}
845
846fn locked_axes(locks: Locks) -> LockedAxes {
847 let flags = [
848 LockedAxes::TRANSLATION_LOCKED_X,
849 LockedAxes::TRANSLATION_LOCKED_Y,
850 LockedAxes::TRANSLATION_LOCKED_Z,
851 LockedAxes::ROTATION_LOCKED_X,
852 LockedAxes::ROTATION_LOCKED_Y,
853 LockedAxes::ROTATION_LOCKED_Z,
854 ];
855 locks
856 .translation
857 .iter()
858 .chain(&locks.rotation)
859 .zip(flags)
860 .filter(|(on, _)| **on)
861 .fold(LockedAxes::empty(), |all, (_, flag)| all | flag)
862}
863
864fn locked(locks: Locks, anchor: &Iso, at: &Iso) -> Iso {
865 let mut translation = at.translation;
866 for axis in 0..3 {
867 if locks.translation[axis] {
868 translation[axis] = anchor.translation[axis];
869 }
870 }
871 let mut free = [0usize; 3];
872 let mut count = 0;
873 for axis in (0..3).filter(|&axis| !locks.rotation[axis]) {
874 free[count] = axis;
875 count += 1;
876 }
877 let rotation = match &free[..count] {
878 [] => anchor.rotation,
879 [axis] => {
880 let relative = at.rotation * anchor.rotation.inverse();
881 let mut parts = [0.0, 0.0, 0.0, relative.w];
882 parts[*axis] = relative.to_array()[*axis];
883 let twist = Quat::from_array(parts);
884 let length = twist.length();
885 let twist = if length > f32::EPSILON {
886 twist / length
887 } else {
888 Quat::IDENTITY
889 };
890 twist * anchor.rotation
891 }
892 _ => at.rotation,
893 };
894 Iso::from_parts(translation, rotation)
895}
896
897fn same_iso(a: &Iso, b: &Iso) -> bool {
898 a.translation == b.translation && a.rotation == b.rotation
899}