Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix interpolation and systems schedule #474

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
1 change: 0 additions & 1 deletion src/pipeline/query_filter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use bevy::prelude::Entity;

pub use rapier::geometry::InteractionGroups;
pub use rapier::pipeline::QueryFilterFlags;

use crate::geometry::CollisionGroups;
Expand Down
57 changes: 3 additions & 54 deletions src/plugin/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,6 @@ use bevy::prelude::Resource;

use crate::math::Vect;

/// Difference between simulation and rendering time
#[derive(Resource, Default)]
pub struct SimulationToRenderTime {
/// Difference between simulation and rendering time
pub diff: f32,
}

/// The different ways of adjusting the timestep length.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TimestepMode {
/// Use a fixed timestep: the physics simulation will be advanced by the fixed value
/// `dt` seconds at each Bevy tick by performing `substeps` of length `dt / substeps`.
Fixed {
/// The physics simulation will be advanced by this total amount at each Bevy tick.
dt: f32,
/// This number of substeps of length `dt / substeps` will be performed at each Bevy tick.
substeps: usize,
},
/// Use a variable timestep: the physics simulation will be advanced by the variable value
/// `min(max_dt, Time::delta_seconds() * time_scale)` seconds at each Bevy tick. If
/// `time_scale > 1.0` then the simulation will appear to run faster than real-time whereas
/// `time_scale < 1.0` makes the simulation run in slow-motion.
Variable {
/// Maximum amount of time the physics simulation may be advanced at each Bevy tick.
max_dt: f32,
/// Multiplier controlling if the physics simulation should advance faster (> 1.0),
/// at the same speed (= 1.0) or slower (< 1.0) than the real time.
time_scale: f32,
/// The number of substeps that will be performed at each tick.
substeps: usize,
},
/// Use a fixed timestep equal to `IntegrationParameters::dt`, but don't step if the
/// physics simulation advanced by a time greater than the real-world elapsed time multiplied by `time_scale`.
/// Rigid-bodies with a component `InterpolatedTransform` attached will use interpolation to
/// estimate the rigid-bodies position in-between steps.
Interpolated {
/// The physics simulation will be advanced by this total amount at each Bevy tick, unless
/// the physics simulation time is ahead of a the real time.
dt: f32,
/// Multiplier controlling if the physics simulation should advance faster (> 1.0),
/// at the same speed (= 1.0) or slower (< 1.0) than the real time.
time_scale: f32,
/// The number of substeps that will be performed whenever the physics simulation is advanced.
substeps: usize,
},
}

#[derive(Resource, Copy, Clone, Debug)]
/// A resource for specifying configuration information for the physics simulation
pub struct RapierConfiguration {
Expand All @@ -58,8 +11,6 @@ pub struct RapierConfiguration {
pub physics_pipeline_active: bool,
/// Specifies if the query pipeline is active and update the query pipeline.
pub query_pipeline_active: bool,
/// Specifies the way the timestep length should be adjusted at each frame.
pub timestep_mode: TimestepMode,
/// Specifies the number of subdivisions along each axes a shape should be subdivided
/// if its scaled representation cannot be represented with the same shape type.
///
Expand All @@ -70,6 +21,8 @@ pub struct RapierConfiguration {
pub scaled_shape_subdivision: u32,
/// Specifies if backend sync should always accept tranform changes, which may be from the writeback stage.
pub force_update_from_transform_changes: bool,
/// The number of substeps that will be performed at each tick.
pub substeps: usize,
}

impl Default for RapierConfiguration {
Expand All @@ -81,13 +34,9 @@ impl Default for RapierConfiguration {
gravity: Vect::Y * -9.81,
physics_pipeline_active: true,
query_pipeline_active: true,
timestep_mode: TimestepMode::Variable {
max_dt: 1.0 / 60.0,
time_scale: 1.0,
substeps: 1,
},
scaled_shape_subdivision: 10,
force_update_from_transform_changes: false,
substeps: 1,
}
}
}
138 changes: 30 additions & 108 deletions src/plugin/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use bevy::prelude::{Entity, EventWriter, GlobalTransform, Query};

use crate::control::{CharacterCollision, MoveShapeOptions, MoveShapeOutput};
use crate::dynamics::TransformInterpolation;
use crate::plugin::configuration::{SimulationToRenderTime, TimestepMode};
use crate::prelude::{CollisionGroups, RapierRigidBodyHandle};
use rapier::control::CharacterAutostep;

Expand Down Expand Up @@ -208,14 +207,11 @@ impl RapierContext {
pub fn step_simulation(
&mut self,
gravity: Vect,
timestep_mode: TimestepMode,
events: Option<(EventWriter<CollisionEvent>, EventWriter<ContactForceEvent>)>,
hooks: &dyn PhysicsHooks,
time: &Time,
sim_to_render_time: &mut SimulationToRenderTime,
mut interpolation_query: Option<
Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
>,
delta_time: f32,
substeps: usize,
mut interpolation_query: Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
) {
let event_queue = events.map(|(ce, fe)| EventQueue {
deleted_colliders: &self.deleted_colliders,
Expand All @@ -229,108 +225,34 @@ impl RapierContext {
.or_else(|| event_queue.as_ref().map(|q| q as &dyn EventHandler))
.unwrap_or(&() as &dyn EventHandler);

match timestep_mode {
TimestepMode::Interpolated {
dt,
time_scale,
substeps,
} => {
self.integration_parameters.dt = dt;

sim_to_render_time.diff += time.delta_seconds();

while sim_to_render_time.diff > 0.0 {
// NOTE: in this comparison we do the same computations we
// will do for the next `while` iteration test, to make sure we
// don't get bit by potential float inaccuracy.
if sim_to_render_time.diff - dt <= 0.0 {
if let Some(interpolation_query) = interpolation_query.as_mut() {
// This is the last simulation step to be executed in the loop
// Update the previous state transforms
for (handle, mut interpolation) in interpolation_query.iter_mut() {
if let Some(body) = self.bodies.get(handle.0) {
interpolation.start = Some(*body.position());
interpolation.end = None;
}
}
}
}

let mut substep_integration_parameters = self.integration_parameters;
substep_integration_parameters.dt = dt / (substeps as Real) * time_scale;

for _ in 0..substeps {
self.pipeline.step(
&(gravity / self.physics_scale).into(),
&substep_integration_parameters,
&mut self.islands,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.bodies,
&mut self.colliders,
&mut self.impulse_joints,
&mut self.multibody_joints,
&mut self.ccd_solver,
None,
hooks,
events,
);
}
self.integration_parameters.dt = delta_time;

let mut substep_integration_parameters = self.integration_parameters;
substep_integration_parameters.dt = delta_time / substeps as Real;

for _ in 0..substeps {
self.pipeline.step(
&(gravity / self.physics_scale).into(),
&substep_integration_parameters,
&mut self.islands,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.bodies,
&mut self.colliders,
&mut self.impulse_joints,
&mut self.multibody_joints,
&mut self.ccd_solver,
None,
hooks,
events,
);
}

sim_to_render_time.diff -= dt;
}
}
TimestepMode::Variable {
max_dt,
time_scale,
substeps,
} => {
self.integration_parameters.dt = (time.delta_seconds() * time_scale).min(max_dt);

let mut substep_integration_parameters = self.integration_parameters;
substep_integration_parameters.dt /= substeps as Real;

for _ in 0..substeps {
self.pipeline.step(
&(gravity / self.physics_scale).into(),
&substep_integration_parameters,
&mut self.islands,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.bodies,
&mut self.colliders,
&mut self.impulse_joints,
&mut self.multibody_joints,
&mut self.ccd_solver,
None,
hooks,
events,
);
}
}
TimestepMode::Fixed { dt, substeps } => {
self.integration_parameters.dt = dt;

let mut substep_integration_parameters = self.integration_parameters;
substep_integration_parameters.dt = dt / (substeps as Real);

for _ in 0..substeps {
self.pipeline.step(
&(gravity / self.physics_scale).into(),
&substep_integration_parameters,
&mut self.islands,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.bodies,
&mut self.colliders,
&mut self.impulse_joints,
&mut self.multibody_joints,
&mut self.ccd_solver,
None,
hooks,
events,
);
}
// NOTE: Update the interpolation data must be after all substeps.
for (handle, mut interpolation) in interpolation_query.iter_mut() {
if let Some(body) = self.bodies.get(handle.0) {
interpolation.start = interpolation.end;
interpolation.end = Some(*body.position());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub use self::configuration::{RapierConfiguration, SimulationToRenderTime, TimestepMode};
pub use self::configuration::RapierConfiguration;
pub use self::context::RapierContext;
pub use self::plugin::{NoUserData, PhysicsSet, RapierPhysicsPlugin, RapierTransformPropagateSet};

Expand Down
Loading