1use super::*;
2
3impl Default for BodyCollider {
5 fn default() -> BodyCollider {
6 BodyCollider::Aabb(AabbCollider::default())
7 }
8}
9
10impl Default for BodyCollider3D {
12 fn default() -> BodyCollider3D {
13 BodyCollider3D::Aabb(AabbCollider3D::default())
14 }
15}
16
17impl Default for PhysicsConfig {
19 fn default() -> PhysicsConfig {
20 PhysicsConfig::new(
21 Vector2D::new(0.0, DEFAULT_GRAVITY),
22 DEFAULT_LINEAR_DAMPING,
23 DEFAULT_ANGULAR_DAMPING,
24 )
25 }
26}
27
28impl RigidBody2D {
30 pub fn new_dynamic(id: u64, position: Vector2D) -> RigidBody2D {
41 let mass: f64 = PHYSICS_DEFAULT_MASS;
42 RigidBody2D::new(
43 id,
44 position,
45 mass,
46 1.0 / mass,
47 DEFAULT_RESTITUTION,
48 DEFAULT_FRICTION,
49 BodyType::Dynamic,
50 )
51 }
52
53 pub fn new_static(id: u64, position: Vector2D) -> RigidBody2D {
64 RigidBody2D::new(
65 id,
66 position,
67 PHYSICS_STATIC_MASS,
68 0.0,
69 DEFAULT_RESTITUTION,
70 DEFAULT_FRICTION,
71 BodyType::Static,
72 )
73 }
74
75 pub fn apply_force(&mut self, force: Vector2D) {
81 *self.get_mut_force_accumulator() += force;
82 }
83
84 pub fn apply_impulse(&mut self, impulse: Vector2D) {
90 let inverse_mass: f64 = self.get_inverse_mass();
91 if inverse_mass == 0.0 {
92 return;
93 }
94 *self.get_mut_velocity() += impulse.scaled(inverse_mass);
95 }
96
97 pub fn update_mass(&mut self, mass: f64) {
104 self.set_mass(mass);
105 self.set_inverse_mass(if mass > 0.0 { 1.0 / mass } else { 0.0 });
106 }
107
108 pub fn is_dynamic(&self) -> bool {
114 self.get_body_type() == BodyType::Dynamic
115 }
116
117 pub fn update_collider(&mut self, collider: BodyCollider) {
123 self.set_collider(Some(collider));
124 }
125
126 pub fn bounding_box(&self) -> Option<Rect> {
132 let collider: Option<BodyCollider> = self.get_collider();
133 match collider? {
134 BodyCollider::Aabb(aabb) => {
135 let aabb_rect: Rect = aabb.get_rect();
136 let mut offset_rect: Rect = aabb_rect;
137 offset_rect.set_x(
138 offset_rect.get_x() + self.get_position().get_x() - aabb_rect.get_width() * 0.5,
139 );
140 offset_rect.set_y(
141 offset_rect.get_y() + self.get_position().get_y()
142 - aabb_rect.get_height() * 0.5,
143 );
144 Some(offset_rect)
145 }
146 BodyCollider::Circle(circle) => {
147 let diameter: f64 = circle.get_circle().get_radius() * 2.0;
148 Some(Rect::from_center(self.get_position(), diameter, diameter))
149 }
150 }
151 }
152}
153
154impl PhysicsWorld2D {
156 pub fn with_config(config: PhysicsConfig) -> PhysicsWorld2D {
166 let mut world: PhysicsWorld2D = PhysicsWorld2D::new(config);
167 world.set_grid(SpatialHashGrid2D::with_default_size());
168 world
169 }
170
171 pub fn add_body(&mut self, body: RigidBody2D) {
177 self.get_mut_bodies().push(body);
178 }
179
180 pub fn remove_body(&mut self, id: u64) {
186 self.get_mut_bodies()
187 .retain(|body: &RigidBody2D| body.get_id() != id);
188 }
189
190 pub fn get_body(&self, id: u64) -> Option<&RigidBody2D> {
200 self.get_bodies()
201 .iter()
202 .find(|body: &&RigidBody2D| body.get_id() == id)
203 }
204
205 pub fn get_body_mut(&mut self, id: u64) -> Option<&mut RigidBody2D> {
215 self.get_mut_bodies()
216 .iter_mut()
217 .find(|body: &&mut RigidBody2D| body.get_id() == id)
218 }
219}
220
221impl Default for PhysicsWorld2D {
223 fn default() -> PhysicsWorld2D {
224 PhysicsWorld2D::with_config(PhysicsConfig::default())
225 }
226}
227
228impl RigidBody2D {
230 fn check_collision_with(&self, other: &RigidBody2D) -> Option<CollisionResult> {
240 let a_bbox: Rect = self.bounding_box()?;
241 let b_bbox: Rect = other.bounding_box()?;
242 if !Rect::broad_phase_alias(a_bbox, b_bbox) {
243 return None;
244 }
245 let self_collider: Option<BodyCollider> = self.get_collider();
246 let other_collider: Option<BodyCollider> = other.get_collider();
247 let position_delta: Vector2D = other.get_position() - self.get_position();
248 match (self_collider, other_collider) {
249 (Some(BodyCollider::Aabb(aabb_a)), Some(BodyCollider::Aabb(aabb_b))) => {
250 let aabb_b_rect: Rect = aabb_b.get_rect();
251 let offset_aabb_b: AabbCollider = AabbCollider::new(Rect::new(
252 aabb_b_rect.get_x() + position_delta.get_x(),
253 aabb_b_rect.get_y() + position_delta.get_y(),
254 aabb_b_rect.get_width(),
255 aabb_b_rect.get_height(),
256 ));
257 aabb_a.collide_with_aabb(&offset_aabb_b)
258 }
259 (Some(BodyCollider::Circle(circle_a)), Some(BodyCollider::Circle(circle_b))) => {
260 let circle_b_inner: Circle = circle_b.get_circle();
261 let offset_circle_b: CircleCollider = CircleCollider::new(Circle::new(
262 circle_b_inner.get_center() + position_delta,
263 circle_b_inner.get_radius(),
264 ));
265 circle_a.collide_with_circle(&offset_circle_b)
266 }
267 (Some(BodyCollider::Aabb(aabb)), Some(BodyCollider::Circle(circle))) => {
268 let circle_inner: Circle = circle.get_circle();
269 let offset_circle: CircleCollider = CircleCollider::new(Circle::new(
270 circle_inner.get_center() + position_delta,
271 circle_inner.get_radius(),
272 ));
273 aabb.collide_with_circle(&offset_circle)
274 }
275 (Some(BodyCollider::Circle(circle)), Some(BodyCollider::Aabb(aabb))) => {
276 let aabb_rect: Rect = aabb.get_rect();
277 let offset_aabb: AabbCollider = AabbCollider::new(Rect::new(
278 aabb_rect.get_x() + position_delta.get_x(),
279 aabb_rect.get_y() + position_delta.get_y(),
280 aabb_rect.get_width(),
281 aabb_rect.get_height(),
282 ));
283 offset_aabb
284 .collide_with_circle(&circle)
285 .map(|mut result: CollisionResult| {
286 result.set_normal(-result.get_normal());
287 result
288 })
289 }
290 _ => None,
291 }
292 }
293
294 fn resolve_collision_with(&mut self, other: &mut RigidBody2D, result: &CollisionResult) {
302 let self_inverse_mass: f64 = self.get_inverse_mass();
303 let other_inverse_mass: f64 = other.get_inverse_mass();
304 let relative_velocity: Vector2D = other.get_velocity() - self.get_velocity();
305 let velocity_along_normal: f64 = relative_velocity.dot(result.get_normal());
306 if velocity_along_normal > 0.0 {
307 return;
308 }
309 let restitution: f64 = self.get_restitution().min(other.get_restitution());
310 let inverse_mass_sum: f64 = self_inverse_mass + other_inverse_mass;
311 if inverse_mass_sum == 0.0 {
312 return;
313 }
314 let impulse_magnitude: f64 =
315 -(1.0 + restitution) * velocity_along_normal / inverse_mass_sum;
316 let impulse: Vector2D = result.get_normal().scaled(impulse_magnitude);
317 *self.get_mut_velocity() -= impulse.scaled(self_inverse_mass);
318 *other.get_mut_velocity() += impulse.scaled(other_inverse_mass);
319 let correction: Vector2D = result
320 .get_normal()
321 .scaled((result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum).max(0.0));
322 *self.get_mut_position() -= correction.scaled(self_inverse_mass);
323 *other.get_mut_position() += correction.scaled(other_inverse_mass);
324 }
325}
326
327impl PhysicsWorld2D {
329 pub fn step(&mut self, delta_time: f64) {
338 let config: PhysicsConfig = self.get_config();
339 let damping_factor: f64 = (1.0 - config.get_linear_damping() * delta_time).max(0.0);
341 let angular_damping: f64 = (1.0 - config.get_angular_damping() * delta_time).max(0.0);
342 let gravity: Vector2D = config.get_gravity();
343 for body in self.get_mut_bodies() {
344 if !body.is_dynamic() {
345 continue;
346 }
347 let body_mass: f64 = body.get_mass();
348 let body_inverse_mass: f64 = body.get_inverse_mass();
349 *body.get_mut_force_accumulator() += gravity.scaled(body_mass);
350 let force: Vector2D = body.get_force_accumulator();
351 *body.get_mut_velocity() += force.scaled(body_inverse_mass * delta_time);
352 *body.get_mut_velocity() *= damping_factor;
354 let current_velocity: Vector2D = body.get_velocity();
355 *body.get_mut_position() += current_velocity.scaled(delta_time);
356 body.set_force_accumulator(Vector2D::zero());
357 *body.get_mut_angular_velocity() *= angular_damping;
358 let current_angular_velocity: f64 = body.get_angular_velocity();
359 *body.get_mut_rotation() += current_angular_velocity * delta_time;
360 }
361 self.resolve_collisions();
362 }
363
364 fn resolve_collisions(&mut self) {
370 let body_count: usize = self.get_bodies().len();
371 if body_count < 2 {
372 return;
373 }
374 let mut pairs: Vec<(usize, usize)> = Vec::new();
379 {
380 let (bodies, grid, query_buffer, query_seen) = (
381 &self.bodies,
382 &mut self.grid,
383 &mut self.query_buffer,
384 &mut self.query_seen,
385 );
386 grid.clear();
387 for (index, body) in bodies.iter().enumerate() {
388 if let Some(bbox) = body.bounding_box() {
389 grid.insert(index, bbox.min(), bbox.max());
390 }
391 }
392 for (i, body) in bodies.iter().enumerate() {
393 let Some(bbox) = body.bounding_box() else {
394 continue;
395 };
396 grid.query_into(bbox.min(), bbox.max(), query_buffer, query_seen);
397 for &j in query_buffer.iter() {
398 if j > i {
399 pairs.push((i, j));
400 }
401 }
402 }
403 }
404 for iteration in 0..PHYSICS_MAX_ITERATIONS {
405 let mut any_collision: bool = false;
406 for &(i, j) in pairs.iter() {
407 let (left, right) = self.get_mut_bodies().split_at_mut(j);
408 let body_a: &mut RigidBody2D = &mut left[i];
409 let body_b: &mut RigidBody2D = &mut right[0];
410 if body_a.get_inverse_mass() == 0.0 && body_b.get_inverse_mass() == 0.0 {
411 continue;
412 }
413 if let Some(result) = body_a.check_collision_with(body_b) {
414 body_a.resolve_collision_with(body_b, &result);
415 any_collision = true;
416 }
417 }
418 if !any_collision {
419 break;
420 }
421 let _ = iteration;
422 }
423 }
424}
425
426impl Default for PhysicsConfig3D {
428 fn default() -> PhysicsConfig3D {
429 PhysicsConfig3D::new(
430 Vector3D::new(0.0, DEFAULT_GRAVITY_3D, 0.0),
431 DEFAULT_LINEAR_DAMPING,
432 DEFAULT_ANGULAR_DAMPING,
433 )
434 }
435}
436
437impl RigidBody3D {
439 pub fn new_dynamic(id: u64, position: Vector3D) -> RigidBody3D {
450 let mass: f64 = PHYSICS_DEFAULT_MASS;
451 RigidBody3D::new(
452 id,
453 position,
454 mass,
455 1.0 / mass,
456 DEFAULT_RESTITUTION,
457 DEFAULT_FRICTION,
458 BodyType::Dynamic,
459 )
460 }
461
462 pub fn new_static(id: u64, position: Vector3D) -> RigidBody3D {
473 RigidBody3D::new(
474 id,
475 position,
476 PHYSICS_STATIC_MASS,
477 0.0,
478 DEFAULT_RESTITUTION,
479 DEFAULT_FRICTION,
480 BodyType::Static,
481 )
482 }
483
484 pub fn apply_force(&mut self, force: Vector3D) {
490 *self.get_mut_force_accumulator() += force;
491 }
492
493 pub fn apply_torque(&mut self, torque: Vector3D) {
499 *self.get_mut_torque_accumulator() += torque;
500 }
501
502 pub fn apply_impulse(&mut self, impulse: Vector3D) {
508 let inverse_mass: f64 = self.get_inverse_mass();
509 if inverse_mass == 0.0 {
510 return;
511 }
512 *self.get_mut_velocity() += impulse.scaled(inverse_mass);
513 }
514
515 pub fn update_mass(&mut self, mass: f64) {
522 self.set_mass(mass);
523 self.set_inverse_mass(if mass > 0.0 { 1.0 / mass } else { 0.0 });
524 }
525
526 pub fn is_dynamic(&self) -> bool {
532 self.get_body_type() == BodyType::Dynamic
533 }
534
535 pub fn update_collider(&mut self, collider: BodyCollider3D) {
541 self.set_collider(Some(collider));
542 }
543
544 pub fn bounding_box(&self) -> Option<AABB3D> {
550 let collider: Option<BodyCollider3D> = self.get_collider();
551 let position: Vector3D = self.get_position();
552 match collider? {
553 BodyCollider3D::Aabb(aabb) => {
554 let center: Vector3D = aabb.get_aabb().center();
555 let size: Vector3D = aabb.get_aabb().size();
556 Some(AABB3D::from_center(
557 position + center,
558 size.get_x(),
559 size.get_y(),
560 size.get_z(),
561 ))
562 }
563 BodyCollider3D::Sphere(sphere) => {
564 let sphere_inner: Sphere = sphere.get_sphere();
565 let diameter: f64 = sphere_inner.get_radius() * 2.0;
566 Some(AABB3D::from_center(
567 position + sphere_inner.get_center(),
568 diameter,
569 diameter,
570 diameter,
571 ))
572 }
573 }
574 }
575}
576
577impl PhysicsWorld3D {
579 pub fn with_config(config: PhysicsConfig3D) -> PhysicsWorld3D {
589 let mut world: PhysicsWorld3D = PhysicsWorld3D::new(config);
590 world.set_grid(SpatialHashGrid3D::with_default_size());
591 world
592 }
593
594 pub fn add_body(&mut self, body: RigidBody3D) {
600 self.get_mut_bodies().push(body);
601 }
602
603 pub fn remove_body(&mut self, id: u64) {
609 self.get_mut_bodies()
610 .retain(|body: &RigidBody3D| body.get_id() != id);
611 }
612
613 pub fn get_body(&self, id: u64) -> Option<&RigidBody3D> {
623 self.get_bodies()
624 .iter()
625 .find(|body: &&RigidBody3D| body.get_id() == id)
626 }
627
628 pub fn get_body_mut(&mut self, id: u64) -> Option<&mut RigidBody3D> {
638 self.get_mut_bodies()
639 .iter_mut()
640 .find(|body: &&mut RigidBody3D| body.get_id() == id)
641 }
642
643 pub fn step(&mut self, delta_time: f64) {
652 let config: PhysicsConfig3D = self.get_config();
653 let damping_factor: f64 = (1.0 - config.get_linear_damping() * delta_time).max(0.0);
655 let angular_damping: f64 = (1.0 - config.get_angular_damping() * delta_time).max(0.0);
656 let gravity: Vector3D = config.get_gravity();
657 for body in self.get_mut_bodies() {
658 if !body.is_dynamic() {
659 continue;
660 }
661 let body_mass: f64 = body.get_mass();
662 let body_inverse_mass: f64 = body.get_inverse_mass();
663 *body.get_mut_force_accumulator() += gravity.scaled(body_mass);
664 let force: Vector3D = body.get_force_accumulator();
665 *body.get_mut_velocity() += force.scaled(body_inverse_mass * delta_time);
666 *body.get_mut_velocity() *= damping_factor;
668 let current_velocity: Vector3D = body.get_velocity();
669 *body.get_mut_position() += current_velocity.scaled(delta_time);
670 body.set_force_accumulator(Vector3D::zero());
671 *body.get_mut_angular_velocity() *= angular_damping;
672 let angular_velocity: Vector3D = body.get_angular_velocity();
673 let rotation_delta: Quaternion = Quaternion::new(
674 angular_velocity.get_x() * delta_time * 0.5,
675 angular_velocity.get_y() * delta_time * 0.5,
676 angular_velocity.get_z() * delta_time * 0.5,
677 1.0,
678 );
679 body.set_rotation((rotation_delta * body.get_rotation()).normalized());
680 body.set_torque_accumulator(Vector3D::zero());
681 }
682 self.resolve_collisions();
683 }
684
685 fn resolve_collisions(&mut self) {
691 let body_count: usize = self.get_bodies().len();
692 if body_count < 2 {
693 return;
694 }
695 let mut pairs: Vec<(usize, usize)> = Vec::new();
700 {
701 let (bodies, grid, query_buffer, query_seen) = (
702 &self.bodies,
703 &mut self.grid,
704 &mut self.query_buffer,
705 &mut self.query_seen,
706 );
707 grid.clear();
708 for (index, body) in bodies.iter().enumerate() {
709 if let Some(bbox) = body.bounding_box() {
710 grid.insert(index, bbox.get_min(), bbox.get_max());
711 }
712 }
713 for (i, body) in bodies.iter().enumerate() {
714 let Some(bbox) = body.bounding_box() else {
715 continue;
716 };
717 grid.query_into(bbox.get_min(), bbox.get_max(), query_buffer, query_seen);
718 for &j in query_buffer.iter() {
719 if j > i {
720 pairs.push((i, j));
721 }
722 }
723 }
724 }
725 for iteration in 0..PHYSICS_MAX_ITERATIONS {
726 let mut any_collision: bool = false;
727 for &(i, j) in pairs.iter() {
728 let (left, right) = self.get_mut_bodies().split_at_mut(j);
729 let body_a: &mut RigidBody3D = &mut left[i];
730 let body_b: &mut RigidBody3D = &mut right[0];
731 if body_a.get_inverse_mass() == 0.0 && body_b.get_inverse_mass() == 0.0 {
732 continue;
733 }
734 if let Some(result) = Self::check_collision_3d(body_a, body_b) {
735 Self::resolve_collision_3d(body_a, body_b, &result);
736 any_collision = true;
737 }
738 }
739 if !any_collision {
740 break;
741 }
742 let _ = iteration;
743 }
744 }
745
746 fn check_collision_3d(a: &RigidBody3D, b: &RigidBody3D) -> Option<CollisionResult3D> {
757 let a_bbox: AABB3D = a.bounding_box()?;
758 let b_bbox: AABB3D = b.bounding_box()?;
759 if !AABB3D::broad_phase(a_bbox, b_bbox) {
760 return None;
761 }
762 let a_collider: Option<BodyCollider3D> = a.get_collider();
763 let b_collider: Option<BodyCollider3D> = b.get_collider();
764 let position_delta: Vector3D = b.get_position() - a.get_position();
765 match (a_collider, b_collider) {
766 (Some(BodyCollider3D::Aabb(aabb_a)), Some(BodyCollider3D::Aabb(aabb_b))) => {
767 let aabb_b_inner: AABB3D = aabb_b.get_aabb();
768 let offset_aabb: AabbCollider3D = AabbCollider3D::new(AABB3D::new(
769 aabb_b_inner.get_min() + position_delta,
770 aabb_b_inner.get_max() + position_delta,
771 ));
772 aabb_a.collide_with_aabb(&offset_aabb)
773 }
774 (Some(BodyCollider3D::Sphere(sphere_a)), Some(BodyCollider3D::Sphere(sphere_b))) => {
775 let sphere_b_inner: Sphere = sphere_b.get_sphere();
776 let offset_sphere: SphereCollider3D = SphereCollider3D::new(Sphere::new(
777 sphere_b_inner.get_center() + position_delta,
778 sphere_b_inner.get_radius(),
779 ));
780 sphere_a.collide_with_sphere(&offset_sphere)
781 }
782 (Some(BodyCollider3D::Aabb(aabb)), Some(BodyCollider3D::Sphere(sphere))) => {
783 let sphere_inner: Sphere = sphere.get_sphere();
784 let offset_sphere: SphereCollider3D = SphereCollider3D::new(Sphere::new(
785 sphere_inner.get_center() + position_delta,
786 sphere_inner.get_radius(),
787 ));
788 aabb.collide_with_sphere(&offset_sphere)
789 }
790 (Some(BodyCollider3D::Sphere(sphere)), Some(BodyCollider3D::Aabb(aabb))) => {
791 let aabb_inner: AABB3D = aabb.get_aabb();
792 let offset_aabb: AabbCollider3D = AabbCollider3D::new(AABB3D::new(
793 aabb_inner.get_min() + position_delta,
794 aabb_inner.get_max() + position_delta,
795 ));
796 offset_aabb
797 .collide_with_sphere(&sphere)
798 .map(|mut result: CollisionResult3D| {
799 result.set_normal(-result.get_normal());
800 result
801 })
802 }
803 _ => None,
804 }
805 }
806
807 fn resolve_collision_3d(a: &mut RigidBody3D, b: &mut RigidBody3D, result: &CollisionResult3D) {
816 let a_inverse_mass: f64 = a.get_inverse_mass();
817 let b_inverse_mass: f64 = b.get_inverse_mass();
818 let relative_velocity: Vector3D = b.get_velocity() - a.get_velocity();
819 let velocity_along_normal: f64 = relative_velocity.dot(result.get_normal());
820 if velocity_along_normal > 0.0 {
821 return;
822 }
823 let restitution: f64 = a.get_restitution().min(b.get_restitution());
824 let inverse_mass_sum: f64 = a_inverse_mass + b_inverse_mass;
825 if inverse_mass_sum == 0.0 {
826 return;
827 }
828 let impulse_magnitude: f64 =
829 -(1.0 + restitution) * velocity_along_normal / inverse_mass_sum;
830 let impulse: Vector3D = result.get_normal().scaled(impulse_magnitude);
831 *a.get_mut_velocity() -= impulse.scaled(a_inverse_mass);
832 *b.get_mut_velocity() += impulse.scaled(b_inverse_mass);
833 let correction: Vector3D = result
834 .get_normal()
835 .scaled((result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum).max(0.0));
836 *a.get_mut_position() -= correction.scaled(a_inverse_mass);
837 *b.get_mut_position() += correction.scaled(b_inverse_mass);
838 }
839}
840
841impl Default for PhysicsWorld3D {
843 fn default() -> PhysicsWorld3D {
844 PhysicsWorld3D::with_config(PhysicsConfig3D::default())
845 }
846}