bevy_rapier3d/plugin/configuration.rs
1//! Components used to configure a simulation run by rapier, these are not modified by bevy_rapier.
2
3use bevy::{
4 prelude::{Component, Resource},
5 reflect::Reflect,
6};
7
8use crate::math::{Real, Vect};
9
10#[cfg(doc)]
11use crate::prelude::TransformInterpolation;
12
13/// The friction model selected by [`IntegrationParameters::friction_model`].
14#[cfg(feature = "dim3")]
15pub use rapier::dynamics::FrictionModel;
16/// The low-level parameters of a context's simulation (see
17/// [`RapierContextSimulation::integration_parameters`](crate::plugin::context::RapierContextSimulation::integration_parameters)
18/// and [`RapierContextInitialization`](crate::plugin::RapierContextInitialization)).
19pub use rapier::dynamics::IntegrationParameters;
20
21/// The different ways of adjusting the timestep length each frame.
22#[derive(Copy, Clone, Debug, PartialEq, Resource)]
23#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
24pub enum TimestepMode {
25 /// Use a fixed timestep: the physics simulation will be advanced by the fixed value
26 /// `dt` seconds at each Bevy tick by performing `substeps` of length `dt / substeps`.
27 Fixed {
28 /// The physics simulation will be advanced by this total amount at each Bevy tick.
29 dt: f32,
30 /// This number of substeps of length `dt / substeps` will be performed at each Bevy tick.
31 substeps: usize,
32 },
33 /// Use a variable timestep: the physics simulation will be advanced by the variable value
34 /// `min(max_dt, Time::delta_seconds() * time_scale)` seconds at each Bevy tick. If
35 /// `time_scale > 1.0` then the simulation will appear to run faster than real-time whereas
36 /// `time_scale < 1.0` makes the simulation run in slow-motion. No step is performed on ticks
37 /// where this is zero (e.g. the first tick, or `time_scale == 0.0`).
38 Variable {
39 /// Maximum amount of time the physics simulation may be advanced at each Bevy tick.
40 max_dt: f32,
41 /// Multiplier controlling if the physics simulation should advance faster (> 1.0),
42 /// at the same speed (= 1.0) or slower (< 1.0) than the real time.
43 time_scale: f32,
44 /// The number of substeps that will be performed at each tick.
45 substeps: usize,
46 },
47 /// Use a fixed timestep equal to `IntegrationParameters::dt`, but don't step if the
48 /// physics simulation advanced by a time greater than the real-world elapsed time multiplied by `time_scale`.
49 /// Rigid-bodies with a component [`TransformInterpolation`] attached will use interpolation to
50 /// estimate the rigid-bodies position in-between steps.
51 Interpolated {
52 /// The physics simulation will be advanced by this total amount at each Bevy tick, unless
53 /// the physics simulation time is ahead of a the real time.
54 dt: f32,
55 /// Multiplier controlling if the physics simulation should advance faster (> 1.0),
56 /// at the same speed (= 1.0) or slower (< 1.0) than the real time.
57 time_scale: f32,
58 /// The number of substeps that will be performed whenever the physics simulation is advanced.
59 substeps: usize,
60 },
61}
62
63impl Default for TimestepMode {
64 fn default() -> Self {
65 TimestepMode::Variable {
66 max_dt: 1.0 / 60.0,
67 time_scale: 1.0,
68 substeps: 1,
69 }
70 }
71}
72
73/// Selects what a simulation step of a Rapier context does.
74#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Reflect)]
75#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
76pub enum SimulationMode {
77 /// Run the full physics pipeline: collision detection, constraints resolution and
78 /// integration of the rigid-bodies' motion.
79 #[default]
80 Full,
81 /// Only run collision detection with Rapier's `CollisionPipeline`.
82 ///
83 /// Collision events, contact pairs, intersections, physics hooks and scene queries keep
84 /// working, but the dynamics are not simulated: no forces, joints or contact responses are
85 /// applied and rigid-bodies only move when their `Transform` is modified (kinematic
86 /// position-based bodies are teleported to their next kinematic position). Contact force
87 /// events are never emitted in this mode.
88 CollisionOnly,
89}
90
91/// Strategy used to keep the broad-phase BVH efficient as colliders move.
92///
93/// Mirrors Rapier’s [`BvhOptimizationStrategy`](rapier::geometry::BvhOptimizationStrategy).
94#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Reflect)]
95#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
96pub enum BroadPhaseOptimizationStrategy {
97 /// Different sub-trees of the BVH are optimized at each step.
98 #[default]
99 SubtreeOptimizer,
100 /// Disables incremental BVH optimization (discouraged, meant for debugging).
101 None,
102}
103
104impl From<BroadPhaseOptimizationStrategy> for rapier::geometry::BvhOptimizationStrategy {
105 fn from(value: BroadPhaseOptimizationStrategy) -> Self {
106 match value {
107 BroadPhaseOptimizationStrategy::SubtreeOptimizer => Self::SubtreeOptimizer,
108 BroadPhaseOptimizationStrategy::None => Self::None,
109 }
110 }
111}
112
113impl From<rapier::geometry::BvhOptimizationStrategy> for BroadPhaseOptimizationStrategy {
114 fn from(value: rapier::geometry::BvhOptimizationStrategy) -> Self {
115 match value {
116 rapier::geometry::BvhOptimizationStrategy::SubtreeOptimizer => Self::SubtreeOptimizer,
117 rapier::geometry::BvhOptimizationStrategy::None => Self::None,
118 }
119 }
120}
121
122#[derive(Component, Copy, Clone, Debug, Reflect)]
123#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
124/// A component for specifying configuration information for the physics simulation
125pub struct RapierConfiguration {
126 /// Specifying the gravity of the physics simulation.
127 pub gravity: Vect,
128 /// Specifies if the physics simulation is active and update the physics world.
129 pub physics_pipeline_active: bool,
130 /// Specifies the number of subdivisions along each axes a shape should be subdivided
131 /// if its scaled representation cannot be represented with the same shape type.
132 ///
133 /// For example, a ball subject to a non-uniform scaling cannot be represented as a ball
134 /// (it’s an ellipsoid). Thus, in order to be compatible with Rapier, the shape is automatically
135 /// discretized into a convex polyhedron, using `scaled_shape_subdivision` as the number of subdivisions
136 /// along each spherical coordinates angle.
137 pub scaled_shape_subdivision: u32,
138 /// Specifies if backend sync should always accept transform changes, which may be from the writeback stage.
139 pub force_update_from_transform_changes: bool,
140 /// Whether this context simulates the full dynamics, or only detects collisions.
141 pub simulation_mode: SimulationMode,
142 /// The number of worker threads of a thread pool dedicated to this context’s simulation.
143 ///
144 /// If `None` (the default), the simulation runs on the thread pool of the calling thread,
145 /// usually Rayon’s global pool. Changes are applied before the next simulation step. This
146 /// is ignored unless the `parallel` feature is enabled (and `unsync-callbacks` is not).
147 pub num_threads: Option<usize>,
148}
149
150impl RapierConfiguration {
151 /// Configures rapier with the specified length unit.
152 ///
153 /// See the documentation of [`IntegrationParameters::length_unit`] for additional details
154 /// on that argument.
155 ///
156 /// The default gravity is automatically scaled by that length unit.
157 pub fn new(length_unit: Real) -> Self {
158 Self {
159 gravity: Vect::Y * -9.81 * length_unit,
160 physics_pipeline_active: true,
161 scaled_shape_subdivision: 10,
162 force_update_from_transform_changes: false,
163 simulation_mode: SimulationMode::Full,
164 num_threads: None,
165 }
166 }
167}