1use crate::{
2 PhysicsSimulation, Scalar,
3 components::{
4 AngularVelocity, ExternalForces, Gravity, LinearVelocity, Mass, ParticleMaterial, Position,
5 Rotation,
6 },
7 utils::quat_from_axis_angle,
8};
9use anput::{query::Query, systems::SystemContext, universe::Res, world::World};
10use std::error::Error;
11
12pub fn apply_external_forces<const LOCKING: bool>(
13 context: SystemContext,
14) -> Result<(), Box<dyn Error>> {
15 let (world, simulation, query) = context.fetch::<(
16 &World,
17 Res<LOCKING, &PhysicsSimulation>,
18 Query<
19 LOCKING,
20 (
21 &mut ExternalForces,
22 &Mass,
23 &mut LinearVelocity,
24 Option<&mut AngularVelocity>,
25 ),
26 >,
27 )>()?;
28
29 for (external_forces, mass, linear_velocity, angular_velocity) in query.query(world) {
30 linear_velocity.value += external_forces.force * mass.inverse() * simulation.delta_time;
31 linear_velocity.value += external_forces.linear_impulse * mass.inverse();
32
33 if let Some(angular_velocity) = angular_velocity {
34 angular_velocity.value +=
35 external_forces.torque * mass.inverse() * simulation.delta_time;
36 angular_velocity.value += external_forces.angular_impulse * mass.inverse();
37 }
38
39 external_forces.clear();
40 }
41
42 Ok(())
43}
44
45pub fn integrate_velocities<const LOCKING: bool>(
46 context: SystemContext,
47) -> Result<(), Box<dyn Error>> {
48 let (world, simulation, query) = context.fetch::<(
49 &World,
50 Res<LOCKING, &PhysicsSimulation>,
51 Query<
52 LOCKING,
53 (
54 &mut Position,
55 Option<&mut Rotation>,
56 &LinearVelocity,
57 Option<&AngularVelocity>,
58 ),
59 >,
60 )>()?;
61
62 for (position, rotation, linear_velocity, angular_velocity) in query.query(world) {
63 position.current += linear_velocity.value * simulation.delta_time;
64
65 if let Some(rotation) = rotation
66 && let Some(angular_velocity) = angular_velocity
67 {
68 let angle = angular_velocity.value.magnitude() * simulation.delta_time;
69 if angle.abs() > Scalar::EPSILON {
70 let axis = angular_velocity.value / angle;
71 rotation.current =
72 (rotation.current * quat_from_axis_angle(axis, angle)).normalized();
73 }
74 }
75 }
76
77 Ok(())
78}
79
80pub fn cache_current_as_previous_state<const LOCKING: bool>(
81 context: SystemContext,
82) -> Result<(), Box<dyn Error>> {
83 let (world, query) = context.fetch::<(
84 &World,
85 Query<LOCKING, (&mut Position, Option<&mut Rotation>)>,
86 )>()?;
87
88 for (position, rotation) in query.query(world) {
89 position.cache_current_as_previous();
90 if let Some(rotation) = rotation {
91 rotation.cache_current_as_previous();
92 }
93 }
94
95 Ok(())
96}
97
98pub fn recalculate_velocities<const LOCKING: bool>(
99 context: SystemContext,
100) -> Result<(), Box<dyn Error>> {
101 let (world, simulation, query) = context.fetch::<(
102 &World,
103 Res<LOCKING, &PhysicsSimulation>,
104 Query<
105 LOCKING,
106 (
107 &Position,
108 Option<&Rotation>,
109 &mut LinearVelocity,
110 Option<&mut AngularVelocity>,
111 ),
112 >,
113 )>()?;
114
115 let inverse_delta_time = simulation.inverse_delta_time();
116
117 for (position, rotation, linear_velocity, angular_velocity) in query.query(world) {
118 linear_velocity.value += position.change() * inverse_delta_time;
119
120 if let Some(rotation) = rotation
121 && let Some(velocity) = angular_velocity
122 {
123 let (angle, axis) = rotation.change().into_angle_axis();
124 velocity.value += axis * (angle * inverse_delta_time);
125 }
126 }
127
128 Ok(())
129}
130
131pub fn apply_gravity<const LOCKING: bool>(context: SystemContext) -> Result<(), Box<dyn Error>> {
132 let (world, simulation, query) = context.fetch::<(
133 &World,
134 Res<LOCKING, &PhysicsSimulation>,
135 Query<LOCKING, (Option<&Gravity>, &mut ExternalForces)>,
136 )>()?;
137
138 for (gravity, external_forces) in query.query(world) {
139 let gravity = gravity.map(|v| v.value).unwrap_or(simulation.gravity);
140 external_forces.accumulate_linear_impulse(gravity * simulation.delta_time);
141 }
142
143 Ok(())
144}
145
146pub fn dampening_solver<const LOCKING: bool>(context: SystemContext) -> Result<(), Box<dyn Error>> {
147 let (world, query) = context.fetch::<(
148 &World,
149 Query<LOCKING, (&mut Position, Option<&mut Rotation>, &ParticleMaterial)>,
150 )>()?;
151
152 for (position, rotation, material) in query.query(world) {
153 let mut delta = position.change();
154 delta *= material.linear_damping;
155 if delta.magnitude_squared()
156 < material.linear_rest_threshold * material.linear_rest_threshold
157 {
158 delta = Default::default();
159 }
160 position.current = position.previous() + delta;
161
162 if let Some(rotation) = rotation {
163 let delta = rotation.change();
164 let (mut angle, axis) = delta.into_angle_axis();
165 angle *= material.angular_damping;
166 if angle.abs() < material.angular_rest_threshold {
167 angle = Default::default();
168 }
169 let delta = quat_from_axis_angle(axis, angle);
170 rotation.current = (rotation.previous() * delta).normalized();
171 }
172 }
173
174 Ok(())
175}