Skip to main content

lumen_engine/node/compositing/
switch.rs

1use crate::node::{NodeId, NodeParams, PortRef};
2
3use crate::gpu::{BoundFrame, CompiledOutput, FrameBindContext, GpuCompileNode, GpuCompiledNode};
4
5/// Selects one raster input according to a controlled layer index.
6#[derive(Debug, Clone, Default, lumen_macros::Delegate)]
7pub struct SwitchParams {
8    /// Zero-based input index to route to the output.
9    #[meta(name = "Selected layer", min = 0, step = 1)]
10    pub selected_layer: i64,
11}
12
13/// Selects one raster input according to a controlled layer index.
14#[derive(Debug, Clone, lumen_macros::Node)]
15#[node(kind = "switch", name = "Switch", category = "compositing")]
16pub struct Switch {
17    pub id: NodeId,
18    #[params]
19    pub params: SwitchParamsDelegate,
20
21    #[input(optional, variadic)]
22    pub layers: Vec<PortRef>,
23}
24
25impl Default for Switch {
26    fn default() -> Self {
27        Self {
28            id: NodeId::new(0),
29            params: SwitchParamsDelegate::default(),
30            layers: Vec::new(),
31        }
32    }
33}
34
35pub fn selected_layer_for_frame(
36    node: &Switch,
37    ctx: &crate::expr::ExpressionContext<'_>,
38) -> crate::Result<Option<usize>> {
39    let params = node.params.eval(&crate::node::NodeParamEvalContext {
40        node_id: node.id,
41        expr: ctx,
42    })?;
43    Ok((params.selected_layer >= 0).then_some(params.selected_layer as usize))
44}
45
46impl GpuCompileNode for Switch {
47    fn compile_gpu(
48        &self,
49        ctx: &mut crate::gpu::CompileContext<'_>,
50        port: &PortRef,
51    ) -> crate::Result<CompiledOutput> {
52        if port.port != "output" {
53            return Err(ctx.missing_output(self.id, &port.port));
54        }
55
56        let selected_layer =
57            selected_layer_for_frame(self, &ctx.expr_context(self.id, "selected_layer"))?;
58        ctx.register_compiled_node(CompiledSwitch {
59            node_id: self.id,
60            selected_layer,
61        });
62
63        let Some(index) = selected_layer else {
64            return Ok(ctx.compile_transparent(self.id));
65        };
66        let Some(layer) = self.layers.get(index) else {
67            return Ok(ctx.compile_transparent(self.id));
68        };
69        if layer.is_empty() {
70            return Ok(ctx.compile_transparent(self.id));
71        }
72        ctx.compile_port(layer)
73    }
74}
75
76#[derive(Debug, Clone)]
77struct CompiledSwitch {
78    node_id: NodeId,
79    selected_layer: Option<usize>,
80}
81
82impl GpuCompiledNode for CompiledSwitch {
83    fn node_id(&self) -> NodeId {
84        self.node_id
85    }
86
87    fn bind(&self, _ctx: &FrameBindContext<'_>, _bound: &mut BoundFrame) -> crate::Result<()> {
88        let _ = self.selected_layer;
89        Ok(())
90    }
91}