1use crate::prelude::*;
6use bevy::prelude::*;
7
8pub struct IntegratorPlugin;
17
18impl Plugin for IntegratorPlugin {
19 fn build(&self, app: &mut App) {
20 app.get_schedule_mut(SubstepSchedule)
21 .expect("add SubstepSchedule first")
22 .add_systems((integrate_pos, integrate_rot).in_set(SubstepSet::Integrate));
23 app.get_schedule_mut(PhysicsSchedule)
24 .expect("add PhysicsSchedule first")
25 .add_systems(
26 apply_impulses
27 .after(PhysicsStepSet::BroadPhase)
28 .before(PhysicsStepSet::Substeps),
29 )
30 .add_systems(clear_forces_and_impulses.after(PhysicsStepSet::SpatialQuery));
31 }
32}
33
34type PosIntegrationComponents = (
35 &'static RigidBody,
36 &'static Position,
37 &'static mut PreviousPosition,
38 &'static mut AccumulatedTranslation,
39 &'static mut LinearVelocity,
40 Option<&'static LinearDamping>,
41 Option<&'static GravityScale>,
42 &'static ExternalForce,
43 &'static Mass,
44 &'static InverseMass,
45 Option<&'static LockedAxes>,
46);
47
48fn integrate_pos(
51 mut bodies: Query<PosIntegrationComponents, Without<Sleeping>>,
52 gravity: Res<Gravity>,
53 time: Res<Time>,
54) {
55 let delta_secs = time.delta_seconds_adjusted();
56
57 for (
58 rb,
59 pos,
60 mut prev_pos,
61 mut translation,
62 mut lin_vel,
63 lin_damping,
64 gravity_scale,
65 external_force,
66 mass,
67 inv_mass,
68 locked_axes,
69 ) in &mut bodies
70 {
71 prev_pos.0 = pos.0;
72
73 if rb.is_static() {
74 continue;
75 }
76
77 let locked_axes = locked_axes.map_or(LockedAxes::default(), |locked_axes| *locked_axes);
78
79 if rb.is_dynamic() {
81 if let Some(damping) = lin_damping {
83 lin_vel.0 *= 1.0 / (1.0 + delta_secs * damping.0);
84 }
85
86 let effective_mass = locked_axes.apply_to_vec(Vector::splat(mass.0));
87 let effective_inv_mass = locked_axes.apply_to_vec(Vector::splat(inv_mass.0));
88
89 let gravitation_force =
91 effective_mass * gravity.0 * gravity_scale.map_or(1.0, |scale| scale.0);
92 let external_forces = gravitation_force + external_force.force();
93 let delta_lin_vel = delta_secs * external_forces * effective_inv_mass;
94 if delta_lin_vel != Vector::ZERO {
96 lin_vel.0 += delta_lin_vel;
97 }
98 }
99 if lin_vel.0 != Vector::ZERO {
100 translation.0 += locked_axes.apply_to_vec(delta_secs * lin_vel.0);
101 }
102 }
103}
104
105type RotIntegrationComponents = (
106 &'static RigidBody,
107 &'static mut Rotation,
108 &'static mut PreviousRotation,
109 &'static mut AngularVelocity,
110 Option<&'static AngularDamping>,
111 &'static ExternalForce,
112 &'static ExternalTorque,
113 &'static Inertia,
114 &'static InverseInertia,
115 Option<&'static LockedAxes>,
116);
117
118#[cfg(feature = "2d")]
121fn integrate_rot(mut bodies: Query<RotIntegrationComponents, Without<Sleeping>>, time: Res<Time>) {
122 let delta_secs = time.delta_seconds_adjusted();
123
124 for (
125 rb,
126 mut rot,
127 mut prev_rot,
128 mut ang_vel,
129 ang_damping,
130 external_force,
131 external_torque,
132 _inertia,
133 inv_inertia,
134 locked_axes,
135 ) in &mut bodies
136 {
137 prev_rot.0 = *rot;
138
139 if rb.is_static() {
140 continue;
141 }
142
143 let locked_axes = locked_axes.map_or(LockedAxes::default(), |locked_axes| *locked_axes);
144
145 if rb.is_dynamic() {
147 if let Some(damping) = ang_damping {
149 if ang_vel.0 != 0.0 && damping.0 != 0.0 {
151 ang_vel.0 *= 1.0 / (1.0 + delta_secs * damping.0);
152 }
153 }
154
155 let effective_inv_inertia = locked_axes.apply_to_rotation(inv_inertia.0);
156
157 let delta_ang_vel = delta_secs
159 * effective_inv_inertia
160 * (external_torque.torque() + external_force.torque());
161 if delta_ang_vel != 0.0 {
163 ang_vel.0 += delta_ang_vel;
164 }
165 }
166 let delta = locked_axes.apply_to_angular_velocity(delta_secs * ang_vel.0);
168 if delta != 0.0 {
169 *rot += Rotation::from_radians(delta);
170 }
171 }
172}
173
174#[cfg(feature = "3d")]
177fn integrate_rot(mut bodies: Query<RotIntegrationComponents, Without<Sleeping>>, time: Res<Time>) {
178 let delta_secs = time.delta_seconds_adjusted();
179
180 for (
181 rb,
182 mut rot,
183 mut prev_rot,
184 mut ang_vel,
185 ang_damping,
186 external_force,
187 external_torque,
188 inertia,
189 inv_inertia,
190 locked_axes,
191 ) in &mut bodies
192 {
193 prev_rot.0 = *rot;
194
195 if rb.is_static() {
196 continue;
197 }
198
199 let locked_axes = locked_axes.map_or(LockedAxes::default(), |locked_axes| *locked_axes);
200
201 if rb.is_dynamic() {
203 if let Some(damping) = ang_damping {
205 if ang_vel.0 != Vector::ZERO && damping.0 != 0.0 {
207 ang_vel.0 *= 1.0 / (1.0 + delta_secs * damping.0);
208 }
209 }
210
211 let effective_inertia = locked_axes.apply_to_rotation(inertia.rotated(&rot).0);
212 let effective_inv_inertia = locked_axes.apply_to_rotation(inv_inertia.rotated(&rot).0);
213
214 let delta_ang_vel = delta_secs
216 * effective_inv_inertia
217 * ((external_torque.torque() + external_force.torque())
218 - ang_vel.0.cross(effective_inertia * ang_vel.0));
219 if delta_ang_vel != Vector::ZERO {
221 ang_vel.0 += delta_ang_vel;
222 }
223 }
224
225 let q = Quaternion::from_vec4(ang_vel.0.extend(0.0)) * rot.0;
226 let effective_dq = locked_axes
227 .apply_to_angular_velocity(delta_secs * 0.5 * q.xyz())
228 .extend(delta_secs * 0.5 * q.w);
229 let delta = Quaternion::from_vec4(effective_dq);
231 if delta != Quaternion::from_xyzw(0.0, 0.0, 0.0, 0.0) {
232 rot.0 = (rot.0 + delta).normalize();
233 }
234 }
235}
236
237type ImpulseQueryComponents = (
238 &'static RigidBody,
239 &'static mut ExternalImpulse,
240 &'static mut ExternalAngularImpulse,
241 &'static mut LinearVelocity,
242 &'static mut AngularVelocity,
243 &'static Rotation,
244 &'static InverseMass,
245 &'static InverseInertia,
246 Option<&'static LockedAxes>,
247);
248
249fn apply_impulses(mut bodies: Query<ImpulseQueryComponents, Without<Sleeping>>) {
250 for (
251 rb,
252 impulse,
253 ang_impulse,
254 mut lin_vel,
255 mut ang_vel,
256 rotation,
257 inv_mass,
258 inv_inertia,
259 locked_axes,
260 ) in &mut bodies
261 {
262 if !rb.is_dynamic() {
263 continue;
264 }
265
266 let locked_axes = locked_axes.map_or(LockedAxes::default(), |locked_axes| *locked_axes);
267
268 let effective_inv_mass = locked_axes.apply_to_vec(Vector::splat(inv_mass.0));
269 let effective_inv_inertia = locked_axes.apply_to_rotation(inv_inertia.rotated(rotation).0);
270
271 let delta_lin_vel = impulse.impulse() * effective_inv_mass;
273 let delta_ang_vel =
274 effective_inv_inertia * (ang_impulse.impulse() + impulse.angular_impulse());
275
276 if delta_lin_vel != Vector::ZERO {
277 lin_vel.0 += delta_lin_vel;
278 }
279 if delta_ang_vel != AngularVelocity::ZERO.0 {
280 ang_vel.0 += delta_ang_vel;
281 }
282 }
283}
284
285type ForceComponents = (
286 &'static mut ExternalForce,
287 &'static mut ExternalTorque,
288 &'static mut ExternalImpulse,
289 &'static mut ExternalAngularImpulse,
290);
291type ForceComponentsChanged = Or<(
292 Changed<ExternalForce>,
293 Changed<ExternalTorque>,
294 Changed<ExternalImpulse>,
295 Changed<ExternalAngularImpulse>,
296)>;
297
298pub fn clear_forces_and_impulses(mut forces: Query<ForceComponents, ForceComponentsChanged>) {
302 for (mut force, mut torque, mut impulse, mut angular_ímpulse) in &mut forces {
303 if !force.persistent {
304 force.clear();
305 }
306 if !torque.persistent {
307 torque.clear();
308 }
309 if !impulse.persistent {
310 impulse.clear();
311 }
312 if !angular_ímpulse.persistent {
313 angular_ímpulse.clear();
314 }
315 }
316}