1use crate::*;
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 PhysicsWorld2D::new(config)
167 }
168
169 pub fn add_body(&mut self, body: RigidBody2D) {
175 self.get_mut_bodies().push(body);
176 }
177
178 pub fn remove_body(&mut self, id: u64) {
184 self.get_mut_bodies()
185 .retain(|body: &RigidBody2D| body.get_id() != id);
186 }
187
188 pub fn get_body(&self, id: u64) -> Option<&RigidBody2D> {
198 self.get_bodies()
199 .iter()
200 .find(|body: &&RigidBody2D| body.get_id() == id)
201 }
202
203 pub fn get_body_mut(&mut self, id: u64) -> Option<&mut RigidBody2D> {
213 self.get_mut_bodies()
214 .iter_mut()
215 .find(|body: &&mut RigidBody2D| body.get_id() == id)
216 }
217}
218
219impl Default for PhysicsWorld2D {
221 fn default() -> PhysicsWorld2D {
222 PhysicsWorld2D::new(PhysicsConfig::default())
223 }
224}
225
226impl RigidBody2D {
228 fn check_collision_with(&self, other: &RigidBody2D) -> Option<CollisionResult> {
238 let a_bbox: Rect = self.bounding_box()?;
239 let b_bbox: Rect = other.bounding_box()?;
240 if !Rect::broad_phase_check(a_bbox, b_bbox) {
241 return None;
242 }
243 let self_collider: Option<BodyCollider> = self.get_collider();
244 let other_collider: Option<BodyCollider> = other.get_collider();
245 let position_delta: Vector2D = other.get_position() - self.get_position();
246 match (self_collider, other_collider) {
247 (Some(BodyCollider::Aabb(aabb_a)), Some(BodyCollider::Aabb(aabb_b))) => {
248 let aabb_b_rect: Rect = aabb_b.get_rect();
249 let offset_aabb_b: AabbCollider = AabbCollider::new(Rect::new(
250 aabb_b_rect.get_x() + position_delta.get_x(),
251 aabb_b_rect.get_y() + position_delta.get_y(),
252 aabb_b_rect.get_width(),
253 aabb_b_rect.get_height(),
254 ));
255 aabb_a.collide_with_aabb(&offset_aabb_b)
256 }
257 (Some(BodyCollider::Circle(circle_a)), Some(BodyCollider::Circle(circle_b))) => {
258 let circle_b_inner: Circle = circle_b.get_circle();
259 let offset_circle_b: CircleCollider = CircleCollider::new(Circle::new(
260 circle_b_inner.get_center() + position_delta,
261 circle_b_inner.get_radius(),
262 ));
263 circle_a.collide_with_circle(&offset_circle_b)
264 }
265 (Some(BodyCollider::Aabb(aabb)), Some(BodyCollider::Circle(circle))) => {
266 let circle_inner: Circle = circle.get_circle();
267 let offset_circle: CircleCollider = CircleCollider::new(Circle::new(
268 circle_inner.get_center() + position_delta,
269 circle_inner.get_radius(),
270 ));
271 aabb.collide_with_circle(&offset_circle)
272 }
273 (Some(BodyCollider::Circle(circle)), Some(BodyCollider::Aabb(aabb))) => {
274 let aabb_rect: Rect = aabb.get_rect();
275 let offset_aabb: AabbCollider = AabbCollider::new(Rect::new(
276 aabb_rect.get_x() + position_delta.get_x(),
277 aabb_rect.get_y() + position_delta.get_y(),
278 aabb_rect.get_width(),
279 aabb_rect.get_height(),
280 ));
281 offset_aabb
282 .collide_with_circle(&circle)
283 .map(|mut result: CollisionResult| {
284 result.set_normal(-result.get_normal());
285 result
286 })
287 }
288 _ => None,
289 }
290 }
291
292 fn resolve_collision_with(&mut self, other: &mut RigidBody2D, result: &CollisionResult) {
300 let self_inverse_mass: f64 = self.get_inverse_mass();
301 let other_inverse_mass: f64 = other.get_inverse_mass();
302 let relative_velocity: Vector2D = other.get_velocity() - self.get_velocity();
303 let velocity_along_normal: f64 = relative_velocity.dot(result.get_normal());
304 if velocity_along_normal > 0.0 {
305 return;
306 }
307 let restitution: f64 = self.get_restitution().min(other.get_restitution());
308 let inverse_mass_sum: f64 = self_inverse_mass + other_inverse_mass;
309 if inverse_mass_sum == 0.0 {
310 return;
311 }
312 let impulse_magnitude: f64 =
313 -(1.0 + restitution) * velocity_along_normal / inverse_mass_sum;
314 let impulse: Vector2D = result.get_normal().scaled(impulse_magnitude);
315 *self.get_mut_velocity() -= impulse.scaled(self_inverse_mass);
316 *other.get_mut_velocity() += impulse.scaled(other_inverse_mass);
317 let correction: Vector2D = result
318 .get_normal()
319 .scaled((result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum).max(0.0));
320 *self.get_mut_position() -= correction.scaled(self_inverse_mass);
321 *other.get_mut_position() += correction.scaled(other_inverse_mass);
322 }
323}
324
325impl PhysicsWorld2D {
327 pub fn step(&mut self, delta_time: f64) {
336 let config: PhysicsConfig = self.get_config();
337 for body in self.get_mut_bodies() {
338 if !body.is_dynamic() {
339 continue;
340 }
341 let body_mass: f64 = body.get_mass();
342 let body_inverse_mass: f64 = body.get_inverse_mass();
343 *body.get_mut_force_accumulator() += config.get_gravity().scaled(body_mass);
344 let force: Vector2D = body.get_force_accumulator();
345 *body.get_mut_velocity() += force.scaled(body_inverse_mass * delta_time);
346 let damping_factor: f64 = (1.0 - config.get_linear_damping() * delta_time).max(0.0);
347 let velocity: Vector2D = body.get_velocity();
348 body.set_velocity(velocity.scaled(damping_factor));
349 let current_velocity: Vector2D = body.get_velocity();
350 *body.get_mut_position() += current_velocity.scaled(delta_time);
351 body.set_force_accumulator(Vector2D::zero());
352 let angular_damping: f64 = (1.0 - config.get_angular_damping() * delta_time).max(0.0);
353 let angular_velocity: f64 = body.get_angular_velocity();
354 *body.get_mut_angular_velocity() = angular_velocity * angular_damping;
355 let current_angular_velocity: f64 = body.get_angular_velocity();
356 *body.get_mut_rotation() += current_angular_velocity * delta_time;
357 }
358 self.resolve_collisions();
359 }
360
361 fn resolve_collisions(&mut self) {
366 let body_count: usize = self.get_bodies().len();
367 for iteration in 0..PHYSICS_MAX_ITERATIONS {
368 let mut any_collision: bool = false;
369 for i in 0..body_count {
370 for j in (i + 1)..body_count {
371 let (left, right) = self.get_mut_bodies().split_at_mut(j);
372 let body_a: &mut RigidBody2D = &mut left[i];
373 let body_b: &mut RigidBody2D = &mut right[0];
374 if body_a.get_inverse_mass() == 0.0 && body_b.get_inverse_mass() == 0.0 {
375 continue;
376 }
377 if let Some(result) = body_a.check_collision_with(body_b) {
378 body_a.resolve_collision_with(body_b, &result);
379 any_collision = true;
380 }
381 }
382 }
383 if !any_collision {
384 break;
385 }
386 let _ = iteration;
387 }
388 }
389}
390
391impl Default for PhysicsConfig3D {
393 fn default() -> PhysicsConfig3D {
394 PhysicsConfig3D::new(
395 Vector3D::new(0.0, DEFAULT_GRAVITY_3D, 0.0),
396 DEFAULT_LINEAR_DAMPING,
397 DEFAULT_ANGULAR_DAMPING,
398 )
399 }
400}
401
402impl RigidBody3D {
404 pub fn new_dynamic(id: u64, position: Vector3D) -> RigidBody3D {
415 let mass: f64 = PHYSICS_DEFAULT_MASS;
416 RigidBody3D::new(
417 id,
418 position,
419 mass,
420 1.0 / mass,
421 DEFAULT_RESTITUTION,
422 DEFAULT_FRICTION,
423 BodyType::Dynamic,
424 )
425 }
426
427 pub fn new_static(id: u64, position: Vector3D) -> RigidBody3D {
438 RigidBody3D::new(
439 id,
440 position,
441 PHYSICS_STATIC_MASS,
442 0.0,
443 DEFAULT_RESTITUTION,
444 DEFAULT_FRICTION,
445 BodyType::Static,
446 )
447 }
448
449 pub fn apply_force(&mut self, force: Vector3D) {
455 *self.get_mut_force_accumulator() += force;
456 }
457
458 pub fn apply_torque(&mut self, torque: Vector3D) {
464 *self.get_mut_torque_accumulator() += torque;
465 }
466
467 pub fn apply_impulse(&mut self, impulse: Vector3D) {
473 let inverse_mass: f64 = self.get_inverse_mass();
474 if inverse_mass == 0.0 {
475 return;
476 }
477 *self.get_mut_velocity() += impulse.scaled(inverse_mass);
478 }
479
480 pub fn update_mass(&mut self, mass: f64) {
487 self.set_mass(mass);
488 self.set_inverse_mass(if mass > 0.0 { 1.0 / mass } else { 0.0 });
489 }
490
491 pub fn is_dynamic(&self) -> bool {
497 self.get_body_type() == BodyType::Dynamic
498 }
499
500 pub fn update_collider(&mut self, collider: BodyCollider3D) {
506 self.set_collider(Some(collider));
507 }
508
509 pub fn bounding_box(&self) -> Option<AABB3D> {
515 let collider: Option<BodyCollider3D> = self.get_collider();
516 let position: Vector3D = self.get_position();
517 match collider? {
518 BodyCollider3D::Aabb(aabb) => {
519 let center: Vector3D = aabb.get_aabb().center();
520 let size: Vector3D = aabb.get_aabb().size();
521 Some(AABB3D::from_center(
522 position + center,
523 size.get_x(),
524 size.get_y(),
525 size.get_z(),
526 ))
527 }
528 BodyCollider3D::Sphere(sphere) => {
529 let sphere_inner: Sphere = sphere.get_sphere();
530 let diameter: f64 = sphere_inner.get_radius() * 2.0;
531 Some(AABB3D::from_center(
532 position + sphere_inner.get_center(),
533 diameter,
534 diameter,
535 diameter,
536 ))
537 }
538 }
539 }
540}
541
542impl PhysicsWorld3D {
544 pub fn with_config(config: PhysicsConfig3D) -> PhysicsWorld3D {
554 PhysicsWorld3D::new(config)
555 }
556
557 pub fn add_body(&mut self, body: RigidBody3D) {
563 self.get_mut_bodies().push(body);
564 }
565
566 pub fn remove_body(&mut self, id: u64) {
572 self.get_mut_bodies()
573 .retain(|body: &RigidBody3D| body.get_id() != id);
574 }
575
576 pub fn get_body(&self, id: u64) -> Option<&RigidBody3D> {
586 self.get_bodies()
587 .iter()
588 .find(|body: &&RigidBody3D| body.get_id() == id)
589 }
590
591 pub fn get_body_mut(&mut self, id: u64) -> Option<&mut RigidBody3D> {
601 self.get_mut_bodies()
602 .iter_mut()
603 .find(|body: &&mut RigidBody3D| body.get_id() == id)
604 }
605
606 pub fn step(&mut self, delta_time: f64) {
615 let config: PhysicsConfig3D = self.get_config();
616 for body in self.get_mut_bodies() {
617 if !body.is_dynamic() {
618 continue;
619 }
620 let body_mass: f64 = body.get_mass();
621 let body_inverse_mass: f64 = body.get_inverse_mass();
622 *body.get_mut_force_accumulator() += config.get_gravity().scaled(body_mass);
623 let force: Vector3D = body.get_force_accumulator();
624 *body.get_mut_velocity() += force.scaled(body_inverse_mass * delta_time);
625 let damping_factor: f64 = (1.0 - config.get_linear_damping() * delta_time).max(0.0);
626 let velocity: Vector3D = body.get_velocity();
627 body.set_velocity(velocity.scaled(damping_factor));
628 let current_velocity: Vector3D = body.get_velocity();
629 *body.get_mut_position() += current_velocity.scaled(delta_time);
630 body.set_force_accumulator(Vector3D::zero());
631 let angular_damping: f64 = (1.0 - config.get_angular_damping() * delta_time).max(0.0);
632 let angular_velocity: Vector3D = body.get_angular_velocity().scaled(angular_damping);
633 body.set_angular_velocity(angular_velocity);
634 let rotation_delta: Quaternion = Quaternion::new(
635 angular_velocity.get_x() * delta_time * 0.5,
636 angular_velocity.get_y() * delta_time * 0.5,
637 angular_velocity.get_z() * delta_time * 0.5,
638 1.0,
639 );
640 body.set_rotation((rotation_delta * body.get_rotation()).normalized());
641 body.set_torque_accumulator(Vector3D::zero());
642 }
643 self.resolve_collisions();
644 }
645
646 fn resolve_collisions(&mut self) {
651 let body_count: usize = self.get_bodies().len();
652 for iteration in 0..PHYSICS_MAX_ITERATIONS {
653 let mut any_collision: bool = false;
654 for i in 0..body_count {
655 for j in (i + 1)..body_count {
656 let (left, right) = self.get_mut_bodies().split_at_mut(j);
657 let body_a: &mut RigidBody3D = &mut left[i];
658 let body_b: &mut RigidBody3D = &mut right[0];
659 if body_a.get_inverse_mass() == 0.0 && body_b.get_inverse_mass() == 0.0 {
660 continue;
661 }
662 if let Some(result) = Self::check_collision_3d(body_a, body_b) {
663 Self::resolve_collision_3d(body_a, body_b, &result);
664 any_collision = true;
665 }
666 }
667 }
668 if !any_collision {
669 break;
670 }
671 let _ = iteration;
672 }
673 }
674
675 fn check_collision_3d(a: &RigidBody3D, b: &RigidBody3D) -> Option<CollisionResult3D> {
686 let a_bbox: AABB3D = a.bounding_box()?;
687 let b_bbox: AABB3D = b.bounding_box()?;
688 if !AABB3D::broad_phase_check(a_bbox, b_bbox) {
689 return None;
690 }
691 let a_collider: Option<BodyCollider3D> = a.get_collider();
692 let b_collider: Option<BodyCollider3D> = b.get_collider();
693 let position_delta: Vector3D = b.get_position() - a.get_position();
694 match (a_collider, b_collider) {
695 (Some(BodyCollider3D::Aabb(aabb_a)), Some(BodyCollider3D::Aabb(aabb_b))) => {
696 let aabb_b_inner: AABB3D = aabb_b.get_aabb();
697 let offset_aabb: AabbCollider3D = AabbCollider3D::new(AABB3D::new(
698 aabb_b_inner.get_min() + position_delta,
699 aabb_b_inner.get_max() + position_delta,
700 ));
701 aabb_a.collide_with_aabb(&offset_aabb)
702 }
703 (Some(BodyCollider3D::Sphere(sphere_a)), Some(BodyCollider3D::Sphere(sphere_b))) => {
704 let sphere_b_inner: Sphere = sphere_b.get_sphere();
705 let offset_sphere: SphereCollider3D = SphereCollider3D::new(Sphere::new(
706 sphere_b_inner.get_center() + position_delta,
707 sphere_b_inner.get_radius(),
708 ));
709 sphere_a.collide_with_sphere(&offset_sphere)
710 }
711 (Some(BodyCollider3D::Aabb(aabb)), Some(BodyCollider3D::Sphere(sphere))) => {
712 let sphere_inner: Sphere = sphere.get_sphere();
713 let offset_sphere: SphereCollider3D = SphereCollider3D::new(Sphere::new(
714 sphere_inner.get_center() + position_delta,
715 sphere_inner.get_radius(),
716 ));
717 aabb.collide_with_sphere(&offset_sphere)
718 }
719 (Some(BodyCollider3D::Sphere(sphere)), Some(BodyCollider3D::Aabb(aabb))) => {
720 let aabb_inner: AABB3D = aabb.get_aabb();
721 let offset_aabb: AabbCollider3D = AabbCollider3D::new(AABB3D::new(
722 aabb_inner.get_min() + position_delta,
723 aabb_inner.get_max() + position_delta,
724 ));
725 offset_aabb
726 .collide_with_sphere(&sphere)
727 .map(|mut result: CollisionResult3D| {
728 result.set_normal(-result.get_normal());
729 result
730 })
731 }
732 _ => None,
733 }
734 }
735
736 fn resolve_collision_3d(a: &mut RigidBody3D, b: &mut RigidBody3D, result: &CollisionResult3D) {
745 let a_inverse_mass: f64 = a.get_inverse_mass();
746 let b_inverse_mass: f64 = b.get_inverse_mass();
747 let relative_velocity: Vector3D = b.get_velocity() - a.get_velocity();
748 let velocity_along_normal: f64 = relative_velocity.dot(result.get_normal());
749 if velocity_along_normal > 0.0 {
750 return;
751 }
752 let restitution: f64 = a.get_restitution().min(b.get_restitution());
753 let inverse_mass_sum: f64 = a_inverse_mass + b_inverse_mass;
754 if inverse_mass_sum == 0.0 {
755 return;
756 }
757 let impulse_magnitude: f64 =
758 -(1.0 + restitution) * velocity_along_normal / inverse_mass_sum;
759 let impulse: Vector3D = result.get_normal().scaled(impulse_magnitude);
760 *a.get_mut_velocity() -= impulse.scaled(a_inverse_mass);
761 *b.get_mut_velocity() += impulse.scaled(b_inverse_mass);
762 let correction: Vector3D = result
763 .get_normal()
764 .scaled((result.get_depth() * PHYSICS_POSITION_PERCENT / inverse_mass_sum).max(0.0));
765 *a.get_mut_position() -= correction.scaled(a_inverse_mass);
766 *b.get_mut_position() += correction.scaled(b_inverse_mass);
767 }
768}
769
770impl Default for PhysicsWorld3D {
772 fn default() -> PhysicsWorld3D {
773 PhysicsWorld3D::new(PhysicsConfig3D::default())
774 }
775}