use bevy::prelude::*;
#[cfg(feature = "2d")]
use bevy_xpbd_2d::{math::PI, prelude::*};
#[cfg(feature = "3d")]
use bevy_xpbd_3d::prelude::*;
pub mod plugin;
pub mod prelude;
#[derive(SystemSet, Debug, PartialEq, Eq, Clone, Hash)]
pub struct InterpolationCopySet;
#[derive(SystemSet, Debug, PartialEq, Eq, Clone, Hash)]
pub enum InterpolationSet {
Interpolation,
PostInterpolation,
}
#[cfg(feature = "3d")]
#[derive(Component)]
pub struct InterpolatedPosition {
pub source: Entity,
pub previous_position: Option<Vec3>,
pub pass_raw: bool,
}
#[cfg(feature = "2d")]
#[derive(Component)]
pub struct InterpolatedPosition {
pub source: Entity,
pub previous_position: Option<Vec2>,
pub pass_raw: bool,
}
impl InterpolatedPosition {
pub fn from_source(source: Entity) -> Self {
Self {
source,
previous_position: None,
pass_raw: false,
}
}
}
#[cfg(feature = "3d")]
#[derive(Component)]
pub struct InterpolatedRotation {
pub source: Entity,
pub previous_rotation: Option<Quat>,
pub pass_raw: bool,
}
#[cfg(feature = "2d")]
#[derive(Component)]
pub struct InterpolatedRotation {
pub source: Entity,
pub previous_rotation: Option<f32>,
pub pass_raw: bool,
}
impl InterpolatedRotation {
pub fn from_source(source: Entity) -> Self {
Self {
source,
previous_rotation: None,
pass_raw: false,
}
}
}
fn copy_position(
mut interp_position_q: Query<&mut InterpolatedPosition>,
source_position_q: Query<&Position>,
) {
for mut interp in interp_position_q.iter_mut() {
let position = source_position_q.get(interp.source).unwrap();
interp.previous_position = Some(position.0);
}
}
fn copy_rotation(
mut interp_rotation_q: Query<&mut InterpolatedRotation>,
source_rotation_q: Query<&Rotation>,
) {
#[cfg(feature = "2d")]
for mut interp in interp_rotation_q.iter_mut() {
let rotation = source_rotation_q.get(interp.source).unwrap();
interp.previous_rotation = Some(rotation.as_radians());
}
#[cfg(feature = "3d")]
for mut interp in interp_rotation_q.iter_mut() {
let rotation = source_rotation_q.get(interp.source).unwrap();
interp.previous_rotation = Some(rotation.0);
}
}
fn interpolate_position(
mut interp_q: Query<(&mut Transform, &InterpolatedPosition)>,
source_q: Query<&Position>,
phys_time: Res<Time<Physics>>,
) {
let (delta, overstep) = match phys_time.timestep_mode() {
TimestepMode::Fixed {
delta, overstep, ..
} => (delta.as_secs_f32(), overstep.as_secs_f32()),
_ => panic!(
"The 'PhysicsTimestep' resource does not hold the 'Fixed' variant. Cannot interpolate."
),
};
for (mut transform, interp_position) in interp_q.iter_mut() {
let current_position = match source_q.get(interp_position.source) {
Ok(val) => val,
Err(_) => {
warn!("Invalid source entity for InterpolatedPosition. The source entity must have a position component.");
continue;
}
};
#[cfg(feature = "2d")]
if interp_position.pass_raw || interp_position.previous_position == None {
transform.translation = Vec3::new(current_position.0.x, current_position.0.y, 0.0);
} else {
let lerp_factor = overstep / delta;
if let Some(previous_position) = interp_position.previous_position {
let interp = previous_position.lerp(current_position.0, lerp_factor);
transform.translation = Vec3::new(interp.x, interp.y, 0.0);
}
}
#[cfg(feature = "3d")]
if interp_position.pass_raw || interp_position.previous_position == None {
transform.translation = Vec3::new(current_position.0.x, current_position.0.y, 0.0);
} else {
let lerp_factor = overstep / delta;
if let Some(previous_position) = interp_position.previous_position {
transform.translation = previous_position.lerp(current_position.0, lerp_factor);
}
}
}
}
fn interpolate_rotation(
mut interp_q: Query<(&mut Transform, &InterpolatedRotation)>,
source_q: Query<&Rotation>,
phys_time: Res<Time<Physics>>,
) {
let (delta, overstep) = match phys_time.timestep_mode() {
TimestepMode::Fixed {
delta, overstep, ..
} => (delta.as_secs_f32(), overstep.as_secs_f32()),
_ => panic!(
"The 'PhysicsTimestep' resource does not hold the 'Fixed' variant. Cannot interpolate."
),
};
for (mut transform, interp_rotation) in interp_q.iter_mut() {
let current_rotation = match source_q.get(interp_rotation.source) {
Ok(val) => val,
Err(_) => {
warn!("Invalid source entity for InterpolatedRotation. The source entity must have a Rotation component.");
continue;
}
};
#[cfg(feature = "2d")]
if interp_rotation.pass_raw || interp_rotation.previous_rotation == None {
transform.rotation = Quat::from(*current_rotation);
} else {
let lerp_factor = overstep / delta;
if let Some(previous_rotation) = interp_rotation.previous_rotation {
let delta = current_rotation.as_radians() - previous_rotation;
let interpolated_angle = if delta.abs() <= PI {
previous_rotation + lerp_factor * delta
} else {
previous_rotation
- (delta / delta.abs()) * lerp_factor * (2.0 * PI - delta.abs())
};
transform.rotation = Quat::from(Rotation::from_radians(interpolated_angle));
}
}
#[cfg(feature = "3d")]
if interp_rotation.pass_raw || interp_rotation.previous_rotation == None {
transform.rotation = current_rotation.0;
} else {
let lerp_factor = overstep / delta;
if let Some(previous_rotation) = interp_rotation.previous_rotation {
transform.rotation = previous_rotation.slerp(current_rotation.0, lerp_factor);
}
}
}
}