bevy_animation_graph/nodes/
loop_node.rs

1use crate::core::animation_graph::{PinMap, TimeUpdate};
2use crate::core::animation_node::{NodeLike, ReflectNodeLike};
3use crate::core::errors::GraphError;
4use crate::core::prelude::DataSpec;
5use crate::interpolation::prelude::InterpolateLinear;
6use crate::prelude::{PassContext, SpecContext};
7use bevy::prelude::*;
8
9#[derive(Reflect, Clone, Debug, Default)]
10#[reflect(Default, NodeLike)]
11pub struct LoopNode {
12    pub interpolation_period: f32,
13}
14
15impl LoopNode {
16    pub const IN_POSE: &'static str = "pose";
17    pub const IN_TIME: &'static str = "time";
18    pub const OUT_POSE: &'static str = "pose";
19
20    pub fn new(interpolation_period: f32) -> Self {
21        Self {
22            interpolation_period,
23        }
24    }
25}
26
27impl NodeLike for LoopNode {
28    fn duration(&self, mut ctx: PassContext) -> Result<(), GraphError> {
29        ctx.set_duration_fwd(None);
30        Ok(())
31    }
32
33    fn update(&self, mut ctx: PassContext) -> Result<(), GraphError> {
34        let input = ctx.time_update_fwd()?;
35        let duration = ctx.duration_back(Self::IN_TIME)?;
36
37        let Some(duration) = duration else {
38            ctx.set_time_update_back(Self::IN_TIME, input);
39            let pose_back = ctx.data_back(Self::IN_POSE)?.into_pose()?;
40            ctx.set_time(pose_back.timestamp);
41            ctx.set_data_fwd(Self::OUT_POSE, pose_back);
42
43            return Ok(());
44        };
45
46        let full_duration = duration + self.interpolation_period;
47
48        let prev_time = ctx.prev_time();
49
50        let (curr_time, t, fw_upd) = match input {
51            TimeUpdate::Delta(dt) => {
52                let curr_time = prev_time + dt;
53                let t = curr_time.rem_euclid(full_duration);
54
55                let fw_upd =
56                    if prev_time.div_euclid(full_duration) != curr_time.div_euclid(full_duration) {
57                        TimeUpdate::Absolute(t)
58                    } else {
59                        TimeUpdate::Delta(dt)
60                    };
61
62                (curr_time, t, fw_upd)
63            }
64            TimeUpdate::Absolute(curr_time) => {
65                let t = curr_time.rem_euclid(full_duration);
66                (curr_time, t, TimeUpdate::Absolute(t))
67            }
68            TimeUpdate::PercentOfEvent { .. } => {
69                todo!("we probably want to return here and issue a warning")
70            }
71        };
72
73        ctx.set_time_update_back(Self::IN_TIME, fw_upd);
74        let mut pose = ctx.data_back(Self::IN_POSE)?.into_pose()?;
75
76        if t > duration && t < full_duration {
77            let mut ctx_temp = ctx.with_temp(true);
78            ctx_temp.set_time_update_back(Self::IN_TIME, TimeUpdate::Absolute(0.));
79            let start_pose = ctx_temp.data_back(Self::IN_POSE)?.into_pose()?;
80            // TODO: How to clear cache? time? pose?
81            // ctx.clear_temp_cache(Self::IN_POSE);
82            let old_time = pose.timestamp;
83            let alpha = (t - duration) / self.interpolation_period;
84            pose = pose.interpolate_linear(&start_pose, alpha);
85            pose.timestamp = old_time;
86        }
87
88        let t_extra = curr_time.div_euclid(full_duration) * full_duration;
89        pose.timestamp += t_extra;
90        ctx.set_time(pose.timestamp);
91        ctx.set_data_fwd(Self::OUT_POSE, pose);
92
93        Ok(())
94    }
95
96    fn data_input_spec(&self, _ctx: SpecContext) -> PinMap<DataSpec> {
97        [(Self::IN_POSE.into(), DataSpec::Pose)].into()
98    }
99
100    fn data_output_spec(&self, _ctx: SpecContext) -> PinMap<DataSpec> {
101        [(Self::OUT_POSE.into(), DataSpec::Pose)].into()
102    }
103
104    fn time_input_spec(&self, _: SpecContext) -> PinMap<()> {
105        [(Self::IN_TIME.into(), ())].into()
106    }
107
108    fn time_output_spec(&self, _: SpecContext) -> Option<()> {
109        Some(())
110    }
111
112    fn display_name(&self) -> String {
113        "🔄 Loop".into()
114    }
115}