lumen_engine/node/processing/
time_remap.rs1use crate::node::{NodeId, NodeParamEvalContext, NodeParams, PortRef};
2
3use crate::gpu::{BoundFrame, CompiledOutput, FrameBindContext, GpuCompileNode, GpuCompiledNode};
4
5#[derive(Debug, Clone, Default, lumen_macros::Delegate)]
7pub struct TimeRemapParams {
8 #[meta(min = 0, step = 1)]
10 pub frame: f64,
11 #[meta()]
13 pub loop_enabled: bool,
14 #[meta(min = 0, step = 1)]
16 pub loop_start: i64,
17 #[meta(min = 0, step = 1)]
19 pub loop_end: i64,
20}
21
22#[derive(Debug, Clone, lumen_macros::Node)]
24#[node(kind = "time_remap", name = "Time Remap", category = "processing")]
25pub struct TimeRemap {
26 pub id: NodeId,
27 #[params]
28 pub params: TimeRemapParamsDelegate,
29
30 #[input()]
31 pub source: PortRef,
32}
33
34#[derive(Debug, Clone, Copy)]
35pub struct TimeRemapSettings {
36 pub frame: f64,
37 pub loop_enabled: bool,
38 pub loop_start: i64,
39 pub loop_end: i64,
40}
41
42impl Default for TimeRemap {
43 fn default() -> Self {
44 Self {
45 id: NodeId::new(0),
46 params: TimeRemapParamsDelegate::default(),
47 source: PortRef::empty(),
48 }
49 }
50}
51
52pub fn remap_frame(settings: TimeRemapSettings) -> u32 {
53 let mut frame = settings.frame.max(0.0).floor() as i64;
54 if settings.loop_enabled && settings.loop_end > settings.loop_start {
55 let span = settings.loop_end - settings.loop_start;
56 frame = settings.loop_start + (frame - settings.loop_start).rem_euclid(span);
57 }
58 frame.max(0) as u32
59}
60
61impl GpuCompileNode for TimeRemap {
62 fn compile_gpu(
63 &self,
64 ctx: &mut crate::gpu::CompileContext<'_>,
65 port: &PortRef,
66 ) -> crate::Result<CompiledOutput> {
67 if port.port != "output" {
68 return Err(ctx.missing_output(self.id, &port.port));
69 }
70 ctx.register_compiled_node(CompiledTimeRemap {
71 node_id: self.id,
72 params: self.params.clone(),
73 });
74 let params = self.params.eval(&NodeParamEvalContext {
75 node_id: self.id,
76 expr: &ctx.expr_context(self.id, "params"),
77 })?;
78 let target_frame = remap_frame(TimeRemapSettings {
79 frame: params.frame,
80 loop_enabled: params.loop_enabled,
81 loop_start: params.loop_start,
82 loop_end: params.loop_end,
83 });
84 ctx.with_frame_context(target_frame, |ctx| ctx.compile_port(&self.source))
85 }
86}
87
88#[derive(Debug, Clone)]
89struct CompiledTimeRemap {
90 node_id: NodeId,
91 params: TimeRemapParamsDelegate,
92}
93
94impl GpuCompiledNode for CompiledTimeRemap {
95 fn node_id(&self) -> NodeId {
96 self.node_id
97 }
98
99 fn bind(&self, ctx: &FrameBindContext<'_>, _bound: &mut BoundFrame) -> crate::Result<()> {
100 let evaluated = self.params.eval(&NodeParamEvalContext {
101 node_id: self.node_id,
102 expr: &ctx.expr_context(self.node_id, "params"),
103 })?;
104 let _ = remap_frame(TimeRemapSettings {
105 frame: evaluated.frame,
106 loop_enabled: evaluated.loop_enabled,
107 loop_start: evaluated.loop_start,
108 loop_end: evaluated.loop_end,
109 });
110 Ok(())
111 }
112}