Skip to main content

lumen_engine/node/compositing/
switch.rs

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