bevy_animation_graph/nodes/
chain_node.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::core::animation_graph::{PinMap, TimeUpdate};
use crate::core::animation_node::{AnimationNode, AnimationNodeType, NodeLike};
use crate::core::errors::GraphError;
use crate::core::pose::Pose;
use crate::core::prelude::DataSpec;
use crate::prelude::{InterpolateLinear, PassContext, SpecContext};
use crate::utils::unwrap::UnwrapVal;
use bevy::prelude::*;

#[derive(Reflect, Clone, Debug, Default)]
#[reflect(Default)]
pub struct ChainNode {
    /// Time in-between animations where the output should interpolate between the last pose of the
    /// first animation and the first pose of the second
    pub interpolation_period: f32,
}

impl ChainNode {
    pub const IN_POSE_A: &'static str = "pose A";
    pub const IN_TIME_A: &'static str = "time A";
    pub const IN_POSE_B: &'static str = "pose B";
    pub const IN_TIME_B: &'static str = "time B";
    pub const OUT_POSE: &'static str = "pose";

    pub fn new(interpolation_period: f32) -> Self {
        Self {
            interpolation_period,
        }
    }

    pub fn wrapped(self, name: impl Into<String>) -> AnimationNode {
        AnimationNode::new_from_nodetype(name.into(), AnimationNodeType::Chain(self))
    }
}

impl NodeLike for ChainNode {
    fn duration(&self, mut ctx: PassContext) -> Result<(), GraphError> {
        let source_duration_1 = ctx.duration_back(Self::IN_TIME_A)?;
        let source_duration_2 = ctx.duration_back(Self::IN_TIME_B)?;

        let out_duration = match (source_duration_1, source_duration_2) {
            (Some(duration_1), Some(duration_2)) => {
                Some(duration_1 + duration_2 + self.interpolation_period)
            }
            (Some(_), None) => None,
            (None, Some(_)) => None,
            (None, None) => None,
        };

        ctx.set_duration_fwd(out_duration);
        Ok(())
    }

    fn update(&self, mut ctx: PassContext) -> Result<(), GraphError> {
        let input = ctx.time_update_fwd()?;
        let duration_1 = ctx.duration_back(Self::IN_TIME_A)?;
        let Some(duration_1) = duration_1 else {
            // First input is infinite, forward time update without change
            ctx.set_time_update_back(Self::IN_TIME_A, input);
            let pose_a: Pose = ctx.data_back(Self::IN_POSE_A)?.val();
            ctx.set_time(pose_a.timestamp);
            ctx.set_data_fwd(Self::OUT_POSE, pose_a);
            return Ok(());
        };
        ctx.set_time_update_back(Self::IN_TIME_A, input);
        let pose_a: Pose = ctx.data_back(Self::IN_POSE_A)?.val();
        let curr_time = pose_a.timestamp;
        ctx.set_time(curr_time);

        if curr_time < duration_1 {
            ctx.set_data_fwd(Self::OUT_POSE, pose_a);
        } else if curr_time - duration_1 - self.interpolation_period >= 0. {
            ctx.set_time_update_back(
                Self::IN_TIME_B,
                TimeUpdate::Absolute(curr_time - duration_1 - self.interpolation_period),
            );
            let mut pose_b: Pose = ctx.data_back(Self::IN_POSE_B)?.val();
            pose_b.timestamp = curr_time;
            ctx.set_data_fwd(Self::OUT_POSE, pose_b);
        } else {
            ctx.set_time_update_back(Self::IN_TIME_B, TimeUpdate::Absolute(0.0));
            let pose_2: Pose = ctx.data_back(Self::IN_POSE_B)?.val();
            let mut out_pose = pose_a.interpolate_linear(
                &pose_2,
                (curr_time - duration_1) / self.interpolation_period,
            );
            out_pose.timestamp = curr_time;
            ctx.set_data_fwd(Self::OUT_POSE, out_pose);
        }

        Ok(())
    }

    fn data_input_spec(&self, _ctx: SpecContext) -> PinMap<DataSpec> {
        [
            (Self::IN_POSE_A.into(), DataSpec::Pose),
            (Self::IN_POSE_B.into(), DataSpec::Pose),
        ]
        .into()
    }

    fn data_output_spec(&self, _ctx: SpecContext) -> PinMap<DataSpec> {
        [(Self::OUT_POSE.into(), DataSpec::Pose)].into()
    }

    fn time_input_spec(&self, _ctx: SpecContext) -> PinMap<()> {
        [(Self::IN_TIME_A.into(), ()), (Self::IN_TIME_B.into(), ())].into()
    }

    fn time_output_spec(&self, _: SpecContext) -> Option<()> {
        Some(())
    }

    fn display_name(&self) -> String {
        "⛓ Chain".into()
    }
}