Skip to main content

avian3d/dynamics/rigid_body/forces/
plugin.rs

1use crate::{
2    dynamics::{
3        integrator::{
4            self, CustomVelocityIntegration, IntegrationSystems, VelocityIntegrationData,
5        },
6        solver::{
7            SolverDiagnostics,
8            solver_body::{SolverBodies, SolverBodyIndex},
9        },
10    },
11    prelude::*,
12};
13use bevy::prelude::*;
14
15use super::AccumulatedLocalAcceleration;
16
17/// A plugin for managing and applying external forces, torques, and accelerations for [rigid bodies](RigidBody).
18///
19/// See the [module-level documentation](crate::dynamics::rigid_body::forces) for more general information about forces in Avian.
20pub struct ForcePlugin;
21
22impl Plugin for ForcePlugin {
23    fn build(&self, app: &mut App) {
24        // Set up system sets.
25        app.configure_sets(
26            PhysicsSchedule,
27            (
28                ForceSystems::ApplyConstantForces
29                    .in_set(IntegrationSystems::UpdateVelocityIncrements)
30                    .before(integrator::pre_process_velocity_increments),
31                ForceSystems::Clear.in_set(SolverSystems::PostSubstep),
32            ),
33        );
34        app.configure_sets(
35            SubstepSchedule,
36            ForceSystems::ApplyLocalAcceleration
37                .in_set(IntegrationSystems::Velocity)
38                .before(integrator::integrate_velocities),
39        );
40
41        // Accumulate constant forces, torques, and accelerations.
42        app.add_systems(
43            PhysicsSchedule,
44            (
45                apply_constant_forces,
46                apply_constant_torques,
47                apply_constant_linear_acceleration,
48                apply_constant_angular_acceleration,
49                apply_constant_local_forces,
50                #[cfg(feature = "3d")]
51                apply_constant_local_torques,
52                apply_constant_local_linear_acceleration,
53                #[cfg(feature = "3d")]
54                apply_constant_local_angular_acceleration,
55            )
56                .chain()
57                .in_set(ForceSystems::ApplyConstantForces),
58        );
59
60        // Apply local forces and accelerations.
61        // This is done in the substepping loop, because the orientations of bodies can change between substeps.
62        app.add_systems(
63            SubstepSchedule,
64            apply_local_acceleration.in_set(ForceSystems::ApplyLocalAcceleration),
65        );
66
67        // Clear accumulated forces and accelerations.
68        app.add_systems(
69            PhysicsSchedule,
70            clear_accumulated_local_acceleration.in_set(ForceSystems::Clear),
71        );
72    }
73}
74
75/// System sets for managing and applying forces, torques, and accelerations for [rigid bodies](RigidBody).
76#[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
77pub enum ForceSystems {
78    /// Adds [`ConstantForce`], [`ConstantTorque`], [`ConstantLinearAcceleration`], and [`ConstantAngularAcceleration`]
79    #[cfg_attr(
80        feature = "2d",
81        doc = "to [`VelocityIntegrationData`], and [`ConstantLocalForce`] and [`ConstantLocalLinearAcceleration`]"
82    )]
83    #[cfg_attr(
84        feature = "3d",
85        doc = "to [`VelocityIntegrationData`], and [`ConstantLocalForce`], [`ConstantLocalTorque`], [`ConstantLocalLinearAcceleration`], and [`ConstantLocalAngularAcceleration`]"
86    )]
87    /// to [`AccumulatedLocalAcceleration`].
88    ApplyConstantForces,
89    /// Applies [`AccumulatedLocalAcceleration`] to the linear and angular velocities of bodies.
90    ApplyLocalAcceleration,
91    /// Clears [`AccumulatedLocalAcceleration`] for all bodies.
92    Clear,
93}
94
95/// Applies [`ConstantForce`] to the accumulated forces.
96fn apply_constant_forces(
97    mut bodies: Query<(&mut VelocityIntegrationData, &ComputedMass, &ConstantForce)>,
98) {
99    bodies
100        .iter_mut()
101        .for_each(|(mut integration, mass, constant_force)| {
102            integration.linear_increment += mass.inverse() * constant_force.0;
103        })
104}
105
106/// Applies [`ConstantTorque`] to the accumulated torques.
107fn apply_constant_torques(
108    solver_bodies: Res<SolverBodies>,
109    mut bodies: Query<(
110        &SolverBodyIndex,
111        &mut VelocityIntegrationData,
112        &ConstantTorque,
113    )>,
114) {
115    bodies
116        .iter_mut()
117        .for_each(|(index, mut integration, constant_torque)| {
118            let Some(inertia) = solver_bodies.get_inertia(*index) else {
119                return;
120            };
121            integration.angular_increment +=
122                inertia.effective_inv_angular_inertia() * constant_torque.0;
123        })
124}
125
126/// Applies [`ConstantLinearAcceleration`] to the linear velocity increments.
127fn apply_constant_linear_acceleration(
128    mut bodies: Query<(&mut VelocityIntegrationData, &ConstantLinearAcceleration)>,
129) {
130    bodies
131        .iter_mut()
132        .for_each(|(mut integration, constant_acceleration)| {
133            integration.linear_increment += constant_acceleration.0;
134        })
135}
136
137/// Applies [`ConstantAngularAcceleration`] to the angular velocity increments.
138fn apply_constant_angular_acceleration(
139    mut bodies: Query<(&mut VelocityIntegrationData, &ConstantAngularAcceleration)>,
140) {
141    bodies
142        .iter_mut()
143        .for_each(|(mut integration, constant_acceleration)| {
144            integration.angular_increment += constant_acceleration.0;
145        })
146}
147
148/// Applies [`ConstantLocalForce`] to the accumulated forces.
149fn apply_constant_local_forces(
150    mut bodies: Query<(
151        &mut AccumulatedLocalAcceleration,
152        &ComputedMass,
153        &ConstantLocalForce,
154    )>,
155) {
156    bodies
157        .iter_mut()
158        .for_each(|(mut acceleration, mass, constant_force)| {
159            acceleration.linear += mass.inverse() * constant_force.0;
160        })
161}
162
163/// Applies [`ConstantLocalTorque`] to the accumulated torques.
164#[cfg(feature = "3d")]
165fn apply_constant_local_torques(
166    mut bodies: Query<(
167        &mut AccumulatedLocalAcceleration,
168        &ComputedAngularInertia,
169        &ConstantLocalTorque,
170    )>,
171) {
172    bodies
173        .iter_mut()
174        .for_each(|(mut acceleration, angular_inertia, constant_torque)| {
175            acceleration.angular += angular_inertia.inverse() * constant_torque.0;
176        })
177}
178
179/// Applies [`ConstantLocalLinearAcceleration`] to the accumulated local acceleration.
180fn apply_constant_local_linear_acceleration(
181    mut bodies: Query<(
182        &mut AccumulatedLocalAcceleration,
183        &ConstantLocalLinearAcceleration,
184    )>,
185) {
186    bodies
187        .iter_mut()
188        .for_each(|(mut acceleration, constant_acceleration)| {
189            acceleration.linear += constant_acceleration.0;
190        })
191}
192
193/// Applies [`ConstantLocalAngularAcceleration`] to the accumulated local acceleration.
194#[cfg(feature = "3d")]
195fn apply_constant_local_angular_acceleration(
196    mut bodies: Query<(
197        &mut AccumulatedLocalAcceleration,
198        &ConstantLocalAngularAcceleration,
199    )>,
200) {
201    bodies
202        .iter_mut()
203        .for_each(|(mut acceleration, constant_acceleration)| {
204            acceleration.angular += constant_acceleration.0;
205        })
206}
207
208/// Applies [`AccumulatedLocalAcceleration`] to the linear and angular velocity of bodies.
209///
210/// This should run in the substepping loop, just before [`IntegrationSystems::Velocity`].
211fn apply_local_acceleration(
212    mut solver_bodies: ResMut<SolverBodies>,
213    bodies: Query<
214        (&SolverBodyIndex, &AccumulatedLocalAcceleration, &Rotation),
215        Without<CustomVelocityIntegration>,
216    >,
217    mut diagnostics: ResMut<SolverDiagnostics>,
218    time: Res<Time<Substeps>>,
219) {
220    let start = crate::utils::Instant::now();
221
222    let delta_secs = time.delta_secs();
223
224    let access = solver_bodies.access();
225
226    bodies.iter().for_each(|(index, acceleration, rotation)| {
227        // SAFETY: Each entity has a unique solver body index, so the accessed bodies are disjoint.
228        let body = unsafe { access.body_unchecked_mut(*index) };
229
230        let rotation = body.delta_rotation * Rot::from(*rotation);
231        let locked_axes = body.flags.locked_axes();
232
233        // Compute the world space velocity increments with locked axes applied.
234        let world_linear_acceleration = locked_axes.apply_to_vec(rotation * acceleration.linear);
235        #[cfg(feature = "3d")]
236        let world_angular_acceleration = locked_axes.apply_to_vec(rotation * acceleration.angular);
237
238        // Apply acceleration.
239        body.linear_velocity += world_linear_acceleration * delta_secs;
240        #[cfg(feature = "3d")]
241        {
242            body.angular_velocity += world_angular_acceleration * delta_secs;
243        }
244    });
245
246    diagnostics.integrate_velocities += start.elapsed();
247}
248
249fn clear_accumulated_local_acceleration(
250    mut query: Query<&mut AccumulatedLocalAcceleration, Changed<AccumulatedLocalAcceleration>>,
251) {
252    for mut acceleration in &mut query {
253        // Change detection is bypassed here so that clearing does not mark the component
254        // as changed, which would cause the system to run again the next step.
255        let acceleration = acceleration.bypass_change_detection();
256        acceleration.linear = Vector::ZERO;
257        #[cfg(feature = "3d")]
258        {
259            acceleration.angular = AngularVector::ZERO;
260        }
261    }
262}