use bevy::{prelude::*, time::Real};
use serde::{Deserialize, Serialize};
use super::codec::{Codec, DecodeCx, EncodeCx, Rate, Rejected};
const GLIDE_MIN: f32 = 0.03;
const GLIDE_MAX: f32 = 0.25;
const GLIDE_SPREAD: f32 = 1.75;
const GLIDE_LEARN: f32 = 0.2;
#[derive(Debug, Clone)]
pub struct TransformCodec {
pub rate: Rate,
pub smooth: bool,
}
impl Default for TransformCodec {
fn default() -> Self {
Self {
rate: Rate::Hz(20.0),
smooth: true,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct TransformWire {
pub translation: [f32; 3],
pub rotation: [f32; 4],
pub scale: [f32; 3],
}
impl From<&Transform> for TransformWire {
fn from(t: &Transform) -> Self {
Self {
translation: t.translation.to_array(),
rotation: t.rotation.to_array(),
scale: t.scale.to_array(),
}
}
}
impl From<TransformWire> for Transform {
fn from(w: TransformWire) -> Self {
Transform {
translation: Vec3::from_array(w.translation),
rotation: Quat::from_array(w.rotation).normalize(),
scale: Vec3::from_array(w.scale),
}
}
}
impl Codec for TransformCodec {
type Source = Transform;
type Wire = TransformWire;
type Target = Glide;
fn name(&self) -> &'static str {
"bevy_iroh/transform/1"
}
fn rate(&self) -> Rate {
self.rate
}
fn encode(&self, src: &Transform, _cx: &EncodeCx) -> Option<TransformWire> {
Some(src.into())
}
fn verify(&self, wire: &TransformWire) -> Result<(), Rejected> {
let finite = wire
.translation
.iter()
.chain(&wire.rotation)
.chain(&wire.scale)
.all(|f| f.is_finite());
if !finite {
return Err("transform is not finite".into());
}
if wire.rotation.iter().map(|f| f * f).sum::<f32>() < 1e-6 {
return Err("rotation is a zero quaternion".into());
}
Ok(())
}
fn decode(&self, wire: TransformWire, cx: &mut DecodeCx) -> Result<Glide, Rejected> {
let to: Transform = wire.into();
let now = cx.world.resource::<Time<Real>>().elapsed_secs_f64();
let nominal = self.rate.interval().max(0.01) as f32;
let previous = cx.world.get::<Glide>(cx.entity);
let from = cx.world.get::<Transform>(cx.entity).copied().unwrap_or(to);
let (expect, last_arrival) = match previous {
Some(g) => {
let gap = (now - g.last_arrival) as f32;
let expect = if gap.is_finite() && gap > 0.0 && gap < 2.0 {
g.expect * (1.0 - GLIDE_LEARN) + gap * GLIDE_LEARN
} else {
g.expect
};
(expect, now)
}
None => (nominal, now),
};
let over = if self.smooth {
(expect * GLIDE_SPREAD).clamp(GLIDE_MIN, GLIDE_MAX)
} else {
0.0
};
Ok(Glide {
from,
to,
progress: if previous.is_some() { 0.0 } else { 1.0 },
over,
expect,
last_arrival,
})
}
}
#[derive(Component, Debug, Clone)]
pub struct Glide {
pub from: Transform,
pub to: Transform,
progress: f32,
over: f32,
expect: f32,
last_arrival: f64,
}
impl Glide {
pub fn target(&self) -> Transform {
self.to
}
}
pub(crate) fn glide(
mut commands: Commands,
time: Res<Time>,
mut glides: Query<(Entity, Option<&mut Transform>, &mut Glide)>,
) {
let dt = time.delta_secs();
for (entity, transform, mut glide) in &mut glides {
let Some(mut transform) = transform else {
commands.entity(entity).insert(glide.to);
glide.progress = 1.0;
continue;
};
if glide.progress >= 1.0 {
continue;
}
glide.progress = if glide.over <= 0.0 {
1.0
} else {
(glide.progress + dt / glide.over).min(1.0)
};
let t = glide.progress;
transform.translation = glide.from.translation.lerp(glide.to.translation, t);
transform.rotation = glide.from.rotation.slerp(glide.to.rotation, t);
transform.scale = glide.from.scale.lerp(glide.to.scale, t);
}
}