1use super::*;
2
3impl Default for BodyCollider {
5 fn default() -> BodyCollider {
11 BodyCollider::Aabb(AabbCollider::default())
12 }
13}
14
15impl Default for BodyCollider3D {
17 fn default() -> BodyCollider3D {
23 BodyCollider3D::Aabb(AabbCollider3D::default())
24 }
25}
26
27impl Default for PhysicsConfig {
29 fn default() -> PhysicsConfig {
35 PhysicsConfig::new(
36 Vector2D::new(0.0, DEFAULT_GRAVITY),
37 DEFAULT_LINEAR_DAMPING,
38 DEFAULT_ANGULAR_DAMPING,
39 )
40 }
41}
42
43impl RigidBody2D {
45 pub fn new_dynamic(id: u64, position: Vector2D) -> RigidBody2D {
56 let mass: f64 = PHYSICS_DEFAULT_MASS;
57 RigidBody2D::new(
58 id,
59 position,
60 mass,
61 1.0 / mass,
62 DEFAULT_RESTITUTION,
63 DEFAULT_FRICTION,
64 BodyType::Dynamic,
65 )
66 }
67
68 pub fn new_static(id: u64, position: Vector2D) -> RigidBody2D {
79 RigidBody2D::new(
80 id,
81 position,
82 PHYSICS_STATIC_MASS,
83 0.0,
84 DEFAULT_RESTITUTION,
85 DEFAULT_FRICTION,
86 BodyType::Static,
87 )
88 }
89
90 pub fn apply_force(&mut self, force: Vector2D) {
96 *self.get_mut_force_accumulator() += force;
97 }
98
99 pub fn apply_impulse(&mut self, impulse: Vector2D) {
105 let inverse_mass: f64 = self.get_inverse_mass();
106 if inverse_mass == 0.0 {
107 return;
108 }
109 *self.get_mut_velocity() += impulse.scaled(inverse_mass);
110 }
111
112 pub fn update_mass(&mut self, mass: f64) {
119 self.set_mass(mass);
120 self.set_inverse_mass(if mass > 0.0 { 1.0 / mass } else { 0.0 });
121 }
122
123 pub fn is_dynamic(&self) -> bool {
129 self.get_body_type() == BodyType::Dynamic
130 }
131
132 pub fn update_collider(&mut self, collider: BodyCollider) {
138 self.set_collider(Some(collider));
139 }
140
141 pub fn bounding_box(&self) -> Option<Rect> {
147 let collider: Option<BodyCollider> = self.get_collider();
148 match collider? {
149 BodyCollider::Aabb(aabb) => {
150 let aabb_rect: Rect = aabb.get_rect();
151 Some(Rect::from_center(
152 aabb_rect.center() + self.get_position(),
153 aabb_rect.get_width(),
154 aabb_rect.get_height(),
155 ))
156 }
157 BodyCollider::Circle(circle) => {
158 let diameter: f64 = circle.get_circle().get_radius() * 2.0;
159 Some(Rect::from_center(self.get_position(), diameter, diameter))
160 }
161 }
162 }
163}
164
165impl PhysicsWorld2D {
167 pub fn with_config(config: PhysicsConfig) -> PhysicsWorld2D {
177 let mut world: PhysicsWorld2D = PhysicsWorld2D::new(config);
178 world.set_grid(SpatialHashGrid2D::with_default_size());
179 world
180 }
181
182 pub fn add_body(&mut self, body: RigidBody2D) {
188 self.get_mut_bodies().push(body);
189 }
190
191 pub fn remove_body(&mut self, id: u64) {
197 self.get_mut_bodies()
198 .retain(|body: &RigidBody2D| body.get_id() != id);
199 }
200
201 pub fn get_body(&self, id: u64) -> Option<&RigidBody2D> {
211 self.get_bodies()
212 .iter()
213 .find(|body: &&RigidBody2D| body.get_id() == id)
214 }
215
216 pub fn get_body_mut(&mut self, id: u64) -> Option<&mut RigidBody2D> {
226 self.get_mut_bodies()
227 .iter_mut()
228 .find(|body: &&mut RigidBody2D| body.get_id() == id)
229 }
230}
231
232impl Default for PhysicsWorld2D {
234 fn default() -> PhysicsWorld2D {
240 PhysicsWorld2D::with_config(PhysicsConfig::default())
241 }
242}
243
244impl RigidBody2D {
246 fn check_collision_with(&self, other: &RigidBody2D) -> Option<CollisionResult> {
256 let a_bbox: Rect = self.bounding_box()?;
257 let b_bbox: Rect = other.bounding_box()?;
258 if !Rect::broad_phase_alias(a_bbox, b_bbox) {
259 return None;
260 }
261 let self_collider: Option<BodyCollider> = self.get_collider();
262 let other_collider: Option<BodyCollider> = other.get_collider();
263 let position_delta: Vector2D = other.get_position() - self.get_position();
264 match (self_collider, other_collider) {
265 (Some(BodyCollider::Aabb(aabb_a)), Some(BodyCollider::Aabb(aabb_b))) => {
266 let aabb_b_rect: Rect = aabb_b.get_rect();
267 let offset_aabb_b: AabbCollider = AabbCollider::new(Rect::new(
268 aabb_b_rect.get_x() + position_delta.get_x(),
269 aabb_b_rect.get_y() + position_delta.get_y(),
270 aabb_b_rect.get_width(),
271 aabb_b_rect.get_height(),
272 ));
273 aabb_a.collide_with_aabb(&offset_aabb_b)
274 }
275 (Some(BodyCollider::Circle(circle_a)), Some(BodyCollider::Circle(circle_b))) => {
276 let circle_b_inner: Circle = circle_b.get_circle();
277 let offset_circle_b: CircleCollider = CircleCollider::new(Circle::new(
278 circle_b_inner.get_center() + position_delta,
279 circle_b_inner.get_radius(),
280 ));
281 circle_a.collide_with_circle(&offset_circle_b)
282 }
283 (Some(BodyCollider::Aabb(aabb)), Some(BodyCollider::Circle(circle))) => {
284 let circle_inner: Circle = circle.get_circle();
285 let offset_circle: CircleCollider = CircleCollider::new(Circle::new(
286 circle_inner.get_center() + position_delta,
287 circle_inner.get_radius(),
288 ));
289 aabb.collide_with_circle(&offset_circle)
290 }
291 (Some(BodyCollider::Circle(circle)), Some(BodyCollider::Aabb(aabb))) => {
292 let aabb_rect: Rect = aabb.get_rect();
293 let offset_aabb: AabbCollider = AabbCollider::new(Rect::new(
294 aabb_rect.get_x() + position_delta.get_x(),
295 aabb_rect.get_y() + position_delta.get_y(),
296 aabb_rect.get_width(),
297 aabb_rect.get_height(),
298 ));
299 offset_aabb
300 .collide_with_circle(&circle)
301 .map(|mut result: CollisionResult| {
302 result.set_normal(-result.get_normal());
303 result
304 })
305 }
306 _ => None,
307 }
308 }
309
310 fn resolve_collision_with(&mut self, other: &mut RigidBody2D, result: &CollisionResult) {
323 let self_inverse_mass: f64 = self.get_inverse_mass();
324 let other_inverse_mass: f64 = other.get_inverse_mass();
325 let inverse_mass_sum: f64 = self_inverse_mass + other_inverse_mass;
326 if inverse_mass_sum == 0.0 {
327 return;
328 }
329 let relative_velocity: Vector2D = other.get_velocity() - self.get_velocity();
330 let velocity_along_normal: f64 = relative_velocity.dot(result.get_normal());
331 let restitution: f64 = self.get_restitution().min(other.get_restitution());
332 let impulse_magnitude: f64 = if velocity_along_normal > 0.0 {
333 0.0
334 } else {
335 -(1.0 + restitution) * velocity_along_normal / inverse_mass_sum
336 };
337 let normal_load: f64 = (result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum)
346 .max(0.0)
347 + impulse_magnitude.abs();
348 let max_friction_impulse: f64 = self.get_friction().min(other.get_friction()) * normal_load;
349 let tangent_velocity: Vector2D =
350 relative_velocity - result.get_normal().scaled(velocity_along_normal);
351 let tangent_speed: f64 = tangent_velocity.magnitude();
352 if max_friction_impulse > 0.0 && tangent_speed > 0.0 {
353 let friction_impulse: Vector2D = tangent_velocity
354 .normalized()
355 .scaled(-(tangent_speed / inverse_mass_sum).min(max_friction_impulse));
356 *self.get_mut_velocity() -= friction_impulse.scaled(self_inverse_mass);
357 *other.get_mut_velocity() += friction_impulse.scaled(other_inverse_mass);
358 }
359 if velocity_along_normal > 0.0 {
360 return;
361 }
362 let impulse: Vector2D = result.get_normal().scaled(impulse_magnitude);
363 *self.get_mut_velocity() -= impulse.scaled(self_inverse_mass);
364 *other.get_mut_velocity() += impulse.scaled(other_inverse_mass);
365 let correction: Vector2D = result
366 .get_normal()
367 .scaled((result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum).max(0.0));
368 *self.get_mut_position() -= correction.scaled(self_inverse_mass);
369 *other.get_mut_position() += correction.scaled(other_inverse_mass);
370 }
371}
372
373impl PhysicsWorld2D {
375 pub fn step(&mut self, delta_time: f64) {
384 let config: PhysicsConfig = self.get_config();
385 let damping_factor: f64 = (1.0 - config.get_linear_damping() * delta_time).max(0.0);
387 let angular_damping: f64 = (1.0 - config.get_angular_damping() * delta_time).max(0.0);
388 let gravity: Vector2D = config.get_gravity();
389 for body in self.get_mut_bodies() {
390 if !body.is_dynamic() {
391 continue;
392 }
393 let body_mass: f64 = body.get_mass();
394 let body_inverse_mass: f64 = body.get_inverse_mass();
395 *body.get_mut_force_accumulator() += gravity.scaled(body_mass);
396 let force: Vector2D = body.get_force_accumulator();
397 *body.get_mut_velocity() += force.scaled(body_inverse_mass * delta_time);
398 *body.get_mut_velocity() *= damping_factor;
400 let current_velocity: Vector2D = body.get_velocity();
401 *body.get_mut_position() += current_velocity.scaled(delta_time);
402 body.set_force_accumulator(Vector2D::zero());
403 *body.get_mut_angular_velocity() *= angular_damping;
404 let current_angular_velocity: f64 = body.get_angular_velocity();
405 *body.get_mut_rotation() += current_angular_velocity * delta_time;
406 }
407 self.resolve_collisions();
408 }
409
410 fn resolve_collisions(&mut self) {
416 let body_count: usize = self.get_bodies().len();
417 if body_count < 2 {
418 return;
419 }
420 self.get_mut_pair_buffer().clear();
429 {
430 let Self {
431 bodies,
432 grid,
433 query_buffer,
434 query_seen,
435 pair_buffer,
436 ..
437 } = self;
438 let bodies: &Vec<RigidBody2D> = bodies;
439 let grid: &mut SpatialHashGrid2D = grid;
440 let query_buffer: &mut Vec<usize> = query_buffer;
441 let query_seen: &mut HashSet<usize> = query_seen;
442 grid.clear();
443 for (index, body) in bodies.iter().enumerate() {
444 if let Some(bbox) = body.bounding_box() {
445 grid.insert(index, bbox.min(), bbox.max());
446 }
447 }
448 let pairs: &mut Vec<(usize, usize)> = pair_buffer;
449 for (i, body) in bodies.iter().enumerate() {
450 let Some(bbox) = body.bounding_box() else {
451 continue;
452 };
453 grid.query_into(bbox.min(), bbox.max(), query_buffer, query_seen);
454 for &j in query_buffer.iter() {
455 if j > i {
456 pairs.push((i, j));
457 }
458 }
459 }
460 }
461 let pairs_snapshot: Vec<(usize, usize)> = take(self.get_mut_pair_buffer());
468 for iteration in 0..PHYSICS_MAX_ITERATIONS {
469 let mut any_collision: bool = false;
470 for &(i, j) in pairs_snapshot.iter() {
471 let (left, right) = self.get_mut_bodies().split_at_mut(j);
472 let body_a: &mut RigidBody2D = &mut left[i];
473 let body_b: &mut RigidBody2D = &mut right[0];
474 if body_a.get_inverse_mass() == 0.0 && body_b.get_inverse_mass() == 0.0 {
475 continue;
476 }
477 if let Some(result) = body_a.check_collision_with(body_b) {
478 body_a.resolve_collision_with(body_b, &result);
479 any_collision = true;
480 }
481 }
482 if !any_collision {
483 break;
484 }
485 let _: u32 = iteration;
486 }
487 self.set_pair_buffer(pairs_snapshot);
488 }
489}
490
491impl Updatable for PhysicsWorld2D {
498 fn update(&mut self, delta_time: f64) {
504 PhysicsWorld2D::step(self, delta_time);
505 }
506}
507
508impl Default for PhysicsConfig3D {
510 fn default() -> PhysicsConfig3D {
516 PhysicsConfig3D::new(
517 Vector3D::new(0.0, DEFAULT_GRAVITY_3D, 0.0),
518 DEFAULT_LINEAR_DAMPING,
519 DEFAULT_ANGULAR_DAMPING,
520 )
521 }
522}
523
524impl RigidBody3D {
526 pub fn new_dynamic(id: u64, position: Vector3D) -> RigidBody3D {
537 let mass: f64 = PHYSICS_DEFAULT_MASS;
538 let mut body: RigidBody3D = RigidBody3D::new(
539 id,
540 position,
541 mass,
542 1.0 / mass,
543 DEFAULT_RESTITUTION,
544 DEFAULT_FRICTION,
545 BodyType::Dynamic,
546 );
547 body.update_inertia(mass);
548 body
549 }
550
551 pub fn new_static(id: u64, position: Vector3D) -> RigidBody3D {
562 RigidBody3D::new(
563 id,
564 position,
565 PHYSICS_STATIC_MASS,
566 0.0,
567 DEFAULT_RESTITUTION,
568 DEFAULT_FRICTION,
569 BodyType::Static,
570 )
571 }
572
573 pub fn apply_force(&mut self, force: Vector3D) {
579 *self.get_mut_force_accumulator() += force;
580 }
581
582 pub fn apply_torque(&mut self, torque: Vector3D) {
588 *self.get_mut_torque_accumulator() += torque;
589 }
590
591 pub fn apply_impulse(&mut self, impulse: Vector3D) {
597 let inverse_mass: f64 = self.get_inverse_mass();
598 if inverse_mass == 0.0 {
599 return;
600 }
601 *self.get_mut_velocity() += impulse.scaled(inverse_mass);
602 }
603
604 pub fn update_mass(&mut self, mass: f64) {
611 self.set_mass(mass);
612 self.set_inverse_mass(if mass > 0.0 { 1.0 / mass } else { 0.0 });
613 }
614
615 pub fn update_inertia(&mut self, inertia: f64) {
622 self.set_inverse_inertia(if inertia > 0.0 { 1.0 / inertia } else { 0.0 });
623 }
624
625 pub fn is_dynamic(&self) -> bool {
631 self.get_body_type() == BodyType::Dynamic
632 }
633
634 pub fn update_collider(&mut self, collider: BodyCollider3D) {
640 self.set_collider(Some(collider));
641 }
642
643 pub fn bounding_box(&self) -> Option<AABB3D> {
649 let collider: Option<BodyCollider3D> = self.get_collider();
650 let position: Vector3D = self.get_position();
651 match collider? {
652 BodyCollider3D::Aabb(aabb) => {
653 let center: Vector3D = aabb.get_aabb().center();
654 let size: Vector3D = aabb.get_aabb().size();
655 Some(AABB3D::from_center(
656 position + center,
657 size.get_x(),
658 size.get_y(),
659 size.get_z(),
660 ))
661 }
662 BodyCollider3D::Sphere(sphere) => {
663 let sphere_inner: Sphere = sphere.get_sphere();
664 let diameter: f64 = sphere_inner.get_radius() * 2.0;
665 Some(AABB3D::from_center(
666 position + sphere_inner.get_center(),
667 diameter,
668 diameter,
669 diameter,
670 ))
671 }
672 }
673 }
674}
675
676impl PhysicsWorld3D {
678 pub fn with_config(config: PhysicsConfig3D) -> PhysicsWorld3D {
688 let mut world: PhysicsWorld3D = PhysicsWorld3D::new(config);
689 world.set_grid(SpatialHashGrid3D::with_default_size());
690 world
691 }
692
693 pub fn add_body(&mut self, body: RigidBody3D) {
699 self.get_mut_bodies().push(body);
700 }
701
702 pub fn remove_body(&mut self, id: u64) {
708 self.get_mut_bodies()
709 .retain(|body: &RigidBody3D| body.get_id() != id);
710 }
711
712 pub fn get_body(&self, id: u64) -> Option<&RigidBody3D> {
722 self.get_bodies()
723 .iter()
724 .find(|body: &&RigidBody3D| body.get_id() == id)
725 }
726
727 pub fn get_body_mut(&mut self, id: u64) -> Option<&mut RigidBody3D> {
737 self.get_mut_bodies()
738 .iter_mut()
739 .find(|body: &&mut RigidBody3D| body.get_id() == id)
740 }
741
742 pub fn step(&mut self, delta_time: f64) {
751 let config: PhysicsConfig3D = self.get_config();
752 let damping_factor: f64 = (1.0 - config.get_linear_damping() * delta_time).max(0.0);
754 let angular_damping: f64 = (1.0 - config.get_angular_damping() * delta_time).max(0.0);
755 let gravity: Vector3D = config.get_gravity();
756 for body in self.get_mut_bodies() {
757 if !body.is_dynamic() {
758 continue;
759 }
760 let body_mass: f64 = body.get_mass();
761 let body_inverse_mass: f64 = body.get_inverse_mass();
762 *body.get_mut_force_accumulator() += gravity.scaled(body_mass);
763 let force: Vector3D = body.get_force_accumulator();
764 *body.get_mut_velocity() += force.scaled(body_inverse_mass * delta_time);
765 *body.get_mut_velocity() *= damping_factor;
767 let current_velocity: Vector3D = body.get_velocity();
768 *body.get_mut_position() += current_velocity.scaled(delta_time);
769 body.set_force_accumulator(Vector3D::zero());
770 *body.get_mut_angular_velocity() *= angular_damping;
771 let body_inverse_inertia: f64 = body.get_inverse_inertia();
772 let torque: Vector3D = body.get_torque_accumulator();
773 *body.get_mut_angular_velocity() += torque.scaled(body_inverse_inertia * delta_time);
774 let angular_velocity: Vector3D = body.get_angular_velocity();
775 let rotation_delta: Quaternion = Quaternion::new(
776 angular_velocity.get_x() * delta_time * 0.5,
777 angular_velocity.get_y() * delta_time * 0.5,
778 angular_velocity.get_z() * delta_time * 0.5,
779 1.0,
780 );
781 body.set_rotation((rotation_delta * body.get_rotation()).normalized());
782 body.set_torque_accumulator(Vector3D::zero());
783 }
784 self.resolve_collisions();
785 }
786
787 fn resolve_collisions(&mut self) {
793 let body_count: usize = self.get_bodies().len();
794 if body_count < 2 {
795 return;
796 }
797 self.get_mut_pair_buffer().clear();
805 let mut bboxes: Vec<(usize, AABB3D)> = take(self.get_mut_bbox_buffer());
808 bboxes.clear();
809 bboxes.reserve(body_count);
810 bboxes.extend(self.get_bodies().iter().enumerate().filter_map(
811 |(index, body): (usize, &RigidBody3D)| {
812 body.bounding_box().map(|bbox: AABB3D| (index, bbox))
813 },
814 ));
815 {
816 let Self {
817 grid,
818 query_buffer,
819 query_seen,
820 pair_buffer,
821 ..
822 } = self;
823 let grid: &mut SpatialHashGrid3D = grid;
824 let query_buffer: &mut Vec<usize> = query_buffer;
825 let query_seen: &mut HashSet<usize> = query_seen;
826 grid.clear();
827 let pairs: &mut Vec<(usize, usize)> = pair_buffer;
828 for (index, bbox) in bboxes.iter() {
829 grid.insert(*index, bbox.get_min(), bbox.get_max());
830 }
831 for (i, bbox) in bboxes.iter() {
832 grid.query_into(bbox.get_min(), bbox.get_max(), query_buffer, query_seen);
833 for &j in query_buffer.iter() {
834 if j > *i {
835 pairs.push((*i, j));
836 }
837 }
838 }
839 }
840 self.set_bbox_buffer(bboxes);
841 let pairs_snapshot: Vec<(usize, usize)> = take(self.get_mut_pair_buffer());
848 for iteration in 0..PHYSICS_MAX_ITERATIONS {
849 let mut any_collision: bool = false;
850 for &(i, j) in pairs_snapshot.iter() {
851 let (left, right) = self.get_mut_bodies().split_at_mut(j);
852 let body_a: &mut RigidBody3D = &mut left[i];
853 let body_b: &mut RigidBody3D = &mut right[0];
854 if body_a.get_inverse_mass() == 0.0 && body_b.get_inverse_mass() == 0.0 {
855 continue;
856 }
857 if let Some(result) = Self::check_collision_3d(body_a, body_b) {
858 Self::resolve_collision_3d(body_a, body_b, &result);
859 any_collision = true;
860 }
861 }
862 if !any_collision {
863 break;
864 }
865 let _: u32 = iteration;
866 }
867 self.set_pair_buffer(pairs_snapshot);
868 }
869
870 fn check_collision_3d(a: &RigidBody3D, b: &RigidBody3D) -> Option<CollisionResult3D> {
880 let a_bbox: AABB3D = a.bounding_box()?;
881 let b_bbox: AABB3D = b.bounding_box()?;
882 if !AABB3D::broad_phase(a_bbox, b_bbox) {
883 return None;
884 }
885 let a_collider: Option<BodyCollider3D> = a.get_collider();
886 let b_collider: Option<BodyCollider3D> = b.get_collider();
887 let position_delta: Vector3D = b.get_position() - a.get_position();
888 match (a_collider, b_collider) {
889 (Some(BodyCollider3D::Aabb(aabb_a)), Some(BodyCollider3D::Aabb(aabb_b))) => {
890 let aabb_b_inner: AABB3D = aabb_b.get_aabb();
891 let offset_aabb: AabbCollider3D = AabbCollider3D::new(AABB3D::new(
892 aabb_b_inner.get_min() + position_delta,
893 aabb_b_inner.get_max() + position_delta,
894 ));
895 aabb_a.collide_with_aabb(&offset_aabb)
896 }
897 (Some(BodyCollider3D::Sphere(sphere_a)), Some(BodyCollider3D::Sphere(sphere_b))) => {
898 let sphere_b_inner: Sphere = sphere_b.get_sphere();
899 let offset_sphere: SphereCollider3D = SphereCollider3D::new(Sphere::new(
900 sphere_b_inner.get_center() + position_delta,
901 sphere_b_inner.get_radius(),
902 ));
903 sphere_a.collide_with_sphere(&offset_sphere)
904 }
905 (Some(BodyCollider3D::Aabb(aabb)), Some(BodyCollider3D::Sphere(sphere))) => {
906 let sphere_inner: Sphere = sphere.get_sphere();
907 let offset_sphere: SphereCollider3D = SphereCollider3D::new(Sphere::new(
908 sphere_inner.get_center() + position_delta,
909 sphere_inner.get_radius(),
910 ));
911 aabb.collide_with_sphere(&offset_sphere)
912 }
913 (Some(BodyCollider3D::Sphere(sphere)), Some(BodyCollider3D::Aabb(aabb))) => {
914 let aabb_inner: AABB3D = aabb.get_aabb();
915 let offset_aabb: AabbCollider3D = AabbCollider3D::new(AABB3D::new(
916 aabb_inner.get_min() + position_delta,
917 aabb_inner.get_max() + position_delta,
918 ));
919 offset_aabb
920 .collide_with_sphere(&sphere)
921 .map(|mut result: CollisionResult3D| {
922 result.set_normal(-result.get_normal());
923 result
924 })
925 }
926 _ => None,
927 }
928 }
929
930 fn resolve_collision_3d(a: &mut RigidBody3D, b: &mut RigidBody3D, result: &CollisionResult3D) {
945 let a_inverse_mass: f64 = a.get_inverse_mass();
946 let b_inverse_mass: f64 = b.get_inverse_mass();
947 let inverse_mass_sum: f64 = a_inverse_mass + b_inverse_mass;
948 if inverse_mass_sum == 0.0 {
949 return;
950 }
951 let relative_velocity: Vector3D = b.get_velocity() - a.get_velocity();
952 let velocity_along_normal: f64 = relative_velocity.dot(result.get_normal());
953 let restitution: f64 = a.get_restitution().min(b.get_restitution());
954 let impulse_magnitude: f64 = if velocity_along_normal > 0.0 {
955 0.0
956 } else {
957 -(1.0 + restitution) * velocity_along_normal / inverse_mass_sum
958 };
959 let normal_load: f64 = (result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum)
960 .max(0.0)
961 + impulse_magnitude.abs();
962 let max_friction_impulse: f64 = a.get_friction().min(b.get_friction()) * normal_load;
963 let tangent_velocity: Vector3D =
964 relative_velocity - result.get_normal().scaled(velocity_along_normal);
965 let tangent_speed: f64 = tangent_velocity.magnitude();
966 if max_friction_impulse > 0.0 && tangent_speed > 0.0 {
967 let friction_impulse: Vector3D = tangent_velocity
968 .normalized()
969 .scaled(-(tangent_speed / inverse_mass_sum).min(max_friction_impulse));
970 *a.get_mut_velocity() -= friction_impulse.scaled(a_inverse_mass);
971 *b.get_mut_velocity() += friction_impulse.scaled(b_inverse_mass);
972 }
973 if velocity_along_normal > 0.0 {
974 return;
975 }
976 let impulse: Vector3D = result.get_normal().scaled(impulse_magnitude);
977 *a.get_mut_velocity() -= impulse.scaled(a_inverse_mass);
978 *b.get_mut_velocity() += impulse.scaled(b_inverse_mass);
979 let correction: Vector3D = result
980 .get_normal()
981 .scaled((result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum).max(0.0));
982 *a.get_mut_position() -= correction.scaled(a_inverse_mass);
983 *b.get_mut_position() += correction.scaled(b_inverse_mass);
984 }
985}
986
987impl Updatable for PhysicsWorld3D {
994 fn update(&mut self, delta_time: f64) {
1000 PhysicsWorld3D::step(self, delta_time);
1001 }
1002}
1003
1004impl Default for PhysicsWorld3D {
1006 fn default() -> PhysicsWorld3D {
1012 PhysicsWorld3D::with_config(PhysicsConfig3D::default())
1013 }
1014}